ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/slirp/slirp.c
Revision: 1.1
Committed: 2005-05-13T09:00:59Z (19 years, 6 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Log Message:
slirp user mode network emulation code from qemu

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     void slirp_init(void)
127     {
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     if (get_dns_addr(&dns_addr) < 0) {
151     fprintf(stderr, "Could not get DNS address\n");
152     exit(1);
153     }
154    
155     inet_aton(CTL_SPECIAL, &special_addr);
156     }
157    
158     #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
159     #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
160     #define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
161    
162     /*
163     * curtime kept to an accuracy of 1ms
164     */
165     #ifdef _WIN32
166     static void updtime(void)
167     {
168     struct _timeb tb;
169    
170     _ftime(&tb);
171     curtime = (u_int)tb.time * (u_int)1000;
172     curtime += (u_int)tb.millitm;
173     }
174     #else
175     static void updtime(void)
176     {
177     gettimeofday(&tt, 0);
178    
179     curtime = (u_int)tt.tv_sec * (u_int)1000;
180     curtime += (u_int)tt.tv_usec / (u_int)1000;
181    
182     if ((tt.tv_usec % 1000) >= 500)
183     curtime++;
184     }
185     #endif
186    
187     void slirp_select_fill(int *pnfds,
188     fd_set *readfds, fd_set *writefds, fd_set *xfds)
189     {
190     struct socket *so, *so_next;
191     struct timeval timeout;
192     int nfds;
193     int tmp_time;
194    
195     /* fail safe */
196     global_readfds = NULL;
197     global_writefds = NULL;
198     global_xfds = NULL;
199    
200     nfds = *pnfds;
201     /*
202     * First, TCP sockets
203     */
204     do_slowtimo = 0;
205     if (link_up) {
206     /*
207     * *_slowtimo needs calling if there are IP fragments
208     * in the fragment queue, or there are TCP connections active
209     */
210     do_slowtimo = ((tcb.so_next != &tcb) ||
211     ((struct ipasfrag *)&ipq != (struct ipasfrag *)ipq.next));
212    
213     for (so = tcb.so_next; so != &tcb; so = so_next) {
214     so_next = so->so_next;
215    
216     /*
217     * See if we need a tcp_fasttimo
218     */
219     if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
220     time_fasttimo = curtime; /* Flag when we want a fasttimo */
221    
222     /*
223     * NOFDREF can include still connecting to local-host,
224     * newly socreated() sockets etc. Don't want to select these.
225     */
226     if (so->so_state & SS_NOFDREF || so->s == -1)
227     continue;
228    
229     /*
230     * Set for reading sockets which are accepting
231     */
232     if (so->so_state & SS_FACCEPTCONN) {
233     FD_SET(so->s, readfds);
234     UPD_NFDS(so->s);
235     continue;
236     }
237    
238     /*
239     * Set for writing sockets which are connecting
240     */
241     if (so->so_state & SS_ISFCONNECTING) {
242     FD_SET(so->s, writefds);
243     UPD_NFDS(so->s);
244     continue;
245     }
246    
247     /*
248     * Set for writing if we are connected, can send more, and
249     * we have something to send
250     */
251     if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
252     FD_SET(so->s, writefds);
253     UPD_NFDS(so->s);
254     }
255    
256     /*
257     * Set for reading (and urgent data) if we are connected, can
258     * receive more, and we have room for it XXX /2 ?
259     */
260     if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
261     FD_SET(so->s, readfds);
262     FD_SET(so->s, xfds);
263     UPD_NFDS(so->s);
264     }
265     }
266    
267     /*
268     * UDP sockets
269     */
270     for (so = udb.so_next; so != &udb; so = so_next) {
271     so_next = so->so_next;
272    
273     /*
274     * See if it's timed out
275     */
276     if (so->so_expire) {
277     if (so->so_expire <= curtime) {
278     udp_detach(so);
279     continue;
280     } else
281     do_slowtimo = 1; /* Let socket expire */
282     }
283    
284     /*
285     * When UDP packets are received from over the
286     * link, they're sendto()'d straight away, so
287     * no need for setting for writing
288     * Limit the number of packets queued by this session
289     * to 4. Note that even though we try and limit this
290     * to 4 packets, the session could have more queued
291     * if the packets needed to be fragmented
292     * (XXX <= 4 ?)
293     */
294     if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
295     FD_SET(so->s, readfds);
296     UPD_NFDS(so->s);
297     }
298     }
299     }
300    
301     /*
302     * Setup timeout to use minimum CPU usage, especially when idle
303     */
304    
305     /*
306     * First, see the timeout needed by *timo
307     */
308     timeout.tv_sec = 0;
309     timeout.tv_usec = -1;
310     /*
311     * If a slowtimo is needed, set timeout to 500ms from the last
312     * slow timeout. If a fast timeout is needed, set timeout within
313     * 200ms of when it was requested.
314     */
315     if (do_slowtimo) {
316     /* XXX + 10000 because some select()'s aren't that accurate */
317     timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
318     if (timeout.tv_usec < 0)
319     timeout.tv_usec = 0;
320     else if (timeout.tv_usec > 510000)
321     timeout.tv_usec = 510000;
322    
323     /* Can only fasttimo if we also slowtimo */
324     if (time_fasttimo) {
325     tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
326     if (tmp_time < 0)
327     tmp_time = 0;
328    
329     /* Choose the smallest of the 2 */
330     if (tmp_time < timeout.tv_usec)
331     timeout.tv_usec = (u_int)tmp_time;
332     }
333     }
334     *pnfds = nfds;
335     }
336    
337     void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
338     {
339     struct socket *so, *so_next;
340     int ret;
341    
342     global_readfds = readfds;
343     global_writefds = writefds;
344     global_xfds = xfds;
345    
346     /* Update time */
347     updtime();
348    
349     /*
350     * See if anything has timed out
351     */
352     if (link_up) {
353     if (time_fasttimo && ((curtime - time_fasttimo) >= 199)) {
354     tcp_fasttimo();
355     time_fasttimo = 0;
356     }
357     if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
358     ip_slowtimo();
359     tcp_slowtimo();
360     last_slowtimo = curtime;
361     }
362     }
363    
364     /*
365     * Check sockets
366     */
367     if (link_up) {
368     /*
369     * Check TCP sockets
370     */
371     for (so = tcb.so_next; so != &tcb; so = so_next) {
372     so_next = so->so_next;
373    
374     /*
375     * FD_ISSET is meaningless on these sockets
376     * (and they can crash the program)
377     */
378     if (so->so_state & SS_NOFDREF || so->s == -1)
379     continue;
380    
381     /*
382     * Check for URG data
383     * This will soread as well, so no need to
384     * test for readfds below if this succeeds
385     */
386     if (FD_ISSET(so->s, xfds))
387     sorecvoob(so);
388     /*
389     * Check sockets for reading
390     */
391     else if (FD_ISSET(so->s, readfds)) {
392     /*
393     * Check for incoming connections
394     */
395     if (so->so_state & SS_FACCEPTCONN) {
396     tcp_connect(so);
397     continue;
398     } /* else */
399     ret = soread(so);
400    
401     /* Output it if we read something */
402     if (ret > 0)
403     tcp_output(sototcpcb(so));
404     }
405    
406     /*
407     * Check sockets for writing
408     */
409     if (FD_ISSET(so->s, writefds)) {
410     /*
411     * Check for non-blocking, still-connecting sockets
412     */
413     if (so->so_state & SS_ISFCONNECTING) {
414     /* Connected */
415     so->so_state &= ~SS_ISFCONNECTING;
416    
417     ret = send(so->s, &ret, 0, 0);
418     if (ret < 0) {
419     /* XXXXX Must fix, zero bytes is a NOP */
420     if (errno == EAGAIN || errno == EWOULDBLOCK ||
421     errno == EINPROGRESS || errno == ENOTCONN)
422     continue;
423    
424     /* else failed */
425     so->so_state = SS_NOFDREF;
426     }
427     /* else so->so_state &= ~SS_ISFCONNECTING; */
428    
429     /*
430     * Continue tcp_input
431     */
432     tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
433     /* continue; */
434     } else
435     ret = sowrite(so);
436     /*
437     * XXXXX If we wrote something (a lot), there
438     * could be a need for a window update.
439     * In the worst case, the remote will send
440     * a window probe to get things going again
441     */
442     }
443    
444     /*
445     * Probe a still-connecting, non-blocking socket
446     * to check if it's still alive
447     */
448     #ifdef PROBE_CONN
449     if (so->so_state & SS_ISFCONNECTING) {
450     ret = recv(so->s, (char *)&ret, 0,0);
451    
452     if (ret < 0) {
453     /* XXX */
454     if (errno == EAGAIN || errno == EWOULDBLOCK ||
455     errno == EINPROGRESS || errno == ENOTCONN)
456     continue; /* Still connecting, continue */
457    
458     /* else failed */
459     so->so_state = SS_NOFDREF;
460    
461     /* tcp_input will take care of it */
462     } else {
463     ret = send(so->s, &ret, 0,0);
464     if (ret < 0) {
465     /* XXX */
466     if (errno == EAGAIN || errno == EWOULDBLOCK ||
467     errno == EINPROGRESS || errno == ENOTCONN)
468     continue;
469     /* else failed */
470     so->so_state = SS_NOFDREF;
471     } else
472     so->so_state &= ~SS_ISFCONNECTING;
473    
474     }
475     tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
476     } /* SS_ISFCONNECTING */
477     #endif
478     }
479    
480     /*
481     * Now UDP sockets.
482     * Incoming packets are sent straight away, they're not buffered.
483     * Incoming UDP data isn't buffered either.
484     */
485     for (so = udb.so_next; so != &udb; so = so_next) {
486     so_next = so->so_next;
487    
488     if (so->s != -1 && FD_ISSET(so->s, readfds)) {
489     sorecvfrom(so);
490     }
491     }
492     }
493    
494     /*
495     * See if we can start outputting
496     */
497     if (if_queued && link_up)
498     if_start();
499    
500     /* clear global file descriptor sets.
501     * these reside on the stack in vl.c
502     * so they're unusable if we're not in
503     * slirp_select_fill or slirp_select_poll.
504     */
505     global_readfds = NULL;
506     global_writefds = NULL;
507     global_xfds = NULL;
508     }
509    
510     #define ETH_ALEN 6
511     #define ETH_HLEN 14
512    
513     #define ETH_P_IP 0x0800 /* Internet Protocol packet */
514     #define ETH_P_ARP 0x0806 /* Address Resolution packet */
515    
516     #define ARPOP_REQUEST 1 /* ARP request */
517     #define ARPOP_REPLY 2 /* ARP reply */
518    
519     struct ethhdr
520     {
521     unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
522     unsigned char h_source[ETH_ALEN]; /* source ether addr */
523     unsigned short h_proto; /* packet type ID field */
524     };
525    
526     struct arphdr
527     {
528     unsigned short ar_hrd; /* format of hardware address */
529     unsigned short ar_pro; /* format of protocol address */
530     unsigned char ar_hln; /* length of hardware address */
531     unsigned char ar_pln; /* length of protocol address */
532     unsigned short ar_op; /* ARP opcode (command) */
533    
534     /*
535     * Ethernet looks like this : This bit is variable sized however...
536     */
537     unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
538     unsigned char ar_sip[4]; /* sender IP address */
539     unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
540     unsigned char ar_tip[4]; /* target IP address */
541     };
542    
543     void arp_input(const uint8_t *pkt, int pkt_len)
544     {
545     struct ethhdr *eh = (struct ethhdr *)pkt;
546     struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
547     uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
548     struct ethhdr *reh = (struct ethhdr *)arp_reply;
549     struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
550     int ar_op;
551     struct ex_list *ex_ptr;
552    
553     ar_op = ntohs(ah->ar_op);
554     switch(ar_op) {
555     case ARPOP_REQUEST:
556     if (!memcmp(ah->ar_tip, &special_addr, 3)) {
557     if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS)
558     goto arp_ok;
559     for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
560     if (ex_ptr->ex_addr == ah->ar_tip[3])
561     goto arp_ok;
562     }
563     return;
564     arp_ok:
565     /* XXX: make an ARP request to have the client address */
566     memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
567    
568     /* ARP request for alias/dns mac address */
569     memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
570     memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
571     reh->h_source[5] = ah->ar_tip[3];
572     reh->h_proto = htons(ETH_P_ARP);
573    
574     rah->ar_hrd = htons(1);
575     rah->ar_pro = htons(ETH_P_IP);
576     rah->ar_hln = ETH_ALEN;
577     rah->ar_pln = 4;
578     rah->ar_op = htons(ARPOP_REPLY);
579     memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
580     memcpy(rah->ar_sip, ah->ar_tip, 4);
581     memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
582     memcpy(rah->ar_tip, ah->ar_sip, 4);
583     slirp_output(arp_reply, sizeof(arp_reply));
584     }
585     break;
586     default:
587     break;
588     }
589     }
590    
591     void slirp_input(const uint8_t *pkt, int pkt_len)
592     {
593     struct mbuf *m;
594     int proto;
595    
596     if (pkt_len < ETH_HLEN)
597     return;
598    
599     proto = ntohs(*(uint16_t *)(pkt + 12));
600     switch(proto) {
601     case ETH_P_ARP:
602     arp_input(pkt, pkt_len);
603     break;
604     case ETH_P_IP:
605     m = m_get();
606     if (!m)
607     return;
608     m->m_len = pkt_len;
609     memcpy(m->m_data, pkt, pkt_len);
610    
611     m->m_data += ETH_HLEN;
612     m->m_len -= ETH_HLEN;
613    
614     ip_input(m);
615     break;
616     default:
617     break;
618     }
619     }
620    
621     /* output the IP packet to the ethernet device */
622     void if_encap(const uint8_t *ip_data, int ip_data_len)
623     {
624     uint8_t buf[1600];
625     struct ethhdr *eh = (struct ethhdr *)buf;
626    
627     if (ip_data_len + ETH_HLEN > sizeof(buf))
628     return;
629    
630     memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
631     memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
632     /* XXX: not correct */
633     eh->h_source[5] = CTL_ALIAS;
634     eh->h_proto = htons(ETH_P_IP);
635     memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
636     slirp_output(buf, ip_data_len + ETH_HLEN);
637     }
638    
639     int slirp_redir(int is_udp, int host_port,
640     struct in_addr guest_addr, int guest_port)
641     {
642     if (is_udp) {
643     if (!udp_listen(htons(host_port), guest_addr.s_addr,
644     htons(guest_port), 0))
645     return -1;
646     } else {
647     if (!solisten(htons(host_port), guest_addr.s_addr,
648     htons(guest_port), 0))
649     return -1;
650     }
651     return 0;
652     }
653    
654     int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
655     int guest_port)
656     {
657     return add_exec(&exec_list, do_pty, (char *)args,
658     addr_low_byte, htons(guest_port));
659     }