ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.15
Committed: 2005-05-13T09:21:21Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.14: +145 -16 lines
Log Message:
Recognize Intel Compilers. Add user mode network stack with slirp from qemu.
Simply use "ether slirp" in the prefs file, no kernel module required. I
could perform up to 450 KB/sec on my DSL line.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3     *
4 gbeauche 1.13 * Basilisk II (C) 1997-2005 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22    
23     #include <sys/ioctl.h>
24     #include <sys/poll.h>
25 cebix 1.3 #include <sys/socket.h>
26 gbeauche 1.9 #include <sys/wait.h>
27 cebix 1.3 #include <netinet/in.h>
28 cebix 1.1 #include <pthread.h>
29     #include <semaphore.h>
30     #include <errno.h>
31     #include <stdio.h>
32 cebix 1.3 #include <map>
33 cebix 1.2
34 cebix 1.5 #if defined(__FreeBSD__) || defined(sgi)
35 cebix 1.1 #include <net/if.h>
36     #endif
37    
38 gbeauche 1.9 #if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
39     #include <linux/if.h>
40     #include <linux/if_tun.h>
41     #endif
42    
43     #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H)
44     #include <net/if.h>
45     #include <net/if_tun.h>
46     #endif
47    
48 gbeauche 1.15 // XXX: slirp is currently not 64-bit clean
49     #if SIZEOF_VOID_P == 4
50     #define HAVE_SLIRP 1
51     #endif
52    
53 cebix 1.1 #include "cpu_emulation.h"
54     #include "main.h"
55     #include "macos_util.h"
56     #include "prefs.h"
57     #include "user_strings.h"
58     #include "ether.h"
59     #include "ether_defs.h"
60 gbeauche 1.15 #include "libslirp.h"
61 cebix 1.1
62 cebix 1.3 #ifndef NO_STD_NAMESPACE
63     using std::map;
64     #endif
65    
66 cebix 1.1 #define DEBUG 0
67     #include "debug.h"
68    
69     #define MONITOR 0
70    
71    
72 gbeauche 1.9 // Ethernet device types
73     enum {
74     NET_IF_SHEEPNET,
75     NET_IF_ETHERTAP,
76     NET_IF_TUNTAP,
77 gbeauche 1.15 NET_IF_SLIRP
78 gbeauche 1.9 };
79    
80     // Constants
81     static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig";
82    
83 cebix 1.1 // Global variables
84     static int fd = -1; // fd of sheep_net device
85     static pthread_t ether_thread; // Packet reception thread
86     static pthread_attr_t ether_thread_attr; // Packet reception thread attributes
87     static bool thread_active = false; // Flag: Packet reception thread installed
88     static sem_t int_ack; // Interrupt acknowledge semaphore
89 cebix 1.2 static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor
90 gbeauche 1.9 static int net_if_type = -1; // Ethernet device type
91 gbeauche 1.10 static char *net_if_name = NULL; // TUN/TAP device name
92 gbeauche 1.9 static const char *net_if_script = NULL; // Network config script
93 gbeauche 1.15 static pthread_t slirp_thread; // Slirp reception thread
94     static bool slirp_thread_active = false; // Flag: Slirp reception threadinstalled
95     static int slirp_output_fd = -1; // fd of slirp output pipe
96 cebix 1.1
97 cebix 1.3 // Attached network protocols, maps protocol type to MacOS handler address
98     static map<uint16, uint32> net_protocols;
99    
100 cebix 1.1 // Prototypes
101     static void *receive_func(void *arg);
102 gbeauche 1.15 static void *slirp_receive_func(void *arg);
103 cebix 1.1
104    
105     /*
106 cebix 1.2 * Start packet reception thread
107     */
108    
109     static bool start_thread(void)
110     {
111     if (sem_init(&int_ack, 0, 0) < 0) {
112     printf("WARNING: Cannot init semaphore");
113     return false;
114     }
115    
116 cebix 1.7 Set_pthread_attr(&ether_thread_attr, 1);
117 cebix 1.2 thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
118     if (!thread_active) {
119     printf("WARNING: Cannot start Ethernet thread");
120     return false;
121     }
122    
123 gbeauche 1.15 #ifdef HAVE_SLIRP
124     if (net_if_type == NET_IF_SLIRP) {
125     slirp_thread_active = (pthread_create(&slirp_thread, NULL, slirp_receive_func, NULL) == 0);
126     if (!slirp_thread_active) {
127     printf("WARNING: Cannot start slirp reception thread\n");
128     return false;
129     }
130     }
131     #endif
132    
133 cebix 1.2 return true;
134     }
135    
136    
137     /*
138     * Stop packet reception thread
139     */
140    
141     static void stop_thread(void)
142     {
143 gbeauche 1.15 #ifdef HAVE_SLIRP
144     if (slirp_thread_active) {
145     pthread_cancel(slirp_thread);
146     pthread_join(slirp_thread, NULL);
147     slirp_thread_active = false;
148     }
149     #endif
150    
151 cebix 1.2 if (thread_active) {
152     pthread_cancel(ether_thread);
153     pthread_join(ether_thread, NULL);
154     sem_destroy(&int_ack);
155     thread_active = false;
156     }
157     }
158    
159    
160     /*
161 gbeauche 1.9 * Execute network script up|down
162     */
163    
164     static bool execute_network_script(const char *action)
165     {
166     if (net_if_script == NULL || net_if_name == NULL)
167     return false;
168    
169     int pid = fork();
170     if (pid >= 0) {
171     if (pid == 0) {
172     char *args[4];
173     args[0] = (char *)net_if_script;
174 gbeauche 1.10 args[1] = net_if_name;
175 gbeauche 1.9 args[2] = (char *)action;
176     args[3] = NULL;
177     execv(net_if_script, args);
178     exit(1);
179     }
180     int status;
181     while (waitpid(pid, &status, 0) != pid);
182     return WIFEXITED(status) && WEXITSTATUS(status) == 0;
183     }
184    
185     return false;
186     }
187    
188    
189     /*
190 cebix 1.1 * Initialization
191     */
192    
193 cebix 1.2 bool ether_init(void)
194 cebix 1.1 {
195     int nonblock = 1;
196     char str[256];
197    
198     // Do nothing if no Ethernet device specified
199     const char *name = PrefsFindString("ether");
200     if (name == NULL)
201 cebix 1.2 return false;
202 cebix 1.1
203 gbeauche 1.9 // Determine Ethernet device type
204     net_if_type = -1;
205     if (strncmp(name, "tap", 3) == 0)
206     net_if_type = NET_IF_ETHERTAP;
207     #if ENABLE_TUNTAP
208     else if (strcmp(name, "tun") == 0)
209     net_if_type = NET_IF_TUNTAP;
210     #endif
211 gbeauche 1.15 #ifdef HAVE_SLIRP
212     else if (strcmp(name, "slirp") == 0)
213     net_if_type = NET_IF_SLIRP;
214     #endif
215 gbeauche 1.9 else
216     net_if_type = NET_IF_SHEEPNET;
217 cebix 1.1
218 gbeauche 1.15 #ifdef HAVE_SLIRP
219     // Initialize slirp library
220     if (net_if_type == NET_IF_SLIRP) {
221     slirp_init();
222    
223     // Open slirp output pipe
224     int fds[2];
225     if (pipe(fds) < 0)
226     return false;
227     fd = fds[0];
228     slirp_output_fd = fds[1];
229     }
230     #endif
231    
232 gbeauche 1.11 // Open sheep_net or ethertap or TUN/TAP device
233 cebix 1.1 char dev_name[16];
234 gbeauche 1.9 switch (net_if_type) {
235     case NET_IF_ETHERTAP:
236 cebix 1.1 sprintf(dev_name, "/dev/%s", name);
237 gbeauche 1.9 break;
238     case NET_IF_TUNTAP:
239 gbeauche 1.11 strcpy(dev_name, "/dev/net/tun");
240 gbeauche 1.9 break;
241     case NET_IF_SHEEPNET:
242 cebix 1.1 strcpy(dev_name, "/dev/sheep_net");
243 gbeauche 1.9 break;
244     }
245 gbeauche 1.15 if (net_if_type != NET_IF_SLIRP) {
246     fd = open(dev_name, O_RDWR);
247     if (fd < 0) {
248     sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
249     WarningAlert(str);
250     goto open_error;
251     }
252 cebix 1.1 }
253    
254 gbeauche 1.9 #if ENABLE_TUNTAP
255     // Open TUN/TAP interface
256     if (net_if_type == NET_IF_TUNTAP) {
257     struct ifreq ifr;
258     memset(&ifr, 0, sizeof(ifr));
259     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
260     strcpy(ifr.ifr_name, "tun%d");
261     if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
262     sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
263     WarningAlert(str);
264     goto open_error;
265     }
266    
267     // Get network config script file path
268     net_if_script = PrefsFindString("etherconfig");
269     if (net_if_script == NULL)
270     net_if_script = ETHERCONFIG_FILE_NAME;
271    
272     // Start network script up
273     if (net_if_script == NULL) {
274     sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found");
275     WarningAlert(str);
276     goto open_error;
277     }
278 gbeauche 1.10 net_if_name = strdup(ifr.ifr_name);
279 gbeauche 1.9 if (!execute_network_script("up")) {
280     sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error");
281     WarningAlert(str);
282     goto open_error;
283     }
284     D(bug("Connected to host network interface: %s\n", net_if_name));
285     }
286     #endif
287    
288 cebix 1.1 #if defined(__linux__)
289     // Attach sheep_net to selected Ethernet card
290 gbeauche 1.9 if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) {
291 cebix 1.1 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
292     WarningAlert(str);
293     goto open_error;
294     }
295     #endif
296    
297     // Set nonblocking I/O
298     ioctl(fd, FIONBIO, &nonblock);
299    
300     // Get Ethernet address
301 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
302 cebix 1.1 pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
303     ether_addr[0] = 0xfe;
304     ether_addr[1] = 0xfd;
305     ether_addr[2] = p >> 24;
306     ether_addr[3] = p >> 16;
307     ether_addr[4] = p >> 8;
308     ether_addr[5] = p;
309 gbeauche 1.15 #ifdef HAVE_SLIRP
310     } else if (net_if_type == NET_IF_SLIRP) {
311     ether_addr[0] = 0x52;
312     ether_addr[1] = 0x54;
313     ether_addr[2] = 0x00;
314     ether_addr[3] = 0x12;
315     ether_addr[4] = 0x34;
316     ether_addr[5] = 0x56;
317     #endif
318 cebix 1.1 } else
319     ioctl(fd, SIOCGIFADDR, ether_addr);
320     D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5]));
321    
322     // Start packet reception thread
323 cebix 1.2 if (!start_thread())
324 cebix 1.1 goto open_error;
325    
326     // Everything OK
327 cebix 1.2 return true;
328 cebix 1.1
329     open_error:
330 cebix 1.2 stop_thread();
331    
332 cebix 1.1 if (fd > 0) {
333     close(fd);
334     fd = -1;
335     }
336 gbeauche 1.15 if (slirp_output_fd >= 0) {
337     close(slirp_output_fd);
338     slirp_output_fd = -1;
339     }
340 cebix 1.2 return false;
341 cebix 1.1 }
342    
343    
344     /*
345     * Deinitialization
346     */
347    
348 cebix 1.2 void ether_exit(void)
349 cebix 1.1 {
350     // Stop reception thread
351     if (thread_active) {
352     pthread_cancel(ether_thread);
353     pthread_join(ether_thread, NULL);
354     sem_destroy(&int_ack);
355     thread_active = false;
356     }
357    
358 gbeauche 1.10 // Shut down TUN/TAP interface
359     if (net_if_type == NET_IF_TUNTAP)
360     execute_network_script("down");
361    
362     // Free TUN/TAP device name
363     if (net_if_name)
364     free(net_if_name);
365    
366 cebix 1.1 // Close sheep_net device
367     if (fd > 0)
368     close(fd);
369 gbeauche 1.15
370     // Close slirp output buffer
371     if (slirp_output_fd > 0)
372     close(slirp_output_fd);
373 cebix 1.1 }
374    
375    
376     /*
377     * Reset
378     */
379    
380 cebix 1.3 void ether_reset(void)
381 cebix 1.1 {
382 cebix 1.3 net_protocols.clear();
383 cebix 1.1 }
384    
385    
386     /*
387     * Add multicast address
388     */
389    
390     int16 ether_add_multicast(uint32 pb)
391     {
392 gbeauche 1.15 switch (net_if_type) {
393     case NET_IF_ETHERTAP:
394     case NET_IF_SHEEPNET:
395     if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
396     D(bug("WARNING: Couldn't enable multicast address\n"));
397     if (net_if_type == NET_IF_ETHERTAP)
398     return noErr;
399     else
400     return eMultiErr;
401     }
402     default:
403 cebix 1.1 return noErr;
404 gbeauche 1.15 }
405 cebix 1.1 }
406    
407    
408     /*
409     * Delete multicast address
410     */
411    
412     int16 ether_del_multicast(uint32 pb)
413     {
414 gbeauche 1.15 switch (net_if_type) {
415     case NET_IF_ETHERTAP:
416     case NET_IF_SHEEPNET:
417     if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
418     D(bug("WARNING: Couldn't disable multicast address\n"));
419     return eMultiErr;
420     }
421     default:
422 cebix 1.1 return noErr;
423 gbeauche 1.15 }
424 cebix 1.1 }
425    
426    
427     /*
428     * Attach protocol handler
429     */
430    
431     int16 ether_attach_ph(uint16 type, uint32 handler)
432     {
433 cebix 1.3 if (net_protocols.find(type) != net_protocols.end())
434 cebix 1.1 return lapProtErr;
435 cebix 1.3 net_protocols[type] = handler;
436     return noErr;
437 cebix 1.1 }
438    
439    
440     /*
441     * Detach protocol handler
442     */
443    
444     int16 ether_detach_ph(uint16 type)
445     {
446 cebix 1.3 if (net_protocols.erase(type) == 0)
447 cebix 1.1 return lapProtErr;
448 cebix 1.3 return noErr;
449 cebix 1.1 }
450    
451    
452     /*
453     * Transmit raw ethernet packet
454     */
455    
456     int16 ether_write(uint32 wds)
457     {
458     // Copy packet to buffer
459     uint8 packet[1516], *p = packet;
460     int len = 0;
461     #if defined(__linux__)
462 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
463 cebix 1.1 *p++ = 0; // Linux ethertap discards the first 2 bytes
464     *p++ = 0;
465     len += 2;
466     }
467     #endif
468 cebix 1.2 len += ether_wds_to_buffer(wds, p);
469 cebix 1.1
470     #if MONITOR
471     bug("Sending Ethernet packet:\n");
472     for (int i=0; i<len; i++) {
473     bug("%02x ", packet[i]);
474     }
475     bug("\n");
476     #endif
477    
478     // Transmit packet
479 gbeauche 1.15 #ifdef HAVE_SLIRP
480     if (net_if_type == NET_IF_SLIRP) {
481     slirp_input(packet, len);
482     return noErr;
483     } else
484     #endif
485 cebix 1.1 if (write(fd, packet, len) < 0) {
486     D(bug("WARNING: Couldn't transmit packet\n"));
487     return excessCollsns;
488     } else
489     return noErr;
490     }
491    
492    
493     /*
494 cebix 1.2 * Start UDP packet reception thread
495     */
496    
497     bool ether_start_udp_thread(int socket_fd)
498     {
499     fd = socket_fd;
500     udp_tunnel = true;
501     return start_thread();
502     }
503    
504    
505     /*
506     * Stop UDP packet reception thread
507     */
508    
509     void ether_stop_udp_thread(void)
510     {
511     stop_thread();
512     fd = -1;
513     }
514    
515    
516     /*
517 gbeauche 1.15 * SLIRP output buffer glue
518     */
519    
520     #ifdef HAVE_SLIRP
521     int slirp_can_output(void)
522     {
523     return 1;
524     }
525    
526     void slirp_output(const uint8 *packet, int len)
527     {
528     write(slirp_output_fd, packet, len);
529     }
530    
531     void *slirp_receive_func(void *arg)
532     {
533     for (;;) {
534     // Wait for packets to arrive
535     fd_set rfds, wfds, xfds;
536     int nfds;
537     struct timeval tv;
538    
539     nfds = -1;
540     FD_ZERO(&rfds);
541     FD_ZERO(&wfds);
542     FD_ZERO(&xfds);
543     slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
544     tv.tv_sec = 0;
545     tv.tv_usec = 16667;
546     if (select(nfds + 1, &rfds, &wfds, &xfds, &tv) >= 0)
547     slirp_select_poll(&rfds, &wfds, &xfds);
548     }
549     return NULL;
550     }
551     #else
552     int slirp_can_output(void)
553     {
554     return 0;
555     }
556    
557     void slirp_output(const uint8 *packet, int len)
558     {
559     }
560     #endif
561    
562    
563     /*
564 cebix 1.1 * Packet reception thread
565     */
566    
567     static void *receive_func(void *arg)
568     {
569     for (;;) {
570    
571     // Wait for packets to arrive
572     struct pollfd pf = {fd, POLLIN, 0};
573     int res = poll(&pf, 1, -1);
574     if (res <= 0)
575     break;
576    
577     // Trigger Ethernet interrupt
578     D(bug(" packet received, triggering Ethernet interrupt\n"));
579     SetInterruptFlag(INTFLAG_ETHER);
580     TriggerInterrupt();
581    
582     // Wait for interrupt acknowledge by EtherInterrupt()
583     sem_wait(&int_ack);
584     }
585     return NULL;
586     }
587    
588    
589     /*
590     * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
591     */
592    
593     void EtherInterrupt(void)
594     {
595     D(bug("EtherIRQ\n"));
596    
597     // Call protocol handler for received packets
598 gbeauche 1.14 EthernetPacket ether_packet;
599     uint32 packet = ether_packet.addr();
600 cebix 1.2 ssize_t length;
601 cebix 1.1 for (;;) {
602    
603 cebix 1.2 if (udp_tunnel) {
604    
605     // Read packet from socket
606     struct sockaddr_in from;
607     socklen_t from_len = sizeof(from);
608 gbeauche 1.14 length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len);
609 cebix 1.2 if (length < 14)
610     break;
611     ether_udp_read(packet, length, &from);
612    
613     } else {
614    
615     // Read packet from sheep_net device
616 cebix 1.1 #if defined(__linux__)
617 gbeauche 1.14 length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514);
618 cebix 1.1 #else
619 gbeauche 1.14 length = read(fd, Mac2HostAddr(packet), 1514);
620 cebix 1.1 #endif
621 cebix 1.2 if (length < 14)
622     break;
623 cebix 1.1
624     #if MONITOR
625 cebix 1.2 bug("Receiving Ethernet packet:\n");
626     for (int i=0; i<length; i++) {
627 gbeauche 1.14 bug("%02x ", ReadMacInt8(packet + i));
628 cebix 1.2 }
629     bug("\n");
630 cebix 1.1 #endif
631    
632 cebix 1.2 // Pointer to packet data (Ethernet header)
633 gbeauche 1.14 uint32 p = packet;
634 cebix 1.1 #if defined(__linux__)
635 gbeauche 1.9 if (net_if_type == NET_IF_ETHERTAP) {
636 cebix 1.2 p += 2; // Linux ethertap has two random bytes before the packet
637     length -= 2;
638     }
639 cebix 1.1 #endif
640    
641 cebix 1.2 // Get packet type
642 gbeauche 1.14 uint16 type = ReadMacInt16(p + 12);
643 cebix 1.1
644 cebix 1.2 // Look for protocol
645 cebix 1.3 uint16 search_type = (type <= 1500 ? 0 : type);
646     if (net_protocols.find(search_type) == net_protocols.end())
647 cebix 1.2 continue;
648 cebix 1.3 uint32 handler = net_protocols[search_type];
649 cebix 1.2
650     // No default handler
651 cebix 1.3 if (handler == 0)
652 cebix 1.2 continue;
653    
654     // Copy header to RHA
655 gbeauche 1.14 Mac2Mac_memcpy(ether_data + ed_RHA, p, 14);
656 cebix 1.2 D(bug(" header %08x%04x %08x%04x %04x\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12)));
657    
658     // Call protocol handler
659     M68kRegisters r;
660     r.d[0] = type; // Packet type
661     r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
662 gbeauche 1.14 r.a[0] = p + 14; // Pointer to packet (Mac address, for ReadPacket)
663 cebix 1.2 r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
664     r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
665 cebix 1.3 D(bug(" calling protocol handler %08x, type %08x, length %08x, data %08x, rha %08x, read_packet %08x\n", handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4]));
666     Execute68k(handler, &r);
667 cebix 1.2 }
668 cebix 1.1 }
669    
670     // Acknowledge interrupt to reception thread
671     D(bug(" EtherIRQ done\n"));
672     sem_post(&int_ack);
673     }