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

# User Rev Content
1 cebix 1.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 cebix 1.2 #include <netinet/in.h>
31     #include <sys/socket.h>
32    
33 cebix 1.1 #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 cebix 1.2 static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor
69 cebix 1.1
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 cebix 1.2 * 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 cebix 1.1 * Initialization
161     */
162    
163 cebix 1.2 bool ether_init(void)
164 cebix 1.1 {
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 cebix 1.2 return false;
172 cebix 1.1
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 cebix 1.2 if (!start_thread())
216 cebix 1.1 goto open_error;
217    
218     // Everything OK
219 cebix 1.2 return true;
220 cebix 1.1
221     open_error:
222 cebix 1.2 stop_thread();
223    
224 cebix 1.1 if (fd > 0) {
225     close(fd);
226     fd = -1;
227     }
228 cebix 1.2 return false;
229 cebix 1.1 }
230    
231    
232     /*
233     * Deinitialization
234     */
235    
236 cebix 1.2 void ether_exit(void)
237 cebix 1.1 {
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 cebix 1.2 len += ether_wds_to_buffer(wds, p);
367 cebix 1.1
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 cebix 1.2 * 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 cebix 1.1 * 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 cebix 1.2 ssize_t length;
445 cebix 1.1 for (;;) {
446    
447 cebix 1.2 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 cebix 1.1 #if defined(__linux__)
461 cebix 1.2 length = read(fd, packet, is_ethertap ? 1516 : 1514);
462 cebix 1.1 #else
463 cebix 1.2 length = read(fd, packet, 1514);
464 cebix 1.1 #endif
465 cebix 1.2 if (length < 14)
466     break;
467 cebix 1.1
468     #if MONITOR
469 cebix 1.2 bug("Receiving Ethernet packet:\n");
470     for (int i=0; i<length; i++) {
471     bug("%02x ", packet[i]);
472     }
473     bug("\n");
474 cebix 1.1 #endif
475    
476 cebix 1.2 // Pointer to packet data (Ethernet header)
477     uint8 *p = packet;
478 cebix 1.1 #if defined(__linux__)
479 cebix 1.2 if (is_ethertap) {
480     p += 2; // Linux ethertap has two random bytes before the packet
481     length -= 2;
482     }
483 cebix 1.1 #endif
484    
485 cebix 1.2 // Get packet type
486     uint16 type = (p[12] << 8) | p[13];
487 cebix 1.1
488 cebix 1.2 // 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 cebix 1.1 }
512    
513     // Acknowledge interrupt to reception thread
514     D(bug(" EtherIRQ done\n"));
515     sem_post(&int_ack);
516     }