ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.20
Committed: 2005-05-15T17:22:12Z (19 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.19: +29 -26 lines
Log Message:
Fix and factor out ether_exit(). Pitifully, MacOS X 10.2 does not make select()
a cancellation point when it is passed a NULL timeout. Workarounded in
receive_func() with a full inline of poll_fd() + pthread_testcancel().

File Contents

# Content
1 /*
2 * ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3 *
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
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 #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>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <map>
35
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"
55 #include "main.h"
56 #include "macos_util.h"
57 #include "prefs.h"
58 #include "user_strings.h"
59 #include "ether.h"
60 #include "ether_defs.h"
61
62 #ifndef NO_STD_NAMESPACE
63 using std::map;
64 #endif
65
66 #define DEBUG 0
67 #include "debug.h"
68
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
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 /*
107 * Start packet reception thread
108 */
109
110 static bool start_thread(void)
111 {
112 if (sem_init(&int_ack, 0, 0) < 0) {
113 printf("WARNING: Cannot init semaphore");
114 return false;
115 }
116
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
137
138 /*
139 * Stop packet reception thread
140 */
141
142 static void stop_thread(void)
143 {
144 #ifdef HAVE_SLIRP
145 if (slirp_thread_active) {
146 #ifdef HAVE_PTHREAD_CANCEL
147 pthread_cancel(slirp_thread);
148 #endif
149 pthread_join(slirp_thread, NULL);
150 slirp_thread_active = false;
151 }
152 #endif
153
154 if (thread_active) {
155 #ifdef HAVE_PTHREAD_CANCEL
156 pthread_cancel(ether_thread);
157 #endif
158 pthread_join(ether_thread, NULL);
159 sem_destroy(&int_ack);
160 thread_active = false;
161 }
162 }
163
164
165 /*
166 * Execute network script up|down
167 */
168
169 static bool execute_network_script(const char *action)
170 {
171 if (net_if_script == NULL || net_if_name == NULL)
172 return false;
173
174 int pid = fork();
175 if (pid >= 0) {
176 if (pid == 0) {
177 char *args[4];
178 args[0] = (char *)net_if_script;
179 args[1] = net_if_name;
180 args[2] = (char *)action;
181 args[3] = NULL;
182 execv(net_if_script, args);
183 exit(1);
184 }
185 int status;
186 while (waitpid(pid, &status, 0) != pid);
187 return WIFEXITED(status) && WEXITSTATUS(status) == 0;
188 }
189
190 return false;
191 }
192
193
194 /*
195 * Initialization
196 */
197
198 bool ether_init(void)
199 {
200 int nonblock = 1;
201 char str[256];
202
203 // Do nothing if no Ethernet device specified
204 const char *name = PrefsFindString("ether");
205 if (name == NULL)
206 return false;
207
208 // Determine Ethernet device type
209 net_if_type = -1;
210 if (strncmp(name, "tap", 3) == 0)
211 net_if_type = NET_IF_ETHERTAP;
212 #if ENABLE_TUNTAP
213 else if (strcmp(name, "tun") == 0)
214 net_if_type = NET_IF_TUNTAP;
215 #endif
216 #ifdef HAVE_SLIRP
217 else if (strcmp(name, "slirp") == 0)
218 net_if_type = NET_IF_SLIRP;
219 #endif
220 else
221 net_if_type = NET_IF_SHEEPNET;
222
223 #ifdef HAVE_SLIRP
224 // Initialize slirp library
225 if (net_if_type == NET_IF_SLIRP) {
226 slirp_init();
227
228 // Open slirp output pipe
229 int fds[2];
230 if (pipe(fds) < 0)
231 return false;
232 fd = fds[0];
233 slirp_output_fd = fds[1];
234 }
235 #endif
236
237 // Open sheep_net or ethertap or TUN/TAP device
238 char dev_name[16];
239 switch (net_if_type) {
240 case NET_IF_ETHERTAP:
241 sprintf(dev_name, "/dev/%s", name);
242 break;
243 case NET_IF_TUNTAP:
244 strcpy(dev_name, "/dev/net/tun");
245 break;
246 case NET_IF_SHEEPNET:
247 strcpy(dev_name, "/dev/sheep_net");
248 break;
249 }
250 if (net_if_type != NET_IF_SLIRP) {
251 fd = open(dev_name, O_RDWR);
252 if (fd < 0) {
253 sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
254 WarningAlert(str);
255 goto open_error;
256 }
257 }
258
259 #if ENABLE_TUNTAP
260 // Open TUN/TAP interface
261 if (net_if_type == NET_IF_TUNTAP) {
262 struct ifreq ifr;
263 memset(&ifr, 0, sizeof(ifr));
264 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
265 strcpy(ifr.ifr_name, "tun%d");
266 if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) {
267 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
268 WarningAlert(str);
269 goto open_error;
270 }
271
272 // Get network config script file path
273 net_if_script = PrefsFindString("etherconfig");
274 if (net_if_script == NULL)
275 net_if_script = ETHERCONFIG_FILE_NAME;
276
277 // Start network script up
278 if (net_if_script == NULL) {
279 sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found");
280 WarningAlert(str);
281 goto open_error;
282 }
283 net_if_name = strdup(ifr.ifr_name);
284 if (!execute_network_script("up")) {
285 sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error");
286 WarningAlert(str);
287 goto open_error;
288 }
289 D(bug("Connected to host network interface: %s\n", net_if_name));
290 }
291 #endif
292
293 #if defined(__linux__)
294 // Attach sheep_net to selected Ethernet card
295 if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) {
296 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
297 WarningAlert(str);
298 goto open_error;
299 }
300 #endif
301
302 // Set nonblocking I/O
303 ioctl(fd, FIONBIO, &nonblock);
304
305 // Get Ethernet address
306 if (net_if_type == NET_IF_ETHERTAP) {
307 pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
308 ether_addr[0] = 0xfe;
309 ether_addr[1] = 0xfd;
310 ether_addr[2] = p >> 24;
311 ether_addr[3] = p >> 16;
312 ether_addr[4] = p >> 8;
313 ether_addr[5] = p;
314 #ifdef HAVE_SLIRP
315 } else if (net_if_type == NET_IF_SLIRP) {
316 ether_addr[0] = 0x52;
317 ether_addr[1] = 0x54;
318 ether_addr[2] = 0x00;
319 ether_addr[3] = 0x12;
320 ether_addr[4] = 0x34;
321 ether_addr[5] = 0x56;
322 #endif
323 } else
324 ioctl(fd, SIOCGIFADDR, ether_addr);
325 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]));
326
327 // Start packet reception thread
328 if (!start_thread())
329 goto open_error;
330
331 // Everything OK
332 return true;
333
334 open_error:
335 stop_thread();
336
337 if (fd > 0) {
338 close(fd);
339 fd = -1;
340 }
341 if (slirp_output_fd >= 0) {
342 close(slirp_output_fd);
343 slirp_output_fd = -1;
344 }
345 return false;
346 }
347
348
349 /*
350 * Deinitialization
351 */
352
353 void ether_exit(void)
354 {
355 // Stop reception threads
356 stop_thread();
357
358 // 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 // Close sheep_net device
367 if (fd > 0)
368 close(fd);
369
370 // Close slirp output buffer
371 if (slirp_output_fd > 0)
372 close(slirp_output_fd);
373 }
374
375
376 /*
377 * Reset
378 */
379
380 void ether_reset(void)
381 {
382 net_protocols.clear();
383 }
384
385
386 /*
387 * Add multicast address
388 */
389
390 int16 ether_add_multicast(uint32 pb)
391 {
392 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 return noErr;
404 }
405 }
406
407
408 /*
409 * Delete multicast address
410 */
411
412 int16 ether_del_multicast(uint32 pb)
413 {
414 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 return noErr;
423 }
424 }
425
426
427 /*
428 * Attach protocol handler
429 */
430
431 int16 ether_attach_ph(uint16 type, uint32 handler)
432 {
433 if (net_protocols.find(type) != net_protocols.end())
434 return lapProtErr;
435 net_protocols[type] = handler;
436 return noErr;
437 }
438
439
440 /*
441 * Detach protocol handler
442 */
443
444 int16 ether_detach_ph(uint16 type)
445 {
446 if (net_protocols.erase(type) == 0)
447 return lapProtErr;
448 return noErr;
449 }
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 if (net_if_type == NET_IF_ETHERTAP) {
463 *p++ = 0; // Linux ethertap discards the first 2 bytes
464 *p++ = 0;
465 len += 2;
466 }
467 #endif
468 len += ether_wds_to_buffer(wds, p);
469
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 #ifdef HAVE_SLIRP
480 if (net_if_type == NET_IF_SLIRP) {
481 slirp_input(packet, len);
482 return noErr;
483 } else
484 #endif
485 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 * 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 * 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 #ifdef HAVE_PTHREAD_TESTCANCEL
550 // Explicit cancellation point if select() was not covered
551 // This seems to be the case on MacOS X 10.2
552 pthread_testcancel();
553 #endif
554 }
555 return NULL;
556 }
557 #else
558 int slirp_can_output(void)
559 {
560 return 0;
561 }
562
563 void slirp_output(const uint8 *packet, int len)
564 {
565 }
566 #endif
567
568
569 /*
570 * Packet reception thread
571 */
572
573 static void *receive_func(void *arg)
574 {
575 for (;;) {
576
577 // Wait for packets to arrive
578 #if HAVE_POLL
579 struct pollfd pf = {fd, POLLIN, 0};
580 int res = poll(&pf, 1, -1);
581 #else
582 fd_set rfds;
583 FD_ZERO(&rfds);
584 FD_SET(fd, &rfds);
585 // A NULL timeout could cause select() to block indefinitely,
586 // even if it is supposed to be a cancellation point [MacOS X]
587 struct timeval tv = { 0, 20000 };
588 int res = select(fd + 1, &rfds, NULL, NULL, &tv);
589 #ifdef HAVE_PTHREAD_TESTCANCEL
590 pthread_testcancel();
591 #endif
592 if (res == 0 || (res == -1 && errno == EINTR))
593 continue;
594 #endif
595 if (res <= 0)
596 break;
597
598 // Trigger Ethernet interrupt
599 D(bug(" packet received, triggering Ethernet interrupt\n"));
600 SetInterruptFlag(INTFLAG_ETHER);
601 TriggerInterrupt();
602
603 // Wait for interrupt acknowledge by EtherInterrupt()
604 sem_wait(&int_ack);
605 }
606 return NULL;
607 }
608
609
610 /*
611 * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
612 */
613
614 void EtherInterrupt(void)
615 {
616 D(bug("EtherIRQ\n"));
617
618 // Call protocol handler for received packets
619 EthernetPacket ether_packet;
620 uint32 packet = ether_packet.addr();
621 ssize_t length;
622 for (;;) {
623
624 if (udp_tunnel) {
625
626 // Read packet from socket
627 struct sockaddr_in from;
628 socklen_t from_len = sizeof(from);
629 length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len);
630 if (length < 14)
631 break;
632 ether_udp_read(packet, length, &from);
633
634 } else {
635
636 // Read packet from sheep_net device
637 #if defined(__linux__)
638 length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514);
639 #else
640 length = read(fd, Mac2HostAddr(packet), 1514);
641 #endif
642 if (length < 14)
643 break;
644
645 #if MONITOR
646 bug("Receiving Ethernet packet:\n");
647 for (int i=0; i<length; i++) {
648 bug("%02x ", ReadMacInt8(packet + i));
649 }
650 bug("\n");
651 #endif
652
653 // Pointer to packet data (Ethernet header)
654 uint32 p = packet;
655 #if defined(__linux__)
656 if (net_if_type == NET_IF_ETHERTAP) {
657 p += 2; // Linux ethertap has two random bytes before the packet
658 length -= 2;
659 }
660 #endif
661
662 // Get packet type
663 uint16 type = ReadMacInt16(p + 12);
664
665 // Look for protocol
666 uint16 search_type = (type <= 1500 ? 0 : type);
667 if (net_protocols.find(search_type) == net_protocols.end())
668 continue;
669 uint32 handler = net_protocols[search_type];
670
671 // No default handler
672 if (handler == 0)
673 continue;
674
675 // Copy header to RHA
676 Mac2Mac_memcpy(ether_data + ed_RHA, p, 14);
677 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)));
678
679 // Call protocol handler
680 M68kRegisters r;
681 r.d[0] = type; // Packet type
682 r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
683 r.a[0] = p + 14; // Pointer to packet (Mac address, for ReadPacket)
684 r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
685 r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
686 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]));
687 Execute68k(handler, &r);
688 }
689 }
690
691 // Acknowledge interrupt to reception thread
692 D(bug(" EtherIRQ done\n"));
693 sem_post(&int_ack);
694 }