| 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 |