Merge branch 'master' into v0.3.0-dev
commit
bb65e37841
|
|
@ -117,7 +117,7 @@ Building will require Microsoft Visual Studio 2017+ and CMake to be installed.
|
||||||
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
|
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
|
||||||
### Linux
|
### Linux
|
||||||
The dependencies are as follows
|
The dependencies are as follows
|
||||||
- CMake 3.2 or above
|
- CMake 3.12 or above
|
||||||
- GCC 4.7 or above, 4.8+ recommended
|
- GCC 4.7 or above, 4.8+ recommended
|
||||||
- `libusb-1.0-0-dev`
|
- `libusb-1.0-0-dev`
|
||||||
- `libpcap0.8-dev`
|
- `libpcap0.8-dev`
|
||||||
|
|
|
||||||
|
|
@ -166,41 +166,51 @@ static inline unsigned int GetVnetAgnosticNetid(size_t fullNetid)
|
||||||
//Basic Functions
|
//Basic Functions
|
||||||
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx *devs, int *devCount, unsigned int *devTypes, unsigned int devTypeCount, POptionsFindNeoEx *POptionsFindNeoEx, unsigned int *zero)
|
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx *devs, int *devCount, unsigned int *devTypes, unsigned int devTypeCount, POptionsFindNeoEx *POptionsFindNeoEx, unsigned int *zero)
|
||||||
{
|
{
|
||||||
if (!devs)
|
if (!devs || !devCount)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
constexpr size_t MAX_DEVICES = 255;
|
if (*devCount < 0 || *devCount > 255)
|
||||||
NeoDevice Nd[MAX_DEVICES];
|
return 0;
|
||||||
|
|
||||||
|
// Find the devices without filtering by the device type
|
||||||
|
// We allow this to find more than the requested number,
|
||||||
|
// as we may filter out some devices.
|
||||||
|
constexpr const size_t MAX_DEVICES = 255;
|
||||||
|
NeoDevice foundDevices[MAX_DEVICES];
|
||||||
int NumDevices = MAX_DEVICES;
|
int NumDevices = MAX_DEVICES;
|
||||||
|
|
||||||
int Count = 0;
|
int filteredDeviceCount = 0;
|
||||||
|
|
||||||
if (!icsneoFindNeoDevices(0, Nd, &NumDevices))
|
if (!icsneoFindNeoDevices(0, foundDevices, &NumDevices))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (auto i = 0; i < NumDevices; i++)
|
for (auto i = 0; i < NumDevices; i++)
|
||||||
{
|
{
|
||||||
|
// Check if the next device would overrun the user's buffer
|
||||||
|
// We check this up here since the documentation allows zero
|
||||||
|
// to be specified.
|
||||||
|
if (filteredDeviceCount >= *devCount)
|
||||||
|
break;
|
||||||
|
|
||||||
if (devTypes && devTypeCount)
|
if (devTypes && devTypeCount)
|
||||||
{
|
{
|
||||||
for (auto j = 0; j < devTypeCount; j++)
|
for (auto j = 0; j < devTypeCount; j++)
|
||||||
{
|
{
|
||||||
if (Nd[i].DeviceType == devTypes[j])
|
if (foundDevices[i].DeviceType == devTypes[j])
|
||||||
{
|
{
|
||||||
devs[Count++].neoDevice = Nd[i];
|
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
devs[Count++].neoDevice = Nd[i];
|
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (devCount)
|
*devCount = filteredDeviceCount;
|
||||||
*devCount = Count;
|
return 1; // If the function succeeds but no devices are found 1 will still be returned and devCount will equal 0
|
||||||
|
|
||||||
return NumDevices > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice *pNeoDevice, int *pNumDevices)
|
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice *pNeoDevice, int *pNumDevices)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class ValueCAN4IndustrialSettings : public IDeviceSettings {
|
||||||
public:
|
public:
|
||||||
ValueCAN4IndustrialSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(valuecan4_industrial_settings_t)) {}
|
ValueCAN4IndustrialSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(valuecan4_industrial_settings_t)) {}
|
||||||
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
|
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
|
||||||
auto cfg = getStructurePointer<valuecan4_1_2_settings_t>();
|
auto cfg = getStructurePointer<valuecan4_industrial_settings_t>();
|
||||||
if(cfg == nullptr)
|
if(cfg == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
switch(net.getNetID()) {
|
switch(net.getNetID()) {
|
||||||
|
|
@ -25,7 +25,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const CANFD_SETTINGS* getCANFDSettingsFor(Network net) const override {
|
const CANFD_SETTINGS* getCANFDSettingsFor(Network net) const override {
|
||||||
auto cfg = getStructurePointer<valuecan4_1_2_settings_t>();
|
auto cfg = getStructurePointer<valuecan4_industrial_settings_t>();
|
||||||
if(cfg == nullptr)
|
if(cfg == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
switch(net.getNetID()) {
|
switch(net.getNetID()) {
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,14 @@ private:
|
||||||
public:
|
public:
|
||||||
uint8_t uuid;
|
uint8_t uuid;
|
||||||
uint8_t macAddress[8];
|
uint8_t macAddress[8];
|
||||||
std::string nameFromWinPCAP;
|
std::string nameFromPCAP;
|
||||||
std::string descriptionFromWinPCAP;
|
std::string descriptionFromPCAP;
|
||||||
std::string fullName;
|
std::string fullName;
|
||||||
pcap_t* fp = nullptr;
|
pcap_t* fp = nullptr;
|
||||||
pcap_stat stats;
|
pcap_stat stats;
|
||||||
};
|
};
|
||||||
static std::vector<NetworkInterface> knownInterfaces;
|
static std::vector<NetworkInterface> knownInterfaces;
|
||||||
NetworkInterface interface;
|
NetworkInterface iface;
|
||||||
|
|
||||||
class EthernetPacket {
|
class EthernetPacket {
|
||||||
public: // Don't worry about endian when setting fields, this is all taken care of in getBytestream
|
public: // Don't worry about endian when setting fields, this is all taken care of in getBytestream
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#include "icsneo/api/eventmanager.h"
|
#include "icsneo/api/eventmanager.h"
|
||||||
#include "icsneo/communication/ethernetpacketizer.h"
|
#include "icsneo/communication/ethernetpacketizer.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
namespace icsneo {
|
namespace icsneo {
|
||||||
|
|
||||||
class PCAP : public Driver {
|
class PCAP : public Driver {
|
||||||
|
|
@ -59,7 +59,7 @@ private:
|
||||||
pcap_stat stats;
|
pcap_stat stats;
|
||||||
};
|
};
|
||||||
static std::vector<NetworkInterface> knownInterfaces;
|
static std::vector<NetworkInterface> knownInterfaces;
|
||||||
NetworkInterface interface;
|
NetworkInterface iface;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,10 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#ifndef __APPLE__
|
#ifdef __linux__
|
||||||
#include <netpacket/packet.h>
|
#include <netpacket/packet.h>
|
||||||
|
#else
|
||||||
|
#include <net/if_dl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
@ -22,7 +24,7 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
static bool warned = false; // Only warn once for failure to open devices
|
static bool warned = false; // Only warn once for failure to open devices
|
||||||
std::vector<PCAPFoundDevice> foundDevices;
|
std::vector<PCAPFoundDevice> foundDevices;
|
||||||
|
|
||||||
// First we ask WinPCAP to give us all of the devices
|
// First we ask PCAP to give us all of the devices
|
||||||
pcap_if_t* alldevs;
|
pcap_if_t* alldevs;
|
||||||
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
@ -49,21 +51,29 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
NetworkInterface netif;
|
NetworkInterface netif;
|
||||||
netif.nameFromWinPCAP = dev->name;
|
netif.nameFromPCAP = dev->name;
|
||||||
if(dev->description)
|
if(dev->description)
|
||||||
netif.descriptionFromWinPCAP = dev->description;
|
netif.descriptionFromPCAP = dev->description;
|
||||||
pcap_addr* currentAddress = dev->addresses;
|
pcap_addr* currentAddress = dev->addresses;
|
||||||
bool hasAddress = false;
|
bool hasAddress = false;
|
||||||
while(!hasAddress && currentAddress != nullptr) {
|
while(!hasAddress && currentAddress != nullptr) {
|
||||||
#ifndef __APPLE__
|
#ifdef __linux__
|
||||||
if(currentAddress->addr && currentAddress->addr->sa_family == AF_PACKET) {
|
if(currentAddress->addr && currentAddress->addr->sa_family == AF_PACKET) {
|
||||||
struct sockaddr_ll* s = (struct sockaddr_ll*)currentAddress->addr;
|
struct sockaddr_ll* s = (struct sockaddr_ll*)currentAddress->addr;
|
||||||
memcpy(netif.macAddress, s->sll_addr, sizeof(netif.macAddress));
|
memcpy(netif.macAddress, s->sll_addr, sizeof(netif.macAddress));
|
||||||
hasAddress = true;
|
hasAddress = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else // macOS and likely other BSDs
|
||||||
//TODO: get adapter address on macOS
|
if(currentAddress->addr && currentAddress->addr->sa_family == AF_LINK) {
|
||||||
|
struct sockaddr_dl* s = (struct sockaddr_dl*)currentAddress->addr;
|
||||||
|
if(s->sdl_alen == 6 && s->sdl_alen + s->sdl_nlen < sizeof(s->sdl_data)) {
|
||||||
|
const uint8_t* mac = (uint8_t*)(s->sdl_data) + s->sdl_nlen;
|
||||||
|
memcpy(netif.macAddress, mac, sizeof(netif.macAddress));
|
||||||
|
hasAddress = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
currentAddress = currentAddress->next;
|
currentAddress = currentAddress->next;
|
||||||
}
|
}
|
||||||
|
|
@ -76,40 +86,46 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
|
|
||||||
pcap_freealldevs(alldevs);
|
pcap_freealldevs(alldevs);
|
||||||
|
|
||||||
for(auto& interface : interfaces) {
|
for(auto& iface : interfaces) {
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
for(auto& known : knownInterfaces)
|
for(auto& known : knownInterfaces)
|
||||||
if(memcmp(interface.macAddress, known.macAddress, sizeof(interface.macAddress)) == 0)
|
if(memcmp(iface.macAddress, known.macAddress, sizeof(iface.macAddress)) == 0)
|
||||||
exists = true;
|
exists = true;
|
||||||
if(!exists)
|
if(!exists)
|
||||||
knownInterfaces.emplace_back(interface);
|
knownInterfaces.emplace_back(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < knownInterfaces.size(); i++) {
|
for(size_t i = 0; i < knownInterfaces.size(); i++) {
|
||||||
auto& interface = knownInterfaces[i];
|
auto& iface = knownInterfaces[i];
|
||||||
// if(interface.fullName.length() == 0)
|
// if(iface.fullName.length() == 0)
|
||||||
// continue; // Win32 did not find this interface in the previous step
|
// continue; // Win32 did not find this interface in the previous step
|
||||||
|
|
||||||
errbuf[0] = '\0';
|
errbuf[0] = '\0';
|
||||||
interface.fp = pcap_open_live(interface.nameFromWinPCAP.c_str(), 65536, 1, -1, errbuf);
|
iface.fp = pcap_open_live(iface.nameFromPCAP.c_str(), 65536, 1,
|
||||||
|
#ifdef __linux__ // -1 is required for instant reporting of new packets
|
||||||
|
-1, // to_ms
|
||||||
|
#else // macOS gives BIOCSRTIMEOUT for -1 and no packets for 0
|
||||||
|
0,
|
||||||
|
#endif
|
||||||
|
errbuf);
|
||||||
// TODO Handle warnings
|
// TODO Handle warnings
|
||||||
// if(strlen(errbuf) != 0) { // This means a warning
|
// if(strlen(errbuf) != 0) { // This means a warning
|
||||||
// std::cout << "Warning for " << interface.nameFromWinPCAP << " " << errbuf << std::endl;
|
// std::cout << "Warning for " << iface.nameFromPCAP << " " << errbuf << std::endl;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if(interface.fp == nullptr) {
|
if(iface.fp == nullptr) {
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
warned = true;
|
warned = true;
|
||||||
EventManager::GetInstance().add(APIEvent::Type::PCAPCouldNotFindDevices, APIEvent::Severity::EventWarning);
|
EventManager::GetInstance().add(APIEvent::Type::PCAPCouldNotFindDevices, APIEvent::Severity::EventWarning);
|
||||||
// std::cout << "pcap_open_live failed for " << interface.nameFromWinPCAP << " with " << errbuf << std::endl;
|
// std::cout << "pcap_open_live failed for " << iface.nameFromPCAP << " with " << errbuf << std::endl;
|
||||||
}
|
}
|
||||||
continue; // Could not open the interface
|
continue; // Could not open the interface
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap_setnonblock(interface.fp, 1, errbuf);
|
pcap_setnonblock(iface.fp, 1, errbuf);
|
||||||
|
|
||||||
EthernetPacket requestPacket;
|
EthernetPacket requestPacket;
|
||||||
memcpy(requestPacket.srcMAC, interface.macAddress, sizeof(requestPacket.srcMAC));
|
memcpy(requestPacket.srcMAC, iface.macAddress, sizeof(requestPacket.srcMAC));
|
||||||
requestPacket.payload.reserve(4);
|
requestPacket.payload.reserve(4);
|
||||||
requestPacket.payload = {
|
requestPacket.payload = {
|
||||||
((1 << 4) | (uint8_t)Network::NetID::Main51), // Packet size of 1 on NETID_MAIN51
|
((1 << 4) | (uint8_t)Network::NetID::Main51), // Packet size of 1 on NETID_MAIN51
|
||||||
|
|
@ -119,13 +135,13 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
requestPacket.payload.insert(requestPacket.payload.begin(), 0xAA);
|
requestPacket.payload.insert(requestPacket.payload.begin(), 0xAA);
|
||||||
|
|
||||||
auto bs = requestPacket.getBytestream();
|
auto bs = requestPacket.getBytestream();
|
||||||
pcap_sendpacket(interface.fp, bs.data(), (int)bs.size());
|
pcap_sendpacket(iface.fp, bs.data(), (int)bs.size());
|
||||||
|
|
||||||
auto timeout = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(50);
|
auto timeout = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(50);
|
||||||
while(std::chrono::high_resolution_clock::now() <= timeout) { // Wait up to 5ms for the response
|
while(std::chrono::high_resolution_clock::now() <= timeout) { // Wait up to 5ms for the response
|
||||||
struct pcap_pkthdr* header;
|
struct pcap_pkthdr* header;
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
auto res = pcap_next_ex(interface.fp, &header, &data);
|
auto res = pcap_next_ex(iface.fp, &header, &data);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
if (!warned) {
|
if (!warned) {
|
||||||
warned = true;
|
warned = true;
|
||||||
|
|
@ -140,7 +156,7 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
EthernetPacket packet(data, header->caplen);
|
EthernetPacket packet(data, header->caplen);
|
||||||
// Is this an ICS response packet (0xCAB2) from an ICS MAC, either to broadcast or directly to us?
|
// Is this an ICS response packet (0xCAB2) from an ICS MAC, either to broadcast or directly to us?
|
||||||
if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70 && (
|
if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70 && (
|
||||||
memcmp(packet.destMAC, interface.macAddress, sizeof(packet.destMAC)) == 0 ||
|
memcmp(packet.destMAC, iface.macAddress, sizeof(packet.destMAC)) == 0 ||
|
||||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) == 0 ||
|
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) == 0 ||
|
||||||
memcmp(packet.destMAC, ICS_UNSET_MAC, sizeof(packet.destMAC)) == 0
|
memcmp(packet.destMAC, ICS_UNSET_MAC, sizeof(packet.destMAC)) == 0
|
||||||
)) {
|
)) {
|
||||||
|
|
@ -173,8 +189,8 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap_close(interface.fp);
|
pcap_close(iface.fp);
|
||||||
interface.fp = nullptr;
|
iface.fp = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundDevices;
|
return foundDevices;
|
||||||
|
|
@ -187,8 +203,8 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
||||||
|
|
||||||
PCAP::PCAP(device_eventhandler_t err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
|
PCAP::PCAP(device_eventhandler_t err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
|
||||||
if(IsHandleValid(device.handle)) {
|
if(IsHandleValid(device.handle)) {
|
||||||
interface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
iface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||||
interface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||||
|
|
||||||
deviceMAC[0] = 0x00;
|
deviceMAC[0] = 0x00;
|
||||||
deviceMAC[1] = 0xFC;
|
deviceMAC[1] = 0xFC;
|
||||||
|
|
@ -209,13 +225,20 @@ bool PCAP::open() {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Open the interface
|
// Open the interface
|
||||||
interface.fp = pcap_open_live(interface.nameFromWinPCAP.c_str(), 65536, 1, -1, errbuf);
|
iface.fp = pcap_open_live(iface.nameFromPCAP.c_str(), 65536, 1,
|
||||||
if(interface.fp == nullptr) {
|
#ifdef __linux__ // -1 is required for instant reporting of new packets
|
||||||
|
-1, // to_ms
|
||||||
|
#else // macOS gives BIOCSRTIMEOUT for -1 and no packets for 0
|
||||||
|
1,
|
||||||
|
#endif
|
||||||
|
errbuf);
|
||||||
|
if(iface.fp == nullptr) {
|
||||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap_setnonblock(interface.fp, 0, errbuf);
|
pcap_setnonblock(iface.fp, 0, errbuf);
|
||||||
|
pcap_set_immediate_mode(iface.fp, 1);
|
||||||
|
|
||||||
// Create threads
|
// Create threads
|
||||||
readThread = std::thread(&PCAP::readTask, this);
|
readThread = std::thread(&PCAP::readTask, this);
|
||||||
|
|
@ -225,7 +248,7 @@ bool PCAP::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCAP::isOpen() {
|
bool PCAP::isOpen() {
|
||||||
return interface.fp != nullptr;
|
return iface.fp != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCAP::close() {
|
bool PCAP::close() {
|
||||||
|
|
@ -233,14 +256,14 @@ bool PCAP::close() {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
closing = true; // Signal the threads that we are closing
|
closing = true; // Signal the threads that we are closing
|
||||||
pcap_breakloop(interface.fp);
|
pcap_breakloop(iface.fp);
|
||||||
pthread_cancel(readThread.native_handle());
|
pthread_cancel(readThread.native_handle());
|
||||||
readThread.join();
|
readThread.join();
|
||||||
writeThread.join();
|
writeThread.join();
|
||||||
closing = false;
|
closing = false;
|
||||||
|
|
||||||
pcap_close(interface.fp);
|
pcap_close(iface.fp);
|
||||||
interface.fp = nullptr;
|
iface.fp = nullptr;
|
||||||
|
|
||||||
uint8_t flush;
|
uint8_t flush;
|
||||||
WriteOperation flushop;
|
WriteOperation flushop;
|
||||||
|
|
@ -253,14 +276,14 @@ bool PCAP::close() {
|
||||||
void PCAP::readTask() {
|
void PCAP::readTask() {
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while (!closing) {
|
while (!closing) {
|
||||||
pcap_dispatch(interface.fp, -1, [](uint8_t* obj, const struct pcap_pkthdr* header, const uint8_t* data) {
|
pcap_dispatch(iface.fp, -1, [](uint8_t* obj, const struct pcap_pkthdr* header, const uint8_t* data) {
|
||||||
PCAP* driver = (PCAP*)obj;
|
PCAP* driver = (PCAP*)obj;
|
||||||
EthernetPacket packet(data, header->caplen);
|
EthernetPacket packet(data, header->caplen);
|
||||||
|
|
||||||
if(packet.etherType != 0xCAB2)
|
if(packet.etherType != 0xCAB2)
|
||||||
return; // Not a packet to host
|
return; // Not a packet to host
|
||||||
|
|
||||||
if(memcmp(packet.destMAC, driver->interface.macAddress, sizeof(packet.destMAC)) != 0 &&
|
if(memcmp(packet.destMAC, driver->iface.macAddress, sizeof(packet.destMAC)) != 0 &&
|
||||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) != 0 &&
|
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) != 0 &&
|
||||||
memcmp(packet.destMAC, ICS_UNSET_MAC, sizeof(packet.destMAC)) != 0)
|
memcmp(packet.destMAC, ICS_UNSET_MAC, sizeof(packet.destMAC)) != 0)
|
||||||
return; // Packet is not addressed to us or broadcast
|
return; // Packet is not addressed to us or broadcast
|
||||||
|
|
@ -280,7 +303,7 @@ void PCAP::writeTask() {
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
|
|
||||||
// Set MAC address of packet
|
// Set MAC address of packet
|
||||||
memcpy(sendPacket.srcMAC, interface.macAddress, sizeof(sendPacket.srcMAC));
|
memcpy(sendPacket.srcMAC, iface.macAddress, sizeof(sendPacket.srcMAC));
|
||||||
memcpy(sendPacket.destMAC, deviceMAC, sizeof(deviceMAC));
|
memcpy(sendPacket.destMAC, deviceMAC, sizeof(deviceMAC));
|
||||||
|
|
||||||
while(!closing) {
|
while(!closing) {
|
||||||
|
|
@ -291,7 +314,7 @@ void PCAP::writeTask() {
|
||||||
sendPacket.payload = std::move(writeOp.bytes);
|
sendPacket.payload = std::move(writeOp.bytes);
|
||||||
auto bs = sendPacket.getBytestream();
|
auto bs = sendPacket.getBytestream();
|
||||||
if(!closing)
|
if(!closing)
|
||||||
pcap_sendpacket(interface.fp, bs.data(), (int)bs.size());
|
pcap_sendpacket(iface.fp, bs.data(), (int)bs.size());
|
||||||
// TODO Handle packet send errors
|
// TODO Handle packet send errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
// Calling pcap.findalldevs_ex too quickly can cause various errors. Retry a few times in this case.
|
// Calling pcap.findalldevs_ex too quickly can cause various errors. Retry a few times in this case.
|
||||||
for(auto retry = 0; retry < 10; retry++) {
|
for(auto retry = 0; retry < 10; retry++) {
|
||||||
auto ret = pcap.findalldevs_ex(PCAP_SRC_IF_STRING, nullptr, &alldevs, errbuf);
|
auto ret = pcap.findalldevs_ex((char*)PCAP_SRC_IF_STRING, nullptr, &alldevs, errbuf);
|
||||||
if(ret == 0) {
|
if(ret == 0) {
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -68,48 +69,48 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// aa->AdapterName constains a unique name of the interface like "{3B1D2791-435A-456F-8A7B-9CB0EEE5DAB3}"
|
// aa->AdapterName constains a unique name of the interface like "{3B1D2791-435A-456F-8A7B-9CB0EEE5DAB3}"
|
||||||
// interface.nameFromWinPCAP has "rpcap://\Device\NPF_{3B1D2791-435A-456F-8A7B-9CB0EEE5DAB3}"
|
// iface.nameFromWinPCAP has "rpcap://\Device\NPF_{3B1D2791-435A-456F-8A7B-9CB0EEE5DAB3}"
|
||||||
// We're comparing strings to match the Win32 info with the WinPCAP info
|
// We're comparing strings to match the Win32 info with the WinPCAP info
|
||||||
for(IP_ADAPTER_ADDRESSES* aa = (IP_ADAPTER_ADDRESSES*)adapterAddressBuffer.data(); aa != nullptr; aa = aa->Next) {
|
for(IP_ADAPTER_ADDRESSES* aa = (IP_ADAPTER_ADDRESSES*)adapterAddressBuffer.data(); aa != nullptr; aa = aa->Next) {
|
||||||
for(auto& interface : interfaces) {
|
for(auto& iface : interfaces) {
|
||||||
if(interface.nameFromWinPCAP.find(aa->AdapterName) == std::string::npos)
|
if(iface.nameFromWinPCAP.find(aa->AdapterName) == std::string::npos)
|
||||||
continue; // This is not the interface that corresponds
|
continue; // This is not the interface that corresponds
|
||||||
|
|
||||||
memcpy(interface.macAddress, aa->PhysicalAddress, sizeof(interface.macAddress));
|
memcpy(iface.macAddress, aa->PhysicalAddress, sizeof(iface.macAddress));
|
||||||
interface.nameFromWin32API = aa->AdapterName;
|
iface.nameFromWin32API = aa->AdapterName;
|
||||||
interface.descriptionFromWin32API = converter.to_bytes(aa->Description);
|
iface.descriptionFromWin32API = converter.to_bytes(aa->Description);
|
||||||
interface.friendlyNameFromWin32API = converter.to_bytes(aa->FriendlyName);
|
iface.friendlyNameFromWin32API = converter.to_bytes(aa->FriendlyName);
|
||||||
if(interface.descriptionFromWin32API.find("LAN9512/LAN9514") != std::string::npos) {
|
if(iface.descriptionFromWin32API.find("LAN9512/LAN9514") != std::string::npos) {
|
||||||
// This is an Ethernet EVB device
|
// This is an Ethernet EVB device
|
||||||
interface.fullName = "Intrepid Ethernet EVB ( " + interface.friendlyNameFromWin32API + " : " + interface.descriptionFromWin32API + " )";
|
iface.fullName = "Intrepid Ethernet EVB ( " + iface.friendlyNameFromWin32API + " : " + iface.descriptionFromWin32API + " )";
|
||||||
} else {
|
} else {
|
||||||
interface.fullName = interface.friendlyNameFromWin32API + " : " + interface.descriptionFromWin32API;
|
iface.fullName = iface.friendlyNameFromWin32API + " : " + iface.descriptionFromWin32API;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& interface : interfaces) {
|
for(auto& iface : interfaces) {
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
for(auto& known : knownInterfaces)
|
for(auto& known : knownInterfaces)
|
||||||
if(memcmp(interface.macAddress, known.macAddress, sizeof(interface.macAddress)) == 0)
|
if(memcmp(iface.macAddress, known.macAddress, sizeof(iface.macAddress)) == 0)
|
||||||
exists = true;
|
exists = true;
|
||||||
if(!exists)
|
if(!exists)
|
||||||
knownInterfaces.emplace_back(interface);
|
knownInterfaces.emplace_back(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto openflags = (PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_NOCAPTURE_LOCAL);
|
constexpr auto openflags = (PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_NOCAPTURE_LOCAL);
|
||||||
for(size_t i = 0; i < knownInterfaces.size(); i++) {
|
for(size_t i = 0; i < knownInterfaces.size(); i++) {
|
||||||
auto& interface = knownInterfaces[i];
|
auto& iface = knownInterfaces[i];
|
||||||
if(interface.fullName.length() == 0)
|
if(iface.fullName.length() == 0)
|
||||||
continue; // Win32 did not find this interface in the previous step
|
continue; // Win32 did not find this interface in the previous step
|
||||||
|
|
||||||
interface.fp = pcap.open(interface.nameFromWinPCAP.c_str(), 1518, openflags, 1, nullptr, errbuf);
|
iface.fp = pcap.open(iface.nameFromWinPCAP.c_str(), 1518, openflags, 1, nullptr, errbuf);
|
||||||
|
|
||||||
if(interface.fp == nullptr)
|
if(iface.fp == nullptr)
|
||||||
continue; // Could not open the interface
|
continue; // Could not open the interface
|
||||||
|
|
||||||
EthernetPacketizer::EthernetPacket requestPacket;
|
EthernetPacketizer::EthernetPacket requestPacket;
|
||||||
memcpy(requestPacket.srcMAC, interface.macAddress, sizeof(requestPacket.srcMAC));
|
memcpy(requestPacket.srcMAC, iface.macAddress, sizeof(requestPacket.srcMAC));
|
||||||
requestPacket.payload.reserve(4);
|
requestPacket.payload.reserve(4);
|
||||||
requestPacket.payload = {
|
requestPacket.payload = {
|
||||||
((1 << 4) | (uint8_t)Network::NetID::Main51), // Packet size of 1 on NETID_MAIN51
|
((1 << 4) | (uint8_t)Network::NetID::Main51), // Packet size of 1 on NETID_MAIN51
|
||||||
|
|
@ -119,13 +120,13 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
requestPacket.payload.insert(requestPacket.payload.begin(), 0xAA);
|
requestPacket.payload.insert(requestPacket.payload.begin(), 0xAA);
|
||||||
|
|
||||||
auto bs = requestPacket.getBytestream();
|
auto bs = requestPacket.getBytestream();
|
||||||
pcap.sendpacket(interface.fp, bs.data(), (int)bs.size());
|
pcap.sendpacket(iface.fp, bs.data(), (int)bs.size());
|
||||||
|
|
||||||
auto timeout = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(5);
|
auto timeout = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(5);
|
||||||
while(std::chrono::high_resolution_clock::now() <= timeout) { // Wait up to 5ms for the response
|
while(std::chrono::high_resolution_clock::now() <= timeout) { // Wait up to 5ms for the response
|
||||||
struct pcap_pkthdr* header;
|
struct pcap_pkthdr* header;
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
auto res = pcap.next_ex(interface.fp, &header, &data);
|
auto res = pcap.next_ex(iface.fp, &header, &data);
|
||||||
if(res < 0) {
|
if(res < 0) {
|
||||||
//std::cout << "pcapnextex failed with " << res << std::endl;
|
//std::cout << "pcapnextex failed with " << res << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|
@ -136,7 +137,7 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
EthernetPacketizer::EthernetPacket packet(data, header->caplen);
|
EthernetPacketizer::EthernetPacket packet(data, header->caplen);
|
||||||
// Is this an ICS response packet (0xCAB2) from an ICS MAC, either to broadcast or directly to us?
|
// Is this an ICS response packet (0xCAB2) from an ICS MAC, either to broadcast or directly to us?
|
||||||
if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70 && (
|
if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70 && (
|
||||||
memcmp(packet.destMAC, interface.macAddress, sizeof(packet.destMAC)) == 0 ||
|
memcmp(packet.destMAC, iface.macAddress, sizeof(packet.destMAC)) == 0 ||
|
||||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) == 0
|
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) == 0
|
||||||
)) {
|
)) {
|
||||||
/* We have received a packet from a device. We don't know if this is the device we're
|
/* We have received a packet from a device. We don't know if this is the device we're
|
||||||
|
|
@ -168,8 +169,8 @@ std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap.close(interface.fp);
|
pcap.close(iface.fp);
|
||||||
interface.fp = nullptr;
|
iface.fp = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundDevices;
|
return foundDevices;
|
||||||
|
|
@ -182,8 +183,8 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
||||||
|
|
||||||
PCAP::PCAP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice), pcap(PCAPDLL::getInstance()), ethPacketizer(err) {
|
PCAP::PCAP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice), pcap(PCAPDLL::getInstance()), ethPacketizer(err) {
|
||||||
if(IsHandleValid(device.handle)) {
|
if(IsHandleValid(device.handle)) {
|
||||||
interface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
iface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||||
interface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||||
|
|
||||||
deviceMAC[0] = 0x00;
|
deviceMAC[0] = 0x00;
|
||||||
deviceMAC[1] = 0xFC;
|
deviceMAC[1] = 0xFC;
|
||||||
|
|
@ -192,7 +193,7 @@ PCAP::PCAP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(er
|
||||||
deviceMAC[4] = (device.handle >> 8) & 0xFF;
|
deviceMAC[4] = (device.handle >> 8) & 0xFF;
|
||||||
deviceMAC[5] = device.handle & 0xFF;
|
deviceMAC[5] = device.handle & 0xFF;
|
||||||
memcpy(ethPacketizer.deviceMAC, deviceMAC, 6);
|
memcpy(ethPacketizer.deviceMAC, deviceMAC, 6);
|
||||||
memcpy(ethPacketizer.hostMAC, interface.macAddress, 6);
|
memcpy(ethPacketizer.hostMAC, iface.macAddress, 6);
|
||||||
} else {
|
} else {
|
||||||
openable = false;
|
openable = false;
|
||||||
}
|
}
|
||||||
|
|
@ -215,8 +216,8 @@ bool PCAP::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the interface
|
// Open the interface
|
||||||
interface.fp = pcap.open(interface.nameFromWinPCAP.c_str(), 65536, PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_NOCAPTURE_LOCAL, 50, nullptr, errbuf);
|
iface.fp = pcap.open(iface.nameFromWinPCAP.c_str(), 65536, PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_NOCAPTURE_LOCAL, 50, nullptr, errbuf);
|
||||||
if(interface.fp == nullptr) {
|
if(iface.fp == nullptr) {
|
||||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +231,7 @@ bool PCAP::open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCAP::isOpen() {
|
bool PCAP::isOpen() {
|
||||||
return interface.fp != nullptr;
|
return iface.fp != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCAP::close() {
|
bool PCAP::close() {
|
||||||
|
|
@ -245,8 +246,8 @@ bool PCAP::close() {
|
||||||
transmitThread.join();
|
transmitThread.join();
|
||||||
closing = false;
|
closing = false;
|
||||||
|
|
||||||
pcap.close(interface.fp);
|
pcap.close(iface.fp);
|
||||||
interface.fp = nullptr;
|
iface.fp = nullptr;
|
||||||
|
|
||||||
uint8_t flush;
|
uint8_t flush;
|
||||||
WriteOperation flushop;
|
WriteOperation flushop;
|
||||||
|
|
@ -262,7 +263,7 @@ void PCAP::readTask() {
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
while(!closing) {
|
while(!closing) {
|
||||||
auto readBytes = pcap.next_ex(interface.fp, &header, &data);
|
auto readBytes = pcap.next_ex(iface.fp, &header, &data);
|
||||||
if(readBytes < 0) {
|
if(readBytes < 0) {
|
||||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||||
break;
|
break;
|
||||||
|
|
@ -333,7 +334,7 @@ void PCAP::transmitTask() {
|
||||||
if(transmitQueueCV.wait_for(lk, std::chrono::milliseconds(100), [this] { return !!transmitQueue; }) && !closing && transmitQueue) {
|
if(transmitQueueCV.wait_for(lk, std::chrono::milliseconds(100), [this] { return !!transmitQueue; }) && !closing && transmitQueue) {
|
||||||
pcap_send_queue* current = transmitQueue;
|
pcap_send_queue* current = transmitQueue;
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
pcap.sendqueue_transmit(interface.fp, current, 0);
|
pcap.sendqueue_transmit(iface.fp, current, 0);
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk2(transmitQueueMutex);
|
std::lock_guard<std::mutex> lk2(transmitQueueMutex);
|
||||||
transmitQueue = nullptr;
|
transmitQueue = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "icsneo/platform/windows/registry.h"
|
#include "icsneo/platform/windows/registry.h"
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,13 @@
|
||||||
|
|
||||||
#define caddr_t char*
|
#define caddr_t char*
|
||||||
|
|
||||||
|
#ifdef __MSC_VER
|
||||||
#if _MSC_VER < 1500
|
#if _MSC_VER < 1500
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#define vsnprintf _vsnprintf
|
#define vsnprintf _vsnprintf
|
||||||
#define strdup _strdup
|
#define strdup _strdup
|
||||||
#endif
|
#endif
|
||||||
|
#endif // __MSC_VER
|
||||||
|
|
||||||
//#define inline __inline
|
//#define inline __inline
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue