HostDev: Added infra and windows IPv4 implementation

This commit is contained in:
Srivats P 2018-07-26 20:53:33 +05:30
parent 0d5e836d3f
commit bc972e4476
14 changed files with 481 additions and 5 deletions

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "../common/abstractprotocol.h"
#include "../common/streambase.h"
#include "devicemanager.h"
#include "interfaceinfo.h"
#include "packetbuffer.h"
#include <QString>
@ -47,6 +48,7 @@ AbstractPort::AbstractPort(int id, const char *device)
minPacketSetSize_ = 1;
deviceManager_ = new DeviceManager(this);
interfaceInfo_ = NULL;
maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats
memset((void*) &stats_, 0, sizeof(stats_));
@ -56,10 +58,13 @@ AbstractPort::AbstractPort(int id, const char *device)
AbstractPort::~AbstractPort()
{
delete deviceManager_;
delete interfaceInfo_;
}
void AbstractPort::init()
{
if (deviceManager_)
deviceManager_->createHostDevices();
}
/*! Can we modify Port with these params? Should modify cause port dirty? */
@ -786,3 +791,8 @@ quint64 AbstractPort::neighborMacAddress(int streamId, int frameIndex)
return 0;
}
const InterfaceInfo* AbstractPort::interfaceInfo() const
{
return interfaceInfo_;
}

View File

@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "../common/protocol.pb.h"
class DeviceManager;
struct InterfaceInfo;
class StreamBase;
class PacketBuffer;
class QIODevice;
@ -77,6 +78,8 @@ public:
bool canModify(const OstProto::Port &port, bool *dirty);
bool modify(const OstProto::Port &port);
const InterfaceInfo* interfaceInfo() const;
virtual OstProto::LinkState linkState() { return linkState_; }
virtual bool hasExclusiveControl() = 0;
virtual bool setExclusiveControl(bool exclusive) = 0;
@ -151,6 +154,7 @@ protected:
StreamStats streamStats_;
//! \todo Need lock for stats access/update
struct InterfaceInfo *interfaceInfo_;
DeviceManager *deviceManager_;
private:
@ -169,7 +173,6 @@ private:
QList<StreamBase*> streamList_;
struct PortStats epochStats_;
};
#endif

View File

@ -20,9 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "devicemanager.h"
#include "abstractport.h"
#include "device.h"
#include "emuldevice.h"
#include "../common/emulation.h"
#include "hostdevice.h"
#include "interfaceinfo.h"
#include "nulldevice.h"
#include "packetbuffer.h"
#include "../common/emulproto.pb.h"
@ -50,6 +52,40 @@ DeviceManager::DeviceManager(AbstractPort *parent)
port_ = parent;
}
void DeviceManager::createHostDevices(void)
{
const InterfaceInfo *ifInfo = port_->interfaceInfo();
if (!ifInfo)
return;
NullDevice bcastDevice(this);
bcastDevice.setMac(kBcastMac);
int count = ifInfo->ip4.size(); // FIXME: IPv6
for (int i = 0; i < count; i++) {
Device *device = HostDevice::create(port_->name(), this);
device->setMac(ifInfo->mac);
device->setIp4(ifInfo->ip4.at(i).address,
ifInfo->ip4.at(i).prefixLength,
ifInfo->ip4.at(i).gateway);
if (deviceList_.contains(device->key())) {
qWarning("%s: error adding host device %s (EEXIST)",
__FUNCTION__, qPrintable(device->config()));
delete device;
continue;
}
hostDeviceList_.append(device);
deviceList_.insert(device->key(), device);
sortedDeviceList_.insert(device->key(), device);
bcastList_.insert(bcastDevice.key(), device);
qDebug("host(add): %s", qPrintable(device->config()));
break;
}
}
DeviceManager::~DeviceManager()
{
foreach(Device *dev, deviceList_)
@ -57,6 +93,9 @@ DeviceManager::~DeviceManager()
foreach(OstProto::DeviceGroup *devGrp, deviceGroupList_)
delete devGrp;
foreach(Device *dev, hostDeviceList_)
delete dev;
}
int DeviceManager::deviceGroupCount()
@ -153,7 +192,7 @@ bool DeviceManager::modifyDeviceGroup(const OstProto::DeviceGroup *deviceGroup)
int DeviceManager::deviceCount()
{
return deviceList_.size();
return deviceList_.size() + 1; // FIXME: why +1 for hostdev?
}
void DeviceManager::getDeviceList(

View File

@ -39,6 +39,8 @@ public:
DeviceManager(AbstractPort *parent = 0);
~DeviceManager();
void createHostDevices();
int deviceGroupCount();
const OstProto::DeviceGroup* deviceGroupAtIndex(int index);
const OstProto::DeviceGroup* deviceGroup(uint deviceGroupId);
@ -71,11 +73,14 @@ private:
Operation oper);
AbstractPort *port_;
QHash<uint, OstProto::DeviceGroup*> deviceGroupList_;
QHash<DeviceKey, Device*> deviceList_; // fast access to devices
QMap<DeviceKey, Device*> sortedDeviceList_; // sorted access to devices
QMultiHash<DeviceKey, Device*> bcastList_;
QHash<quint16, uint> tpidList_; // Key: TPID, Value: RefCount
QList<Device*> hostDeviceList_; // TODO: use to add/remove from devicelist on useHostDevice flag toggle
};
#endif

View File

@ -6,10 +6,12 @@ linux*:system(grep -q IFLA_STATS64 /usr/include/linux/if_link.h): \
DEFINES += HAVE_IFLA_STATS64
INCLUDEPATH += "../rpc"
win32 {
# Support Windows Vista and above only
DEFINES += WIN32_LEAN_AND_MEAN NTDDI_VERSION=0x06000000 _WIN32_WINNT=0x0600
DEFINES += HAVE_REMOTE WPCAP
CONFIG += console
QMAKE_LFLAGS += -static
LIBS += -lwpcap -lpacket
LIBS += -lwpcap -lpacket -liphlpapi
CONFIG(debug, debug|release) {
LIBS += -L"../common/debug" -lostproto
LIBS += -L"../rpc/debug" -lpbrpc
@ -49,6 +51,7 @@ SOURCES += \
pcaptxthread.cpp \
bsdport.cpp \
linuxport.cpp \
winhostdevice.cpp \
winpcapport.cpp
SOURCES += myservice.cpp
SOURCES += pcapextra.cpp

45
server/hostdevice.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _HOST_DEVICE_H
#define _HOST_DEVICE_H
#include "winhostdevice.h"
class DeviceManager;
/*!
* HostDevice abstracts the various OS-specific host device classes
*/
class HostDevice
{
public:
static Device* create(QString portName, DeviceManager *deviceManager)
{
#ifdef Q_OS_WIN32
return new WindowsHostDevice(portName, deviceManager);
#else
return nullptr;
#endif
}
};
#endif

44
server/interfaceinfo.h Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _INTERFACE_INFO_H
#define _INTERFACE_INFO_H
#include "../common/uint128.h"
#include <QtGlobal>
template <typename IpType>
struct IpConfig
{
IpType address;
int prefixLength;
IpType gateway;
};
using Ip4Config = IpConfig<quint32>;
using Ip6Config = IpConfig<UInt128>;
struct InterfaceInfo
{
quint64 mac;
QList<Ip4Config> ip4;
QList<Ip6Config> ip6;
};
#endif

47
server/nulldevice.h Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _NULL_DEVICE_H
#define _NULL_DEVICE_H
#include "device.h"
/*!
* NullDevice does nothing.
* Since the base Device class is abstract, NullDevice is a convenience
* subclass that can be instantiated for DeviceKey related operations
*/
class NullDevice: public Device
{
public:
using Device::Device;
virtual void receivePacket(PacketBuffer* /*pktBuf*/) {}
virtual void clearNeighbors(Device::NeighborSet /*set*/) {}
virtual void getNeighbors(OstEmul::DeviceNeighborList* /*neighbors*/) {}
protected:
virtual quint64 arpLookup(quint32 /*ip*/) { return 0; }
virtual quint64 ndpLookup(UInt128 /*ip*/) { return 0; }
virtual void sendArpRequest(quint32 /*tgtIp*/) {}
virtual void sendNeighborSolicit(UInt128 /*tgtIp*/) {}
};
#endif

View File

@ -63,6 +63,8 @@ PcapPort::PcapPort(int id, const char *device)
void PcapPort::init()
{
AbstractPort::init();
if (!monitorTx_->isDirectional())
transmitter_->useExternalStats(&stats_);

View File

@ -32,7 +32,6 @@ PortManager *PortManager::instance_ = NULL;
#if defined(Q_OS_WIN32)
#include <QUuid>
#include <ipHlpApi.h>
#define NETIO_STATUS NTSTATUS
// Define the function prototypes since they are not defined in ipHlpApi.h
NETIO_STATUS WINAPI ConvertInterfaceGuidToLuid(
const GUID *InterfaceGuid, PNET_LUID InterfaceLuid);
@ -52,6 +51,10 @@ PortManager::PortManager()
qDebug("PCAP Lib: %s", pcap_lib_version());
qDebug("Retrieving the device list from the local machine\n");
#if defined(Q_OS_WIN32)
WinPcapPort::populateAdapterList();
#endif
txRateAccuracy = rateAccuracy();
pcap_if_t *deviceList = GetPortList();

132
server/winhostdevice.cpp Normal file
View File

@ -0,0 +1,132 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "winhostdevice.h"
#include <QUuid>
WindowsHostDevice::WindowsHostDevice(QString portName,
DeviceManager *deviceManager)
: Device(deviceManager)
{
GUID guid = static_cast<GUID>(QUuid(portName.right(38)));
ulong status = ConvertInterfaceGuidToLuid(&guid, &luid_);
if (status != NO_ERROR) {
qWarning("ConvertInterfaceGuidToLuid failed for %s with error %lx",
qPrintable(portName), status);
luid_.Value = 0;
}
}
void WindowsHostDevice::receivePacket(PacketBuffer* /*pktBuf*/)
{
// Do Nothing
}
void WindowsHostDevice::clearNeighbors(Device::NeighborSet set)
{
// No need to do anything - see AbstractPort::resolveDeviceNeighbors()
// on when this is used
if (set == kUnresolvedNeighbors)
return;
NET_IFINDEX ifIndex;
ulong status = ConvertInterfaceLuidToIndex(&luid_, &ifIndex);
if (status != NO_ERROR) {
qWarning("luid2ifIdx convert failed with error %lx", status);
return;
}
status = FlushIpNetTable2(AF_UNSPEC, ifIndex);
if(status != NO_ERROR)
qWarning("Flush ARP/ND table failed with error %lx", status);
}
void WindowsHostDevice::getNeighbors(OstEmul::DeviceNeighborList *neighbors)
{
PMIB_IPNET_TABLE2 nbrs = NULL;
// TODO: optimization: use AF_UNSPEC only if hasIp4 and hasIp6
ulong status = GetIpNetTable2(AF_UNSPEC, &nbrs) != NO_ERROR;
if (status != NO_ERROR) {
qWarning("Get ARP/ND table failed with error %lx", status);
return;
}
for (uint i = 0; i < nbrs->NumEntries; i++) {
if (nbrs->Table[i].InterfaceLuid.Value != luid_.Value)
continue;
if (nbrs->Table[i].Address.si_family == AF_INET) {
OstEmul::ArpEntry *arp = neighbors->add_arp();
arp->set_ip4(qToBigEndian(quint32(
nbrs->Table[i].Address.Ipv4.sin_addr.s_addr)));
arp->set_mac(qFromBigEndian<quint64>(
nbrs->Table[i].PhysicalAddress) >> 16);
}
// TODO: IPv6
}
FreeMibTable(nbrs);
}
quint64 WindowsHostDevice::arpLookup(quint32 ip)
{
if (!luid_.Value)
return 0;
MIB_IPNET_ROW2 arpEntry;
arpEntry.InterfaceLuid = luid_;
arpEntry.Address.si_family = AF_INET;
arpEntry.Address.Ipv4.sin_addr.s_addr = qToBigEndian(ip);
if ((GetIpNetEntry2(&arpEntry) == NO_ERROR)
&& (arpEntry.PhysicalAddressLength == 6)) {
return qFromBigEndian<quint64>(arpEntry.PhysicalAddress) >> 16;
}
else
return 0;
}
quint64 WindowsHostDevice::ndpLookup(UInt128 /*ip*/)
{
if (!luid_.Value)
return 0;
return 0; // TODO
}
void WindowsHostDevice::sendArpRequest(quint32 tgtIp)
{
SOCKADDR_INET src;
src.Ipv4.sin_addr.s_addr = qToBigEndian(ip4_);
MIB_IPNET_ROW2 arpEntry;
arpEntry.InterfaceLuid = luid_;
arpEntry.Address.si_family = AF_INET;
arpEntry.Address.Ipv4.sin_addr.s_addr = qToBigEndian(tgtIp);
ulong status = ResolveIpNetEntry2(&arpEntry, &src);
if (ResolveIpNetEntry2(&arpEntry, &src) != NO_ERROR)
qWarning("Resolve arp failed with error %lx", status);
}
void WindowsHostDevice::sendNeighborSolicit(UInt128 /*tgtIp*/)
{
// TODO
}

48
server/winhostdevice.h Normal file
View File

@ -0,0 +1,48 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _WINDOWS_HOST_DEVICE_H
#define _WINDOWS_HOST_DEVICE_H
#include "device.h"
#include <Ws2tcpip.h>
#include <iphlpapi.h>
class WindowsHostDevice: public Device
{
public:
WindowsHostDevice(QString portName, DeviceManager *deviceManager);
virtual ~WindowsHostDevice() {};
virtual void receivePacket(PacketBuffer *pktBuf);
virtual void clearNeighbors(Device::NeighborSet set);
virtual void getNeighbors(OstEmul::DeviceNeighborList *neighbors);
protected:
virtual quint64 arpLookup(quint32 ip);
virtual quint64 ndpLookup(UInt128 ip);
virtual void sendArpRequest(quint32 tgtIp);
virtual void sendNeighborSolicit(UInt128 tgtIp);
private:
NET_LUID luid_;
};
#endif

View File

@ -19,16 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "winpcapport.h"
#include "interfaceinfo.h"
#include <QCoreApplication>
#include <QProcess>
#ifdef Q_OS_WIN32
PIP_ADAPTER_ADDRESSES WinPcapPort::adapterList_ = NULL;
const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114;
WinPcapPort::WinPcapPort(int id, const char *device, const char *description)
: PcapPort(id, device)
{
populateInterfaceInfo();
monitorRx_->stop();
monitorTx_->stop();
monitorRx_->wait();
@ -225,4 +230,86 @@ void WinPcapPort::PortMonitor::run()
}
}
void WinPcapPort::populateInterfaceInfo()
{
if (!adapterList_) {
qWarning("Adapter List not available");
return;
}
PIP_ADAPTER_ADDRESSES adapter = adapterList_;
while (!QString(name()).endsWith(QString(adapter->AdapterName)))
adapter = adapter->Next;
if (!adapter) {
qWarning("Adapter info not found for %s", name());
return;
}
interfaceInfo_ = new InterfaceInfo;
if (adapter->PhysicalAddressLength == 6) {
interfaceInfo_->mac = qFromBigEndian<quint64>(
adapter->PhysicalAddress) >> 16;
}
else
interfaceInfo_->mac = 0;
qDebug("mac = %llx", interfaceInfo_->mac);
#define SOCKET_ADDRESS_FAMILY(x) \
(x.lpSockaddr->sa_family)
#define SOCKET_ADDRESS_IP4(x) \
(qFromBigEndian<quint32>(((sockaddr_in*)(x.lpSockaddr))->sin_addr.S_un.S_addr));
// We may have multiple gateways - use the first for each family
quint32 ip4Gateway = 0;
PIP_ADAPTER_GATEWAY_ADDRESS gateway = adapter->FirstGatewayAddress;
while (gateway) {
if (SOCKET_ADDRESS_FAMILY(gateway->Address) == AF_INET) {
ip4Gateway = SOCKET_ADDRESS_IP4(gateway->Address);
break;
}
gateway = gateway->Next;
}
// TODO: IPv6 Gateway
PIP_ADAPTER_UNICAST_ADDRESS ucast = adapter->FirstUnicastAddress;
while (ucast) {
if (SOCKET_ADDRESS_FAMILY(ucast->Address) == AF_INET) {
Ip4Config ip;
ip.address = SOCKET_ADDRESS_IP4(ucast->Address);
ip.prefixLength = ucast->OnLinkPrefixLength;
ip.gateway = ip4Gateway;
interfaceInfo_->ip4.append(ip);
}
// TODO: IPv6
ucast = ucast->Next;
}
#undef SOCKET_ADDRESS_FAMILY
#undef SOCKET_ADDRESS_IP4
}
void WinPcapPort::populateAdapterList()
{
DWORD ret;
ULONG bufLen = 15*1024; // MS recommended starting size
while (1) {
adapterList_ = (IP_ADAPTER_ADDRESSES *) malloc(bufLen);
ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_GATEWAYS, 0,
adapterList_, &bufLen);
if (ret == ERROR_BUFFER_OVERFLOW) {
free(adapterList_);
continue;
}
break;
}
if (ret != NO_ERROR) {
free(adapterList_);
adapterList_ = NULL;
return;
}
}
#endif

View File

@ -28,6 +28,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <packet32.h>
#include <iphlpapi.h>
class WinPcapPort : public PcapPort
{
public:
@ -38,6 +40,8 @@ public:
virtual bool hasExclusiveControl();
virtual bool setExclusiveControl(bool exclusive);
static void populateAdapterList();
protected:
class PortMonitor: public PcapPort::PortMonitor
{
@ -47,8 +51,12 @@ protected:
void run();
};
private:
void populateInterfaceInfo();
LPADAPTER adapter_;
PPACKET_OID_DATA linkStateOid_ ;
static PIP_ADAPTER_ADDRESSES adapterList_;
};
#endif