ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/ether_unix.cpp
Revision: 1.2
Committed: 2001-07-12T19:48:27Z (23 years, 4 months ago) by cebix
Branch: MAIN
Changes since 1.1: +137 -79 lines
Log Message:
- Implemented AppleTalk-over-UDP tunnelling, activated by setting "udptunnel"
  to "true". This uses the BSD socket API, so it's fairly portable (currently
  only imeplemented under Unix, though). This works by sending raw Ethernet
  packets as UDP packets to a fixed port number ("udpport", default is 6066),
  using IP broadcasts to simulate Ethernet broad- and multicasts. Currently
  only tested with AppleTalk.

File Contents

# Content
1 /*
2 * ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
3 *
4 * Basilisk II (C) 1997-2001 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 #include <sys/ioctl.h>
24 #include <sys/poll.h>
25 #include <pthread.h>
26 #include <semaphore.h>
27 #include <errno.h>
28 #include <stdio.h>
29
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32
33 #if defined(__FreeBSD__)
34 #include <net/if.h>
35 #endif
36
37 #include "cpu_emulation.h"
38 #include "main.h"
39 #include "macos_util.h"
40 #include "prefs.h"
41 #include "user_strings.h"
42 #include "ether.h"
43 #include "ether_defs.h"
44
45 #define DEBUG 0
46 #include "debug.h"
47
48 #define MONITOR 0
49
50
51 // List of attached protocols
52 struct NetProtocol {
53 NetProtocol *next;
54 uint16 type;
55 uint32 handler;
56 };
57
58 static NetProtocol *prot_list = NULL;
59
60
61 // Global variables
62 static int fd = -1; // fd of sheep_net device
63 static pthread_t ether_thread; // Packet reception thread
64 static pthread_attr_t ether_thread_attr; // Packet reception thread attributes
65 static bool thread_active = false; // Flag: Packet reception thread installed
66 static sem_t int_ack; // Interrupt acknowledge semaphore
67 static bool is_ethertap; // Flag: Ethernet device is ethertap
68 static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor
69
70 // Prototypes
71 static void *receive_func(void *arg);
72
73
74 /*
75 * Find protocol in list
76 */
77
78 static NetProtocol *find_protocol(uint16 type)
79 {
80 // All 802.2 types are the same
81 if (type <= 1500)
82 type = 0;
83
84 // Search list (we could use hashing here but there are usually only three
85 // handlers installed: 0x0000 for AppleTalk and 0x0800/0x0806 for TCP/IP)
86 NetProtocol *p = prot_list;
87 while (p) {
88 if (p->type == type)
89 return p;
90 p = p->next;
91 }
92 return NULL;
93 }
94
95
96 /*
97 * Remove all protocols
98 */
99
100 static void remove_all_protocols(void)
101 {
102 NetProtocol *p = prot_list;
103 while (p) {
104 NetProtocol *next = p->next;
105 delete p;
106 p = next;
107 }
108 prot_list = NULL;
109 }
110
111
112 /*
113 * Start packet reception thread
114 */
115
116 static bool start_thread(void)
117 {
118 if (sem_init(&int_ack, 0, 0) < 0) {
119 printf("WARNING: Cannot init semaphore");
120 return false;
121 }
122
123 pthread_attr_init(&ether_thread_attr);
124 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
125 if (geteuid() == 0) {
126 pthread_attr_setinheritsched(&ether_thread_attr, PTHREAD_EXPLICIT_SCHED);
127 pthread_attr_setschedpolicy(&ether_thread_attr, SCHED_FIFO);
128 struct sched_param fifo_param;
129 fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2 + 1;
130 pthread_attr_setschedparam(&ether_thread_attr, &fifo_param);
131 }
132 #endif
133
134 thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
135 if (!thread_active) {
136 printf("WARNING: Cannot start Ethernet thread");
137 return false;
138 }
139
140 return true;
141 }
142
143
144 /*
145 * Stop packet reception thread
146 */
147
148 static void stop_thread(void)
149 {
150 if (thread_active) {
151 pthread_cancel(ether_thread);
152 pthread_join(ether_thread, NULL);
153 sem_destroy(&int_ack);
154 thread_active = false;
155 }
156 }
157
158
159 /*
160 * Initialization
161 */
162
163 bool ether_init(void)
164 {
165 int nonblock = 1;
166 char str[256];
167
168 // Do nothing if no Ethernet device specified
169 const char *name = PrefsFindString("ether");
170 if (name == NULL)
171 return false;
172
173 // Is it Ethertap?
174 is_ethertap = (strncmp(name, "tap", 3) == 0);
175
176 // Open sheep_net or ethertap device
177 char dev_name[16];
178 if (is_ethertap)
179 sprintf(dev_name, "/dev/%s", name);
180 else
181 strcpy(dev_name, "/dev/sheep_net");
182 fd = open(dev_name, O_RDWR);
183 if (fd < 0) {
184 sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
185 WarningAlert(str);
186 goto open_error;
187 }
188
189 #if defined(__linux__)
190 // Attach sheep_net to selected Ethernet card
191 if (!is_ethertap && ioctl(fd, SIOCSIFLINK, name) < 0) {
192 sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
193 WarningAlert(str);
194 goto open_error;
195 }
196 #endif
197
198 // Set nonblocking I/O
199 ioctl(fd, FIONBIO, &nonblock);
200
201 // Get Ethernet address
202 if (is_ethertap) {
203 pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
204 ether_addr[0] = 0xfe;
205 ether_addr[1] = 0xfd;
206 ether_addr[2] = p >> 24;
207 ether_addr[3] = p >> 16;
208 ether_addr[4] = p >> 8;
209 ether_addr[5] = p;
210 } else
211 ioctl(fd, SIOCGIFADDR, ether_addr);
212 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]));
213
214 // Start packet reception thread
215 if (!start_thread())
216 goto open_error;
217
218 // Everything OK
219 return true;
220
221 open_error:
222 stop_thread();
223
224 if (fd > 0) {
225 close(fd);
226 fd = -1;
227 }
228 return false;
229 }
230
231
232 /*
233 * Deinitialization
234 */
235
236 void ether_exit(void)
237 {
238 // Stop reception thread
239 if (thread_active) {
240 pthread_cancel(ether_thread);
241 pthread_join(ether_thread, NULL);
242 sem_destroy(&int_ack);
243 thread_active = false;
244 }
245
246 // Close sheep_net device
247 if (fd > 0)
248 close(fd);
249
250 // Remove all protocols
251 remove_all_protocols();
252 }
253
254
255 /*
256 * Reset
257 */
258
259 void EtherReset(void)
260 {
261 remove_all_protocols();
262 }
263
264
265 /*
266 * Add multicast address
267 */
268
269 int16 ether_add_multicast(uint32 pb)
270 {
271 if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
272 D(bug("WARNING: Couldn't enable multicast address\n"));
273 if (is_ethertap)
274 return noErr;
275 else
276 return eMultiErr;
277 } else
278 return noErr;
279 }
280
281
282 /*
283 * Delete multicast address
284 */
285
286 int16 ether_del_multicast(uint32 pb)
287 {
288 if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) {
289 D(bug("WARNING: Couldn't disable multicast address\n"));
290 return eMultiErr;
291 } else
292 return noErr;
293 }
294
295
296 /*
297 * Attach protocol handler
298 */
299
300 int16 ether_attach_ph(uint16 type, uint32 handler)
301 {
302 // Already attached?
303 NetProtocol *p = find_protocol(type);
304 if (p != NULL)
305 return lapProtErr;
306 else {
307 // No, create and attach
308 p = new NetProtocol;
309 p->next = prot_list;
310 p->type = type;
311 p->handler = handler;
312 prot_list = p;
313 return noErr;
314 }
315 }
316
317
318 /*
319 * Detach protocol handler
320 */
321
322 int16 ether_detach_ph(uint16 type)
323 {
324 NetProtocol *p = find_protocol(type);
325 if (p != NULL) {
326 NetProtocol *q = prot_list;
327 if (p == q) {
328 prot_list = p->next;
329 delete p;
330 return noErr;
331 }
332 while (q) {
333 if (q->next == p) {
334 q->next = p->next;
335 delete p;
336 return noErr;
337 }
338 q = q->next;
339 }
340 return lapProtErr;
341 } else
342 return lapProtErr;
343 }
344
345
346 /*
347 * Transmit raw ethernet packet
348 */
349
350 int16 ether_write(uint32 wds)
351 {
352 // Set source address
353 uint32 hdr = ReadMacInt32(wds + 2);
354 Host2Mac_memcpy(hdr + 6, ether_addr, 6);
355
356 // Copy packet to buffer
357 uint8 packet[1516], *p = packet;
358 int len = 0;
359 #if defined(__linux__)
360 if (is_ethertap) {
361 *p++ = 0; // Linux ethertap discards the first 2 bytes
362 *p++ = 0;
363 len += 2;
364 }
365 #endif
366 len += ether_wds_to_buffer(wds, p);
367
368 #if MONITOR
369 bug("Sending Ethernet packet:\n");
370 for (int i=0; i<len; i++) {
371 bug("%02x ", packet[i]);
372 }
373 bug("\n");
374 #endif
375
376 // Transmit packet
377 if (write(fd, packet, len) < 0) {
378 D(bug("WARNING: Couldn't transmit packet\n"));
379 return excessCollsns;
380 } else
381 return noErr;
382 }
383
384
385 /*
386 * Start UDP packet reception thread
387 */
388
389 bool ether_start_udp_thread(int socket_fd)
390 {
391 fd = socket_fd;
392 udp_tunnel = true;
393 return start_thread();
394 }
395
396
397 /*
398 * Stop UDP packet reception thread
399 */
400
401 void ether_stop_udp_thread(void)
402 {
403 stop_thread();
404 fd = -1;
405 }
406
407
408 /*
409 * Packet reception thread
410 */
411
412 static void *receive_func(void *arg)
413 {
414 for (;;) {
415
416 // Wait for packets to arrive
417 struct pollfd pf = {fd, POLLIN, 0};
418 int res = poll(&pf, 1, -1);
419 if (res <= 0)
420 break;
421
422 // Trigger Ethernet interrupt
423 D(bug(" packet received, triggering Ethernet interrupt\n"));
424 SetInterruptFlag(INTFLAG_ETHER);
425 TriggerInterrupt();
426
427 // Wait for interrupt acknowledge by EtherInterrupt()
428 sem_wait(&int_ack);
429 }
430 return NULL;
431 }
432
433
434 /*
435 * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
436 */
437
438 void EtherInterrupt(void)
439 {
440 D(bug("EtherIRQ\n"));
441
442 // Call protocol handler for received packets
443 uint8 packet[1516];
444 ssize_t length;
445 for (;;) {
446
447 if (udp_tunnel) {
448
449 // Read packet from socket
450 struct sockaddr_in from;
451 socklen_t from_len = sizeof(from);
452 length = recvfrom(fd, packet, 1514, 0, (struct sockaddr *)&from, &from_len);
453 if (length < 14)
454 break;
455 ether_udp_read(packet, length, &from);
456
457 } else {
458
459 // Read packet from sheep_net device
460 #if defined(__linux__)
461 length = read(fd, packet, is_ethertap ? 1516 : 1514);
462 #else
463 length = read(fd, packet, 1514);
464 #endif
465 if (length < 14)
466 break;
467
468 #if MONITOR
469 bug("Receiving Ethernet packet:\n");
470 for (int i=0; i<length; i++) {
471 bug("%02x ", packet[i]);
472 }
473 bug("\n");
474 #endif
475
476 // Pointer to packet data (Ethernet header)
477 uint8 *p = packet;
478 #if defined(__linux__)
479 if (is_ethertap) {
480 p += 2; // Linux ethertap has two random bytes before the packet
481 length -= 2;
482 }
483 #endif
484
485 // Get packet type
486 uint16 type = (p[12] << 8) | p[13];
487
488 // Look for protocol
489 NetProtocol *prot = find_protocol(type);
490 if (prot == NULL)
491 continue;
492
493 // No default handler
494 if (prot->handler == 0)
495 continue;
496
497 // Copy header to RHA
498 Host2Mac_memcpy(ether_data + ed_RHA, p, 14);
499 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)));
500
501 // Call protocol handler
502 M68kRegisters r;
503 r.d[0] = type; // Packet type
504 r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
505 r.a[0] = (uint32)p + 14; // Pointer to packet (host address, for ReadPacket)
506 r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
507 r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
508 D(bug(" calling protocol handler %08x, type %08x, length %08x, data %08x, rha %08x, read_packet %08x\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4]));
509 Execute68k(prot->handler, &r);
510 }
511 }
512
513 // Acknowledge interrupt to reception thread
514 D(bug(" EtherIRQ done\n"));
515 sem_post(&int_ack);
516 }