Commit | Line | Data |
---|---|---|
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 | |
246 | extern FILE *sim_log;\r | |
247 | \r | |
248 | \r | |
249 | /*============================================================================*/\r | |
250 | /* OS-independant ethernet routines */\r | |
251 | /*============================================================================*/\r | |
252 | \r | |
253 | t_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 | |
298 | void 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 | |
305 | static 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 | |
351 | uint32 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 | |
362 | void 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 | |
375 | void eth_setcrc(ETH_DEV* dev, int need_crc)\r | |
376 | {\r | |
377 | dev->need_crc = need_crc;\r | |
378 | }\r | |
379 | \r | |
380 | void 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 | |
424 | void 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 | |
429 | char* 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 | |
439 | char* 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 | |
465 | int 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 | |
485 | char* 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 | |
506 | void 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 | |
513 | t_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 | |
534 | t_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 | |
555 | void 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 | |
563 | void 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 | |
575 | void 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 | |
613 | t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit)\r | |
614 | {return SCPE_NOFNC;}\r | |
615 | t_stat eth_close (ETH_DEV* dev)\r | |
616 | {return SCPE_NOFNC;}\r | |
617 | t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r | |
618 | {return SCPE_NOFNC;}\r | |
619 | t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)\r | |
620 | {return SCPE_NOFNC;}\r | |
621 | t_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 | |
624 | int 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 | |
651 | static HINSTANCE hDll = 0; /* handle to DLL */\r | |
652 | static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */\r | |
653 | static char* no_wpcap = "wpcap load failure";\r | |
654 | \r | |
655 | /* define pointers to pcap functions needed */\r | |
656 | static void (*p_pcap_close) (pcap_t *);\r | |
657 | static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, char *, int, bpf_u_int32);\r | |
658 | static int (*p_pcap_datalink) (pcap_t *);\r | |
659 | static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *);\r | |
660 | static int (*p_pcap_findalldevs) (pcap_if_t **, char *);\r | |
661 | static void (*p_pcap_freealldevs) (pcap_if_t *);\r | |
662 | static void (*p_pcap_freecode) (struct bpf_program *);\r | |
663 | static char* (*p_pcap_geterr) (pcap_t *);\r | |
664 | static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *);\r | |
665 | static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *);\r | |
666 | static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len);\r | |
667 | static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *);\r | |
668 | static char* (*p_pcap_lib_version) (void);\r | |
669 | \r | |
670 | /* load function pointer from DLL */\r | |
671 | void 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 | |
682 | int 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 | |
734 | void pcap_close(pcap_t* a) {\r | |
735 | if (load_wpcap() != 0) {\r | |
736 | p_pcap_close(a);\r | |
737 | }\r | |
738 | }\r | |
739 | \r | |
740 | int 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 | |
748 | int 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 | |
756 | int 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 | |
764 | int 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 | |
774 | void 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 | |
780 | void 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 | |
786 | char* 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 | |
794 | int 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 | |
802 | pcap_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 | |
810 | int 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 | |
818 | int 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 | |
841 | int 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 | |
854 | void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data);\r | |
855 | \r | |
856 | static void *\r | |
857 | _eth_reader(void *arg)\r | |
858 | {\r | |
859 | ETH_DEV* volatile dev = (ETH_DEV*)arg;\r | |
860 | int status;\r | |
861 | struct 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, ð_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, ð_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 | |
892 | t_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 | |
981 | t_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 | |
1007 | t_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 | |
1055 | t_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 | |
1085 | void 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 | |
1158 | t_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, ð_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 | |
1200 | t_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 | |
1369 | int 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, ®hnd)) != 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, ®type, regval, ®len)) != 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 | |
1432 | int 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 |