1 /** \file   rawnetarch_win32.c
2  * \brief   Raw ethernet interface, Win32 stuff
3  *
4  * \author  Spiro Trikaliotis <Spiro.Trikaliotis@gmx.de>
5  */
6 
7 /*
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #ifdef HAVE_RAWNET
31 
32 /* #define WPCAP */
33 
34 #include "pcap.h"
35 
36 #include <assert.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "lib.h"
42 #include "log.h"
43 #include "rawnet.h"
44 #include "rawnetarch.h"
45 
46 typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *);
47 typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *);
48 typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *);
49 typedef int (*pcap_datalink_t)(pcap_t *);
50 typedef int (*pcap_findalldevs_t)(pcap_if_t **, char *);
51 typedef void (*pcap_freealldevs_t)(pcap_if_t *);
52 typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size);
53 typedef char *(*pcap_lookupdev_t)(char *);
54 
55 /** #define RAWNET_DEBUG_ARCH 1 **/
56 /** #define RAWNET_DEBUG_PKTDUMP 1 **/
57 
58 /* #define RAWNET_DEBUG_FRAMES - might be defined in rawnetarch.h ! */
59 
60 #define RAWNET_DEBUG_WARN 1 /* this should not be deactivated */
61 
62 static pcap_open_live_t p_pcap_open_live;
63 static pcap_dispatch_t p_pcap_dispatch;
64 static pcap_setnonblock_t p_pcap_setnonblock;
65 static pcap_findalldevs_t p_pcap_findalldevs;
66 static pcap_freealldevs_t p_pcap_freealldevs;
67 static pcap_sendpacket_t p_pcap_sendpacket;
68 static pcap_datalink_t p_pcap_datalink;
69 static pcap_lookupdev_t p_pcap_lookupdev;
70 
71 static HINSTANCE pcap_library = NULL;
72 
73 /* ------------------------------------------------------------------------- */
74 /*    variables needed                                                       */
75 
76 static log_t rawnet_arch_log = LOG_ERR;
77 
78 static pcap_if_t *EthernetPcapNextDev = NULL;
79 static pcap_if_t *EthernetPcapAlldevs = NULL;
80 static pcap_t *EthernetPcapFP = NULL;
81 
82 static char EthernetPcapErrbuf[PCAP_ERRBUF_SIZE];
83 
84 #ifdef RAWNET_DEBUG_PKTDUMP
85 
debug_output(const char * text,uint8_t * what,int count)86 static void debug_output(const char *text, uint8_t *what, int count)
87 {
88     char buffer[256];
89     char *p = buffer;
90     char *pbuffer1 = what;
91     int len1 = count;
92     int i;
93 
94     sprintf(buffer, "\n%s: length = %u\n", text, len1);
95     OutputDebugString(buffer);
96     do {
97         p = buffer;
98         for (i = 0; (i < 8) && len1 > 0; len1--, i++) {
99             sprintf( p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++);
100             p += 3;
101         }
102         *(p - 1) = '\n';
103         *p = 0;
104         OutputDebugString(buffer);
105     } while (len1 > 0);
106 }
107 #endif /* #ifdef RAWNET_DEBUG_PKTDUMP */
108 
109 
EthernetPcapFreeLibrary(void)110 static void EthernetPcapFreeLibrary(void)
111 {
112     if (pcap_library) {
113         if (!FreeLibrary(pcap_library)) {
114             log_message(rawnet_arch_log, "FreeLibrary WPCAP.DLL failed!");
115         }
116         pcap_library = NULL;
117 
118         p_pcap_open_live = NULL;
119         p_pcap_dispatch = NULL;
120         p_pcap_setnonblock = NULL;
121         p_pcap_findalldevs = NULL;
122         p_pcap_freealldevs = NULL;
123         p_pcap_sendpacket = NULL;
124         p_pcap_datalink = NULL;
125         p_pcap_lookupdev = NULL;
126     }
127 }
128 
129 /* since I don't like typing too much... */
130 #define GET_PROC_ADDRESS_AND_TEST( _name_ )                              \
131     p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_);     \
132     if (!p_##_name_ ) {                                                  \
133         log_message(rawnet_arch_log, "GetProcAddress " #_name_ " failed!"); \
134         EthernetPcapFreeLibrary();                                            \
135         return FALSE;                                                    \
136     }
137 
EthernetPcapLoadLibrary(void)138 static BOOL EthernetPcapLoadLibrary(void)
139 {
140     if (!pcap_library) {
141         pcap_library = LoadLibrary(TEXT("wpcap.dll"));
142 
143         if (!pcap_library) {
144             log_message(rawnet_arch_log, "LoadLibrary WPCAP.DLL failed!");
145             return FALSE;
146         }
147 
148         GET_PROC_ADDRESS_AND_TEST(pcap_open_live);
149         GET_PROC_ADDRESS_AND_TEST(pcap_dispatch);
150         GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock);
151         GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs);
152         GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs);
153         GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket);
154         GET_PROC_ADDRESS_AND_TEST(pcap_datalink);
155         GET_PROC_ADDRESS_AND_TEST(pcap_lookupdev);
156     }
157 
158     return TRUE;
159 }
160 
161 #undef GET_PROC_ADDRESS_AND_TEST
162 
163 
164 /*
165  These functions let the UI enumerate the available interfaces.
166 
167  First, rawnet_arch_enumadapter_open() is used to start enumeration.
168 
169  rawnet_arch_enumadapter is then used to gather information for each adapter present
170  on the system, where:
171 
172    ppname points to a pointer which will hold the name of the interface
173    ppdescription points to a pointer which will hold the description of the interface
174 
175    For each of these parameters, new memory is allocated, so it has to be
176    freed with lib_free().
177 
178  rawnet_arch_enumadapter_close() must be used to stop processing.
179 
180  Each function returns 1 on success, and 0 on failure.
181  rawnet_arch_enumadapter() only fails if there is no more adpater; in this case,
182    *ppname and *ppdescription are not altered.
183 */
rawnet_arch_enumadapter_open(void)184 int rawnet_arch_enumadapter_open(void)
185 {
186     if (!EthernetPcapLoadLibrary()) {
187         return 0;
188     }
189 
190     if ((*p_pcap_findalldevs)(&EthernetPcapAlldevs, EthernetPcapErrbuf) == -1) {
191         log_message(rawnet_arch_log, "ERROR in rawnet_arch_enumadapter_open: pcap_findalldevs: '%s'", EthernetPcapErrbuf);
192         return 0;
193     }
194 
195     if (!EthernetPcapAlldevs) {
196         log_message(rawnet_arch_log, "ERROR in rawnet_arch_enumadapter_open, finding all pcap devices - Do we have the necessary privilege rights?");
197         return 0;
198     }
199 
200     EthernetPcapNextDev = EthernetPcapAlldevs;
201 
202     return 1;
203 }
204 
rawnet_arch_enumadapter(char ** ppname,char ** ppdescription)205 int rawnet_arch_enumadapter(char **ppname, char **ppdescription)
206 {
207     if (!EthernetPcapNextDev) {
208         return 0;
209     }
210 
211     *ppname = lib_stralloc(EthernetPcapNextDev->name);
212     *ppdescription = lib_stralloc(EthernetPcapNextDev->description);
213 
214     EthernetPcapNextDev = EthernetPcapNextDev->next;
215 
216     return 1;
217 }
218 
rawnet_arch_enumadapter_close(void)219 int rawnet_arch_enumadapter_close(void)
220 {
221     if (EthernetPcapAlldevs) {
222         (*p_pcap_freealldevs)(EthernetPcapAlldevs);
223         EthernetPcapAlldevs = NULL;
224     }
225     return 1;
226 }
227 
EthernetPcapOpenAdapter(const char * interface_name)228 static BOOL EthernetPcapOpenAdapter(const char *interface_name)
229 {
230     pcap_if_t *EthernetPcapDevice = NULL;
231 
232     if (!rawnet_enumadapter_open()) {
233         return FALSE;
234     } else {
235         /* look if we can find the specified adapter */
236         char *pname;
237         char *pdescription;
238         BOOL  found = FALSE;
239 
240         if (interface_name) {
241             /* we have an interface name, try it */
242             EthernetPcapDevice = EthernetPcapAlldevs;
243 
244             while (rawnet_enumadapter(&pname, &pdescription)) {
245                 if (strcmp(pname, interface_name) == 0) {
246                     found = TRUE;
247                 }
248                 lib_free(pname);
249                 lib_free(pdescription);
250                 if (found) break;
251                 EthernetPcapDevice = EthernetPcapNextDev;
252             }
253         }
254 
255         if (!found) {
256             /* just take the first adapter */
257             EthernetPcapDevice = EthernetPcapAlldevs;
258         }
259     }
260 
261     EthernetPcapFP = (*p_pcap_open_live)(EthernetPcapDevice->name, 1700, 1, 20, EthernetPcapErrbuf);
262     if (EthernetPcapFP == NULL) {
263         log_message(rawnet_arch_log, "ERROR opening adapter: '%s'", EthernetPcapErrbuf);
264         rawnet_enumadapter_close();
265         return FALSE;
266     }
267 
268     if ((*p_pcap_setnonblock)(EthernetPcapFP, 1, EthernetPcapErrbuf) < 0) {
269         log_message(rawnet_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", EthernetPcapErrbuf);
270     }
271 
272     /* Check the link layer. We support only Ethernet for simplicity. */
273     if ((*p_pcap_datalink)(EthernetPcapFP) != DLT_EN10MB) {
274         log_message(rawnet_arch_log, "ERROR: Ethernet works only on Ethernet networks.");
275         rawnet_enumadapter_close();
276         return FALSE;
277     }
278 
279     rawnet_enumadapter_close();
280     return TRUE;
281 }
282 
283 /* ------------------------------------------------------------------------- */
284 /*    the architecture-dependend functions                                   */
285 
rawnet_arch_init(void)286 int rawnet_arch_init(void)
287 {
288     rawnet_arch_log = log_open("EthernetARCH");
289 
290     if (!EthernetPcapLoadLibrary()) {
291         return 0;
292     }
293 
294     return 1;
295 }
296 
rawnet_arch_pre_reset(void)297 void rawnet_arch_pre_reset( void )
298 {
299 #ifdef RAWNET_DEBUG_ARCH
300     log_message(rawnet_arch_log, "rawnet_arch_pre_reset().");
301 #endif
302 }
303 
rawnet_arch_post_reset(void)304 void rawnet_arch_post_reset( void )
305 {
306 #ifdef RAWNET_DEBUG_ARCH
307     log_message(rawnet_arch_log, "rawnet_arch_post_reset().");
308 #endif
309 }
310 
rawnet_arch_activate(const char * interface_name)311 int rawnet_arch_activate(const char *interface_name)
312 {
313 #ifdef RAWNET_DEBUG_ARCH
314     log_message(rawnet_arch_log, "rawnet_arch_activate().");
315 #endif
316     if (!EthernetPcapOpenAdapter(interface_name)) {
317         return 0;
318     }
319     return 1;
320 }
321 
rawnet_arch_deactivate(void)322 void rawnet_arch_deactivate( void )
323 {
324 #ifdef RAWNET_DEBUG_ARCH
325     log_message(rawnet_arch_log, "rawnet_arch_deactivate().");
326 #endif
327 }
328 
rawnet_arch_set_mac(const uint8_t mac[6])329 void rawnet_arch_set_mac( const uint8_t mac[6] )
330 {
331 #if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES)
332     log_message(rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
333 #endif
334 }
335 
rawnet_arch_set_hashfilter(const uint32_t hash_mask[2])336 void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2])
337 {
338 #if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES)
339     log_message(rawnet_arch_log, "New hash filter set: %08X:%08X.", hash_mask[1], hash_mask[0]);
340 #endif
341 }
342 
343 /* int bBroadcast   - broadcast */
344 /* int bIA          - individual address (IA) */
345 /* int bMulticast   - multicast if address passes the hash filter */
346 /* int bCorrect     - accept correct frames */
347 /* int bPromiscuous - promiscuous mode */
348 /* int bIAHash      - accept if IA passes the hash filter */
349 
350 
rawnet_arch_recv_ctl(int bBroadcast,int bIA,int bMulticast,int bCorrect,int bPromiscuous,int bIAHash)351 void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash)
352 {
353 #if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES)
354     log_message(rawnet_arch_log, "rawnet_arch_recv_ctl() called with the following parameters:" );
355     log_message(rawnet_arch_log, "\tbBroadcast   = %s", bBroadcast ? "TRUE" : "FALSE" );
356     log_message(rawnet_arch_log, "\tbIA          = %s", bIA ? "TRUE" : "FALSE" );
357     log_message(rawnet_arch_log, "\tbMulticast   = %s", bMulticast ? "TRUE" : "FALSE" );
358     log_message(rawnet_arch_log, "\tbCorrect     = %s", bCorrect ? "TRUE" : "FALSE" );
359     log_message(rawnet_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE" );
360     log_message(rawnet_arch_log, "\tbIAHash      = %s", bIAHash ? "TRUE" : "FALSE" );
361 #endif
362 }
363 
rawnet_arch_line_ctl(int bEnableTransmitter,int bEnableReceiver)364 void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver)
365 {
366 #if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES)
367     log_message(rawnet_arch_log, "rawnet_arch_line_ctl() called with the following parameters:" );
368     log_message(rawnet_arch_log, "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE" );
369     log_message(rawnet_arch_log, "\tbEnableReceiver    = %s", bEnableReceiver ? "TRUE" : "FALSE" );
370 #endif
371 }
372 
373 typedef struct Ethernet_PCAP_internal_s {
374     unsigned int len;
375     uint8_t *buffer;
376 } Ethernet_PCAP_internal_t;
377 
378 /* Callback function invoked by libpcap for every incoming packet */
EthernetPcapPacketHandler(u_char * param,const struct pcap_pkthdr * header,const u_char * pkt_data)379 static void EthernetPcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
380 {
381     Ethernet_PCAP_internal_t *pinternal = (void*)param;
382 
383     /* determine the count of bytes which has been returned,
384      * but make sure not to overrun the buffer
385      */
386     if (header->caplen < pinternal->len) {
387         pinternal->len = header->caplen;
388     }
389 
390     memcpy(pinternal->buffer, pkt_data, pinternal->len);
391 }
392 
393 /* the following function receives a frame.
394 
395    If there's none, it returns a -1.
396    If there is one, it returns the length of the frame in bytes.
397 
398    It copies the frame to *buffer and returns the number of copied
399    bytes as return value.
400 
401    At most 'len' bytes are copied.
402 */
rawnet_arch_receive_frame(Ethernet_PCAP_internal_t * pinternal)403 static int rawnet_arch_receive_frame(Ethernet_PCAP_internal_t *pinternal)
404 {
405     int ret = -1;
406 
407     /* check if there is something to receive */
408     if ((*p_pcap_dispatch)(EthernetPcapFP, 1, EthernetPcapPacketHandler, (void*)pinternal) != 0) {
409         /* Something has been received */
410         ret = pinternal->len;
411     }
412 
413 #ifdef RAWNET_DEBUG_ARCH
414     log_message(rawnet_arch_log, "rawnet_arch_receive_frame() called, returns %d.", ret);
415 #endif
416 
417     return ret;
418 }
419 
420 /* int force       - FORCE: Delete waiting frames in transmit buffer */
421 /* int onecoll     - ONECOLL: Terminate after just one collision */
422 /* int inhibit_crc - INHIBITCRC: Do not append CRC to the transmission */
423 /* int tx_pad_dis  - TXPADDIS: Disable padding to 60 Bytes */
424 /* int txlength    - Frame length */
425 /* uint8_t *txframe   - Pointer to the frame to be transmitted */
426 
rawnet_arch_transmit(int force,int onecoll,int inhibit_crc,int tx_pad_dis,int txlength,uint8_t * txframe)427 void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe)
428 {
429 #ifdef RAWNET_DEBUG_ARCH
430     log_message(rawnet_arch_log, "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u",
431                 force ? "TRUE" : "FALSE",
432                 onecoll ? "TRUE" : "FALSE",
433                 inhibit_crc ? "TRUE" : "FALSE",
434                 tx_pad_dis ?  "TRUE" : "FALSE",
435                 txlength);
436 #endif
437 
438 #ifdef RAWNET_DEBUG_PKTDUMP
439     debug_output("Transmit frame: ", txframe, txlength);
440 #endif /* #ifdef RAWNET_DEBUG_PKTDUMP */
441 
442     if ((*p_pcap_sendpacket)(EthernetPcapFP, txframe, txlength) == -1) {
443         log_message(rawnet_arch_log, "WARNING! Could not send packet!");
444     }
445 }
446 
447 /*
448   rawnet_arch_receive()
449 
450   This function checks if there was a frame received.
451   If so, it returns 1, else 0.
452 
453   If there was no frame, none of the parameters is changed!
454 
455   If there was a frame, the following actions are done:
456 
457   - at maximum *plen byte are transferred into the buffer given by pbuffer
458   - *plen gets the length of the received frame, EVEN if this is more
459     than has been copied to pbuffer!
460   - if the dest. address was accepted by the hash filter, *phashed is set, else
461     cleared.
462   - if the dest. address was accepted by the hash filter, *phash_index is
463     set to the number of the rule leading to the acceptance
464   - if the receive was ok (good CRC and valid length), *prx_ok is set,
465     else cleared.
466   - if the dest. address was accepted because it's exactly our MAC address
467     (set by rawnet_arch_set_mac()), *pcorrect_mac is set, else cleared.
468   - if the dest. address was accepted since it was a broadcast address,
469     *pbroadcast is set, else cleared.
470   - if the received frame had a crc error, *pcrc_error is set, else cleared
471 */
472 
473 /* uint8_t *pbuffer     - where to store a frame */
474 /* int *plen         - IN: maximum length of frame to copy;
475                        OUT: length of received frame
476                             OUT can be bigger than IN if received frame was
477                             longer than supplied buffer */
478 /* int *phashed      - set if the dest. address is accepted by the hash filter */
479 /* int *phash_index  - hash table index if hashed == TRUE */
480 /* int *prx_ok       - set if good CRC and valid length */
481 /* int *pcorrect_mac - set if dest. address is exactly our IA */
482 /* int *pbroadcast   - set if dest. address is a broadcast address */
483 /* int *pcrc_error   - set if received frame had a CRC error */
484 
rawnet_arch_receive(uint8_t * pbuffer,int * plen,int * phashed,int * phash_index,int * prx_ok,int * pcorrect_mac,int * pbroadcast,int * pcrc_error)485 int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error)
486 {
487     int len;
488 
489     Ethernet_PCAP_internal_t internal;
490 
491     internal.len = *plen;
492     internal.buffer = pbuffer;
493 
494 #ifdef RAWNET_DEBUG_ARCH
495     log_message(rawnet_arch_log, "rawnet_arch_receive() called, with *plen=%u.", *plen);
496 #endif
497 
498     assert((*plen & 1) == 0);
499 
500     len = rawnet_arch_receive_frame(&internal);
501 
502     if (len != -1) {
503 
504 #ifdef RAWNET_DEBUG_PKTDUMP
505         debug_output("Received frame: ", internal.buffer, internal.len);
506 #endif /* #ifdef RAWNET_DEBUG_PKTDUMP */
507 
508         if (len & 1) {
509             ++len;
510         }
511 
512         *plen = len;
513 
514         /* we don't decide if this frame fits the needs;
515          * by setting all zero, we let ethernet.c do the work
516          * for us
517          */
518         *phashed = *phash_index = *pbroadcast = *pcorrect_mac = *pcrc_error = 0;
519 
520         /* this frame has been received correctly */
521         *prx_ok = 1;
522 
523         return 1;
524     }
525 
526     return 0;
527 }
528 
rawnet_arch_get_standard_interface(void)529 char *rawnet_arch_get_standard_interface(void)
530 {
531     char *dev, errbuf[PCAP_ERRBUF_SIZE];
532 
533     if (!EthernetPcapLoadLibrary()) {
534         return NULL;
535     }
536 
537     dev = lib_stralloc((*p_pcap_lookupdev)(errbuf));
538 
539     return dev;
540 }
541 #endif /* #ifdef HAVE_RAWNET */
542