ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/ether.cpp
Revision: 1.5
Committed: 2001-07-12T19:48:25Z (23 years, 4 months ago) by cebix
Branch: MAIN
Changes since 1.4: +269 -37 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.cpp - Ethernet device driver
3     *
4 cebix 1.4 * Basilisk II (C) 1997-2001 Christian Bauer
5 cebix 1.1 *
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     /*
22     * SEE ALSO
23     * Inside Macintosh: Devices, chapter 1 "Device Manager"
24     * Inside Macintosh: Networking, chapter 11 "Ethernet, Token Ring, and FDDI"
25     * Inside AppleTalk, chapter 3 "Ethernet and TokenTalk Link Access Protocols"
26     */
27    
28     #include <string.h>
29    
30     #include "sysdeps.h"
31     #include "cpu_emulation.h"
32     #include "main.h"
33     #include "macos_util.h"
34     #include "emul_op.h"
35 cebix 1.5 #include "prefs.h"
36 cebix 1.1 #include "ether.h"
37     #include "ether_defs.h"
38    
39 cebix 1.5 #if SUPPORTS_UDP_TUNNEL
40     #include <netinet/in.h>
41     #include <sys/socket.h>
42     #include <sys/ioctl.h>
43     #include <netdb.h>
44     #include <errno.h>
45     #endif
46    
47     #include <map>
48    
49     #ifndef NO_STD_NAMESPACE
50     using std::map;
51     #endif
52    
53 cebix 1.1 #define DEBUG 0
54     #include "debug.h"
55    
56 cebix 1.5 #define MONITOR 0
57    
58 cebix 1.1
59     // Global variables
60 cebix 1.5 uint8 ether_addr[6]; // Ethernet address (set by ether_init())
61     static bool net_open = false; // Flag: initialization succeeded, network device open (set by EtherInit())
62    
63     static bool udp_tunnel = false; // Flag: tunnelling AppleTalk over UDP using BSD socket API
64     static uint16 udp_port;
65     static int udp_socket = -1;
66    
67     // Mac address of driver data in MacOS RAM
68     uint32 ether_data = 0;
69 cebix 1.1
70 cebix 1.5 // Attached network protocols for UDP tunneling, maps protocol type to MacOS handler address
71     static map<uint16, uint32> udp_protocols;
72    
73    
74     /*
75     * Initialization
76     */
77    
78     void EtherInit(void)
79     {
80     net_open = false;
81     udp_tunnel = false;
82    
83     #if SUPPORTS_UDP_TUNNEL
84     // UDP tunnelling requested?
85     if (PrefsFindBool("udptunnel")) {
86     udp_tunnel = true;
87     udp_port = PrefsFindInt32("udpport");
88    
89     // Open UDP socket
90     udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
91     if (udp_socket < 0) {
92     perror("socket");
93     return;
94     }
95    
96     // Bind to specified address and port
97     struct sockaddr_in sa;
98     memset(&sa, 0, sizeof(sa));
99     sa.sin_family = AF_INET;
100     sa.sin_addr.s_addr = INADDR_ANY;
101     sa.sin_port = htons(udp_port);
102     if (bind(udp_socket, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
103     perror("bind");
104     close(udp_socket);
105     udp_socket = -1;
106     return;
107     }
108    
109     // Retrieve local IP address (or at least one of them)
110     socklen_t sa_len = sizeof(sa);
111     getsockname(udp_socket, (struct sockaddr *)&sa, &sa_len);
112     uint32 udp_ip = sa.sin_addr.s_addr;
113     if (udp_ip == INADDR_ANY || udp_ip == INADDR_LOOPBACK) {
114     char name[256];
115     gethostname(name, sizeof(name));
116     struct hostent *local = gethostbyname(name);
117     if (local)
118     udp_ip = *(uint32 *)local->h_addr_list[0];
119     }
120     udp_ip = ntohl(udp_ip);
121    
122     // Construct dummy Ethernet address from local IP address
123     ether_addr[0] = 'B';
124     ether_addr[1] = '2';
125     ether_addr[2] = udp_ip >> 24;
126     ether_addr[3] = udp_ip >> 16;
127     ether_addr[4] = udp_ip >> 8;
128     ether_addr[5] = udp_ip;
129     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]));
130    
131     // Set socket options
132     int on = 1;
133     setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
134     ioctl(udp_socket, FIONBIO, &on);
135    
136     // Start thread for packet reception
137     if (!ether_start_udp_thread(udp_socket)) {
138     close(udp_socket);
139     udp_socket = -1;
140     return;
141     }
142    
143     net_open = true;
144     } else
145     #endif
146     if (ether_init())
147     net_open = true;
148     }
149    
150    
151     /*
152     * Deinitialization
153     */
154    
155     void EtherExit(void)
156     {
157     if (net_open) {
158     #if SUPPORTS_UDP_TUNNEL
159     if (udp_tunnel) {
160     if (udp_socket >= 0) {
161     ether_stop_udp_thread();
162     close(udp_socket);
163     udp_socket = -1;
164     }
165     } else
166     #endif
167     ether_exit();
168     net_open = false;
169     }
170     }
171    
172    
173     /*
174     * Check whether Ethernet address is AppleTalk broadcast address
175     */
176    
177     static inline bool is_apple_talk_broadcast(uint8 *p)
178     {
179     return p[0] == 0x09 && p[1] == 0x00 && p[2] == 0x07
180     && p[3] == 0xff && p[4] == 0xff && p[5] == 0xff;
181     }
182 cebix 1.1
183    
184     /*
185     * Driver Open() routine
186     */
187    
188     int16 EtherOpen(uint32 pb, uint32 dce)
189     {
190     D(bug("EtherOpen\n"));
191    
192     // Allocate driver data
193     M68kRegisters r;
194     r.d[0] = SIZEOF_etherdata;
195     Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
196     if (r.a[0] == 0)
197     return openErr;
198     ether_data = r.a[0];
199 cebix 1.5 D(bug(" data %08x\n", ether_data));
200 cebix 1.1
201     WriteMacInt16(ether_data + ed_DeferredTask + qType, dtQType);
202     WriteMacInt32(ether_data + ed_DeferredTask + dtAddr, ether_data + ed_Code);
203     WriteMacInt32(ether_data + ed_DeferredTask + dtParam, ether_data + ed_Result);
204     // Deferred function for signalling that packet write is complete (pointer to mydtResult in a1)
205     WriteMacInt16(ether_data + ed_Code, 0x2019); // move.l (a1)+,d0 (result)
206     WriteMacInt16(ether_data + ed_Code + 2, 0x2251); // move.l (a1),a1 (dce)
207     WriteMacInt32(ether_data + ed_Code + 4, 0x207808fc); // move.l JIODone,a0
208     WriteMacInt16(ether_data + ed_Code + 8, 0x4ed0); // jmp (a0)
209    
210     WriteMacInt32(ether_data + ed_DCE, dce);
211     // ReadPacket/ReadRest routines
212     WriteMacInt16(ether_data + ed_ReadPacket, 0x6010); // bra 2
213     WriteMacInt16(ether_data + ed_ReadPacket + 2, 0x3003); // move.w d3,d0
214     WriteMacInt16(ether_data + ed_ReadPacket + 4, 0x9041); // sub.w d1,d0
215     WriteMacInt16(ether_data + ed_ReadPacket + 6, 0x4a43); // tst.w d3
216     WriteMacInt16(ether_data + ed_ReadPacket + 8, 0x6702); // beq 1
217     WriteMacInt16(ether_data + ed_ReadPacket + 10, M68K_EMUL_OP_ETHER_READ_PACKET);
218     WriteMacInt16(ether_data + ed_ReadPacket + 12, 0x3600); //1 move.w d0,d3
219     WriteMacInt16(ether_data + ed_ReadPacket + 14, 0x7000); // moveq #0,d0
220     WriteMacInt16(ether_data + ed_ReadPacket + 16, 0x4e75); // rts
221     WriteMacInt16(ether_data + ed_ReadPacket + 18, M68K_EMUL_OP_ETHER_READ_PACKET); //2
222     WriteMacInt16(ether_data + ed_ReadPacket + 20, 0x4a43); // tst.w d3
223     WriteMacInt16(ether_data + ed_ReadPacket + 22, 0x4e75); // rts
224     return 0;
225     }
226    
227    
228     /*
229     * Driver Control() routine
230     */
231    
232     int16 EtherControl(uint32 pb, uint32 dce)
233     {
234     uint16 code = ReadMacInt16(pb + csCode);
235     D(bug("EtherControl %d\n", code));
236     switch (code) {
237 cebix 1.5 case 1: // KillIO
238 cebix 1.1 return -1;
239    
240     case kENetAddMulti: // Add multicast address
241 cebix 1.5 D(bug(" AddMulti %08x%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4)));
242     if (net_open && !udp_tunnel)
243 cebix 1.1 return ether_add_multicast(pb);
244 cebix 1.5 return noErr;
245 cebix 1.1
246     case kENetDelMulti: // Delete multicast address
247 cebix 1.5 D(bug(" DelMulti %08x%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4)));
248     if (net_open && !udp_tunnel)
249 cebix 1.1 return ether_del_multicast(pb);
250 cebix 1.5 return noErr;
251    
252     case kENetAttachPH: { // Attach protocol handler
253     uint16 type = ReadMacInt16(pb + eProtType);
254     uint32 handler = ReadMacInt32(pb + ePointer);
255     D(bug(" AttachPH prot %04x, handler %08x\n", type, handler));
256     if (net_open) {
257     if (udp_tunnel) {
258     if (udp_protocols.find(type) != udp_protocols.end())
259     return lapProtErr;
260     udp_protocols[type] = handler;
261     } else
262     return ether_attach_ph(type, handler);
263     }
264     return noErr;
265     }
266    
267     case kENetDetachPH: { // Detach protocol handler
268     uint16 type = ReadMacInt16(pb + eProtType);
269     D(bug(" DetachPH prot %04x\n", type));
270     if (net_open) {
271     if (udp_tunnel) {
272     if (udp_protocols.erase(type) == 0)
273     return lapProtErr;
274     } else
275     return ether_detach_ph(type);
276     }
277     return noErr;
278     }
279 cebix 1.1
280 cebix 1.5 case kENetWrite: { // Transmit raw Ethernet packet
281     uint32 wds = ReadMacInt32(pb + ePointer);
282     D(bug(" EtherWrite\n"));
283     if (ReadMacInt16(wds) < 14)
284 cebix 1.1 return eLenErr; // Header incomplete
285 cebix 1.5 if (net_open) {
286     #if SUPPORTS_UDP_TUNNEL
287     if (udp_tunnel) {
288    
289     // Copy packet to buffer
290     uint8 packet[1514];
291     int len = ether_wds_to_buffer(wds, packet);
292    
293     // Set source address and extract destination address
294     memcpy(packet + 6, ether_addr, 6);
295     uint32 dest_ip;
296     if (packet[0] == 'B' && packet[1] == '2')
297     dest_ip = (packet[2] << 24) | (packet[3] << 16) | (packet[4] << 8) | packet[5];
298     else if (is_apple_talk_broadcast(packet))
299     dest_ip = INADDR_BROADCAST;
300     else
301     return eMultiErr;
302    
303     #if MONITOR
304     bug("Sending Ethernet packet:\n");
305     for (int i=0; i<len; i++) {
306     bug("%02x ", packet[i]);
307     }
308     bug("\n");
309     #endif
310    
311     // Send packet
312     struct sockaddr_in sa;
313     sa.sin_family = AF_INET;
314     sa.sin_addr.s_addr = htonl(dest_ip);
315     sa.sin_port = htons(udp_port);
316     if (sendto(udp_socket, packet, len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
317     D(bug("WARNING: Couldn't transmit packet\n"));
318     return excessCollsns;
319     }
320     } else
321     #endif
322     return ether_write(wds);
323     }
324     return noErr;
325     }
326 cebix 1.1
327     case kENetGetInfo: { // Get device information/statistics
328 cebix 1.5 D(bug(" GetInfo buf %08x, size %d\n", ReadMacInt32(pb + ePointer), ReadMacInt16(pb + eBuffSize)));
329 cebix 1.1
330     // Collect info (only ethernet address)
331     uint8 buf[18];
332     memset(buf, 0, 18);
333     memcpy(buf, ether_addr, 6);
334    
335     // Transfer info to supplied buffer
336     int16 size = ReadMacInt16(pb + eBuffSize);
337     if (size > 18)
338     size = 18;
339     WriteMacInt16(pb + eDataSize, size); // Number of bytes actually written
340 cebix 1.2 Host2Mac_memcpy(ReadMacInt32(pb + ePointer), buf, size);
341 cebix 1.1 return noErr;
342     }
343    
344     case kENetSetGeneral: // Set general mode (always in general mode)
345 cebix 1.5 D(bug(" SetGeneral\n"));
346 cebix 1.1 return noErr;
347    
348     default:
349     printf("WARNING: Unknown EtherControl(%d)\n", code);
350     return controlErr;
351     }
352     }
353    
354    
355     /*
356     * Ethernet ReadPacket routine
357     */
358    
359     void EtherReadPacket(uint8 **src, uint32 &dest, uint32 &len, uint32 &remaining)
360     {
361 cebix 1.5 D(bug("EtherReadPacket src %p, dest %08x, len %08x, remaining %08x\n", *src, dest, len, remaining));
362 cebix 1.1 uint32 todo = len > remaining ? remaining : len;
363 cebix 1.2 Host2Mac_memcpy(dest, *src, todo);
364 cebix 1.1 *src += todo;
365     dest += todo;
366     len -= todo;
367     remaining -= todo;
368     }
369 cebix 1.5
370    
371     #if SUPPORTS_UDP_TUNNEL
372     /*
373     * Read packet from UDP socket
374     */
375    
376     void ether_udp_read(uint8 *packet, int length, struct sockaddr_in *from)
377     {
378     // Drop packets sent by us
379     if (memcmp(packet + 6, ether_addr, 6) == 0)
380     return;
381    
382     #if MONITOR
383     bug("Receiving Ethernet packet:\n");
384     for (int i=0; i<length; i++) {
385     bug("%02x ", packet[i]);
386     }
387     bug("\n");
388     #endif
389    
390     // Get packet type
391     uint16 type = (packet[12] << 8) | packet[13];
392    
393     // Look for protocol
394     uint16 search_type = (type <= 1500 ? 0 : type);
395     if (udp_protocols.find(search_type) == udp_protocols.end())
396     return;
397     uint32 handler = udp_protocols[search_type];
398     if (handler == 0)
399     return;
400    
401     // Copy header to RHA
402     Host2Mac_memcpy(ether_data + ed_RHA, packet, 14);
403     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)));
404    
405     // Call protocol handler
406     M68kRegisters r;
407     r.d[0] = type; // Packet type
408     r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket)
409     r.a[0] = (uint32)packet + 14; // Pointer to packet (host address, for ReadPacket)
410     r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA
411     r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines
412     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]));
413     Execute68k(handler, &r);
414     }
415     #endif