You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libiec61850/hal/ethernet/win32/ethernet_win32.c

515 lines
12 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "stack_config.h"
#include <string.h>
#include <stdio.h>
#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 <malloc.h>
#include <winsock2.h>
#include <iphlpapi.h>
#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, const 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) */