ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/slirp/slirp.c
Revision: 1.6
Committed: 2006-04-26T06:26:14Z (18 years, 1 month ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.5: +2 -2 lines
Log Message:
FAST_TIMO is 2 ms not 2 usec. Noticed on Windows but this obviously can't
fix my problem with slirp_select_fill() there as it still doesn't fill in
any socket descriptor. :-/ I think I won't bother much for Windows...

File Contents

# User Rev Content
1 gbeauche 1.1 #include "slirp.h"
2    
3     /* host address */
4     struct in_addr our_addr;
5     /* host dns address */
6     struct in_addr dns_addr;
7     /* host loopback address */
8     struct in_addr loopback_addr;
9    
10     /* address for slirp virtual addresses */
11     struct in_addr special_addr;
12    
13     const uint8_t special_ethaddr[6] = {
14     0x52, 0x54, 0x00, 0x12, 0x35, 0x00
15     };
16    
17     uint8_t client_ethaddr[6];
18    
19     int do_slowtimo;
20     int link_up;
21     struct timeval tt;
22     FILE *lfd;
23     struct ex_list *exec_list;
24    
25     /* XXX: suppress those select globals */
26     fd_set *global_readfds, *global_writefds, *global_xfds;
27    
28     #ifdef _WIN32
29    
30     static int get_dns_addr(struct in_addr *pdns_addr)
31     {
32     FIXED_INFO *FixedInfo=NULL;
33     ULONG BufLen;
34     DWORD ret;
35     IP_ADDR_STRING *pIPAddr;
36     struct in_addr tmp_addr;
37    
38     FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
39     BufLen = sizeof(FIXED_INFO);
40    
41     if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
42     if (FixedInfo) {
43     GlobalFree(FixedInfo);
44     FixedInfo = NULL;
45     }
46     FixedInfo = GlobalAlloc(GPTR, BufLen);
47     }
48    
49     if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
50     printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
51     if (FixedInfo) {
52     GlobalFree(FixedInfo);
53     FixedInfo = NULL;
54     }
55     return -1;
56     }
57    
58     pIPAddr = &(FixedInfo->DnsServerList);
59     inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
60     *pdns_addr = tmp_addr;
61     #if 0
62     printf( "DNS Servers:\n" );
63     printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String );
64    
65     pIPAddr = FixedInfo -> DnsServerList.Next;
66     while ( pIPAddr ) {
67     printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String );
68     pIPAddr = pIPAddr ->Next;
69     }
70     #endif
71     if (FixedInfo) {
72     GlobalFree(FixedInfo);
73     FixedInfo = NULL;
74     }
75     return 0;
76     }
77    
78     #else
79    
80     static int get_dns_addr(struct in_addr *pdns_addr)
81     {
82     char buff[512];
83     char buff2[256];
84     FILE *f;
85     int found = 0;
86     struct in_addr tmp_addr;
87    
88     f = fopen("/etc/resolv.conf", "r");
89     if (!f)
90     return -1;
91    
92     lprint("IP address of your DNS(s): ");
93     while (fgets(buff, 512, f) != NULL) {
94     if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
95     if (!inet_aton(buff2, &tmp_addr))
96     continue;
97     if (tmp_addr.s_addr == loopback_addr.s_addr)
98     tmp_addr = our_addr;
99     /* If it's the first one, set it to dns_addr */
100     if (!found)
101     *pdns_addr = tmp_addr;
102     else
103     lprint(", ");
104     if (++found > 3) {
105     lprint("(more)");
106     break;
107     } else
108     lprint("%s", inet_ntoa(tmp_addr));
109     }
110     }
111     fclose(f);
112     if (!found)
113     return -1;
114     return 0;
115     }
116    
117     #endif
118    
119     #ifdef _WIN32
120     void slirp_cleanup(void)
121     {
122     WSACleanup();
123     }
124     #endif
125    
126 gbeauche 1.2 int slirp_init(void)
127 gbeauche 1.1 {
128     // debug_init("/tmp/slirp.log", DEBUG_DEFAULT);
129    
130     #ifdef _WIN32
131     {
132     WSADATA Data;
133     WSAStartup(MAKEWORD(2,0), &Data);
134     atexit(slirp_cleanup);
135     }
136     #endif
137    
138     link_up = 1;
139    
140     if_init();
141     ip_init();
142    
143     /* Initialise mbufs *after* setting the MTU */
144     m_init();
145    
146     /* set default addresses */
147     getouraddr();
148     inet_aton("127.0.0.1", &loopback_addr);
149    
150 gbeauche 1.2 if (get_dns_addr(&dns_addr) < 0)
151     return -1;
152 gbeauche 1.1
153     inet_aton(CTL_SPECIAL, &special_addr);
154 gbeauche 1.2 return 0;
155 gbeauche 1.1 }
156    
157     #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
158     #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
159     #define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
160    
161     /*
162     * curtime kept to an accuracy of 1ms
163     */
164     #ifdef _WIN32
165     static void updtime(void)
166     {
167     struct _timeb tb;
168    
169     _ftime(&tb);
170     curtime = (u_int)tb.time * (u_int)1000;
171     curtime += (u_int)tb.millitm;
172     }
173     #else
174     static void updtime(void)
175     {
176     gettimeofday(&tt, 0);
177    
178     curtime = (u_int)tt.tv_sec * (u_int)1000;
179     curtime += (u_int)tt.tv_usec / (u_int)1000;
180    
181     if ((tt.tv_usec % 1000) >= 500)
182     curtime++;
183     }
184     #endif
185    
186 gbeauche 1.5 int slirp_select_fill(int *pnfds,
187     fd_set *readfds, fd_set *writefds, fd_set *xfds)
188 gbeauche 1.1 {
189     struct socket *so, *so_next;
190     int nfds;
191 gbeauche 1.5 int timeout, tmp_time;
192 gbeauche 1.1
193     /* fail safe */
194     global_readfds = NULL;
195     global_writefds = NULL;
196     global_xfds = NULL;
197    
198     nfds = *pnfds;
199     /*
200     * First, TCP sockets
201     */
202     do_slowtimo = 0;
203     if (link_up) {
204     /*
205     * *_slowtimo needs calling if there are IP fragments
206     * in the fragment queue, or there are TCP connections active
207     */
208     do_slowtimo = ((tcb.so_next != &tcb) ||
209     ((struct ipasfrag *)&ipq != (struct ipasfrag *)ipq.next));
210    
211     for (so = tcb.so_next; so != &tcb; so = so_next) {
212     so_next = so->so_next;
213    
214     /*
215     * See if we need a tcp_fasttimo
216     */
217     if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
218     time_fasttimo = curtime; /* Flag when we want a fasttimo */
219    
220     /*
221     * NOFDREF can include still connecting to local-host,
222     * newly socreated() sockets etc. Don't want to select these.
223     */
224     if (so->so_state & SS_NOFDREF || so->s == -1)
225     continue;
226    
227     /*
228     * Set for reading sockets which are accepting
229     */
230     if (so->so_state & SS_FACCEPTCONN) {
231     FD_SET(so->s, readfds);
232     UPD_NFDS(so->s);
233     continue;
234     }
235    
236     /*
237     * Set for writing sockets which are connecting
238     */
239     if (so->so_state & SS_ISFCONNECTING) {
240     FD_SET(so->s, writefds);
241     UPD_NFDS(so->s);
242     continue;
243     }
244    
245     /*
246     * Set for writing if we are connected, can send more, and
247     * we have something to send
248     */
249     if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
250     FD_SET(so->s, writefds);
251     UPD_NFDS(so->s);
252     }
253    
254     /*
255     * Set for reading (and urgent data) if we are connected, can
256     * receive more, and we have room for it XXX /2 ?
257     */
258     if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
259     FD_SET(so->s, readfds);
260     FD_SET(so->s, xfds);
261     UPD_NFDS(so->s);
262     }
263     }
264    
265     /*
266     * UDP sockets
267     */
268     for (so = udb.so_next; so != &udb; so = so_next) {
269     so_next = so->so_next;
270    
271     /*
272     * See if it's timed out
273     */
274     if (so->so_expire) {
275     if (so->so_expire <= curtime) {
276     udp_detach(so);
277     continue;
278     } else
279     do_slowtimo = 1; /* Let socket expire */
280     }
281    
282     /*
283     * When UDP packets are received from over the
284     * link, they're sendto()'d straight away, so
285     * no need for setting for writing
286     * Limit the number of packets queued by this session
287     * to 4. Note that even though we try and limit this
288     * to 4 packets, the session could have more queued
289     * if the packets needed to be fragmented
290     * (XXX <= 4 ?)
291     */
292     if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
293     FD_SET(so->s, readfds);
294     UPD_NFDS(so->s);
295     }
296     }
297     }
298    
299     /*
300     * Setup timeout to use minimum CPU usage, especially when idle
301     */
302 gbeauche 1.5
303     timeout = -1;
304    
305 gbeauche 1.1 /*
306 gbeauche 1.5 * If a slowtimo is needed, set timeout to 5ms from the last
307 gbeauche 1.1 * slow timeout. If a fast timeout is needed, set timeout within
308 gbeauche 1.5 * 2ms of when it was requested.
309 gbeauche 1.1 */
310 gbeauche 1.5 # define SLOW_TIMO 5
311     # define FAST_TIMO 2
312 gbeauche 1.1 if (do_slowtimo) {
313 gbeauche 1.5 timeout = (SLOW_TIMO - (curtime - last_slowtimo)) * 1000;
314     if (timeout < 0)
315     timeout = 0;
316     else if (timeout > (SLOW_TIMO * 1000))
317     timeout = SLOW_TIMO * 1000;
318 gbeauche 1.1
319     /* Can only fasttimo if we also slowtimo */
320     if (time_fasttimo) {
321 gbeauche 1.5 tmp_time = (FAST_TIMO - (curtime - time_fasttimo)) * 1000;
322 gbeauche 1.1 if (tmp_time < 0)
323 gbeauche 1.5 tmp_time = 0;
324 gbeauche 1.1
325     /* Choose the smallest of the 2 */
326 gbeauche 1.5 if (tmp_time < timeout)
327     timeout = tmp_time;
328 gbeauche 1.1 }
329     }
330 gbeauche 1.5 *pnfds = nfds;
331    
332     /*
333     * Adjust the timeout to make the minimum timeout
334     * 2ms (XXX?) to lessen the CPU load
335     */
336 gbeauche 1.6 if (timeout < (FAST_TIMO * 1000))
337     timeout = FAST_TIMO * 1000;
338 gbeauche 1.5
339     return timeout;
340 gbeauche 1.1 }
341    
342     void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
343     {
344     struct socket *so, *so_next;
345     int ret;
346    
347     global_readfds = readfds;
348     global_writefds = writefds;
349     global_xfds = xfds;
350    
351     /* Update time */
352     updtime();
353    
354     /*
355     * See if anything has timed out
356     */
357     if (link_up) {
358 gbeauche 1.5 if (time_fasttimo && ((curtime - time_fasttimo) >= FAST_TIMO)) {
359 gbeauche 1.1 tcp_fasttimo();
360     time_fasttimo = 0;
361     }
362 gbeauche 1.5 if (do_slowtimo && ((curtime - last_slowtimo) >= SLOW_TIMO)) {
363 gbeauche 1.1 ip_slowtimo();
364     tcp_slowtimo();
365     last_slowtimo = curtime;
366     }
367     }
368    
369     /*
370     * Check sockets
371     */
372     if (link_up) {
373     /*
374     * Check TCP sockets
375     */
376     for (so = tcb.so_next; so != &tcb; so = so_next) {
377     so_next = so->so_next;
378    
379     /*
380     * FD_ISSET is meaningless on these sockets
381     * (and they can crash the program)
382     */
383     if (so->so_state & SS_NOFDREF || so->s == -1)
384     continue;
385    
386     /*
387     * Check for URG data
388     * This will soread as well, so no need to
389     * test for readfds below if this succeeds
390     */
391     if (FD_ISSET(so->s, xfds))
392     sorecvoob(so);
393     /*
394     * Check sockets for reading
395     */
396     else if (FD_ISSET(so->s, readfds)) {
397     /*
398     * Check for incoming connections
399     */
400     if (so->so_state & SS_FACCEPTCONN) {
401     tcp_connect(so);
402     continue;
403     } /* else */
404     ret = soread(so);
405    
406     /* Output it if we read something */
407     if (ret > 0)
408     tcp_output(sototcpcb(so));
409     }
410    
411     /*
412     * Check sockets for writing
413     */
414     if (FD_ISSET(so->s, writefds)) {
415     /*
416     * Check for non-blocking, still-connecting sockets
417     */
418     if (so->so_state & SS_ISFCONNECTING) {
419     /* Connected */
420     so->so_state &= ~SS_ISFCONNECTING;
421    
422     ret = send(so->s, &ret, 0, 0);
423     if (ret < 0) {
424     /* XXXXX Must fix, zero bytes is a NOP */
425     if (errno == EAGAIN || errno == EWOULDBLOCK ||
426     errno == EINPROGRESS || errno == ENOTCONN)
427     continue;
428    
429     /* else failed */
430     so->so_state = SS_NOFDREF;
431     }
432     /* else so->so_state &= ~SS_ISFCONNECTING; */
433    
434     /*
435     * Continue tcp_input
436     */
437     tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
438     /* continue; */
439     } else
440     ret = sowrite(so);
441     /*
442     * XXXXX If we wrote something (a lot), there
443     * could be a need for a window update.
444     * In the worst case, the remote will send
445     * a window probe to get things going again
446     */
447     }
448    
449     /*
450     * Probe a still-connecting, non-blocking socket
451     * to check if it's still alive
452     */
453     #ifdef PROBE_CONN
454     if (so->so_state & SS_ISFCONNECTING) {
455     ret = recv(so->s, (char *)&ret, 0,0);
456    
457     if (ret < 0) {
458     /* XXX */
459     if (errno == EAGAIN || errno == EWOULDBLOCK ||
460     errno == EINPROGRESS || errno == ENOTCONN)
461     continue; /* Still connecting, continue */
462    
463     /* else failed */
464     so->so_state = SS_NOFDREF;
465    
466     /* tcp_input will take care of it */
467     } else {
468     ret = send(so->s, &ret, 0,0);
469     if (ret < 0) {
470     /* XXX */
471     if (errno == EAGAIN || errno == EWOULDBLOCK ||
472     errno == EINPROGRESS || errno == ENOTCONN)
473     continue;
474     /* else failed */
475     so->so_state = SS_NOFDREF;
476     } else
477     so->so_state &= ~SS_ISFCONNECTING;
478    
479     }
480     tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
481     } /* SS_ISFCONNECTING */
482     #endif
483     }
484    
485     /*
486     * Now UDP sockets.
487     * Incoming packets are sent straight away, they're not buffered.
488     * Incoming UDP data isn't buffered either.
489     */
490     for (so = udb.so_next; so != &udb; so = so_next) {
491     so_next = so->so_next;
492    
493     if (so->s != -1 && FD_ISSET(so->s, readfds)) {
494     sorecvfrom(so);
495     }
496     }
497     }
498    
499     /*
500     * See if we can start outputting
501     */
502     if (if_queued && link_up)
503     if_start();
504    
505     /* clear global file descriptor sets.
506     * these reside on the stack in vl.c
507     * so they're unusable if we're not in
508     * slirp_select_fill or slirp_select_poll.
509     */
510     global_readfds = NULL;
511     global_writefds = NULL;
512     global_xfds = NULL;
513     }
514    
515     #define ETH_ALEN 6
516     #define ETH_HLEN 14
517    
518     #define ETH_P_IP 0x0800 /* Internet Protocol packet */
519     #define ETH_P_ARP 0x0806 /* Address Resolution packet */
520    
521     #define ARPOP_REQUEST 1 /* ARP request */
522     #define ARPOP_REPLY 2 /* ARP reply */
523    
524     struct ethhdr
525     {
526     unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
527     unsigned char h_source[ETH_ALEN]; /* source ether addr */
528     unsigned short h_proto; /* packet type ID field */
529     };
530    
531     struct arphdr
532     {
533     unsigned short ar_hrd; /* format of hardware address */
534     unsigned short ar_pro; /* format of protocol address */
535     unsigned char ar_hln; /* length of hardware address */
536     unsigned char ar_pln; /* length of protocol address */
537     unsigned short ar_op; /* ARP opcode (command) */
538    
539     /*
540     * Ethernet looks like this : This bit is variable sized however...
541     */
542     unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
543     unsigned char ar_sip[4]; /* sender IP address */
544     unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
545     unsigned char ar_tip[4]; /* target IP address */
546     };
547    
548     void arp_input(const uint8_t *pkt, int pkt_len)
549     {
550     struct ethhdr *eh = (struct ethhdr *)pkt;
551     struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
552     uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
553     struct ethhdr *reh = (struct ethhdr *)arp_reply;
554     struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
555     int ar_op;
556     struct ex_list *ex_ptr;
557    
558     ar_op = ntohs(ah->ar_op);
559     switch(ar_op) {
560     case ARPOP_REQUEST:
561     if (!memcmp(ah->ar_tip, &special_addr, 3)) {
562     if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS)
563     goto arp_ok;
564     for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
565     if (ex_ptr->ex_addr == ah->ar_tip[3])
566     goto arp_ok;
567     }
568     return;
569     arp_ok:
570     /* XXX: make an ARP request to have the client address */
571     memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
572    
573     /* ARP request for alias/dns mac address */
574     memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
575     memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
576     reh->h_source[5] = ah->ar_tip[3];
577     reh->h_proto = htons(ETH_P_ARP);
578    
579     rah->ar_hrd = htons(1);
580     rah->ar_pro = htons(ETH_P_IP);
581     rah->ar_hln = ETH_ALEN;
582     rah->ar_pln = 4;
583     rah->ar_op = htons(ARPOP_REPLY);
584     memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
585     memcpy(rah->ar_sip, ah->ar_tip, 4);
586     memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
587     memcpy(rah->ar_tip, ah->ar_sip, 4);
588     slirp_output(arp_reply, sizeof(arp_reply));
589     }
590     break;
591     default:
592     break;
593     }
594     }
595    
596     void slirp_input(const uint8_t *pkt, int pkt_len)
597     {
598     struct mbuf *m;
599     int proto;
600    
601     if (pkt_len < ETH_HLEN)
602     return;
603    
604 gbeauche 1.3 proto = (pkt[12] << 8) | pkt[13];
605 gbeauche 1.1 switch(proto) {
606     case ETH_P_ARP:
607     arp_input(pkt, pkt_len);
608     break;
609     case ETH_P_IP:
610     m = m_get();
611     if (!m)
612     return;
613     m->m_len = pkt_len;
614     memcpy(m->m_data, pkt, pkt_len);
615    
616     m->m_data += ETH_HLEN;
617     m->m_len -= ETH_HLEN;
618    
619     ip_input(m);
620     break;
621     default:
622     break;
623     }
624     }
625    
626     /* output the IP packet to the ethernet device */
627     void if_encap(const uint8_t *ip_data, int ip_data_len)
628     {
629     uint8_t buf[1600];
630     struct ethhdr *eh = (struct ethhdr *)buf;
631    
632     if (ip_data_len + ETH_HLEN > sizeof(buf))
633     return;
634    
635     memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
636     memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
637     /* XXX: not correct */
638     eh->h_source[5] = CTL_ALIAS;
639     eh->h_proto = htons(ETH_P_IP);
640     memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
641     slirp_output(buf, ip_data_len + ETH_HLEN);
642     }
643    
644     int slirp_redir(int is_udp, int host_port,
645     struct in_addr guest_addr, int guest_port)
646     {
647     if (is_udp) {
648     if (!udp_listen(htons(host_port), guest_addr.s_addr,
649     htons(guest_port), 0))
650     return -1;
651     } else {
652     if (!solisten(htons(host_port), guest_addr.s_addr,
653     htons(guest_port), 0))
654     return -1;
655     }
656     return 0;
657     }
658    
659     int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
660     int guest_port)
661     {
662     return add_exec(&exec_list, do_pty, (char *)args,
663     addr_low_byte, htons(guest_port));
664     }