/* * ethernet_win32.c * * Copyright 2013 Michael Zillgith * * This file is part of libIEC61850. * * libIEC61850 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * libIEC61850 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libIEC61850. If not, see . * * See COPYING file for the complete license text. */ #include "stack_config.h" #include #include #include "hal_ethernet.h" #include "lib_memory.h" #ifndef DEBUG_HAL_ETHERNET #define DEBUG_HAL_ETHERNET 1 #endif // Set to 1 to workaround WaitForMutlipleObjects problem (returns timeout even when packets are received) #define ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET 1 #if (CONFIG_INCLUDE_ETHERNET_WINDOWS == 1) #include #include #include #ifndef __GNUC__ #pragma comment (lib, "IPHLPAPI.lib") #endif #define HAVE_REMOTE // Enable WinPcap specific extension: pcap_getevent() #define WPCAP #include "pcap.h" struct sEthernetSocket { pcap_t* rawSocket; struct bpf_program etherTypeFilter; }; struct sEthernetHandleSet { HANDLE *handles; int nhandles; }; #ifdef __GNUC__ /* detect MINGW */ #ifndef __MINGW64_VERSION_MAJOR #define MAX_ADAPTER_ADDRESS_LENGTH 8 typedef struct _IP_ADAPTER_ADDRESSES { union { ULONGLONG Alignment; struct { ULONG Length; DWORD IfIndex; }; }; struct _IP_ADAPTER_ADDRESSES *Next; PCHAR AdapterName; void* FirstUnicastAddress; void* FirstAnycastAddress; void* FirstMulticastAddress; void* FirstDnsServerAddress; PWCHAR DnsSuffix; PWCHAR Description; PWCHAR FriendlyName; BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; DWORD PhysicalAddressLength; DWORD Flags; DWORD Mtu; DWORD IfType; } IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES; typedef ULONG (WINAPI* pgetadaptersaddresses)(ULONG family, ULONG flags, PVOID reserved, PIP_ADAPTER_ADDRESSES AdapterAddresses, PULONG SizePointer); static pgetadaptersaddresses GetAdaptersAddresses; static bool dllLoaded = false; static void loadDLLs(void) { HINSTANCE hDll = LoadLibrary("iphlpapi.dll"); if (hDll == NULL) { if (DEBUG_HAL_ETHERNET) printf("Error loading iphlpapi.dll!\n"); return; } GetAdaptersAddresses = (pgetadaptersaddresses) GetProcAddress(hDll, "GetAdaptersAddresses"); if (GetAdaptersAddresses == NULL) printf("Error loading GetAdaptersAddresses from iphlpapi.dll (%d)\n", (int) GetLastError()); } #endif /* __MINGW64_VERSION_MAJOR */ #endif /* __GNUC__ */ EthernetHandleSet EthernetHandleSet_new(void) { EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet)); if (result != NULL) { result->handles = NULL; result->nhandles = 0; } return result; } void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { #if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1 #else if (self != NULL && sock != NULL) { int i = self->nhandles++; self->handles = (HANDLE *) realloc(self->handles, self->nhandles * sizeof(HANDLE)); self->handles[i] = pcap_getevent(sock->rawSocket); } #endif } void EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock) { #if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1 #else if ((self != NULL) && (sock != NULL)) { HANDLE h = pcap_getevent(sock->rawSocket); int i; for (i = 0; i < self->nhandles; i++) { if (self->handles[i] == h) { memmove(&self->handles[i], &self->handles[i+1], sizeof(HANDLE) * (self->nhandles - i - 1)); self->nhandles--; return; } } } #endif } int EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs) { #if ETHERNET_WIN32_DISABLE_ETHERNET_HANDLESET == 1 return 1; #else int result; if ((self != NULL) && (self->nhandles > 0)) { DWORD ret = WaitForMultipleObjects(self->nhandles, self->handles, 0, timeoutMs); if (ret == WAIT_TIMEOUT) result = 0; else if (ret == WAIT_FAILED) result = -1; else result = (int)ret; } else { result = -1; } return result; #endif } void EthernetHandleSet_destroy(EthernetHandleSet self) { if (self->handles) free(self->handles); GLOBAL_FREEMEM(self); } static char* getInterfaceName(int interfaceIndex) { char errbuf[PCAP_ERRBUF_SIZE]; char* interfaceName = NULL; pcap_if_t *devices; pcap_if_t *device; /* Get the ethernet device list */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &devices, errbuf) == -1) { printf("pcap_findalldevs_ex: %s\n", errbuf); return NULL; } bool ifaceFound = false; /* Search device list for requested interface) */ int i = 0; for(device = devices; device != NULL; device= device->next) { if (i == interfaceIndex) { interfaceName = (char*) malloc(strlen(device->name) + 1); strcpy(interfaceName, device->name); if (DEBUG_HAL_ETHERNET) printf("Use interface (%s)\n", interfaceName); ifaceFound = true; break; } i++; } if (!ifaceFound) { if (DEBUG_HAL_ETHERNET) printf("No ethernet interfaces found! Make sure WinPcap is installed.\n"); return NULL; } pcap_freealldevs(devices); return interfaceName; } static void getAdapterMacAddress(char* pcapAdapterName, uint8_t* macAddress) { PIP_ADAPTER_ADDRESSES pAddresses = NULL; ULONG outBufLen = 0; pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(65000); if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) { free(pAddresses); pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen); } if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == NO_ERROR) { PIP_ADAPTER_ADDRESSES pAddress = pAddresses; while (pAddress != NULL) { DWORD addressLength = pAddress->PhysicalAddressLength; if (addressLength == 6) { int i; if (DEBUG_HAL_ETHERNET) { printf("Adapter %s: ", pAddress->AdapterName); for (i = 0; i < (int) addressLength; i++) { printf("%02x ", pAddress->PhysicalAddress[i]); } } if (strstr(pcapAdapterName, pAddress->AdapterName) != 0) { if (DEBUG_HAL_ETHERNET) printf(" requested found!"); for (i = 0; i < (int) addressLength; i++) { macAddress[i] = pAddress->PhysicalAddress[i]; } } printf("\n"); } pAddress = pAddress->Next; } free(pAddresses); } else { printf("Error getting device addresses!\n"); } } void Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) { #ifdef __GNUC__ #ifndef __MINGW64_VERSION_MAJOR if (!dllLoaded) { loadDLLs(); dllLoaded = true; } #endif #endif char* endPtr; long interfaceIndex = strtol(interfaceId, &endPtr, 10); if ((*interfaceId != '\0') && (*endPtr != '\0')) { printf("Ethernet_getInterfaceMACAddress: invalid interface number %s\n", interfaceId); return; } char* interfaceName = getInterfaceName((int) interfaceIndex); getAdapterMacAddress(interfaceName, addr); free(interfaceName); } EthernetSocket Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress) { pcap_t *pcapSocket; char errbuf[PCAP_ERRBUF_SIZE]; int interfaceIndex = atoi(interfaceId); char* interfaceName = getInterfaceName(interfaceIndex); if ((pcapSocket = pcap_open_live(interfaceName, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf)) == NULL) { printf("Open ethernet socket failed for device %s\n", interfaceName); free(interfaceName); return NULL; } free(interfaceName); EthernetSocket ethernetSocket = (EthernetSocket) calloc(1, sizeof(struct sEthernetSocket)); ethernetSocket->rawSocket = pcapSocket; return ethernetSocket; } void Ethernet_destroySocket(EthernetSocket ethSocket) { pcap_close(ethSocket->rawSocket); free(ethSocket); } void Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize) { if (pcap_sendpacket(ethSocket->rawSocket, buffer, packetSize) != 0) printf("Error sending the packet: %s\n", pcap_geterr(ethSocket->rawSocket)); } void Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode) { /* not implemented */ } void Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) { /* not implemented */ } void Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) { char filterString[100]; sprintf(filterString, "(ether proto 0x%04x) or (vlan and ether proto 0x%04x)", etherType, etherType); if (pcap_compile(ethSocket->rawSocket, &(ethSocket->etherTypeFilter), filterString, 1, 0) < 0) { printf("Compiling packet filter failed!\n"); return; } if (pcap_setfilter(ethSocket->rawSocket, &(ethSocket->etherTypeFilter)) < 0) { printf("Setting packet filter failed!\n"); } } int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) { struct pcap_pkthdr* header; uint8_t* packetData; int pcapCode = pcap_next_ex(self->rawSocket, &header, (const unsigned char**) &packetData); if (pcapCode > 0) { int packetSize = header->caplen; if (packetSize > bufferSize) packetSize = bufferSize; memcpy(buffer, packetData, packetSize); return packetSize; } else { if (pcapCode < 0) printf("winpcap error\n"); return 0; } } bool Ethernet_isSupported() { return true; } #else bool Ethernet_isSupported() { return false; } EthernetHandleSet EthernetHandleSet_new(void) { return NULL; } void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { } int EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs) { return 0; } void EthernetHandleSet_destroy(EthernetHandleSet self) { } void Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) { } EthernetSocket Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress) { return NULL; } void Ethernet_destroySocket(EthernetSocket ethSocket) { } void Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize) { } void Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode) { } void Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) { } void Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) { } int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) { return 0; } #endif /* (CONFIG_INCLUDE_ETHERNET_WINDOWS == 1) */