1 |
/* |
2 |
* ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD) |
3 |
* |
4 |
* Basilisk II (C) 1997-2008 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 |
/* |
24 |
* NOTES concerning MacOS X issues: |
25 |
* - poll() does not exist in 10.2.8, but is available in 10.4.4 |
26 |
* - select(), and very likely poll(), are not cancellation points. So |
27 |
* the ethernet thread doesn't stop on exit. An explicit check is |
28 |
* performed to workaround this problem. |
29 |
*/ |
30 |
#if (defined __APPLE__ && defined __MACH__) || ! defined HAVE_POLL |
31 |
#define USE_POLL 0 |
32 |
#else |
33 |
#define USE_POLL 1 |
34 |
#endif |
35 |
|
36 |
// Define to let the slirp library determine the right timeout for select() |
37 |
#define USE_SLIRP_TIMEOUT 1 |
38 |
|
39 |
#ifdef HAVE_SYS_POLL_H |
40 |
#include <sys/poll.h> |
41 |
#endif |
42 |
#include <sys/ioctl.h> |
43 |
#include <sys/socket.h> |
44 |
#include <sys/wait.h> |
45 |
#include <netinet/in.h> |
46 |
#include <pthread.h> |
47 |
#include <semaphore.h> |
48 |
#include <errno.h> |
49 |
#include <stdio.h> |
50 |
#include <signal.h> |
51 |
#include <map> |
52 |
|
53 |
#if defined(__FreeBSD__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__)) |
54 |
#include <net/if.h> |
55 |
#endif |
56 |
|
57 |
#if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H) |
58 |
#include <linux/if.h> |
59 |
#include <linux/if_tun.h> |
60 |
#endif |
61 |
|
62 |
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H) |
63 |
#include <net/if.h> |
64 |
#include <net/if_tun.h> |
65 |
#endif |
66 |
|
67 |
#ifdef HAVE_SLIRP |
68 |
#include "libslirp.h" |
69 |
#endif |
70 |
|
71 |
#include "cpu_emulation.h" |
72 |
#include "main.h" |
73 |
#include "macos_util.h" |
74 |
#include "prefs.h" |
75 |
#include "user_strings.h" |
76 |
#include "ether.h" |
77 |
#include "ether_defs.h" |
78 |
|
79 |
#ifndef NO_STD_NAMESPACE |
80 |
using std::map; |
81 |
#endif |
82 |
|
83 |
#define DEBUG 0 |
84 |
#include "debug.h" |
85 |
|
86 |
#define STATISTICS 0 |
87 |
#define MONITOR 0 |
88 |
|
89 |
|
90 |
// Ethernet device types |
91 |
enum { |
92 |
NET_IF_SHEEPNET, |
93 |
NET_IF_ETHERTAP, |
94 |
NET_IF_TUNTAP, |
95 |
NET_IF_SLIRP |
96 |
}; |
97 |
|
98 |
// Constants |
99 |
static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig"; |
100 |
|
101 |
// Global variables |
102 |
static int fd = -1; // fd of sheep_net device |
103 |
static pthread_t ether_thread; // Packet reception thread |
104 |
static pthread_attr_t ether_thread_attr; // Packet reception thread attributes |
105 |
static bool thread_active = false; // Flag: Packet reception thread installed |
106 |
static sem_t int_ack; // Interrupt acknowledge semaphore |
107 |
static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor |
108 |
static int net_if_type = -1; // Ethernet device type |
109 |
static char *net_if_name = NULL; // TUN/TAP device name |
110 |
static const char *net_if_script = NULL; // Network config script |
111 |
static pthread_t slirp_thread; // Slirp reception thread |
112 |
static bool slirp_thread_active = false; // Flag: Slirp reception threadinstalled |
113 |
static int slirp_output_fd = -1; // fd of slirp output pipe |
114 |
static int slirp_input_fds[2] = { -1, -1 }; // fds of slirp input pipe |
115 |
#ifdef SHEEPSHAVER |
116 |
static bool net_open = false; // Flag: initialization succeeded, network device open |
117 |
static uint8 ether_addr[6]; // Our Ethernet address |
118 |
#else |
119 |
const bool ether_driver_opened = true; // Flag: is the MacOS driver opened? |
120 |
#endif |
121 |
|
122 |
// Attached network protocols, maps protocol type to MacOS handler address |
123 |
static map<uint16, uint32> net_protocols; |
124 |
|
125 |
// Prototypes |
126 |
static void *receive_func(void *arg); |
127 |
static void *slirp_receive_func(void *arg); |
128 |
static int16 ether_do_add_multicast(uint8 *addr); |
129 |
static int16 ether_do_del_multicast(uint8 *addr); |
130 |
static int16 ether_do_write(uint32 arg); |
131 |
static void ether_do_interrupt(void); |
132 |
|
133 |
|
134 |
/* |
135 |
* Start packet reception thread |
136 |
*/ |
137 |
|
138 |
static bool start_thread(void) |
139 |
{ |
140 |
if (sem_init(&int_ack, 0, 0) < 0) { |
141 |
printf("WARNING: Cannot init semaphore"); |
142 |
return false; |
143 |
} |
144 |
|
145 |
Set_pthread_attr(ðer_thread_attr, 1); |
146 |
thread_active = (pthread_create(ðer_thread, ðer_thread_attr, receive_func, NULL) == 0); |
147 |
if (!thread_active) { |
148 |
printf("WARNING: Cannot start Ethernet thread"); |
149 |
return false; |
150 |
} |
151 |
|
152 |
#ifdef HAVE_SLIRP |
153 |
if (net_if_type == NET_IF_SLIRP) { |
154 |
slirp_thread_active = (pthread_create(&slirp_thread, NULL, slirp_receive_func, NULL) == 0); |
155 |
if (!slirp_thread_active) { |
156 |
printf("WARNING: Cannot start slirp reception thread\n"); |
157 |
return false; |
158 |
} |
159 |
} |
160 |
#endif |
161 |
|
162 |
return true; |
163 |
} |
164 |
|
165 |
|
166 |
/* |
167 |
* Stop packet reception thread |
168 |
*/ |
169 |
|
170 |
static void stop_thread(void) |
171 |
{ |
172 |
#ifdef HAVE_SLIRP |
173 |
if (slirp_thread_active) { |
174 |
#ifdef HAVE_PTHREAD_CANCEL |
175 |
pthread_cancel(slirp_thread); |
176 |
#endif |
177 |
pthread_join(slirp_thread, NULL); |
178 |
slirp_thread_active = false; |
179 |
} |
180 |
#endif |
181 |
|
182 |
if (thread_active) { |
183 |
#ifdef HAVE_PTHREAD_CANCEL |
184 |
pthread_cancel(ether_thread); |
185 |
#endif |
186 |
pthread_join(ether_thread, NULL); |
187 |
sem_destroy(&int_ack); |
188 |
thread_active = false; |
189 |
} |
190 |
} |
191 |
|
192 |
|
193 |
/* |
194 |
* Execute network script up|down |
195 |
*/ |
196 |
|
197 |
static bool execute_network_script(const char *action) |
198 |
{ |
199 |
if (net_if_script == NULL || net_if_name == NULL) |
200 |
return false; |
201 |
|
202 |
int pid = fork(); |
203 |
if (pid >= 0) { |
204 |
if (pid == 0) { |
205 |
char *args[4]; |
206 |
args[0] = (char *)net_if_script; |
207 |
args[1] = net_if_name; |
208 |
args[2] = (char *)action; |
209 |
args[3] = NULL; |
210 |
execv(net_if_script, args); |
211 |
exit(1); |
212 |
} |
213 |
int status; |
214 |
while (waitpid(pid, &status, 0) != pid); |
215 |
return WIFEXITED(status) && WEXITSTATUS(status) == 0; |
216 |
} |
217 |
|
218 |
return false; |
219 |
} |
220 |
|
221 |
|
222 |
/* |
223 |
* Initialization |
224 |
*/ |
225 |
|
226 |
bool ether_init(void) |
227 |
{ |
228 |
int val; |
229 |
char str[256]; |
230 |
|
231 |
// Do nothing if no Ethernet device specified |
232 |
const char *name = PrefsFindString("ether"); |
233 |
if (name == NULL) |
234 |
return false; |
235 |
|
236 |
// Determine Ethernet device type |
237 |
net_if_type = -1; |
238 |
if (strncmp(name, "tap", 3) == 0) |
239 |
net_if_type = NET_IF_ETHERTAP; |
240 |
#if ENABLE_TUNTAP |
241 |
else if (strcmp(name, "tun") == 0) |
242 |
net_if_type = NET_IF_TUNTAP; |
243 |
#endif |
244 |
#ifdef HAVE_SLIRP |
245 |
else if (strcmp(name, "slirp") == 0) |
246 |
net_if_type = NET_IF_SLIRP; |
247 |
#endif |
248 |
else |
249 |
net_if_type = NET_IF_SHEEPNET; |
250 |
|
251 |
// Don't raise SIGPIPE, let errno be set to EPIPE |
252 |
struct sigaction sigpipe_sa; |
253 |
if (sigaction(SIGPIPE, NULL, &sigpipe_sa) == 0) { |
254 |
assert(sigpipe_sa.sa_handler == SIG_DFL || sigpipe_sa.sa_handler == SIG_IGN); |
255 |
sigfillset(&sigpipe_sa.sa_mask); |
256 |
sigpipe_sa.sa_flags = 0; |
257 |
sigpipe_sa.sa_handler = SIG_IGN; |
258 |
sigaction(SIGPIPE, &sigpipe_sa, NULL); |
259 |
} |
260 |
|
261 |
#ifdef HAVE_SLIRP |
262 |
// Initialize slirp library |
263 |
if (net_if_type == NET_IF_SLIRP) { |
264 |
if (slirp_init() < 0) { |
265 |
sprintf(str, "%s", GetString(STR_SLIRP_NO_DNS_FOUND_WARN)); |
266 |
WarningAlert(str); |
267 |
return false; |
268 |
} |
269 |
|
270 |
// Open slirp output pipe |
271 |
int fds[2]; |
272 |
if (pipe(fds) < 0) |
273 |
return false; |
274 |
fd = fds[0]; |
275 |
slirp_output_fd = fds[1]; |
276 |
|
277 |
// Open slirp input pipe |
278 |
if (pipe(slirp_input_fds) < 0) |
279 |
return false; |
280 |
} |
281 |
#endif |
282 |
|
283 |
// Open sheep_net or ethertap or TUN/TAP device |
284 |
char dev_name[16]; |
285 |
switch (net_if_type) { |
286 |
case NET_IF_ETHERTAP: |
287 |
sprintf(dev_name, "/dev/%s", name); |
288 |
break; |
289 |
case NET_IF_TUNTAP: |
290 |
strcpy(dev_name, "/dev/net/tun"); |
291 |
break; |
292 |
case NET_IF_SHEEPNET: |
293 |
strcpy(dev_name, "/dev/sheep_net"); |
294 |
break; |
295 |
} |
296 |
if (net_if_type != NET_IF_SLIRP) { |
297 |
fd = open(dev_name, O_RDWR); |
298 |
if (fd < 0) { |
299 |
sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno)); |
300 |
WarningAlert(str); |
301 |
goto open_error; |
302 |
} |
303 |
} |
304 |
|
305 |
#if ENABLE_TUNTAP |
306 |
// Open TUN/TAP interface |
307 |
if (net_if_type == NET_IF_TUNTAP) { |
308 |
struct ifreq ifr; |
309 |
memset(&ifr, 0, sizeof(ifr)); |
310 |
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; |
311 |
strcpy(ifr.ifr_name, "tun%d"); |
312 |
if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) { |
313 |
sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno)); |
314 |
WarningAlert(str); |
315 |
goto open_error; |
316 |
} |
317 |
|
318 |
// Get network config script file path |
319 |
net_if_script = PrefsFindString("etherconfig"); |
320 |
if (net_if_script == NULL) |
321 |
net_if_script = ETHERCONFIG_FILE_NAME; |
322 |
|
323 |
// Start network script up |
324 |
if (net_if_script == NULL) { |
325 |
sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found"); |
326 |
WarningAlert(str); |
327 |
goto open_error; |
328 |
} |
329 |
net_if_name = strdup(ifr.ifr_name); |
330 |
if (!execute_network_script("up")) { |
331 |
sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error"); |
332 |
WarningAlert(str); |
333 |
goto open_error; |
334 |
} |
335 |
D(bug("Connected to host network interface: %s\n", net_if_name)); |
336 |
} |
337 |
#endif |
338 |
|
339 |
#if defined(__linux__) |
340 |
// Attach sheep_net to selected Ethernet card |
341 |
if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) { |
342 |
sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno)); |
343 |
WarningAlert(str); |
344 |
goto open_error; |
345 |
} |
346 |
#endif |
347 |
|
348 |
// Set nonblocking I/O |
349 |
#ifdef USE_FIONBIO |
350 |
int nonblock = 1; |
351 |
if (ioctl(fd, FIONBIO, &nonblock) < 0) { |
352 |
sprintf(str, GetString(STR_BLOCKING_NET_SOCKET_WARN), strerror(errno)); |
353 |
WarningAlert(str); |
354 |
goto open_error; |
355 |
} |
356 |
#else |
357 |
val = fcntl(fd, F_GETFL, 0); |
358 |
if (val < 0 || fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) { |
359 |
sprintf(str, GetString(STR_BLOCKING_NET_SOCKET_WARN), strerror(errno)); |
360 |
WarningAlert(str); |
361 |
goto open_error; |
362 |
} |
363 |
#endif |
364 |
|
365 |
// Get Ethernet address |
366 |
if (net_if_type == NET_IF_ETHERTAP) { |
367 |
pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID |
368 |
ether_addr[0] = 0xfe; |
369 |
ether_addr[1] = 0xfd; |
370 |
ether_addr[2] = p >> 24; |
371 |
ether_addr[3] = p >> 16; |
372 |
ether_addr[4] = p >> 8; |
373 |
ether_addr[5] = p; |
374 |
#ifdef HAVE_SLIRP |
375 |
} else if (net_if_type == NET_IF_SLIRP) { |
376 |
ether_addr[0] = 0x52; |
377 |
ether_addr[1] = 0x54; |
378 |
ether_addr[2] = 0x00; |
379 |
ether_addr[3] = 0x12; |
380 |
ether_addr[4] = 0x34; |
381 |
ether_addr[5] = 0x56; |
382 |
#endif |
383 |
} else |
384 |
ioctl(fd, SIOCGIFADDR, ether_addr); |
385 |
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])); |
386 |
|
387 |
// Start packet reception thread |
388 |
if (!start_thread()) |
389 |
goto open_error; |
390 |
|
391 |
// Everything OK |
392 |
return true; |
393 |
|
394 |
open_error: |
395 |
stop_thread(); |
396 |
|
397 |
if (fd > 0) { |
398 |
close(fd); |
399 |
fd = -1; |
400 |
} |
401 |
if (slirp_input_fds[0] >= 0) { |
402 |
close(slirp_input_fds[0]); |
403 |
slirp_input_fds[0] = -1; |
404 |
} |
405 |
if (slirp_input_fds[1] >= 0) { |
406 |
close(slirp_input_fds[1]); |
407 |
slirp_input_fds[1] = -1; |
408 |
} |
409 |
if (slirp_output_fd >= 0) { |
410 |
close(slirp_output_fd); |
411 |
slirp_output_fd = -1; |
412 |
} |
413 |
return false; |
414 |
} |
415 |
|
416 |
|
417 |
/* |
418 |
* Deinitialization |
419 |
*/ |
420 |
|
421 |
void ether_exit(void) |
422 |
{ |
423 |
// Stop reception threads |
424 |
stop_thread(); |
425 |
|
426 |
// Shut down TUN/TAP interface |
427 |
if (net_if_type == NET_IF_TUNTAP) |
428 |
execute_network_script("down"); |
429 |
|
430 |
// Free TUN/TAP device name |
431 |
if (net_if_name) |
432 |
free(net_if_name); |
433 |
|
434 |
// Close sheep_net device |
435 |
if (fd > 0) |
436 |
close(fd); |
437 |
|
438 |
// Close slirp input buffer |
439 |
if (slirp_input_fds[0] >= 0) |
440 |
close(slirp_input_fds[0]); |
441 |
if (slirp_input_fds[1] >= 0) |
442 |
close(slirp_input_fds[1]); |
443 |
|
444 |
// Close slirp output buffer |
445 |
if (slirp_output_fd > 0) |
446 |
close(slirp_output_fd); |
447 |
|
448 |
#if STATISTICS |
449 |
// Show statistics |
450 |
printf("%ld messages put on write queue\n", num_wput); |
451 |
printf("%ld error acks\n", num_error_acks); |
452 |
printf("%ld packets transmitted (%ld raw, %ld normal)\n", num_tx_packets, num_tx_raw_packets, num_tx_normal_packets); |
453 |
printf("%ld tx packets dropped because buffer full\n", num_tx_buffer_full); |
454 |
printf("%ld packets received\n", num_rx_packets); |
455 |
printf("%ld packets passed upstream (%ld Fast Path, %ld normal)\n", num_rx_fastpath + num_unitdata_ind, num_rx_fastpath, num_unitdata_ind); |
456 |
printf("EtherIRQ called %ld times\n", num_ether_irq); |
457 |
printf("%ld rx packets dropped due to low memory\n", num_rx_no_mem); |
458 |
printf("%ld rx packets dropped because no stream found\n", num_rx_dropped); |
459 |
printf("%ld rx packets dropped because stream not ready\n", num_rx_stream_not_ready); |
460 |
printf("%ld rx packets dropped because no memory for unitdata_ind\n", num_rx_no_unitdata_mem); |
461 |
#endif |
462 |
} |
463 |
|
464 |
|
465 |
/* |
466 |
* Glue around low-level implementation |
467 |
*/ |
468 |
|
469 |
#ifdef SHEEPSHAVER |
470 |
// Error codes |
471 |
enum { |
472 |
eMultiErr = -91, |
473 |
eLenErr = -92, |
474 |
lapProtErr = -94, |
475 |
excessCollsns = -95 |
476 |
}; |
477 |
|
478 |
// Initialize ethernet |
479 |
void EtherInit(void) |
480 |
{ |
481 |
net_open = false; |
482 |
|
483 |
// Do nothing if the user disabled the network |
484 |
if (PrefsFindBool("nonet")) |
485 |
return; |
486 |
|
487 |
net_open = ether_init(); |
488 |
} |
489 |
|
490 |
// Exit ethernet |
491 |
void EtherExit(void) |
492 |
{ |
493 |
ether_exit(); |
494 |
net_open = false; |
495 |
} |
496 |
|
497 |
// Get ethernet hardware address |
498 |
void AO_get_ethernet_address(uint32 arg) |
499 |
{ |
500 |
uint8 *addr = Mac2HostAddr(arg); |
501 |
if (net_open) |
502 |
OTCopy48BitAddress(ether_addr, addr); |
503 |
else { |
504 |
addr[0] = 0x12; |
505 |
addr[1] = 0x34; |
506 |
addr[2] = 0x56; |
507 |
addr[3] = 0x78; |
508 |
addr[4] = 0x9a; |
509 |
addr[5] = 0xbc; |
510 |
} |
511 |
D(bug("AO_get_ethernet_address: got address %02x%02x%02x%02x%02x%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])); |
512 |
} |
513 |
|
514 |
// Add multicast address |
515 |
void AO_enable_multicast(uint32 addr) |
516 |
{ |
517 |
if (net_open) |
518 |
ether_do_add_multicast(Mac2HostAddr(addr)); |
519 |
} |
520 |
|
521 |
// Disable multicast address |
522 |
void AO_disable_multicast(uint32 addr) |
523 |
{ |
524 |
if (net_open) |
525 |
ether_do_del_multicast(Mac2HostAddr(addr)); |
526 |
} |
527 |
|
528 |
// Transmit one packet |
529 |
void AO_transmit_packet(uint32 mp) |
530 |
{ |
531 |
if (net_open) { |
532 |
switch (ether_do_write(mp)) { |
533 |
case noErr: |
534 |
num_tx_packets++; |
535 |
break; |
536 |
case excessCollsns: |
537 |
num_tx_buffer_full++; |
538 |
break; |
539 |
} |
540 |
} |
541 |
} |
542 |
|
543 |
// Copy packet data from message block to linear buffer |
544 |
static inline int ether_arg_to_buffer(uint32 mp, uint8 *p) |
545 |
{ |
546 |
return ether_msgb_to_buffer(mp, p); |
547 |
} |
548 |
|
549 |
// Ethernet interrupt |
550 |
void EtherIRQ(void) |
551 |
{ |
552 |
D(bug("EtherIRQ\n")); |
553 |
num_ether_irq++; |
554 |
|
555 |
OTEnterInterrupt(); |
556 |
ether_do_interrupt(); |
557 |
OTLeaveInterrupt(); |
558 |
|
559 |
// Acknowledge interrupt to reception thread |
560 |
D(bug(" EtherIRQ done\n")); |
561 |
sem_post(&int_ack); |
562 |
} |
563 |
#else |
564 |
// Add multicast address |
565 |
int16 ether_add_multicast(uint32 pb) |
566 |
{ |
567 |
return ether_do_add_multicast(Mac2HostAddr(pb + eMultiAddr)); |
568 |
} |
569 |
|
570 |
// Disable multicast address |
571 |
int16 ether_del_multicast(uint32 pb) |
572 |
{ |
573 |
return ether_do_del_multicast(Mac2HostAddr(pb + eMultiAddr)); |
574 |
} |
575 |
|
576 |
// Transmit one packet |
577 |
int16 ether_write(uint32 wds) |
578 |
{ |
579 |
return ether_do_write(wds); |
580 |
} |
581 |
|
582 |
// Copy packet data from WDS to linear buffer |
583 |
static inline int ether_arg_to_buffer(uint32 wds, uint8 *p) |
584 |
{ |
585 |
return ether_wds_to_buffer(wds, p); |
586 |
} |
587 |
|
588 |
// Dispatch packet to protocol handler |
589 |
static void ether_dispatch_packet(uint32 p, uint32 length) |
590 |
{ |
591 |
// Get packet type |
592 |
uint16 type = ReadMacInt16(p + 12); |
593 |
|
594 |
// Look for protocol |
595 |
uint16 search_type = (type <= 1500 ? 0 : type); |
596 |
if (net_protocols.find(search_type) == net_protocols.end()) |
597 |
return; |
598 |
uint32 handler = net_protocols[search_type]; |
599 |
|
600 |
// No default handler |
601 |
if (handler == 0) |
602 |
return; |
603 |
|
604 |
// Copy header to RHA |
605 |
Mac2Mac_memcpy(ether_data + ed_RHA, p, 14); |
606 |
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))); |
607 |
|
608 |
// Call protocol handler |
609 |
M68kRegisters r; |
610 |
r.d[0] = type; // Packet type |
611 |
r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket) |
612 |
r.a[0] = p + 14; // Pointer to packet (Mac address, for ReadPacket) |
613 |
r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA |
614 |
r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines |
615 |
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])); |
616 |
Execute68k(handler, &r); |
617 |
} |
618 |
|
619 |
// Ethernet interrupt |
620 |
void EtherInterrupt(void) |
621 |
{ |
622 |
D(bug("EtherIRQ\n")); |
623 |
ether_do_interrupt(); |
624 |
|
625 |
// Acknowledge interrupt to reception thread |
626 |
D(bug(" EtherIRQ done\n")); |
627 |
sem_post(&int_ack); |
628 |
} |
629 |
#endif |
630 |
|
631 |
|
632 |
/* |
633 |
* Reset |
634 |
*/ |
635 |
|
636 |
void ether_reset(void) |
637 |
{ |
638 |
net_protocols.clear(); |
639 |
} |
640 |
|
641 |
|
642 |
/* |
643 |
* Add multicast address |
644 |
*/ |
645 |
|
646 |
static int16 ether_do_add_multicast(uint8 *addr) |
647 |
{ |
648 |
switch (net_if_type) { |
649 |
case NET_IF_ETHERTAP: |
650 |
case NET_IF_SHEEPNET: |
651 |
if (ioctl(fd, SIOCADDMULTI, addr) < 0) { |
652 |
D(bug("WARNING: Couldn't enable multicast address\n")); |
653 |
if (net_if_type == NET_IF_ETHERTAP) |
654 |
return noErr; |
655 |
else |
656 |
return eMultiErr; |
657 |
} |
658 |
default: |
659 |
return noErr; |
660 |
} |
661 |
} |
662 |
|
663 |
|
664 |
/* |
665 |
* Delete multicast address |
666 |
*/ |
667 |
|
668 |
static int16 ether_do_del_multicast(uint8 *addr) |
669 |
{ |
670 |
switch (net_if_type) { |
671 |
case NET_IF_ETHERTAP: |
672 |
case NET_IF_SHEEPNET: |
673 |
if (ioctl(fd, SIOCDELMULTI, addr) < 0) { |
674 |
D(bug("WARNING: Couldn't disable multicast address\n")); |
675 |
return eMultiErr; |
676 |
} |
677 |
default: |
678 |
return noErr; |
679 |
} |
680 |
} |
681 |
|
682 |
|
683 |
/* |
684 |
* Attach protocol handler |
685 |
*/ |
686 |
|
687 |
int16 ether_attach_ph(uint16 type, uint32 handler) |
688 |
{ |
689 |
if (net_protocols.find(type) != net_protocols.end()) |
690 |
return lapProtErr; |
691 |
net_protocols[type] = handler; |
692 |
return noErr; |
693 |
} |
694 |
|
695 |
|
696 |
/* |
697 |
* Detach protocol handler |
698 |
*/ |
699 |
|
700 |
int16 ether_detach_ph(uint16 type) |
701 |
{ |
702 |
if (net_protocols.erase(type) == 0) |
703 |
return lapProtErr; |
704 |
return noErr; |
705 |
} |
706 |
|
707 |
|
708 |
/* |
709 |
* Transmit raw ethernet packet |
710 |
*/ |
711 |
|
712 |
static int16 ether_do_write(uint32 arg) |
713 |
{ |
714 |
// Copy packet to buffer |
715 |
uint8 packet[1516], *p = packet; |
716 |
int len = 0; |
717 |
#if defined(__linux__) |
718 |
if (net_if_type == NET_IF_ETHERTAP) { |
719 |
*p++ = 0; // Linux ethertap discards the first 2 bytes |
720 |
*p++ = 0; |
721 |
len += 2; |
722 |
} |
723 |
#endif |
724 |
len += ether_arg_to_buffer(arg, p); |
725 |
|
726 |
#if MONITOR |
727 |
bug("Sending Ethernet packet:\n"); |
728 |
for (int i=0; i<len; i++) { |
729 |
bug("%02x ", packet[i]); |
730 |
} |
731 |
bug("\n"); |
732 |
#endif |
733 |
|
734 |
// Transmit packet |
735 |
#ifdef HAVE_SLIRP |
736 |
if (net_if_type == NET_IF_SLIRP) { |
737 |
const int slirp_input_fd = slirp_input_fds[1]; |
738 |
write(slirp_input_fd, &len, sizeof(len)); |
739 |
write(slirp_input_fd, packet, len); |
740 |
return noErr; |
741 |
} else |
742 |
#endif |
743 |
if (write(fd, packet, len) < 0) { |
744 |
D(bug("WARNING: Couldn't transmit packet\n")); |
745 |
return excessCollsns; |
746 |
} else |
747 |
return noErr; |
748 |
} |
749 |
|
750 |
|
751 |
/* |
752 |
* Start UDP packet reception thread |
753 |
*/ |
754 |
|
755 |
bool ether_start_udp_thread(int socket_fd) |
756 |
{ |
757 |
fd = socket_fd; |
758 |
udp_tunnel = true; |
759 |
return start_thread(); |
760 |
} |
761 |
|
762 |
|
763 |
/* |
764 |
* Stop UDP packet reception thread |
765 |
*/ |
766 |
|
767 |
void ether_stop_udp_thread(void) |
768 |
{ |
769 |
stop_thread(); |
770 |
fd = -1; |
771 |
} |
772 |
|
773 |
|
774 |
/* |
775 |
* SLIRP output buffer glue |
776 |
*/ |
777 |
|
778 |
#ifdef HAVE_SLIRP |
779 |
int slirp_can_output(void) |
780 |
{ |
781 |
return 1; |
782 |
} |
783 |
|
784 |
void slirp_output(const uint8 *packet, int len) |
785 |
{ |
786 |
write(slirp_output_fd, packet, len); |
787 |
} |
788 |
|
789 |
void *slirp_receive_func(void *arg) |
790 |
{ |
791 |
const int slirp_input_fd = slirp_input_fds[0]; |
792 |
|
793 |
for (;;) { |
794 |
// Wait for packets to arrive |
795 |
fd_set rfds, wfds, xfds; |
796 |
int nfds; |
797 |
struct timeval tv; |
798 |
|
799 |
// ... in the input queue |
800 |
FD_ZERO(&rfds); |
801 |
FD_SET(slirp_input_fd, &rfds); |
802 |
tv.tv_sec = 0; |
803 |
tv.tv_usec = 0; |
804 |
if (select(slirp_input_fd + 1, &rfds, NULL, NULL, &tv) > 0) { |
805 |
int len; |
806 |
read(slirp_input_fd, &len, sizeof(len)); |
807 |
uint8 packet[1516]; |
808 |
assert(len <= sizeof(packet)); |
809 |
read(slirp_input_fd, packet, len); |
810 |
slirp_input(packet, len); |
811 |
} |
812 |
|
813 |
// ... in the output queue |
814 |
nfds = -1; |
815 |
FD_ZERO(&rfds); |
816 |
FD_ZERO(&wfds); |
817 |
FD_ZERO(&xfds); |
818 |
int timeout = slirp_select_fill(&nfds, &rfds, &wfds, &xfds); |
819 |
#if ! USE_SLIRP_TIMEOUT |
820 |
timeout = 10000; |
821 |
#endif |
822 |
tv.tv_sec = 0; |
823 |
tv.tv_usec = timeout; |
824 |
if (select(nfds + 1, &rfds, &wfds, &xfds, &tv) >= 0) |
825 |
slirp_select_poll(&rfds, &wfds, &xfds); |
826 |
|
827 |
#ifdef HAVE_PTHREAD_TESTCANCEL |
828 |
// Explicit cancellation point if select() was not covered |
829 |
// This seems to be the case on MacOS X 10.2 |
830 |
pthread_testcancel(); |
831 |
#endif |
832 |
} |
833 |
return NULL; |
834 |
} |
835 |
#else |
836 |
int slirp_can_output(void) |
837 |
{ |
838 |
return 0; |
839 |
} |
840 |
|
841 |
void slirp_output(const uint8 *packet, int len) |
842 |
{ |
843 |
} |
844 |
#endif |
845 |
|
846 |
|
847 |
/* |
848 |
* Packet reception thread |
849 |
*/ |
850 |
|
851 |
static void *receive_func(void *arg) |
852 |
{ |
853 |
for (;;) { |
854 |
|
855 |
// Wait for packets to arrive |
856 |
#if USE_POLL |
857 |
struct pollfd pf = {fd, POLLIN, 0}; |
858 |
int res = poll(&pf, 1, -1); |
859 |
#else |
860 |
fd_set rfds; |
861 |
FD_ZERO(&rfds); |
862 |
FD_SET(fd, &rfds); |
863 |
// A NULL timeout could cause select() to block indefinitely, |
864 |
// even if it is supposed to be a cancellation point [MacOS X] |
865 |
struct timeval tv = { 0, 20000 }; |
866 |
int res = select(fd + 1, &rfds, NULL, NULL, &tv); |
867 |
#ifdef HAVE_PTHREAD_TESTCANCEL |
868 |
pthread_testcancel(); |
869 |
#endif |
870 |
if (res == 0 || (res == -1 && errno == EINTR)) |
871 |
continue; |
872 |
#endif |
873 |
if (res <= 0) |
874 |
break; |
875 |
|
876 |
if (ether_driver_opened) { |
877 |
// Trigger Ethernet interrupt |
878 |
D(bug(" packet received, triggering Ethernet interrupt\n")); |
879 |
SetInterruptFlag(INTFLAG_ETHER); |
880 |
TriggerInterrupt(); |
881 |
|
882 |
// Wait for interrupt acknowledge by EtherInterrupt() |
883 |
sem_wait(&int_ack); |
884 |
} else |
885 |
Delay_usec(20000); |
886 |
} |
887 |
return NULL; |
888 |
} |
889 |
|
890 |
|
891 |
/* |
892 |
* Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers |
893 |
*/ |
894 |
|
895 |
void ether_do_interrupt(void) |
896 |
{ |
897 |
// Call protocol handler for received packets |
898 |
EthernetPacket ether_packet; |
899 |
uint32 packet = ether_packet.addr(); |
900 |
ssize_t length; |
901 |
for (;;) { |
902 |
|
903 |
#ifndef SHEEPSHAVER |
904 |
if (udp_tunnel) { |
905 |
|
906 |
// Read packet from socket |
907 |
struct sockaddr_in from; |
908 |
socklen_t from_len = sizeof(from); |
909 |
length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len); |
910 |
if (length < 14) |
911 |
break; |
912 |
ether_udp_read(packet, length, &from); |
913 |
|
914 |
} else |
915 |
#endif |
916 |
{ |
917 |
|
918 |
// Read packet from sheep_net device |
919 |
#if defined(__linux__) |
920 |
length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514); |
921 |
#else |
922 |
length = read(fd, Mac2HostAddr(packet), 1514); |
923 |
#endif |
924 |
if (length < 14) |
925 |
break; |
926 |
|
927 |
#if MONITOR |
928 |
bug("Receiving Ethernet packet:\n"); |
929 |
for (int i=0; i<length; i++) { |
930 |
bug("%02x ", ReadMacInt8(packet + i)); |
931 |
} |
932 |
bug("\n"); |
933 |
#endif |
934 |
|
935 |
// Pointer to packet data (Ethernet header) |
936 |
uint32 p = packet; |
937 |
#if defined(__linux__) |
938 |
if (net_if_type == NET_IF_ETHERTAP) { |
939 |
p += 2; // Linux ethertap has two random bytes before the packet |
940 |
length -= 2; |
941 |
} |
942 |
#endif |
943 |
|
944 |
// Dispatch packet |
945 |
ether_dispatch_packet(p, length); |
946 |
} |
947 |
} |
948 |
} |