First Commit of my working state
[simh.git] / sim_ether.c
CommitLineData
196ba1fc
PH
1/* sim_ether.c: OS-dependent network routines\r
2 ------------------------------------------------------------------------------\r
3 Copyright (c) 2002-2007, David T. Hittner\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of the author shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from the author.\r
25\r
26 ------------------------------------------------------------------------------\r
27\r
28 This ethernet simulation is based on the PCAP and WinPcap packages.\r
29\r
30 PCAP/WinPcap was chosen as the basis for network code since it is the most\r
31 "universal" of the various network packages available. Using this style has\r
32 allowed rapid network development for the major SIMH platforms. Developing\r
33 a network package specifically for SIMH was rejected due to the time required;\r
34 the advantage would be a more easily compiled and integrated code set.\r
35\r
36 There are various problems associated with use of ethernet networking, which\r
37 would be true regardless of the network package used, since there are no\r
38 universally accepted networking methods. The most serious of these is getting\r
39 the proper networking package loaded onto the system, since most environments\r
40 do not come with the network interface packages loaded.\r
41\r
42 The second most serious network issue relates to security. The network\r
43 simulation needs to simulate operating system level functionality (packet\r
44 driving). However, the host network programming interfaces tend to operate at\r
45 the user level of functionality, so getting to the full functionality of\r
46 the network interface usually requires that the person executing the\r
47 network code be a privileged user of the host system. See the PCAP/WinPcap\r
48 documentation for the appropriate host platform if unprivileged use of\r
49 networking is needed - there may be known workarounds.\r
50\r
51 Define one of the two macros below to enable networking:\r
52 USE_NETWORK - Create statically linked network code\r
53 USE_SHARED - Create dynamically linked network code (_WIN32 only)\r
54\r
55 ------------------------------------------------------------------------------\r
56\r
57 Supported/Tested Platforms:\r
58\r
59 Windows(NT,2K,XP,2K3) WinPcap V3.0+\r
60 Linux libpcap at least 0.9\r
61 OpenBSD,FreeBSD,NetBSD libpcap at least 0.9\r
62 MAC OS/X libpcap at least 0.9\r
63 Solaris Sparc libpcap at least 0.9\r
64 Solaris Intel libpcap at least 0.9\r
65 AIX ??\r
66 HP/UX ??\r
67 Compaq Tru64 Unix ??\r
68 VMS Alpha/Itanium VMS only, needs VMS libpcap\r
69 \r
70 WinPcap is available from: \r
71 http://winpcap.polito.it/\r
72 libpcap for VMS is available from: \r
73 http://simh.trailing-edge.com/sources/vms-pcap.zip\r
74 libpcap for other Unix platforms is available at: \r
75 Current Version: http://www.tcpdump.org/daily/libpcap-current.tar.gz\r
76 Released Version: http://www.tcpdump.org/release/\r
77 Note: You can only use the released version if it is at least \r
78 version 0.9\r
79\r
80 \r
81 We've gotten the tarball, unpacked, built and installed it with:\r
82 gzip -dc libpcap-current.tar.gz | tar xvf -\r
83 cd libpcap-directory-name\r
84 ./configure\r
85 make\r
86 make install\r
87 Note: The "make install" step generally will have to be done as root.\r
88 This will install libpcap in /usr/local/lib and /usr/local/include\r
89 It is then important to make sure that you get the just installed \r
90 libpcap components referenced during your build. This is generally \r
91 achieved by invoking gcc with: \r
92 -isystem /usr/local/include -L /usr/local/lib\r
93\r
94\r
95 Note: Building for the platforms indicated above, with the indicated libpcap, \r
96 should automatically leverage the appropriate mechanisms contained here. \r
97 Things are structured so that it is likely to work for any other as yet \r
98 untested platform. If it works for you, please let the author know so we \r
99 can update the table above. If it doesn't work, then the following #define \r
100 variables can influence the operation on an untested platform.\r
101\r
102 USE_BPF - Determines if this code leverages a libpcap/WinPcap \r
103 provided bpf packet filtering facility. All tested \r
104 environments have bpf facilities that work the way we \r
105 need them to. However a new one might not. undefine \r
106 this variable to let this code do its own filtering.\r
107 USE_SETNONBLOCK - Specifies whether the libpcap environment's non-blocking \r
108 semantics are to be leveraged. This helps to manage the \r
109 varying behaviours of the kernel packet facilities \r
110 leveraged by libpcap.\r
111 USE_READER_THREAD - Specifies that packet reading should be done in the \r
112 context of a separate thread. The Posix threading \r
113 APIs are used. This option is less efficient than the\r
114 default non-threaded approach, but it exists since some \r
115 platforms don't want to work with nonblocking libpcap \r
116 semantics. OpenBSD and NetBSD either don't have pthread \r
117 APIs available, or they are too buggy to be useful. \r
118 Using the threaded approach may require special compile \r
119 and/or link time switches (i.e. -lpthread or -pthread, \r
120 etc.) Consult the documentation for your platform as \r
121 needed.\r
122 MUST_DO_SELECT - Specifies that when USE_READER_THREAD is active, that \r
123 select() should be used to determin when available \r
124 packets are ready for reading. Otherwise, we depend \r
125 on the libpcap/kernel packet timeout specified on \r
126 pcap_open_live. If USE_READER_THREAD is not set, then \r
127 MUST_DO_SELECT is irrelevant\r
128\r
129 NEED_PCAP_SENDPACKET\r
130 - Specifies that you are using an older version of libpcap\r
131 which doesn't provide a pcap_sendpacket API.\r
132\r
133 NOTE: Changing these defines is done in either sim_ether.h OR on the global \r
134 compiler command line which builds all of the modules included in a\r
135 simulator.\r
136\r
137 ------------------------------------------------------------------------------\r
138\r
139 Modification history:\r
140\r
141 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada)\r
142 15-May-07 DTH Added dynamic loading of wpcap.dll;\r
143 Corrected exceed max index bug in ethX lookup\r
144 04-May-07 DTH Corrected failure to look up ethernet device names in\r
145 the registry on Windows XP x64\r
146 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman)\r
147 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter\r
148 15-Dec-05 DTH Patched eth_host_devices [remove non-ethernet devices]\r
149 (from Mark Pizzolato and Galen Tackett, 08-Jun-05)\r
150 Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05)\r
151 30-Nov-05 DTH Added option to regenerate CRC on received packets; some\r
152 ethernet devices need to pass it on to the simulation, and by\r
153 the time libpcap/winpcap gets the packet, the host OS network\r
154 layer has already stripped CRC out of the packet\r
155 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt)\r
156 25-Mar-04 MP Revised comments and minor #defines to deal with updated\r
157 libpcap which now provides pcap_sendpacket on all platforms.\r
158 04-Feb-04 MP Returned success/fail status from eth_write to support\r
159 determining if the current libpcap connection can successfully \r
160 write packets.\r
161 Added threaded approach to reading packets since\r
162 this works better on some platforms (solaris intel) than the \r
163 inconsistently implemented non-blocking read approach.\r
164 04-Feb-04 DTH Converted ETH_DEBUG to sim_debug\r
165 13-Jan-04 MP tested and fixed on OpenBSD, NetBS and FreeBSD.\r
166 09-Jan-04 MP removed the BIOCSHDRCMPLT ioctl() for OS/X\r
167 05-Jan-04 DTH Added eth_mac_scan\r
168 30-Dec-03 DTH Cleaned up queue routines, added no network support message\r
169 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq\r
170 15-Dec-03 MP polished generic libpcap support.\r
171 05-Dec-03 DTH Genericized eth_devices() and #ifdefs\r
172 03-Dec-03 MP Added Solaris support\r
173 02-Dec-03 DTH Corrected decnet fix to use reflection counting\r
174 01-Dec-03 DTH Added BPF source filtering and reflection counting\r
175 28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs()\r
176 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code\r
177 19-Nov-03 MP Fixed BPF functionality on Linux/BSD.\r
178 17-Nov-03 DTH Added xBSD simplification\r
179 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code\r
180 13-Nov-03 DTH Merged in __FreeBSD__ support\r
181 21-Oct-03 MP Added enriched packet dumping for debugging\r
182 20-Oct-03 MP Added support for multiple ethernet devices on VMS\r
183 20-Sep-03 Ankan Add VMS support (Alpha only)\r
184 29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to\r
185 format ethernet addresses the way the BPF compile engine\r
186 wants to see them.\r
187 Added BPF support to filter packets\r
188 Added missing printf in eth_close\r
189 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection.\r
190 06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace\r
191 30-May-03 DTH Changed WIN32 to _WIN32 for consistency\r
192 07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also\r
193 work on Red Hat 6.2-sparc and Debian 3.0r1-sparc.\r
194 03-Mar-03 MP Changed logging to be consistent on stdout and sim_log\r
195 01-Feb-03 MP Changed type of local variables in eth_packet_trace to\r
196 conform to the interface needs of eth_mac_fmt wich produces\r
197 char data instead of unsigned char data. Suggested by the\r
198 DECC compiler.\r
199 15-Jan-03 DTH Corrected PacketGetAdapterNames parameter2 datatype\r
200 26-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source\r
201 Added networking documentation\r
202 Changed _DEBUG to ETH_DEBUG\r
203 20-Dec-02 MP Added display of packet CRC to the eth_packet_trace.\r
204 This helps distinguish packets with identical lengths\r
205 and protocols.\r
206 05-Dec-02 MP With the goal of draining the input buffer more rapidly\r
207 changed eth_read to call pcap_dispatch repeatedly until\r
208 either a timeout returns nothing or a packet allowed by\r
209 the filter is seen. This more closely reflects how the\r
210 pcap layer will work when the filtering is actually done\r
211 by a bpf filter.\r
212 31-Oct-02 DTH Added USE_NETWORK conditional\r
213 Reworked not attached test\r
214 Added OpenBSD support (from Federico Schwindt)\r
215 Added ethX detection simplification (from Megan Gentry)\r
216 Removed sections of temporary code\r
217 Added parameter validation\r
218 23-Oct-02 DTH Beta 5 released\r
219 22-Oct-02 DTH Added all_multicast and promiscuous support\r
220 Fixed not attached behavior\r
221 21-Oct-02 DTH Added NetBSD support (from Jason Thorpe)\r
222 Patched buffer size to make sure entire packet is read in\r
223 Made 'ethX' check characters passed as well as length\r
224 Corrected copyright again\r
225 16-Oct-02 DTH Beta 4 released\r
226 Corrected copyright\r
227 09-Oct-02 DTH Beta 3 released\r
228 Added pdp11 write acceleration (from Patrick Caulfield)\r
229 08-Oct-02 DTH Beta 2 released\r
230 Integrated with 2.10-0p4\r
231 Added variable vector and copyrights\r
232 04-Oct-02 DTH Added linux support (from Patrick Caulfield)\r
233 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11\r
234 24-Sep-02 DTH Finished eth_devices, eth_getname\r
235 18-Sep-02 DTH Callbacks implemented\r
236 13-Sep-02 DTH Basic packet read/write written\r
237 20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation\r
238\r
239 ------------------------------------------------------------------------------\r
240*/\r
241\r
242#include <ctype.h>\r
243#include "sim_ether.h"\r
244#include "sim_sock.h"\r
245\r
246extern FILE *sim_log;\r
247\r
248\r
249/*============================================================================*/\r
250/* OS-independant ethernet routines */\r
251/*============================================================================*/\r
252\r
253t_stat eth_mac_scan (ETH_MAC* mac, char* strmac)\r
254{\r
255 int i, j;\r
256 short unsigned int num;\r
257 char cptr[18];\r
258 int len = strlen(strmac);\r
259 const ETH_MAC zeros = {0,0,0,0,0,0};\r
260 const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\r
261 ETH_MAC newmac;\r
262\r
263 /* format of string must be 6 double-digit hex bytes with valid separators\r
264 ideally, this mac scanner could allow more flexible formatting later */\r
265 if (len != 17) return SCPE_ARG;\r
266\r
267 /* copy string to local storage for mangling */\r
268 strcpy(cptr, strmac);\r
269\r
270 /* make sure byte separators are OK */\r
271 for (i=2; i<len; i=i+3) {\r
272 if ((cptr[i] != '-') && \r
273 (cptr[i] != '.') &&\r
274 (cptr[i] != ':')) return SCPE_ARG;\r
275 cptr[i] = '\0';\r
276 }\r
277\r
278 /* get and set address bytes */\r
279 for (i=0, j=0; i<len; i=i+3, j++) {\r
280 int valid = strspn(&cptr[i], "0123456789abcdefABCDEF");\r
281 if (valid < 2) return SCPE_ARG;\r
282 sscanf(&cptr[i], "%hx", &num);\r
283 newmac[j] = (unsigned char) num;\r
284 }\r
285\r
286 /* final check - mac cannot be broadcast or multicast address */\r
287 if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */\r
288 !memcmp(newmac, ones, sizeof(ETH_MAC)) || /* broadcast */\r
289 (newmac[0] & 0x01) /* multicast */\r
290 )\r
291 return SCPE_ARG;\r
292\r
293 /* new mac is OK, copy into passed mac */\r
294 memcpy (*mac, newmac, sizeof(ETH_MAC));\r
295 return SCPE_OK;\r
296}\r
297\r
298void eth_mac_fmt(ETH_MAC* mac, char* buff)\r
299{\r
300 uint8* m = (uint8*) mac;\r
301 sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", m[0], m[1], m[2], m[3], m[4], m[5]);\r
302 return;\r
303}\r
304\r
305static const uint32 crcTable[256] = {\r
306 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,\r
307 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,\r
308 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,\r
309 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,\r
310 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,\r
311 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,\r
312 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,\r
313 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\r
314 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,\r
315 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,\r
316 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,\r
317 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,\r
318 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,\r
319 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,\r
320 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,\r
321 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\r
322 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,\r
323 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,\r
324 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,\r
325 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\r
326 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,\r
327 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,\r
328 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,\r
329 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\r
330 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,\r
331 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,\r
332 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,\r
333 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\r
334 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,\r
335 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,\r
336 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,\r
337 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\r
338 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,\r
339 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,\r
340 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,\r
341 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,\r
342 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,\r
343 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,\r
344 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,\r
345 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\r
346 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,\r
347 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,\r
348 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\r
349};\r
350\r
351uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len)\r
352{\r
353 const uint32 mask = 0xFFFFFFFF;\r
354 const unsigned char* buf = (const unsigned char*)vbuf;\r
355\r
356 crc ^= mask;\r
357 while (0 != len--)\r
358 crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ];\r
359 return(crc ^ mask);\r
360}\r
361\r
362void eth_add_crc32(ETH_PACK* packet)\r
363{\r
364 if (packet->len <= ETH_MAX_PACKET) {\r
365 uint32 crc = eth_crc32(0, packet->msg, packet->len); /* calculate CRC */\r
366 uint32 ncrc = htonl(crc); /* CRC in network order */\r
367 int size = sizeof(ncrc); /* size of crc field */\r
368 memcpy(&packet->msg[packet->len], &ncrc, size); /* append crc to packet */\r
369 packet->crc_len = packet->len + size; /* set packet crc length */\r
370 } else {\r
371 packet->crc_len = 0; /* appending crc would destroy packet */\r
372 }\r
373}\r
374\r
375void eth_setcrc(ETH_DEV* dev, int need_crc)\r
376{\r
377 dev->need_crc = need_crc;\r
378}\r
379\r
380void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int dmp)\r
381{\r
382 if (dev->dptr->dctrl & dev->dbit) {\r
383 char src[20];\r
384 char dst[20];\r
385 unsigned short* proto = (unsigned short*) &msg[12];\r
386 uint32 crc = eth_crc32(0, msg, len);\r
387 eth_mac_fmt((ETH_MAC*)&msg[0], dst);\r
388 eth_mac_fmt((ETH_MAC*)&msg[6], src);\r
389 sim_debug(dev->dbit, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n",\r
390 txt, dst, src, ntohs(*proto), len, crc);\r
391 if (dmp) {\r
392 int i, same, group, sidx, oidx;\r
393 char outbuf[80], strbuf[18];\r
394 static char hex[] = "0123456789ABCDEF";\r
395 for (i=same=0; i<len; i += 16) {\r
396 if ((i > 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) {\r
397 ++same;\r
398 continue;\r
399 }\r
400 if (same > 0) {\r
401 sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), i-1);\r
402 same = 0;\r
403 }\r
404 group = (((len - i) > 16) ? 16 : (len - i));\r
405 for (sidx=oidx=0; sidx<group; ++sidx) {\r
406 outbuf[oidx++] = ' ';\r
407 outbuf[oidx++] = hex[(msg[i+sidx]>>4)&0xf];\r
408 outbuf[oidx++] = hex[msg[i+sidx]&0xf];\r
409 if (isprint(msg[i+sidx]))\r
410 strbuf[sidx] = msg[i+sidx];\r
411 else\r
412 strbuf[sidx] = '.';\r
413 }\r
414 outbuf[oidx] = '\0';\r
415 strbuf[sidx] = '\0';\r
416 sim_debug(dev->dbit, dev->dptr, "%04X%-48s %s\r\n", i, outbuf, strbuf);\r
417 }\r
418 if (same > 0)\r
419 sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), len-1);\r
420 }\r
421 }\r
422}\r
423\r
424void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, char* txt)\r
425{\r
426 eth_packet_trace_ex(dev, msg, len, txt, 1/*len > ETH_MAX_PACKET*/);\r
427}\r
428\r
429char* eth_getname(int number, char* name)\r
430{\r
431 ETH_LIST list[ETH_MAX_DEVICE];\r
432 int count = eth_devices(ETH_MAX_DEVICE, list);\r
433\r
434 if (count <= number) return 0;\r
435 strcpy(name, list[number].name);\r
436 return name;\r
437}\r
438\r
439char* eth_getname_bydesc(char* desc, char* name)\r
440{\r
441 ETH_LIST list[ETH_MAX_DEVICE];\r
442 int count = eth_devices(ETH_MAX_DEVICE, list);\r
443 int i;\r
444 int j=strlen(desc);\r
445\r
446 for (i=0; i<count; i++) {\r
447 int found = 1;\r
448 int k = strlen(list[i].desc);\r
449\r
450 if (j != k) continue;\r
451 for (k=0; k<j; k++)\r
452 if (tolower(list[i].desc[k]) != tolower(desc[k]))\r
453 found = 0;\r
454 if (found == 0) continue;\r
455\r
456 /* found a case-insensitive description match */\r
457 strcpy(name, list[i].name);\r
458 return name;\r
459 }\r
460 /* not found */\r
461 return 0;\r
462}\r
463\r
464/* strncasecmp() is not available on all platforms */\r
465int eth_strncasecmp(char* string1, char* string2, int len)\r
466{\r
467 int i;\r
468 unsigned char s1, s2;\r
469\r
470 for (i=0; i<len; i++) {\r
471 s1 = string1[i];\r
472 s2 = string2[i];\r
473 if (islower (s1)) s1 = toupper (s1);\r
474 if (islower (s2)) s2 = toupper (s2);\r
475\r
476 if (s1 < s2)\r
477 return -1;\r
478 if (s1 > s2)\r
479 return 1;\r
480 if (s1 == 0) return 0;\r
481 }\r
482 return 0;\r
483}\r
484\r
485char* eth_getname_byname(char* name, char* temp)\r
486{\r
487 ETH_LIST list[ETH_MAX_DEVICE];\r
488 int count = eth_devices(ETH_MAX_DEVICE, list);\r
489 int i, n, found;\r
490\r
491 found = 0;\r
492 n = strlen(name);\r
493 for (i=0; i<count && !found; i++) {\r
494 if (eth_strncasecmp(name, list[i].name, n) == 0) {\r
495 found = 1;\r
496 strcpy(temp, list[i].name); /* only case might be different */\r
497 }\r
498 }\r
499 if (found) {\r
500 return temp;\r
501 } else {\r
502 return 0;\r
503 }\r
504}\r
505\r
506void eth_zero(ETH_DEV* dev)\r
507{\r
508 /* set all members to NULL OR 0 */\r
509 memset(dev, 0, sizeof(ETH_DEV));\r
510 dev->reflections = -1; /* not established yet */\r
511}\r
512\r
513t_stat eth_show (FILE* st, UNIT* uptr, int32 val, void* desc)\r
514{\r
515 ETH_LIST list[ETH_MAX_DEVICE];\r
516 int number = eth_devices(ETH_MAX_DEVICE, list);\r
517\r
518 fprintf(st, "ETH devices:\n");\r
519 if (number == -1)\r
520 fprintf(st, " network support not available in simulator\n");\r
521 else\r
522 if (number == 0)\r
523 fprintf(st, " no network devices are available\n");\r
524 else {\r
525 int i, min, len;\r
526 for (i=0, min=0; i<number; i++)\r
527 if ((len = strlen(list[i].name)) > min) min = len;\r
528 for (i=0; i<number; i++)\r
529 fprintf(st," %d %-*s (%s)\n", i, min, list[i].name, list[i].desc);\r
530 }\r
531 return SCPE_OK;\r
532}\r
533\r
534t_stat ethq_init(ETH_QUE* que, int max)\r
535{\r
536 /* create dynamic queue if it does not exist */\r
537 if (!que->item) {\r
538 size_t size = sizeof(struct eth_item) * max;\r
539 que->max = max;\r
540 que->item = (struct eth_item *) malloc(size);\r
541 if (que->item) {\r
542 /* init dynamic memory */\r
543 memset(que->item, 0, size);\r
544 } else {\r
545 /* failed to allocate memory */\r
546 char* msg = "EthQ: failed to allocate dynamic queue[%d]\r\n";\r
547 printf(msg, max);\r
548 if (sim_log) fprintf(sim_log, msg, max);\r
549 return SCPE_MEM;\r
550 };\r
551 };\r
552 return SCPE_OK;\r
553}\r
554\r
555void ethq_clear(ETH_QUE* que)\r
556{\r
557 /* clear packet array */\r
558 memset(que->item, 0, sizeof(struct eth_item) * que->max);\r
559 /* clear rest of structure */\r
560 que->count = que->head = que->tail = que->loss = que->high = 0;\r
561}\r
562\r
563void ethq_remove(ETH_QUE* que)\r
564{\r
565 struct eth_item* item = &que->item[que->head];\r
566\r
567 if (que->count) {\r
568 memset(item, 0, sizeof(struct eth_item));\r
569 if (++que->head == que->max)\r
570 que->head = 0;\r
571 que->count--;\r
572 }\r
573}\r
574\r
575void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status)\r
576{\r
577 struct eth_item* item;\r
578\r
579 /* if queue empty, set pointers to beginning */\r
580 if (!que->count) {\r
581 que->head = 0;\r
582 que->tail = -1;\r
583 }\r
584\r
585 /* find new tail of the circular queue */\r
586 if (++que->tail == que->max)\r
587 que->tail = 0;\r
588 if (++que->count > que->max) {\r
589 que->count = que->max;\r
590 /* lose oldest packet */\r
591 if (++que->head == que->max)\r
592 que->head = 0;\r
593 que->loss++;\r
594 }\r
595 if (que->count > que->high)\r
596 que->high = que->count;\r
597\r
598 /* set information in (new) tail item */\r
599 item = &que->item[que->tail];\r
600 item->type = type;\r
601 item->packet.len = pack->len;\r
602 item->packet.used = 0;\r
603 item->packet.crc_len = pack->crc_len;\r
604 memcpy(item->packet.msg, pack->msg, ((pack->len > pack->crc_len) ? pack->len : pack->crc_len));\r
605 item->packet.status = status;\r
606}\r
607\r
608/*============================================================================*/\r
609/* Non-implemented versions */\r
610/*============================================================================*/\r
611\r
612#if !defined (USE_NETWORK) && !defined(USE_SHARED)\r
613t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)\r
614 {return SCPE_NOFNC;}\r
615t_stat eth_close (ETH_DEV* dev)\r
616 {return SCPE_NOFNC;}\r
617t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r
618 {return SCPE_NOFNC;}\r
619t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r
620 {return SCPE_NOFNC;}\r
621t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* addresses,\r
622 ETH_BOOL all_multicast, ETH_BOOL promiscuous)\r
623 {return SCPE_NOFNC;}\r
624int eth_devices (int max, ETH_LIST* dev)\r
625 {return -1;}\r
626#else /* endif unimplemented */\r
627\r
628/*============================================================================*/\r
629/* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */\r
630/* OpenVMS Alpha uses a WinPcap port and an associated execlet */\r
631/*============================================================================*/\r
632\r
633#if defined (xBSD) && !defined(__APPLE__)\r
634#include <sys/ioctl.h>\r
635#include <net/bpf.h>\r
636#endif /* xBSD */\r
637\r
638#include <pcap.h>\r
639#include <string.h>\r
640\r
641/* Allows windows to look up user-defined adapter names */\r
642#if defined(_WIN32)\r
643#include <winreg.h>\r
644#endif\r
645\r
646#if defined(_WIN32) && defined(USE_SHARED)\r
647/* Dynamic DLL loading technique and modified source comes from\r
648 Etherial/WireShark capture_pcap.c */\r
649\r
650/* Dynamic DLL load variables */\r
651static HINSTANCE hDll = 0; /* handle to DLL */\r
652static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */\r
653static char* no_wpcap = "wpcap load failure";\r
654\r
655/* define pointers to pcap functions needed */\r
656static void (*p_pcap_close) (pcap_t *);\r
657static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, char *, int, bpf_u_int32);\r
658static int (*p_pcap_datalink) (pcap_t *);\r
659static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *);\r
660static int (*p_pcap_findalldevs) (pcap_if_t **, char *);\r
661static void (*p_pcap_freealldevs) (pcap_if_t *);\r
662static void (*p_pcap_freecode) (struct bpf_program *);\r
663static char* (*p_pcap_geterr) (pcap_t *);\r
664static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *);\r
665static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *);\r
666static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len);\r
667static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *);\r
668static char* (*p_pcap_lib_version) (void);\r
669\r
670/* load function pointer from DLL */\r
671void load_function(char* function, void** func_ptr) {\r
672 *func_ptr = GetProcAddress(hDll, function);\r
673 if (*func_ptr == 0) {\r
674 char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n";\r
675 printf (msg, function);\r
676 if (sim_log) fprintf (sim_log, msg, function);\r
677 dll_loaded = 3;\r
678 }\r
679}\r
680\r
681/* load wpcap.dll as required */\r
682int load_wpcap(void) {\r
683 switch(dll_loaded) {\r
684 case 0: /* not loaded */\r
685 /* attempt to load DLL */\r
686 hDll = LoadLibrary(TEXT("wpcap.dll"));\r
687 if (hDll == 0) {\r
688 /* failed to load DLL */\r
689 char* msg = "Eth: Failed to load wpcap.dll\r\n";\r
690 char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n";\r
691 printf (msg);\r
692 printf (msg2);\r
693 if (sim_log) {\r
694 fprintf (sim_log, msg);\r
695 fprintf (sim_log, msg2);\r
696 }\r
697 dll_loaded = 2;\r
698 break;\r
699 } else {\r
700 /* DLL loaded OK */\r
701 dll_loaded = 1;\r
702 }\r
703\r
704 /* load required functions; sets dll_load=3 on error */\r
705 load_function("pcap_close", (void**) &p_pcap_close);\r
706 load_function("pcap_compile", (void**) &p_pcap_compile);\r
707 load_function("pcap_datalink", (void**) &p_pcap_datalink);\r
708 load_function("pcap_dispatch", (void**) &p_pcap_dispatch);\r
709 load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs);\r
710 load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs);\r
711 load_function("pcap_freecode", (void**) &p_pcap_freecode);\r
712 load_function("pcap_geterr", (void**) &p_pcap_geterr);\r
713 load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet);\r
714 load_function("pcap_open_live", (void**) &p_pcap_open_live);\r
715 load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket);\r
716 load_function("pcap_setfilter", (void**) &p_pcap_setfilter);\r
717 load_function("pcap_lib_version", (void**) &p_pcap_lib_version);\r
718\r
719 if (dll_loaded == 1) {\r
720 /* log successful load */\r
721 char* version = p_pcap_lib_version();\r
722 printf("%s\n", version);\r
723 if (sim_log)\r
724 fprintf(sim_log, "%s\n", version);\r
725 }\r
726 break;\r
727 default: /* loaded or failed */\r
728 break;\r
729 }\r
730 return (dll_loaded == 1) ? 1 : 0;\r
731}\r
732\r
733/* define functions with dynamic revectoring */\r
734void pcap_close(pcap_t* a) {\r
735 if (load_wpcap() != 0) {\r
736 p_pcap_close(a);\r
737 }\r
738}\r
739\r
740int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) {\r
741 if (load_wpcap() != 0) {\r
742 return p_pcap_compile(a, b, c, d, e);\r
743 } else {\r
744 return 0;\r
745 }\r
746}\r
747\r
748int pcap_datalink(pcap_t* a) {\r
749 if (load_wpcap() != 0) {\r
750 return p_pcap_datalink(a);\r
751 } else {\r
752 return 0;\r
753 }\r
754}\r
755\r
756int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) {\r
757 if (load_wpcap() != 0) {\r
758 return p_pcap_dispatch(a, b, c, d);\r
759 } else {\r
760 return 0;\r
761 }\r
762}\r
763\r
764int pcap_findalldevs(pcap_if_t** a, char* b) {\r
765 if (load_wpcap() != 0) {\r
766 return p_pcap_findalldevs(a, b);\r
767 } else {\r
768 *a = 0;\r
769 strcpy(b, no_wpcap);\r
770 return -1;\r
771 }\r
772}\r
773\r
774void pcap_freealldevs(pcap_if_t* a) {\r
775 if (load_wpcap() != 0) {\r
776 p_pcap_freealldevs(a);\r
777 }\r
778}\r
779\r
780void pcap_freecode(struct bpf_program* a) {\r
781 if (load_wpcap() != 0) {\r
782 p_pcap_freecode(a);\r
783 }\r
784}\r
785\r
786char* pcap_geterr(pcap_t* a) {\r
787 if (load_wpcap() != 0) {\r
788 return p_pcap_geterr(a);\r
789 } else {\r
790 return (char*) 0;\r
791 }\r
792}\r
793\r
794int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) {\r
795 if (load_wpcap() != 0) {\r
796 return p_pcap_lookupnet(a, b, c, d);\r
797 } else {\r
798 return 0;\r
799 }\r
800}\r
801\r
802pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) {\r
803 if (load_wpcap() != 0) {\r
804 return p_pcap_open_live(a, b, c, d, e);\r
805 } else {\r
806 return (pcap_t*) 0;\r
807 }\r
808}\r
809\r
810int pcap_sendpacket(pcap_t* a, const u_char* b, int c) {\r
811 if (load_wpcap() != 0) {\r
812 return p_pcap_sendpacket(a, b, c);\r
813 } else {\r
814 return 0;\r
815 }\r
816}\r
817\r
818int pcap_setfilter(pcap_t* a, struct bpf_program* b) {\r
819 if (load_wpcap() != 0) {\r
820 return p_pcap_setfilter(a, b);\r
821 } else {\r
822 return 0;\r
823 }\r
824}\r
825#endif\r
826\r
827/* Some platforms have always had pcap_sendpacket */\r
828#if defined(_WIN32) || defined(VMS)\r
829#define HAS_PCAP_SENDPACKET 1\r
830#else\r
831/* The latest libpcap and WinPcap all have pcap_sendpacket */\r
832#if !defined (NEED_PCAP_SENDPACKET)\r
833#define HAS_PCAP_SENDPACKET 1\r
834#endif\r
835#endif\r
836\r
837#if !defined (HAS_PCAP_SENDPACKET)\r
838/* libpcap has no function to write a packet, so we need to implement\r
839 pcap_sendpacket() for compatibility with the WinPcap base code.\r
840 Return value: 0=Success, -1=Failure */\r
841int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len)\r
842{\r
843#if defined (__linux)\r
844 return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1;\r
845#else\r
846 return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1;\r
847#endif /* linux */\r
848}\r
849#endif /* !HAS_PCAP_SENDPACKET */\r
850\r
851#if defined (USE_READER_THREAD)\r
852#include <pthread.h>\r
853\r
854void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data);\r
855\r
856static void *\r
857_eth_reader(void *arg)\r
858{\r
859ETH_DEV* volatile dev = (ETH_DEV*)arg;\r
860int status;\r
861struct timeval timeout;\r
862\r
863 timeout.tv_sec = 0;\r
864 timeout.tv_usec = 200*1000;\r
865\r
866 sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n");\r
867\r
868 while (dev->handle) {\r
869#if defined (MUST_DO_SELECT)\r
870 int sel_ret;\r
871\r
872 fd_set setl;\r
873 FD_ZERO(&setl);\r
874 FD_SET(pcap_get_selectable_fd((pcap_t *)dev->handle), &setl);\r
875 sel_ret = select(1+pcap_get_selectable_fd((pcap_t *)dev->handle), &setl, NULL, NULL, &timeout);\r
876 if (sel_ret < 0 && errno != EINTR) break;\r
877 if (sel_ret > 0) {\r
878 /* dispatch read request queue available packets */\r
879 status = pcap_dispatch((pcap_t*)dev->handle, -1, &eth_callback, (u_char*)dev);\r
880 }\r
881#else\r
882 /* dispatch read request queue available packets */\r
883 status = pcap_dispatch((pcap_t*)dev->handle, 1, &eth_callback, (u_char*)dev);\r
884#endif\r
885 }\r
886\r
887 sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n");\r
888 return NULL;\r
889}\r
890#endif\r
891\r
892t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)\r
893{\r
894 const int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ;\r
895 char errbuf[PCAP_ERRBUF_SIZE];\r
896 char temp[1024];\r
897 char* savname = name;\r
898 int num;\r
899 char* msg;\r
900\r
901 /* initialize device */\r
902 eth_zero(dev);\r
903\r
904 /* translate name of type "ethX" to real device name */\r
905 if ((strlen(name) == 4)\r
906 && (tolower(name[0]) == 'e')\r
907 && (tolower(name[1]) == 't')\r
908 && (tolower(name[2]) == 'h')\r
909 && isdigit(name[3])\r
910 ) {\r
911 num = atoi(&name[3]);\r
912 savname = eth_getname(num, temp);\r
913 if (savname == 0) /* didn't translate */\r
914 return SCPE_OPENERR;\r
915 } else {\r
916 /* are they trying to use device description? */\r
917 savname = eth_getname_bydesc(name, temp);\r
918 if (savname == 0) { /* didn't translate */\r
919 /* probably is not ethX and has no description */\r
920 savname = eth_getname_byname(name, temp);\r
921 if (savname == 0) /* didn't translate */\r
922 return SCPE_OPENERR;\r
923 }\r
924 }\r
925\r
926 /* attempt to connect device */\r
927 memset(errbuf, 0, sizeof(errbuf));\r
928 dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf);\r
929 if (!dev->handle) { /* can't open device */\r
930 msg = "Eth: pcap_open_live error - %s\r\n";\r
931 printf (msg, errbuf);\r
932 if (sim_log) fprintf (sim_log, msg, errbuf);\r
933 return SCPE_OPENERR;\r
934 } else {\r
935 msg = "Eth: opened %s\r\n";\r
936 printf (msg, savname);\r
937 if (sim_log) fprintf (sim_log, msg, savname);\r
938 }\r
939\r
940 /* save name of device */\r
941 dev->name = malloc(strlen(savname)+1);\r
942 strcpy(dev->name, savname);\r
943\r
944 /* save debugging information */\r
945 dev->dptr = dptr;\r
946 dev->dbit = dbit;\r
947\r
948#if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__)\r
949 /* Tell the kernel that the header is fully-formed when it gets it.\r
950 This is required in order to fake the src address. */\r
951 {\r
952 int one = 1;\r
953 ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one);\r
954 }\r
955#endif /* xBSD */\r
956\r
957#if defined (USE_READER_THREAD)\r
958 {\r
959 pthread_attr_t attr;\r
960\r
961 ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */\r
962 pthread_mutex_init (&dev->lock, NULL);\r
963 pthread_attr_init(&attr);\r
964 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);\r
965 pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev);\r
966 pthread_attr_destroy(&attr);\r
967 }\r
968#else /* !defined (USE_READER_THREAD */\r
969#ifdef USE_SETNONBLOCK\r
970 /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */\r
971 if (pcap_setnonblock (dev->handle, 1, errbuf) == -1) {\r
972 msg = "Eth: Failed to set non-blocking: %s\r\n";\r
973 printf (msg, errbuf);\r
974 if (sim_log) fprintf (sim_log, msg, errbuf);\r
975 }\r
976#endif\r
977#endif /* !defined (USE_READER_THREAD */\r
978 return SCPE_OK;\r
979}\r
980\r
981t_stat eth_close(ETH_DEV* dev)\r
982{\r
983 char* msg = "Eth: closed %s\r\n";\r
984 pcap_t *pcap;\r
985\r
986 /* make sure device exists */\r
987 if (!dev) return SCPE_UNATT;\r
988\r
989 /* close the device */\r
990 pcap = (pcap_t *)dev->handle;\r
991 dev->handle = NULL;\r
992 pcap_close(pcap);\r
993 printf (msg, dev->name);\r
994 if (sim_log) fprintf (sim_log, msg, dev->name);\r
995\r
996#if defined (USE_READER_THREAD)\r
997 pthread_join (dev->reader_thread, NULL);\r
998#endif\r
999\r
1000 /* clean up the mess */\r
1001 free(dev->name);\r
1002 eth_zero(dev);\r
1003\r
1004 return SCPE_OK;\r
1005}\r
1006\r
1007t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac)\r
1008{\r
1009 ETH_PACK send, recv;\r
1010 t_stat status;\r
1011 int i;\r
1012 struct timeval delay;\r
1013\r
1014 /* build a packet */\r
1015 memset (&send, 0, sizeof(ETH_PACK));\r
1016 send.len = ETH_MIN_PACKET; /* minimum packet size */\r
1017 memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */\r
1018 memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */\r
1019 send.msg[12] = 0x90; /* loopback packet type */\r
1020 for (i=14; i<send.len; i++)\r
1021 send.msg[i] = 32 + i; /* gibberish */\r
1022\r
1023 dev->reflections = 0;\r
1024 eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0);\r
1025\r
1026 /* send the packet */\r
1027 status = eth_write (dev, &send, NULL);\r
1028 if (status != SCPE_OK) {\r
1029 char *msg;\r
1030 msg = "Eth: Error Transmitting packet: %s\r\n"\r
1031 "You may need to run as root, or install a libpcap version\r\n"\r
1032 "which is at least 0.9 from www.tcpdump.org\r\n";\r
1033 printf(msg, strerror(errno));\r
1034 if (sim_log) fprintf (sim_log, msg, strerror(errno));\r
1035 return status;\r
1036 }\r
1037\r
1038 /* if/when we have a sim_os_msleep() we'll use it here instead of this select() */\r
1039 delay.tv_sec = 0;\r
1040 delay.tv_usec = 50*1000;\r
1041 select(0, NULL, NULL, NULL, &delay); /* make sure things settle into the read path */\r
1042\r
1043 /* empty the read queue and count the reflections */\r
1044 do {\r
1045 memset (&recv, 0, sizeof(ETH_PACK));\r
1046 status = eth_read (dev, &recv, NULL);\r
1047 if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0)\r
1048 dev->reflections++;\r
1049 } while (recv.len > 0);\r
1050\r
1051 sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections);\r
1052 return dev->reflections;\r
1053}\r
1054\r
1055t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r
1056{\r
1057 int status = 1; /* default to failure */\r
1058\r
1059 /* make sure device exists */\r
1060 if (!dev) return SCPE_UNATT;\r
1061\r
1062 /* make sure packet exists */\r
1063 if (!packet) return SCPE_ARG;\r
1064\r
1065 /* make sure packet is acceptable length */\r
1066 if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) {\r
1067 eth_packet_trace (dev, packet->msg, packet->len, "writing");\r
1068\r
1069 /* dispatch write request (synchronous; no need to save write info to dev) */\r
1070 status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len);\r
1071\r
1072 /* detect sending of decnet loopback packet */\r
1073 if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg)) \r
1074 dev->decnet_self_sent += dev->reflections;\r
1075\r
1076 } /* if packet->len */\r
1077\r
1078 /* call optional write callback function */\r
1079 if (routine)\r
1080 (routine)(status);\r
1081\r
1082 return ((status == 0) ? SCPE_OK : SCPE_IOERR);\r
1083}\r
1084\r
1085void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data)\r
1086{\r
1087 ETH_DEV* dev = (ETH_DEV*) info;\r
1088#ifdef USE_BPF\r
1089 int to_me = 1;\r
1090#else /* !USE_BPF */\r
1091 int to_me = 0;\r
1092 int from_me = 0;\r
1093 int i;\r
1094\r
1095#ifdef ETH_DEBUG\r
1096// eth_packet_trace (dev, data, header->len, "received");\r
1097#endif\r
1098 for (i = 0; i < dev->addr_count; i++) {\r
1099 if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1;\r
1100 if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1;\r
1101 }\r
1102\r
1103 /* all multicast mode? */\r
1104 if (dev->all_multicast && (data[0] & 0x01)) to_me = 1;\r
1105\r
1106 /* promiscuous mode? */\r
1107 if (dev->promiscuous) to_me = 1;\r
1108#endif /* USE_BPF */\r
1109\r
1110 /* detect sending of decnet loopback packet */\r
1111 if (DECNET_SELF_FRAME(dev->decnet_addr, data)) {\r
1112 /* lower reflection count - if already zero, pass it on */\r
1113 if (dev->decnet_self_sent > 0) {\r
1114 dev->decnet_self_sent--;\r
1115 to_me = 0;\r
1116 } \r
1117#ifndef USE_BPF\r
1118 else\r
1119 from_me = 0;\r
1120#endif\r
1121 }\r
1122\r
1123#ifdef USE_BPF\r
1124 if (to_me) {\r
1125#else /* !USE_BPF */\r
1126 if (to_me && !from_me) {\r
1127#endif\r
1128#if defined (USE_READER_THREAD)\r
1129 ETH_PACK tmp_packet;\r
1130\r
1131 /* set data in passed read packet */\r
1132 tmp_packet.len = header->len;\r
1133 memcpy(tmp_packet.msg, data, header->len);\r
1134 if (dev->need_crc)\r
1135 eth_add_crc32(&tmp_packet);\r
1136\r
1137 eth_packet_trace (dev, tmp_packet.msg, tmp_packet.len, "rcvqd");\r
1138\r
1139 pthread_mutex_lock (&dev->lock);\r
1140 ethq_insert(&dev->read_queue, 2, &tmp_packet, 0);\r
1141 pthread_mutex_unlock (&dev->lock);\r
1142#else\r
1143 /* set data in passed read packet */\r
1144 dev->read_packet->len = header->len;\r
1145 memcpy(dev->read_packet->msg, data, header->len);\r
1146 if (dev->need_crc)\r
1147 eth_add_crc32(dev->read_packet);\r
1148\r
1149 eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading");\r
1150\r
1151 /* call optional read callback function */\r
1152 if (dev->read_callback)\r
1153 (dev->read_callback)(0);\r
1154#endif\r
1155 }\r
1156}\r
1157\r
1158t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r
1159{\r
1160 int status;\r
1161\r
1162 /* make sure device exists */\r
1163\r
1164 if (!dev) return SCPE_UNATT;\r
1165\r
1166 /* make sure packet exists */\r
1167 if (!packet) return SCPE_ARG;\r
1168\r
1169#if !defined (USE_READER_THREAD)\r
1170 /* set read packet */\r
1171 dev->read_packet = packet;\r
1172 packet->len = 0;\r
1173\r
1174 /* set optional callback routine */\r
1175 dev->read_callback = routine;\r
1176\r
1177 /* dispatch read request to either receive a filtered packet or timeout */\r
1178 do {\r
1179 status = pcap_dispatch((pcap_t*)dev->handle, 1, &eth_callback, (u_char*)dev);\r
1180 } while ((status) && (0 == packet->len));\r
1181\r
1182#else /* USE_READER_THREAD */\r
1183\r
1184 status = 0;\r
1185 pthread_mutex_lock (&dev->lock);\r
1186 if (dev->read_queue.count > 0) {\r
1187 ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head];\r
1188 packet->len = item->packet.len;\r
1189 memcpy(packet->msg, item->packet.msg, packet->len);\r
1190 if (routine)\r
1191 routine(status);\r
1192 ethq_remove(&dev->read_queue);\r
1193 }\r
1194 pthread_mutex_unlock (&dev->lock); \r
1195#endif\r
1196\r
1197 return SCPE_OK;\r
1198}\r
1199\r
1200t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses,\r
1201 ETH_BOOL all_multicast, ETH_BOOL promiscuous)\r
1202{\r
1203 int i;\r
1204 bpf_u_int32 bpf_subnet, bpf_netmask;\r
1205 char buf[110+66*ETH_FILTER_MAX];\r
1206 char errbuf[PCAP_ERRBUF_SIZE];\r
1207 char mac[20];\r
1208 char* buf2;\r
1209 t_stat status;\r
1210#ifdef USE_BPF\r
1211 struct bpf_program bpf;\r
1212 char* msg;\r
1213#endif\r
1214\r
1215 /* make sure device exists */\r
1216 if (!dev) return SCPE_UNATT;\r
1217\r
1218 /* filter count OK? */\r
1219 if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX))\r
1220 return SCPE_ARG;\r
1221 else\r
1222 if (!addresses) return SCPE_ARG;\r
1223\r
1224 /* set new filter addresses */\r
1225 for (i = 0; i < addr_count; i++)\r
1226 memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC));\r
1227 dev->addr_count = addr_count;\r
1228\r
1229 /* store other flags */\r
1230 dev->all_multicast = all_multicast;\r
1231 dev->promiscuous = promiscuous;\r
1232\r
1233 /* print out filter information if debugging */\r
1234 if (dev->dptr->dctrl & dev->dbit) {\r
1235 sim_debug(dev->dbit, dev->dptr, "Filter Set\n");\r
1236 for (i = 0; i < addr_count; i++) {\r
1237 char mac[20];\r
1238 eth_mac_fmt(&dev->filter_address[i], mac);\r
1239 sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac);\r
1240 }\r
1241 if (dev->all_multicast)\r
1242 sim_debug(dev->dbit, dev->dptr, "All Multicast\n");\r
1243 if (dev->promiscuous)\r
1244 sim_debug(dev->dbit, dev->dptr, "Promiscuous\n");\r
1245 }\r
1246\r
1247 /* test reflections */\r
1248 if (dev->reflections == -1)\r
1249 status = eth_reflect(dev, dev->filter_address[0]);\r
1250\r
1251 /* setup BPF filters and other fields to minimize packet delivery */\r
1252 strcpy(buf, "");\r
1253\r
1254 /* construct destination filters - since the real ethernet interface was set\r
1255 into promiscuous mode by eth_open(), we need to filter out the packets that\r
1256 our simulated interface doesn't want. */\r
1257 if (!dev->promiscuous) {\r
1258 for (i = 0; i < addr_count; i++) {\r
1259 eth_mac_fmt(&dev->filter_address[i], mac);\r
1260 if (!strstr(buf, mac)) /* eliminate duplicates */\r
1261 sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "", mac);\r
1262 }\r
1263 if (dev->all_multicast)\r
1264 sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "");\r
1265 }\r
1266\r
1267 /* construct source filters - this prevents packets from being reflected back \r
1268 by systems where WinPcap and libpcap cause packet reflections. Note that\r
1269 some systems do not reflect packets at all. This *assumes* that the \r
1270 simulated NIC will not send out packets with multicast source fields. */\r
1271 if ((addr_count > 0) && (dev->reflections > 0)) {\r
1272 if (strlen(buf) > 0)\r
1273 sprintf(&buf[strlen(buf)], " and ");\r
1274 sprintf (&buf[strlen(buf)], "not (");\r
1275 buf2 = &buf[strlen(buf)];\r
1276 for (i = 0; i < addr_count; i++) {\r
1277 if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */\r
1278 eth_mac_fmt(&dev->filter_address[i], mac);\r
1279 if (!strstr(buf2, mac)) /* eliminate duplicates */\r
1280 sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac);\r
1281 }\r
1282 sprintf (&buf[strlen(buf)], ")");\r
1283 }\r
1284 /* When starting, DECnet sends out a packet with the source and destination\r
1285 addresses set to the same value as the DECnet MAC address. This packet is\r
1286 designed to find and help diagnose DECnet address conflicts. Normally, this\r
1287 packet would not be seen by the sender, only by the other machine that has\r
1288 the same DECnet address. If the ethernet subsystem is reflecting packets,\r
1289 DECnet will fail to start if it sees the reflected packet, since it thinks\r
1290 another system is using this DECnet address. We have to let these packets\r
1291 through, so that if another machine has the same DECnet address that we\r
1292 can detect it. Both eth_write() and eth_callback() help by checking the\r
1293 reflection count - eth_write() adds the reflection count to\r
1294 dev->decnet_self_sent, and eth_callback() check the value - if the\r
1295 dev->decnet_self_sent count is zero, then the packet has come from another\r
1296 machine with the same address, and needs to be passed on to the simulated\r
1297 machine. */\r
1298 memset(dev->decnet_addr, 0, sizeof(ETH_MAC));\r
1299 /* check for decnet address in filters */\r
1300 if ((addr_count) && (dev->reflections > 0)) {\r
1301 for (i = 0; i < addr_count; i++) {\r
1302 eth_mac_fmt(&dev->filter_address[i], mac);\r
1303 if (memcmp(mac, "AA:00:04", 8) == 0) {\r
1304 memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC));\r
1305 /* let packets through where dst and src are the same as our decnet address */\r
1306 sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac);\r
1307 break;\r
1308 }\r
1309 }\r
1310 }\r
1311 sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf);\r
1312\r
1313\r
1314 /* get netmask, which is required for compiling */\r
1315 if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) {\r
1316 bpf_netmask = 0;\r
1317 }\r
1318\r
1319#ifdef USE_BPF\r
1320 /* compile filter string */\r
1321 if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) {\r
1322 sprintf(errbuf, "%s", pcap_geterr(dev->handle));\r
1323 msg = "Eth: pcap_compile error: %s\r\n";\r
1324 printf(msg, errbuf);\r
1325 if (sim_log) fprintf (sim_log, msg, errbuf);\r
1326 /* show erroneous BPF string */\r
1327 msg = "Eth: BPF string is: |%s|\r\n";\r
1328 printf (msg, buf);\r
1329 if (sim_log) fprintf (sim_log, msg, buf);\r
1330 } else {\r
1331 /* apply compiled filter string */\r
1332 if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) {\r
1333 sprintf(errbuf, "%s", pcap_geterr(dev->handle));\r
1334 msg = "Eth: pcap_setfilter error: %s\r\n";\r
1335 printf(msg, errbuf);\r
1336 if (sim_log) fprintf (sim_log, msg, errbuf);\r
1337 } else {\r
1338#ifdef USE_SETNONBLOCK\r
1339 /* set file non-blocking */\r
1340 status = pcap_setnonblock (dev->handle, 1, errbuf);\r
1341#endif /* USE_SETNONBLOCK */\r
1342 }\r
1343 pcap_freecode(&bpf);\r
1344 }\r
1345#endif /* USE_BPF */\r
1346\r
1347 return SCPE_OK;\r
1348}\r
1349\r
1350/*\r
1351 The libpcap provided API pcap_findalldevs() on most platforms, will \r
1352 leverage the getifaddrs() API if it is available in preference to \r
1353 alternate platform specific methods of determining the interface list.\r
1354\r
1355 A limitation of getifaddrs() is that it returns only interfaces which\r
1356 have associated addresses. This may not include all of the interesting\r
1357 interfaces that we are interested in since a host may have dedicated\r
1358 interfaces for a simulator, which is otherwise unused by the host.\r
1359\r
1360 One could hand craft the the build of libpcap to specifically use \r
1361 alternate methods to implement pcap_findalldevs(). However, this can \r
1362 get tricky, and would then result in a sort of deviant libpcap.\r
1363\r
1364 This routine exists to allow platform specific code to validate and/or \r
1365 extend the set of available interfaces to include any that are not\r
1366 returned by pcap_findalldevs.\r
1367\r
1368*/\r
1369int eth_host_devices(int used, int max, ETH_LIST* list)\r
1370{\r
1371 pcap_t* conn;\r
1372 int i, j, datalink;\r
1373 char errbuf[PCAP_ERRBUF_SIZE];\r
1374\r
1375 for (i=0; i<used; ++i) {\r
1376 /* Cull any non-ethernet interface types */\r
1377 conn = pcap_open_live(list[i].name, ETH_MAX_PACKET, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf);\r
1378 if (NULL != conn) datalink = pcap_datalink(conn), pcap_close(conn);\r
1379 if ((NULL == conn) || (datalink != DLT_EN10MB)) {\r
1380 for (j=i; j<used-1; ++j)\r
1381 list[j] = list[j+1];\r
1382 --used;\r
1383 --i;\r
1384 }\r
1385 } /* for */\r
1386\r
1387#if defined(_WIN32)\r
1388 /* replace device description with user-defined adapter name (if defined) */\r
1389 for (i=0; i<used; i++) {\r
1390 char regkey[2048];\r
1391 char regval[2048];\r
1392 LONG status;\r
1393 DWORD reglen, regtype;\r
1394 HKEY reghnd;\r
1395\r
1396 /* These registry keys don't seem to exist for all devices, so we simply ignore errors. */\r
1397 /* Windows XP x64 registry uses wide characters by default,\r
1398 so we force use of narrow characters by using the 'A'(ANSI) version of RegOpenKeyEx.\r
1399 This could cause some problems later, if this code is internationalized. Ideally,\r
1400 the pcap lookup will return wide characters, and we should use them to build a wide\r
1401 registry key, rather than hardcoding the string as we do here. */\r
1402 if(list[i].name[strlen( "\\Device\\NPF_" )] == '{') {\r
1403 sprintf( regkey, "SYSTEM\\CurrentControlSet\\Control\\Network\\"\r
1404 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%hs\\Connection", list[i].name+\r
1405 strlen( "\\Device\\NPF_" ) );\r
1406 if((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_QUERY_VALUE, &reghnd)) != ERROR_SUCCESS) {\r
1407 continue;\r
1408 }\r
1409 reglen = sizeof(regval);\r
1410\r
1411 /* look for user-defined adapter name, bail if not found */ \r
1412 /* same comment about Windows XP x64 (above) using RegQueryValueEx */\r
1413 if((status = RegQueryValueExA (reghnd, "Name", NULL, &regtype, regval, &reglen)) != ERROR_SUCCESS) {\r
1414 RegCloseKey (reghnd);\r
1415 continue;\r
1416 }\r
1417 /* make sure value is the right type, bail if not acceptable */\r
1418 if((regtype != REG_SZ) || (reglen > sizeof(regval))) {\r
1419 RegCloseKey (reghnd);\r
1420 continue;\r
1421 }\r
1422 /* registry value seems OK, finish up and replace description */\r
1423 RegCloseKey (reghnd );\r
1424 sprintf (list[i].desc, "%s", regval);\r
1425 }\r
1426 } /* for */\r
1427#endif\r
1428\r
1429 return used;\r
1430}\r
1431\r
1432int eth_devices(int max, ETH_LIST* list)\r
1433{\r
1434 pcap_if_t* alldevs;\r
1435 pcap_if_t* dev;\r
1436 int i = 0;\r
1437 char errbuf[PCAP_ERRBUF_SIZE];\r
1438\r
1439#ifndef DONT_USE_PCAP_FINDALLDEVS\r
1440 /* retrieve the device list */\r
1441 if (pcap_findalldevs(&alldevs, errbuf) == -1) {\r
1442 char* msg = "Eth: error in pcap_findalldevs: %s\r\n";\r
1443 printf (msg, errbuf);\r
1444 if (sim_log) fprintf (sim_log, msg, errbuf);\r
1445 } else {\r
1446 /* copy device list into the passed structure */\r
1447 for (i=0, dev=alldevs; dev; dev=dev->next) {\r
1448 if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue;\r
1449 list[i].num = i;\r
1450 sprintf(list[i].name, "%s", dev->name);\r
1451 if (dev->description)\r
1452 sprintf(list[i].desc, "%s", dev->description);\r
1453 else\r
1454 sprintf(list[i].desc, "%s", "No description available");\r
1455 if (i++ >= max) break;\r
1456 }\r
1457\r
1458 /* free device list */\r
1459 pcap_freealldevs(alldevs);\r
1460 }\r
1461#endif\r
1462\r
1463 /* Add any host specific devices and/or validate those already found */\r
1464 i = eth_host_devices(i, max, list);\r
1465\r
1466 /* return device count */\r
1467 return i;\r
1468}\r
1469\r
1470#endif /* USE_NETWORK */\r