ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
(Generate patch)

Comparing BasiliskII/src/Unix/ether_unix.cpp (file contents):
Revision 1.5 by cebix, 2001-09-02T13:50:05Z vs.
Revision 1.19 by gbeauche, 2005-05-14T17:33:57Z

# Line 1 | Line 1
1   /*
2   *  ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3   *
4 < *  Basilisk II (C) 1997-2001 Christian Bauer
4 > *  Basilisk II (C) 1997-2005 Christian Bauer
5   *
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
# Line 20 | Line 20
20  
21   #include "sysdeps.h"
22  
23 < #include <sys/ioctl.h>
23 > #ifdef HAVE_SYS_POLL_H
24   #include <sys/poll.h>
25 + #endif
26 + #include <sys/ioctl.h>
27   #include <sys/socket.h>
28 + #include <sys/wait.h>
29   #include <netinet/in.h>
30   #include <pthread.h>
31   #include <semaphore.h>
# Line 30 | Line 33
33   #include <stdio.h>
34   #include <map>
35  
36 < #if defined(__FreeBSD__) || defined(sgi)
36 > #if defined(__FreeBSD__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__))
37 > #include <net/if.h>
38 > #endif
39 >
40 > #if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
41 > #include <linux/if.h>
42 > #include <linux/if_tun.h>
43 > #endif
44 >
45 > #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H)
46   #include <net/if.h>
47 + #include <net/if_tun.h>
48 + #endif
49 +
50 + #ifdef HAVE_SLIRP
51 + #include "libslirp.h"
52   #endif
53  
54   #include "cpu_emulation.h"
# Line 52 | Line 69 | using std::map;
69   #define MONITOR 0
70  
71  
72 + // Ethernet device types
73 + enum {
74 +        NET_IF_SHEEPNET,
75 +        NET_IF_ETHERTAP,
76 +        NET_IF_TUNTAP,
77 +        NET_IF_SLIRP
78 + };
79 +
80 + // Constants
81 + static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig";
82 +
83   // 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
61 static bool is_ethertap;                                        // Flag: Ethernet device is ethertap
89   static bool udp_tunnel;                                         // Flag: UDP tunnelling active, fd is the socket descriptor
90 + static int net_if_type = -1;                            // Ethernet device type
91 + static char *net_if_name = NULL;                        // TUN/TAP device name
92 + static const char *net_if_script = NULL;        // Network config script
93 + 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  
97   // Attached network protocols, maps protocol type to MacOS handler address
98   static map<uint16, uint32> net_protocols;
99  
100   // Prototypes
101   static void *receive_func(void *arg);
102 + static void *slirp_receive_func(void *arg);
103 + static int poll_fd(int fd);
104  
105  
106   /*
# Line 79 | Line 114 | static bool start_thread(void)
114                  return false;
115          }
116  
117 <        pthread_attr_init(&ether_thread_attr);
83 < #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
84 <        if (geteuid() == 0) {
85 <                pthread_attr_setinheritsched(&ether_thread_attr, PTHREAD_EXPLICIT_SCHED);
86 <                pthread_attr_setschedpolicy(&ether_thread_attr, SCHED_FIFO);
87 <                struct sched_param fifo_param;
88 <                fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2 + 1;
89 <                pthread_attr_setschedparam(&ether_thread_attr, &fifo_param);
90 <        }
91 < #endif
92 <
117 >        Set_pthread_attr(&ether_thread_attr, 1);
118          thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
119          if (!thread_active) {
120                  printf("WARNING: Cannot start Ethernet thread");
121                  return false;
122          }
123  
124 + #ifdef HAVE_SLIRP
125 +        if (net_if_type == NET_IF_SLIRP) {
126 +                slirp_thread_active = (pthread_create(&slirp_thread, NULL, slirp_receive_func, NULL) == 0);
127 +                if (!slirp_thread_active) {
128 +                        printf("WARNING: Cannot start slirp reception thread\n");
129 +                        return false;
130 +                }
131 +        }
132 + #endif
133 +
134          return true;
135   }
136  
# Line 106 | Line 141 | static bool start_thread(void)
141  
142   static void stop_thread(void)
143   {
144 + #ifdef HAVE_SLIRP
145 +        if (slirp_thread_active) {
146 +                pthread_cancel(slirp_thread);
147 +                pthread_join(slirp_thread, NULL);
148 +                slirp_thread_active = false;
149 +        }
150 + #endif
151 +
152          if (thread_active) {
153                  pthread_cancel(ether_thread);
154                  pthread_join(ether_thread, NULL);
# Line 116 | Line 159 | static void stop_thread(void)
159  
160  
161   /*
162 + *  Execute network script up|down
163 + */
164 +
165 + static bool execute_network_script(const char *action)
166 + {
167 +        if (net_if_script == NULL || net_if_name == NULL)
168 +                return false;
169 +
170 +        int pid = fork();
171 +        if (pid >= 0) {
172 +                if (pid == 0) {
173 +                        char *args[4];
174 +                        args[0] = (char *)net_if_script;
175 +                        args[1] = net_if_name;
176 +                        args[2] = (char *)action;
177 +                        args[3] = NULL;
178 +                        execv(net_if_script, args);
179 +                        exit(1);
180 +                }
181 +                int status;
182 +                while (waitpid(pid, &status, 0) != pid);
183 +                return WIFEXITED(status) && WEXITSTATUS(status) == 0;
184 +        }
185 +
186 +        return false;
187 + }
188 +
189 +
190 + /*
191   *  Initialization
192   */
193  
# Line 129 | Line 201 | bool ether_init(void)
201          if (name == NULL)
202                  return false;
203  
204 <        // Is it Ethertap?
205 <        is_ethertap = (strncmp(name, "tap", 3) == 0);
204 >        // Determine Ethernet device type
205 >        net_if_type = -1;
206 >        if (strncmp(name, "tap", 3) == 0)
207 >                net_if_type = NET_IF_ETHERTAP;
208 > #if ENABLE_TUNTAP
209 >        else if (strcmp(name, "tun") == 0)
210 >                net_if_type = NET_IF_TUNTAP;
211 > #endif
212 > #ifdef HAVE_SLIRP
213 >        else if (strcmp(name, "slirp") == 0)
214 >                net_if_type = NET_IF_SLIRP;
215 > #endif
216 >        else
217 >                net_if_type = NET_IF_SHEEPNET;
218 >
219 > #ifdef HAVE_SLIRP
220 >        // Initialize slirp library
221 >        if (net_if_type == NET_IF_SLIRP) {
222 >                slirp_init();
223 >
224 >                // Open slirp output pipe
225 >                int fds[2];
226 >                if (pipe(fds) < 0)
227 >                        return false;
228 >                fd = fds[0];
229 >                slirp_output_fd = fds[1];
230 >        }
231 > #endif
232  
233 <        // Open sheep_net or ethertap device
233 >        // Open sheep_net or ethertap or TUN/TAP device
234          char dev_name[16];
235 <        if (is_ethertap)
235 >        switch (net_if_type) {
236 >        case NET_IF_ETHERTAP:
237                  sprintf(dev_name, "/dev/%s", name);
238 <        else
238 >                break;
239 >        case NET_IF_TUNTAP:
240 >                strcpy(dev_name, "/dev/net/tun");
241 >                break;
242 >        case NET_IF_SHEEPNET:
243                  strcpy(dev_name, "/dev/sheep_net");
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;
244 >                break;
245 >        }
246 >        if (net_if_type != NET_IF_SLIRP) {
247 >                fd = open(dev_name, O_RDWR);
248 >                if (fd < 0) {
249 >                        sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
250 >                        WarningAlert(str);
251 >                        goto open_error;
252 >                }
253 >        }
254 >
255 > #if ENABLE_TUNTAP
256 >        // Open TUN/TAP interface
257 >        if (net_if_type == NET_IF_TUNTAP) {
258 >                struct ifreq ifr;
259 >                memset(&ifr, 0, sizeof(ifr));
260 >                ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
261 >                strcpy(ifr.ifr_name, "tun%d");
262 >                if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
263 >                        sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
264 >                        WarningAlert(str);
265 >                        goto open_error;
266 >                }
267 >
268 >                // Get network config script file path
269 >                net_if_script = PrefsFindString("etherconfig");
270 >                if (net_if_script == NULL)
271 >                        net_if_script = ETHERCONFIG_FILE_NAME;
272 >
273 >                // Start network script up
274 >                if (net_if_script == NULL) {
275 >                        sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found");
276 >                        WarningAlert(str);
277 >                        goto open_error;
278 >                }
279 >                net_if_name = strdup(ifr.ifr_name);
280 >                if (!execute_network_script("up")) {
281 >                        sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error");
282 >                        WarningAlert(str);
283 >                        goto open_error;
284 >                }
285 >                D(bug("Connected to host network interface: %s\n", net_if_name));
286          }
287 + #endif
288  
289   #if defined(__linux__)
290          // Attach sheep_net to selected Ethernet card
291 <        if (!is_ethertap && ioctl(fd, SIOCSIFLINK, name) < 0) {
291 >        if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) {
292                  sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
293                  WarningAlert(str);
294                  goto open_error;
# Line 158 | Line 299 | bool ether_init(void)
299          ioctl(fd, FIONBIO, &nonblock);
300  
301          // Get Ethernet address
302 <        if (is_ethertap) {
302 >        if (net_if_type == NET_IF_ETHERTAP) {
303                  pid_t p = getpid();     // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
304                  ether_addr[0] = 0xfe;
305                  ether_addr[1] = 0xfd;
# Line 166 | Line 307 | bool ether_init(void)
307                  ether_addr[3] = p >> 16;
308                  ether_addr[4] = p >> 8;
309                  ether_addr[5] = p;
310 + #ifdef HAVE_SLIRP
311 +        } else if (net_if_type == NET_IF_SLIRP) {
312 +                ether_addr[0] = 0x52;
313 +                ether_addr[1] = 0x54;
314 +                ether_addr[2] = 0x00;
315 +                ether_addr[3] = 0x12;
316 +                ether_addr[4] = 0x34;
317 +                ether_addr[5] = 0x56;
318 + #endif
319          } else
320                  ioctl(fd, SIOCGIFADDR, ether_addr);
321          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]));
# Line 184 | Line 334 | open_error:
334                  close(fd);
335                  fd = -1;
336          }
337 +        if (slirp_output_fd >= 0) {
338 +                close(slirp_output_fd);
339 +                slirp_output_fd = -1;
340 +        }
341          return false;
342   }
343  
# Line 202 | Line 356 | void ether_exit(void)
356                  thread_active = false;
357          }
358  
359 +        // Shut down TUN/TAP interface
360 +        if (net_if_type == NET_IF_TUNTAP)
361 +                execute_network_script("down");
362 +
363 +        // Free TUN/TAP device name
364 +        if (net_if_name)
365 +                free(net_if_name);
366 +
367          // Close sheep_net device
368          if (fd > 0)
369                  close(fd);
370 +
371 +        // Close slirp output buffer
372 +        if (slirp_output_fd > 0)
373 +                close(slirp_output_fd);
374   }
375  
376  
# Line 224 | Line 390 | void ether_reset(void)
390  
391   int16 ether_add_multicast(uint32 pb)
392   {
393 <        if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
394 <                D(bug("WARNING: Couldn't enable multicast address\n"));
395 <                if (is_ethertap)
396 <                        return noErr;
397 <                else
398 <                        return eMultiErr;
399 <        } else
393 >        switch (net_if_type) {
394 >        case NET_IF_ETHERTAP:
395 >        case NET_IF_SHEEPNET:
396 >                if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
397 >                        D(bug("WARNING: Couldn't enable multicast address\n"));
398 >                        if (net_if_type == NET_IF_ETHERTAP)
399 >                                return noErr;
400 >                        else
401 >                                return eMultiErr;
402 >                }
403 >        default:
404                  return noErr;
405 +        }
406   }
407  
408  
# Line 241 | Line 412 | int16 ether_add_multicast(uint32 pb)
412  
413   int16 ether_del_multicast(uint32 pb)
414   {
415 <        if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
416 <                D(bug("WARNING: Couldn't disable multicast address\n"));
417 <                return eMultiErr;
418 <        } else
415 >        switch (net_if_type) {
416 >        case NET_IF_ETHERTAP:
417 >        case NET_IF_SHEEPNET:
418 >                if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
419 >                        D(bug("WARNING: Couldn't disable multicast address\n"));
420 >                        return eMultiErr;
421 >                }
422 >        default:
423                  return noErr;
424 +        }
425   }
426  
427  
# Line 284 | Line 460 | int16 ether_write(uint32 wds)
460          uint8 packet[1516], *p = packet;
461          int len = 0;
462   #if defined(__linux__)
463 <        if (is_ethertap) {
463 >        if (net_if_type == NET_IF_ETHERTAP) {
464                  *p++ = 0;       // Linux ethertap discards the first 2 bytes
465                  *p++ = 0;
466                  len += 2;
# Line 301 | Line 477 | int16 ether_write(uint32 wds)
477   #endif
478  
479          // Transmit packet
480 + #ifdef HAVE_SLIRP
481 +        if (net_if_type == NET_IF_SLIRP) {
482 +                slirp_input(packet, len);
483 +                return noErr;
484 +        } else
485 + #endif
486          if (write(fd, packet, len) < 0) {
487                  D(bug("WARNING: Couldn't transmit packet\n"));
488                  return excessCollsns;
# Line 333 | Line 515 | void ether_stop_udp_thread(void)
515  
516  
517   /*
518 + *  SLIRP output buffer glue
519 + */
520 +
521 + #ifdef HAVE_SLIRP
522 + int slirp_can_output(void)
523 + {
524 +        return 1;
525 + }
526 +
527 + void slirp_output(const uint8 *packet, int len)
528 + {
529 +        write(slirp_output_fd, packet, len);
530 + }
531 +
532 + void *slirp_receive_func(void *arg)
533 + {
534 +        for (;;) {
535 +                // Wait for packets to arrive
536 +                fd_set rfds, wfds, xfds;
537 +                int nfds;
538 +                struct timeval tv;
539 +
540 +                nfds = -1;
541 +                FD_ZERO(&rfds);
542 +                FD_ZERO(&wfds);
543 +                FD_ZERO(&xfds);
544 +                slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
545 +                tv.tv_sec = 0;
546 +                tv.tv_usec = 16667;
547 +                if (select(nfds + 1, &rfds, &wfds, &xfds, &tv) >= 0)
548 +                        slirp_select_poll(&rfds, &wfds, &xfds);
549 +        }
550 +        return NULL;
551 + }
552 + #else
553 + int slirp_can_output(void)
554 + {
555 +        return 0;
556 + }
557 +
558 + void slirp_output(const uint8 *packet, int len)
559 + {
560 + }
561 + #endif
562 +
563 +
564 + /*
565 + *  Wait for data to arrive
566 + */
567 +
568 + static inline int poll_fd(int fd)
569 + {
570 + #ifdef HAVE_POLL
571 +        struct pollfd pf = {fd, POLLIN, 0};
572 +        return poll(&pf, 1, -1);
573 + #else
574 +        fd_set rfds;
575 +        FD_ZERO(&rfds);
576 +        FD_SET(fd, &rfds);
577 +        return select(fd + 1, &rfds, NULL, NULL, NULL);
578 + #endif
579 + }
580 +
581 +
582 + /*
583   *  Packet reception thread
584   */
585  
# Line 341 | Line 588 | static void *receive_func(void *arg)
588          for (;;) {
589  
590                  // Wait for packets to arrive
591 <                struct pollfd pf = {fd, POLLIN, 0};
345 <                int res = poll(&pf, 1, -1);
591 >                int res = poll_fd(fd);
592                  if (res <= 0)
593                          break;
594  
# Line 367 | Line 613 | void EtherInterrupt(void)
613          D(bug("EtherIRQ\n"));
614  
615          // Call protocol handler for received packets
616 <        uint8 packet[1516];
616 >        EthernetPacket ether_packet;
617 >        uint32 packet = ether_packet.addr();
618          ssize_t length;
619          for (;;) {
620  
# Line 376 | Line 623 | void EtherInterrupt(void)
623                          // Read packet from socket
624                          struct sockaddr_in from;
625                          socklen_t from_len = sizeof(from);
626 <                        length = recvfrom(fd, packet, 1514, 0, (struct sockaddr *)&from, &from_len);
626 >                        length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len);
627                          if (length < 14)
628                                  break;
629                          ether_udp_read(packet, length, &from);
# Line 385 | Line 632 | void EtherInterrupt(void)
632  
633                          // Read packet from sheep_net device
634   #if defined(__linux__)
635 <                        length = read(fd, packet, is_ethertap ? 1516 : 1514);
635 >                        length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514);
636   #else
637 <                        length = read(fd, packet, 1514);
637 >                        length = read(fd, Mac2HostAddr(packet), 1514);
638   #endif
639                          if (length < 14)
640                                  break;
# Line 395 | Line 642 | void EtherInterrupt(void)
642   #if MONITOR
643                          bug("Receiving Ethernet packet:\n");
644                          for (int i=0; i<length; i++) {
645 <                                bug("%02x ", packet[i]);
645 >                                bug("%02x ", ReadMacInt8(packet + i));
646                          }
647                          bug("\n");
648   #endif
649  
650                          // Pointer to packet data (Ethernet header)
651 <                        uint8 *p = packet;
651 >                        uint32 p = packet;
652   #if defined(__linux__)
653 <                        if (is_ethertap) {
653 >                        if (net_if_type == NET_IF_ETHERTAP) {
654                                  p += 2;                 // Linux ethertap has two random bytes before the packet
655                                  length -= 2;
656                          }
657   #endif
658  
659                          // Get packet type
660 <                        uint16 type = (p[12] << 8) | p[13];
660 >                        uint16 type = ReadMacInt16(p + 12);
661  
662                          // Look for protocol
663                          uint16 search_type = (type <= 1500 ? 0 : type);
# Line 423 | Line 670 | void EtherInterrupt(void)
670                                  continue;
671  
672                          // Copy header to RHA
673 <                        Host2Mac_memcpy(ether_data + ed_RHA, p, 14);
673 >                        Mac2Mac_memcpy(ether_data + ed_RHA, p, 14);
674                          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)));
675  
676                          // Call protocol handler
677                          M68kRegisters r;
678                          r.d[0] = type;                                                                  // Packet type
679                          r.d[1] = length - 14;                                                   // Remaining packet length (without header, for ReadPacket)
680 <                        r.a[0] = (uint32)p + 14;                                                // Pointer to packet (host address, for ReadPacket)
680 >                        r.a[0] = p + 14;                                                                // Pointer to packet (Mac address, for ReadPacket)
681                          r.a[3] = ether_data + ed_RHA + 14;                              // Pointer behind header in RHA
682                          r.a[4] = ether_data + ed_ReadPacket;                    // Pointer to ReadPacket/ReadRest routines
683                          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]));

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines