ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/slirp/slirp.c
Revision: 1.8
Committed: 2007-11-03T11:11:42Z (16 years, 7 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +6 -1 lines
Log Message:
Update to slirp sources from QEMU 0.8.2:
- set slirp client hostname
- fix slirp redirection on systems without a useful host IP address
- separate alias_addr (10.0.2.2) from our_addr (Ed Swierk)
- fix 32+ KB packets handling (Ed Swierk)
- fix UDP broadcast translation error
- solaris port (Ben Taylor)

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