From 90dcd9dca10ac819cdfdd1fc3fb487bfb6148404 Mon Sep 17 00:00:00 2001 From: Patrick Grimm Date: Fri, 4 Oct 2024 20:46:34 +0200 Subject: [PATCH] win32: fix ethernet and bip6 (#774) Switch from WinPcap ton npcap. Include npcap sdk in cmake add libs for ipv6 in cmake fix ethernet uninitialized var --- CMakeLists.txt | 34 ++++++- ports/win32/bip6.c | 6 +- ports/win32/ethernet.c | 215 ++++++++++++++++++++++++++++++++--------- 3 files changed, 202 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3217a8e..1c70d95e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -673,8 +673,8 @@ elseif(WIN32) target_link_libraries(${PROJECT_NAME} PRIVATE winmm - $<$:ws2_32> - $<$:iphlpapi>) + $<$:ws2_32> + $<$:iphlpapi>) target_sources(${PROJECT_NAME} PRIVATE ports/win32/bacport.h @@ -683,10 +683,38 @@ elseif(WIN32) ports/win32/datetime-init.c $<$:ports/win32/dlmstp.c> # ports/win32/dlmstp-mm.c - $<$:ports/win32/ethernet.c> ports/win32/mstimer-init.c $<$:ports/win32/rs485.c> $<$:ports/win32/rs485.h>) + + if(BACDL_ETHERNET) + include(ExternalProject) + set(PCAP_LIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/npcap/Lib/x64) + set(PCAP_LIB_WPCAP ${PCAP_LIB_DIR}/wpcap.lib) + set(PCAP_LIB_PACKET ${PCAP_LIB_DIR}/Packet.lib) + set(PCAP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/npcap/Include) + message(STATUS "BACNET: npcap Include:..................\"${PCAP_INCLUDE}\"") + message(STATUS "BACNET: npcap Lib wpcap:................\"${PCAP_LIB_WPCAP}\"") + message(STATUS "BACNET: npcap Lib Packet:...............\"${PCAP_LIB_PACKET}\"") + ExternalProject_Add(npcap + URL https://npcap.com/dist/npcap-sdk-1.13.zip + URL_HASH SHA1=8d5bb6f3adb813374402344a8d2a12b9cb725197 + DOWNLOAD_EXTRACT_TIMESTAMP true + SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/npcap + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/npcap/Lib/x64 + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + BUILD_BYPRODUCTS ${PCAP_LIB_WPCAP} + BUILD_BYPRODUCTS ${PCAP_LIB_PACKET}) + include_directories(${PCAP_INCLUDE}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${PCAP_LIB_WPCAP}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${PCAP_LIB_PACKET}) + target_sources(${PROJECT_NAME} PRIVATE + ports/win32/ethernet.c) + endif() elseif(APPLE) message(STATUS "BACNET: building for APPLE") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) diff --git a/ports/win32/bip6.c b/ports/win32/bip6.c index 3b78c9a9..75e20bbb 100644 --- a/ports/win32/bip6.c +++ b/ports/win32/bip6.c @@ -393,9 +393,9 @@ int bip6_send_mpdu( * Otherwise, -1 shall be returned and errno set to indicate the error. */ int bip6_send_pdu( - const BACNET_ADDRESS *dest, - const BACNET_NPDU_DATA *npdu_data, - const uint8_t *pdu, + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, unsigned pdu_len) { return bvlc6_send_pdu(dest, npdu_data, pdu, pdu_len); diff --git a/ports/win32/ethernet.c b/ports/win32/ethernet.c index da829da7..d17da46e 100644 --- a/ports/win32/ethernet.c +++ b/ports/win32/ethernet.c @@ -15,21 +15,20 @@ #include "bacnet/datalink/ethernet.h" #include "bacnet/bacdcode.h" -/* Uses WinPCap to access raw ethernet */ +/* Uses Npcap to access raw ethernet */ /* Notes: */ /* To make ethernet.c work under win32, you have to: */ -/* 1. install winpcap 3.1 development pack; */ -/* 2. install Microsoft Platform SDK Feb 2003. */ +/* 1. install Npcap 1.80 installer for Windows; */ +/* 2. install msys2 x86_64 */ /* 3. remove or modify functions used for log such as */ /* "LogError()", "LogInfo()", which were implemented */ /* as a wrapper of Log4cpp. */ /* -- Kevin Liao */ +/* -- Patrick Grimm */ /* includes for accessing ethernet by using winpcap */ #include "pcap.h" -#include "packet32.h" #include "ntddndis.h" -#include "remote-ext.h" /* commonly used comparison address for ethernet */ uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = { @@ -46,14 +45,105 @@ static char pcap_errbuf[PCAP_ERRBUF_SIZE + 1]; static pcap_t *pcap_eth802_fp = NULL; /* 802.2 file handle, from winpcap */ static unsigned eth_timeout = 100; +/* #######Begin Packet32.h copy########*/ +/*! + \brief Structure containing an OID request. + + It is used by the PacketRequest() function to send an OID to the interface + card driver. It can be used, for example, to retrieve the status of the error + counters on the adapter, its MAC address, the list of the multicast groups + defined on it, and so on. +*/ +struct _PACKET_OID_DATA { + ULONG Oid; /* ///< OID code. See the Microsoft DDK + documentation or the file ntddndis.h + ///< for a complete list of valid codes. */ + ULONG Length; /* ///< Length of the data field + _Field_size_full_(Length)*/ + UCHAR Data[1]; /* ///< variable-lenght field that contains the + information passed to or received + ///< from the adapter.*/ +}; +typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA; + +#define MAX_LINK_NAME_LENGTH \ + 64 /* //< Maximum length of the devices symbolic links*/ +#define ADAPTER_NAME_LENGTH \ + 256 + 12 /*///< Maximum length for the name of an adapter. The value is \ + the same used by the IP Helper API.*/ +typedef struct WAN_ADAPTER_INT + WAN_ADAPTER; /*///< Describes an opened wan (dialup, VPN...) network adapter + using the NetMon API*/ +typedef WAN_ADAPTER + *PWAN_ADAPTER; /*///< Describes an opened wan (dialup, VPN...) network + adapter using the NetMon API*/ + +/*! + \brief Describes an opened network adapter. + + This structure is the most important for the functioning of packet.dll, but + the great part of its fields should be ignored by the user, since the library + offers functions that avoid to cope with low-level parameters +*/ +typedef struct _ADAPTER { + HANDLE hFile; /* ///< \internal Handle to an open instance of the + NPF driver.*/ + CHAR + SymbolicLink[MAX_LINK_NAME_LENGTH]; /*///< \internal A string containing + the name of the network adapter + currently opened.*/ + int NumWrites; /* ///< \internal Number of times a packets written + on this adapter will be repeated + ///< on the wire.*/ + HANDLE + ReadEvent; /* ///< A notification event associated with the read +calls on the adapter. +///< It can be passed to standard Win32 functions (like WaitForSingleObject +///< or WaitForMultipleObjects) to wait until the driver's buffer contains some +///< data. It is particularly useful in GUI applications that need to wait +///< concurrently on several events. The PacketSetMinToCopy() +///< function can be used to define the minimum amount of data in the kernel +buffer +///< that will cause the event to be signalled. */ + + UINT ReadTimeOut; /*///< \internal The amount of time PacketReceivePacket + will wait for the ReadEvent to be signalled before + issuing a ReadFile.*/ + CHAR Name[ADAPTER_NAME_LENGTH]; + PWAN_ADAPTER pWanAdapter; + UINT Flags; /* ///< Adapter's flags. Tell if this adapter must be + treated in a different way.*/ + +#ifdef HAVE_AIRPCAP_API + PAirpcapHandle AirpcapAd; +#endif /*// HAVE_AIRPCAP_API*/ +} ADAPTER, *LPADAPTER; + +_Ret_maybenull_ LPADAPTER PacketOpenAdapter(_In_ PCCH AdapterName); +_Success_(return) BOOLEAN PacketRequest( + _In_ LPADAPTER AdapterObject, + _In_ BOOLEAN Set, + _Inout_ PPACKET_OID_DATA OidData); +VOID PacketCloseAdapter(_In_ _Post_invalid_ LPADAPTER lpAdapter); + +/* #######End Packet32.h copy##########*/ + /* couple of external func for runtime error logging, you can simply */ -/* replace them with standard "printf(...)" */ -/* Logging extern functions: Info level */ -extern void LogInfo(const char *msg); -/* Logging extern functions: Error level*/ -extern void LogError(const char *msg); -/* Logging extern functions: Debug level*/ -extern void LogDebug(const char *msg); +/* Logging functions: Info level */ +void LogInfo(const char *msg) +{ + fprintf(stdout, "info ethernet: %s", msg); +} +/* Logging functions: Error level*/ +void LogError(const char *msg) +{ + fprintf(stderr, "error ethernet: %s", msg); +} +/* Logging functions: Debug level*/ +void LogDebug(const char *msg) +{ + fprintf(stdout, "debug ethernet: %s", msg); +} bool ethernet_valid(void) { @@ -66,7 +156,7 @@ void ethernet_cleanup(void) pcap_close(pcap_eth802_fp); pcap_eth802_fp = NULL; } - LogInfo("ethernet.c: ethernet_cleanup() ok.\n"); + LogInfo("ethernet_cleanup() ok.\n"); } void ethernet_set_timeout(unsigned timeout) @@ -108,7 +198,9 @@ bool ethernet_init(char *if_name) BOOLEAN result; CHAR str[sizeof(PACKET_OID_DATA) + 128]; int i; - char msg[200]; + char msg[400]; + int devnum; + char *device = NULL; if (ethernet_valid()) { ethernet_cleanup(); @@ -118,24 +210,42 @@ bool ethernet_init(char *if_name) * Find the interface user specified */ /* Retrieve the device list */ - if (pcap_findalldevs(&pcap_all_if, pcap_errbuf) == -1) { - snprintf( - msg, sizeof(msg), "ethernet.c: error in pcap_findalldevs: %s\n", - pcap_errbuf); + if (pcap_findalldevs(&pcap_all_if, pcap_errbuf) == PCAP_ERROR) { + snprintf(msg, sizeof(msg), "pcap_findalldevs: %s\n", pcap_errbuf); LogError(msg); return false; } /* Scan the list printing every entry */ + devnum = atoi(if_name); + i = 0; for (dev = pcap_all_if; dev; dev = dev->next) { - if (strcmp(if_name, dev->name) == 0) { - break; + /* struct pcap_addr *dev_addr;*/ + i++; + if (devnum == i) { + device = dev->name; + snprintf(msg, sizeof(msg), "interface select index: %i\n", i); + LogInfo(msg); + } + if ((dev->flags & PCAP_IF_UP) && !(dev->flags & PCAP_IF_LOOPBACK) && + (dev->flags & PCAP_IF_RUNNING) && + (dev->flags & PCAP_IF_CONNECTION_STATUS_CONNECTED)) { + snprintf(msg, sizeof(msg), "interface index: %i\n", i); + LogInfo(msg); + snprintf(msg, sizeof(msg), " name: %s\n", dev->name); + LogInfo(msg); + snprintf(msg, sizeof(msg), " description: %s\n", dev->description); + LogInfo(msg); } } pcap_freealldevs(pcap_all_if); /* we don't need it anymore */ - if (dev == NULL) { + if (if_name == NULL) { + snprintf(msg, sizeof(msg), "interface index not set\n"); + LogError(msg); + return false; + } + if (device == NULL) { snprintf( - msg, sizeof(msg), "ethernet.c: specified interface not found: %s\n", - if_name); + msg, sizeof(msg), "specified interface not found: %s\n", if_name); LogError(msg); return false; } @@ -144,12 +254,11 @@ bool ethernet_init(char *if_name) * Get local MAC address */ ZeroMemory(str, sizeof(PACKET_OID_DATA) + 128); - lpAdapter = PacketOpenAdapter(if_name); + lpAdapter = PacketOpenAdapter(device); if (lpAdapter == NULL) { ethernet_cleanup(); snprintf( - msg, sizeof(msg), - "ethernet.c: error in PacketOpenAdapter(\"%s\")\n", if_name); + msg, sizeof(msg), "local mac PacketOpenAdapter(\"%s\")\n", device); LogError(msg); return false; } @@ -160,7 +269,7 @@ bool ethernet_init(char *if_name) if (!result) { PacketCloseAdapter(lpAdapter); ethernet_cleanup(); - LogError("ethernet.c: error in PacketRequest()\n"); + LogError("local mac PacketRequest()\n"); return false; } for (i = 0; i < 6; ++i) { @@ -168,16 +277,23 @@ bool ethernet_init(char *if_name) } PacketCloseAdapter(lpAdapter); + snprintf( + msg, sizeof(msg), "local mac %02x:%02x:%02x:%02x:%02x:%02x \n", + Ethernet_MAC_Address[0], Ethernet_MAC_Address[1], + Ethernet_MAC_Address[2], Ethernet_MAC_Address[3], + Ethernet_MAC_Address[4], Ethernet_MAC_Address[5]); + LogInfo(msg); + /** * Open interface for subsequent sending and receiving */ /* Open the output device */ - pcap_eth802_fp = pcap_open( - if_name, /* name of the device */ + pcap_eth802_fp = pcap_open_live( + device, /* name of the device */ ETHERNET_MPDU_MAX, /* portion of the packet to capture */ PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ eth_timeout, /* read timeout */ - NULL, /* authentication on the remote machine */ + /* NULL, */ /* authentication on the remote machine */ pcap_errbuf /* error buffer */ ); if (pcap_eth802_fp == NULL) { @@ -185,14 +301,14 @@ bool ethernet_init(char *if_name) ethernet_cleanup(); snprintf( msg, sizeof(msg), - "ethernet.c: unable to open the adapter. %s is not supported by " - "WinPcap\n", - if_name); + "unable to open the adapter. %s is not supported by " + "Npcap\n", + device); LogError(msg); return false; } - LogInfo("ethernet.c: ethernet_init() ok.\n"); + LogInfo("ethernet_init() ok.\n"); atexit(ethernet_cleanup); @@ -201,21 +317,21 @@ bool ethernet_init(char *if_name) /* function to send a packet out the 802.2 socket */ /* returns bytes sent success, negative on failure */ -int ethernet_send( +int ethernet_send_dst( BACNET_ADDRESS *dest, /* destination address */ BACNET_ADDRESS *src, /* source address */ uint8_t *pdu, /* any data to be sent - may be null */ unsigned pdu_len /* number of bytes of data */ ) { - int bytes = 0; + /* int bytes = 0; */ uint8_t mtu[ETHERNET_MPDU_MAX] = { 0 }; int mtu_len = 0; int i = 0; /* don't waste time if the socket is not valid */ if (!ethernet_valid()) { - LogError("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + LogError("invalid 802.2 ethernet interface descriptor!\n"); return -1; } /* load destination ethernet MAC address */ @@ -225,7 +341,7 @@ int ethernet_send( mtu_len++; } } else { - LogError("ethernet.c: invalid destination MAC address!\n"); + LogError("invalid destination MAC address!\n"); return -2; } @@ -236,11 +352,11 @@ int ethernet_send( mtu_len++; } } else { - LogError("ethernet.c: invalid source MAC address!\n"); + LogError("invalid source MAC address!\n"); return -3; } if ((14 + 3 + pdu_len) > ETHERNET_MPDU_MAX) { - LogError("ethernet.c: PDU is too big to send!\n"); + LogError("PDU is too big to send!\n"); return -4; } /* packet length */ @@ -257,7 +373,7 @@ int ethernet_send( /* did it get sent? */ char msg[200]; snprintf( - msg, sizeof(msg), "ethernet.c: error sending packet: %s\n", + msg, sizeof(msg), "error sending packet: %s\n", pcap_geterr(pcap_eth802_fp)); LogError(msg); return -5; @@ -270,12 +386,14 @@ int ethernet_send( /* returns number of bytes sent on success, negative on failure */ int ethernet_send_pdu( BACNET_ADDRESS *dest, /* destination address */ + BACNET_NPDU_DATA *npdu_data, /* network information */ uint8_t *pdu, /* any data to be sent - may be null */ unsigned pdu_len /* number of bytes of data */ ) { int i = 0; /* counter */ BACNET_ADDRESS src = { 0 }; /* source address */ + (void)npdu_data; for (i = 0; i < 6; i++) { src.mac[i] = Ethernet_MAC_Address[i]; @@ -283,7 +401,7 @@ int ethernet_send_pdu( } /* function to send a packet out the 802.2 socket */ /* returns 1 on success, 0 on failure */ - return ethernet_send( + return ethernet_send_dst( dest, /* destination address */ &src, /* source address */ pdu, /* any data to be sent - may be null */ @@ -303,12 +421,14 @@ uint16_t ethernet_receive( { struct pcap_pkthdr *header; int res; - u_char *pkt_data; + const u_char *pkt_data; uint16_t pdu_len = 0; /* return value */ + /* unused ? */ + (void)timeout; /* Make sure the socket is open */ if (!ethernet_valid()) { - LogError("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + LogError("invalid 802.2 ethernet interface descriptor!\n"); return 0; } @@ -317,8 +437,9 @@ uint16_t ethernet_receive( if (res < 0) { char msg[200]; snprintf( - msg, sizeof(), "ethernet.c: error in receiving packet: %s\n", + msg, sizeof(msg), "error in receiving packet: %s\n", pcap_geterr(pcap_eth802_fp)); + LogError(msg); return 0; } else if (res == 0) { return 0; @@ -330,7 +451,7 @@ uint16_t ethernet_receive( /* the signature of an 802.2 BACnet packet */ if ((pkt_data[14] != 0x82) && (pkt_data[15] != 0x82)) { - /*eth_log_error("ethernet.c: Non-BACnet packet\n"); */ + /*LogError("ethernet.c: Non-BACnet packet\n"); */ return 0; } /* copy the source address */ @@ -341,7 +462,7 @@ uint16_t ethernet_receive( /* the Ethernet card is in promiscious mode */ if ((memcmp(&pkt_data[0], Ethernet_MAC_Address, 6) != 0) && (memcmp(&pkt_data[0], Ethernet_Broadcast, 6) != 0)) { - /*eth_log_error( "ethernet.c: This packet isn't for us\n"); */ + /*LogError( "ethernet.c: This packet isn't for us\n"); */ return 0; }