ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.16
Committed: 2005-05-13T14:02:36Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.15: +2 -4 lines
Log Message:
Some 64-bit fixes to bootp, icmp, udp (cu-seeme). However, it can happen
that on certain occasions, it doesn't work. Some timing problem?

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