HostDev: Added Linux host device code

This commit is contained in:
Srivats P 2018-09-02 19:45:08 +05:30
parent f58c4e309c
commit 73043f6fe6
9 changed files with 558 additions and 16 deletions

35
common/qtport.h Normal file
View File

@ -0,0 +1,35 @@
/*
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 _QT_PORT_H
#define _QT_PORT_H
//
// Make Qt stuff portable across Qt versions
//
#if QT_VERSION < 0x050700
template <typename T>
T qFromBigEndian(const void *src)
{
return qFromBigEndian<T>((const uchar*)src);
}
#endif
#endif

View File

@ -281,6 +281,7 @@ bool Device::isOrigin(const PacketBuffer *pktBuf)
// We expect pktBuf to point to EthType on entry
quint64 Device::neighborMac(const PacketBuffer *pktBuf)
{
quint64 mac = 0;
const uchar *pktData = pktBuf->data();
quint16 ethType = qFromBigEndian<quint16>(pktData);
@ -295,17 +296,19 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
if (pktBuf->length() < ipHdrLen) {
qDebug("incomplete IPv4 header: expected %d, actual %d",
ipHdrLen, pktBuf->length());
return false;
return mac;
}
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
if ((dstIp & 0xF0000000) == 0xE0000000) { // Mcast IP?
qDebug("mcast dst %x", dstIp);
return (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF);
mac = (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF);
qDebug("mcast dst %08x, mac: %012llx", dstIp, mac);
}
else {
tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_;
mac = arpLookup(tgtIp);
qDebug("tgtIp: %08x, mac: %012llx", tgtIp, mac);
}
tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_;
return arpLookup(tgtIp);
}
else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6
UInt128 dstIp, tgtIp;
@ -313,21 +316,26 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
if (pktBuf->length() < (kIp6HdrLen+2)) {
qDebug("incomplete IPv6 header: expected %d, actual %d",
kIp6HdrLen, pktBuf->length()-2);
return false;
return mac;
}
dstIp = qFromBigEndian<UInt128>(pktData + 24);
if (dstIp.toArray()[0] == 0xFF) { // Mcast IP?
qDebug("mcast dst %s",
qPrintable(QHostAddress(dstIp.toArray()).toString()));
return (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
mac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
qDebug("mcast dst %s, mac: %012llx",
qPrintable(QHostAddress(dstIp.toArray()).toString()),
mac);
}
else {
tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_;
mac = ndpLookup(tgtIp);
qDebug("tgtIp %s, mac: %012llx",
qPrintable(QHostAddress(dstIp.toArray()).toString()),
mac);
}
tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_;
return ndpLookup(tgtIp);
}
return false;
return mac;
}
/*

View File

@ -31,6 +31,10 @@ win32 {
LIBS += -L"../rpc" -lpbrpc
POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a"
}
linux-g++ {
INCLUDEPATH += "/usr/include/libnl3"
LIBS += -lnl-3 -lnl-route-3
}
LIBS += -lm
LIBS += -lprotobuf
HEADERS += drone.h \
@ -50,6 +54,7 @@ SOURCES += \
pcaptxstats.cpp \
pcaptxthread.cpp \
bsdport.cpp \
linuxhostdevice.cpp \
linuxport.cpp \
winhostdevice.cpp \
winpcapport.cpp

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#ifndef _HOST_DEVICE_H
#define _HOST_DEVICE_H
#include "linuxhostdevice.h"
#include "winhostdevice.h"
class DeviceManager;
@ -32,8 +33,10 @@ class HostDevice
public:
static Device* create(QString portName, DeviceManager *deviceManager)
{
#ifdef Q_OS_WIN32
#if defined(Q_OS_WIN32)
return new WindowsHostDevice(portName, deviceManager);
#elif defined(Q_OS_LINUX)
return new LinuxHostDevice(portName, deviceManager);
#else
(void)portName; // squelch unused warning
(void)deviceManager; // squelch unused warning

274
server/linuxhostdevice.cpp Normal file
View File

@ -0,0 +1,274 @@
/*
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 "linuxhostdevice.h"
#ifdef Q_OS_LINUX
#include "../common/qtport.h"
#include <QHostAddress>
#include <netlink/route/link.h>
#include <netlink/route/neighbour.h>
LinuxHostDevice::LinuxHostDevice(QString portName,
DeviceManager *deviceManager)
: Device(deviceManager)
{
ifName_ = portName;
netSock_ = nl_socket_alloc();
if (!netSock_) {
qWarning("Failed to open netlink socket for %s", qPrintable(ifName_));
return;
}
if (nl_connect(netSock_, NETLINK_ROUTE) < 0) {
qWarning("Failed to connect netlink socket for %s", qPrintable(ifName_));
return;
}
rtnl_link *link;
if (rtnl_link_get_kernel(netSock_, 0, qPrintable(ifName_), &link) < 0) {
qWarning("Failed to get rtnet link from kernel for %s", qPrintable(ifName_));
return;
}
ifIndex_ = rtnl_link_get_ifindex(link);
qDebug("Port %s: ifIndex %d", qPrintable(ifName_), ifIndex_);
rtnl_link_put(link);
}
void LinuxHostDevice::receivePacket(PacketBuffer* /*pktBuf*/)
{
// Do Nothing
}
void LinuxHostDevice::clearNeighbors(Device::NeighborSet set)
{
// No need to do anything - see AbstractPort::resolveDeviceNeighbors()
// on when this is used
if (set == kUnresolvedNeighbors)
return;
nl_cache *neighCache;
if (rtnl_neigh_alloc_cache(netSock_, &neighCache) < 0) {
qWarning("Failed to get neigh cache from kernel");
return;
}
if (!neighCache) {
qWarning("Neigh cache empty");
return;
}
int count=0, fail=0;
rtnl_neigh *neigh = (rtnl_neigh*) nl_cache_get_first(neighCache);
while (neigh) {
if ((rtnl_neigh_get_ifindex(neigh) == ifIndex_)
&& (rtnl_neigh_get_family(neigh) == AF_INET
|| rtnl_neigh_get_family(neigh) == AF_INET6)
&& !(rtnl_neigh_get_state(neigh) & (NUD_PERMANENT|NUD_NOARP))) {
count++;
if (rtnl_neigh_delete(netSock_, neigh, 0) < 0)
fail++;
}
neigh = (rtnl_neigh*) nl_cache_get_next(OBJ_CAST(neigh));
}
nl_cache_put(neighCache);
qDebug("Flush ARP/ND table for ifIndex %u: %d/%d deleted",
ifIndex_, count - fail, count);
}
void LinuxHostDevice::getNeighbors(OstEmul::DeviceNeighborList *neighbors)
{
nl_cache *neighCache;
if (rtnl_neigh_alloc_cache(netSock_, &neighCache) < 0) {
qWarning("Failed to get neigh cache from kernel");
return;
}
if (!neighCache) {
qWarning("Neigh cache empty");
return;
}
rtnl_neigh *neigh = (rtnl_neigh*) nl_cache_get_first(neighCache);
while (neigh) {
if ((rtnl_neigh_get_ifindex(neigh) == ifIndex_)
&& !(rtnl_neigh_get_state(neigh) & NUD_NOARP)) {
if (rtnl_neigh_get_family(neigh) == AF_INET) {
OstEmul::ArpEntry *arp = neighbors->add_arp();
arp->set_ip4(qFromBigEndian<quint32>(
nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh))));
nl_addr *lladdr = rtnl_neigh_get_lladdr(neigh);
arp->set_mac(lladdr ?
qFromBigEndian<quint64>(
nl_addr_get_binary_addr(lladdr)) >> 16 :
0);
}
else if (rtnl_neigh_get_family(neigh) == AF_INET6) {
OstEmul::NdpEntry *ndp = neighbors->add_ndp();
ndp->mutable_ip6()->set_hi(qFromBigEndian<quint64>(
nl_addr_get_binary_addr(
rtnl_neigh_get_dst(neigh))));
ndp->mutable_ip6()->set_lo(qFromBigEndian<quint64>((const uchar*)
nl_addr_get_binary_addr(
rtnl_neigh_get_dst(neigh))+8));
nl_addr *lladdr = rtnl_neigh_get_lladdr(neigh);
ndp->set_mac(lladdr ?
qFromBigEndian<quint64>(
nl_addr_get_binary_addr(lladdr)) >> 16 :
0);
}
}
neigh = (rtnl_neigh*) nl_cache_get_next(OBJ_CAST(neigh));
}
nl_cache_put(neighCache);
}
quint64 LinuxHostDevice::arpLookup(quint32 ip)
{
quint64 mac = 0;
nl_cache *neighCache;
if (rtnl_neigh_alloc_cache(netSock_, &neighCache) < 0) {
qWarning("Failed to get neigh cache from kernel");
return mac;
}
if (!neighCache) {
qWarning("Neigh cache empty");
return mac;
}
quint32 ipBig = qToBigEndian(ip);
nl_addr *dst = nl_addr_build(AF_INET, &ipBig, sizeof(ipBig));
#if 0
//
// libnl 3.2.[15..21] have a bug in rtnl_neigh_get and fail to find entry
// https://github.com/tgraf/libnl/commit/8571f58f23763d8db7365d02c9b27832ad3d7005
//
rtnl_neigh *neigh = rtnl_neigh_get(neighCache, ifIndex_, dst);
if (neigh) {
mac = qFromBigEndian<quint64>(
nl_addr_get_binary_addr(rtnl_neigh_get_lladdr(neigh))) >> 16;
rtnl_neigh_put(neigh);
}
#else
rtnl_neigh *neigh = (rtnl_neigh*) nl_cache_get_first(neighCache);
while (neigh) {
if ((rtnl_neigh_get_ifindex(neigh) == ifIndex_)
&& (rtnl_neigh_get_family(neigh) == AF_INET)
&& !nl_addr_cmp(rtnl_neigh_get_dst(neigh), dst)) {
nl_addr *lladdr = rtnl_neigh_get_lladdr(neigh);
if (lladdr)
mac = qFromBigEndian<quint64>(
nl_addr_get_binary_addr(lladdr)) >> 16;
break;
}
neigh = (rtnl_neigh*) nl_cache_get_next(OBJ_CAST(neigh));
}
#endif
nl_addr_put(dst);
nl_cache_put(neighCache);
return mac;
}
quint64 LinuxHostDevice::ndpLookup(UInt128 ip)
{
quint64 mac = 0;
nl_cache *neighCache;
if (rtnl_neigh_alloc_cache(netSock_, &neighCache) < 0) {
qWarning("Failed to get neigh cache from kernel");
return mac;
}
if (!neighCache) {
qWarning("Neigh cache empty");
return mac;
}
nl_addr *dst = nl_addr_build(AF_INET6, ip.toArray(), 16);
#if 0
//
// libnl 3.2.[15..21] have a bug in rtnl_neigh_get and fail to find entry
// https://github.com/tgraf/libnl/commit/8571f58f23763d8db7365d02c9b27832ad3d7005
//
rtnl_neigh *neigh = rtnl_neigh_get(neighCache, ifIndex_, dst);
if (neigh) {
mac = qFromBigEndian<quint64>(
nl_addr_get_binary_addr(rtnl_neigh_get_lladdr(neigh))) >> 16;
rtnl_neigh_put(neigh);
}
#else
rtnl_neigh *neigh = (rtnl_neigh*) nl_cache_get_first(neighCache);
while (neigh) {
if ((rtnl_neigh_get_ifindex(neigh) == ifIndex_)
&& (rtnl_neigh_get_family(neigh) == AF_INET6)
&& !nl_addr_cmp(rtnl_neigh_get_dst(neigh), dst)) {
nl_addr *lladdr = rtnl_neigh_get_lladdr(neigh);
if (lladdr)
mac = qFromBigEndian<quint64>(
nl_addr_get_binary_addr(lladdr)) >> 16;
break;
}
neigh = (rtnl_neigh*) nl_cache_get_next(OBJ_CAST(neigh));
}
#endif
nl_addr_put(dst);
nl_cache_put(neighCache);
return mac;
}
void LinuxHostDevice::sendArpRequest(quint32 tgtIp)
{
quint32 ipBig = qToBigEndian(tgtIp);
nl_addr *dst = nl_addr_build(AF_INET, &ipBig, sizeof(ipBig));
rtnl_neigh *neigh = rtnl_neigh_alloc();
rtnl_neigh_set_ifindex(neigh, ifIndex_);
rtnl_neigh_set_state(neigh, NUD_NONE);
rtnl_neigh_set_dst(neigh, dst);
rtnl_neigh_set_flags(neigh, NTF_USE); // force kernel to send ARP request
if (int err = rtnl_neigh_add(netSock_, neigh, NLM_F_CREATE) < 0)
qWarning("Resolve arp failed for port %s ip %08x: %s",
qPrintable(ifName_), tgtIp, strerror(err));
rtnl_neigh_put(neigh);
nl_addr_put(dst);
}
void LinuxHostDevice::sendNeighborSolicit(UInt128 tgtIp)
{
nl_addr *dst = nl_addr_build(AF_INET6, tgtIp.toArray(), 16);
rtnl_neigh *neigh = rtnl_neigh_alloc();
rtnl_neigh_set_ifindex(neigh, ifIndex_);
rtnl_neigh_set_state(neigh, NUD_NONE);
rtnl_neigh_set_dst(neigh, dst);
rtnl_neigh_set_flags(neigh, NTF_USE); // force kernel to send ARP request
if (int err = rtnl_neigh_add(netSock_, neigh, NLM_F_CREATE) < 0)
qWarning("Resolve ndp failed for port %s ip %016llx-%016llx: %s",
qPrintable(ifName_), tgtIp.hi64(), tgtIp.lo64(), strerror(err));
rtnl_neigh_put(neigh);
nl_addr_put(dst);
}
#endif

51
server/linuxhostdevice.h Normal file
View File

@ -0,0 +1,51 @@
/*
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 _LINUX_HOST_DEVICE_H
#define _LINUX_HOST_DEVICE_H
#include "device.h"
#ifdef Q_OS_LINUX
class LinuxHostDevice: public Device
{
public:
LinuxHostDevice(QString portName, DeviceManager *deviceManager);
virtual ~LinuxHostDevice() {};
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:
QString ifName_;
int ifIndex_{-1};
struct nl_sock *netSock_{nullptr};
};
#endif
#endif

View File

@ -19,15 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "linuxport.h"
#include "interfaceinfo.h"
#ifdef Q_OS_LINUX
#include "../common/qtport.h"
#include <QByteArray>
#include <QHash>
#include <QTime>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <netlink/route/addr.h>
#include <netlink/route/link.h>
#include <netlink/route/route.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
@ -50,12 +56,19 @@ typedef struct rtnl_link_stats64 x_rtnl_link_stats;
typedef struct rtnl_link_stats x_rtnl_link_stats;
#endif
nl_sock *LinuxPort::netSock_{nullptr};
nl_cache *LinuxPort::linkCache_{nullptr};
nl_cache *LinuxPort::addressCache_{nullptr};
nl_cache *LinuxPort::routeCache_{nullptr};
LinuxPort::LinuxPort(int id, const char *device)
: PcapPort(id, device)
{
isPromisc_ = true;
clearPromisc_ = false;
populateInterfaceInfo();
// We don't need per port Rx/Tx monitors for Linux
delete monitorRx_;
delete monitorTx_;
@ -115,6 +128,35 @@ LinuxPort::~LinuxPort()
}
}
void LinuxPort::classInit() // FIXME: rename
{
netSock_ = nl_socket_alloc();
if (!netSock_) {
qWarning("Failed to open netlink socket");
return;
}
if (nl_connect(netSock_, NETLINK_ROUTE) < 0) {
qWarning("Failed to connect netlink socket");
return;
}
if (rtnl_link_alloc_cache(netSock_, AF_UNSPEC, &linkCache_) < 0) {
qWarning("Failed to populate link cache");
return;
}
if (rtnl_addr_alloc_cache(netSock_, &addressCache_) < 0) {
qWarning("Failed to populate addr cache");
return;
}
if (rtnl_route_alloc_cache(netSock_, AF_UNSPEC, 0, &routeCache_) < 0) {
qWarning("Failed to populate addr cache");
return;
}
}
void LinuxPort::init()
{
if (!monitor_->isRunning())
@ -124,6 +166,8 @@ void LinuxPort::init()
if (!isPromisc_)
addNote("Non Promiscuous Mode");
AbstractPort::init();
}
OstProto::LinkState LinuxPort::linkState()
@ -143,6 +187,116 @@ bool LinuxPort::setExclusiveControl(bool /*exclusive*/)
return false;
}
void LinuxPort::populateInterfaceInfo()
{
//
// Find Mac
//
if (!linkCache_) {
qWarning("rtnetlink link cache empty for %s", name());
return;
}
rtnl_link *link = rtnl_link_get_by_name(linkCache_, name());
if (!link) {
qWarning("rtnetlink link not found for %s", name());
return;
}
nl_addr *addr = rtnl_link_get_addr(link);
if (nl_addr_get_family(addr) != AF_LLC) {
qWarning("unexpected mac family found for %s:%d",
name(), nl_addr_get_family(addr));
rtnl_link_put(link);
return;
}
if (nl_addr_get_prefixlen(addr) != 48) {
qWarning("unexpected mac length for %s:%d",
name(), nl_addr_get_prefixlen(addr));
rtnl_link_put(link);
return;
}
quint64 mac = qFromBigEndian<quint64>(nl_addr_get_binary_addr(addr)) >> 16;
if (!mac) {
qWarning("zero mac for %s - skipping", name());
rtnl_link_put(link);
return;
}
int ifIndex = rtnl_link_get_ifindex(link);
rtnl_link_put(link);
interfaceInfo_ = new InterfaceInfo;
interfaceInfo_->mac = mac;
//
// Find gateways
//
quint32 gw4 = 0;
UInt128 gw6 = UInt128(0,0); // FIXME - just 0
for (rtnl_route *rt = routeCache_ ? (rtnl_route*) nl_cache_get_first(routeCache_) : 0;
rt && !gw4 && gw6 == UInt128(0,0); // FIXME: !UInt128
rt = (rtnl_route*) nl_cache_get_next(OBJ_CAST(rt))) {
if (rtnl_route_get_table(rt) != RT_TABLE_MAIN) // we want only main RTT
continue;
nl_addr *pfx = rtnl_route_get_dst(rt);
if (nl_addr_get_len(pfx)) // default route has len = 0
continue;
if (!rtnl_route_get_nnexthops(rt)) // at least one nh is required
continue;
rtnl_nexthop *nh = rtnl_route_nexthop_n(rt, 0);
if (rtnl_route_nh_get_ifindex(nh) != ifIndex) // ignore gw on other links
continue;
if (!gw4 && rtnl_route_get_family(rt) == AF_INET) {
gw4 = qFromBigEndian<quint32>(
nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(nh)));
}
else if (gw6 != UInt128(0,0) && rtnl_route_get_family(rt) == AF_INET6) { // FIXME: !gw6
gw6 = UInt128((quint8*)
nl_addr_get_binary_addr(rtnl_route_nh_get_gateway(nh)));
}
}
//
// Find self IP
//
bool foundIp4 = false;
bool foundIp6 = false;
if (!addressCache_) {
qWarning("rtnetlink address cache empty for %s", name());
return;
}
rtnl_addr *l3addr = (rtnl_addr*) nl_cache_get_first(addressCache_);
while (l3addr && !foundIp4 && !foundIp6) {
if (rtnl_addr_get_ifindex(l3addr) == ifIndex) {
if (rtnl_addr_get_family(l3addr) == AF_INET && !foundIp4) {
Ip4Config ip;
ip.address = qFromBigEndian<quint32>(
nl_addr_get_binary_addr(
rtnl_addr_get_local(l3addr)));
ip.prefixLength = rtnl_addr_get_prefixlen(l3addr);
ip.gateway = gw4;
interfaceInfo_->ip4.append(ip);
}
else if (rtnl_addr_get_family(l3addr) == AF_INET6 && !foundIp6) {
Ip6Config ip;
ip.address = UInt128((quint8*)nl_addr_get_binary_addr(
rtnl_addr_get_local(l3addr)));
ip.prefixLength = rtnl_addr_get_prefixlen(l3addr);
ip.gateway = gw6;
interfaceInfo_->ip6.append(ip);
}
}
l3addr = (rtnl_addr*) nl_cache_get_next((nl_object*)l3addr);
}
}
LinuxPort::StatsMonitor::StatsMonitor()
: QThread()
{

View File

@ -38,6 +38,8 @@ public:
virtual bool hasExclusiveControl();
virtual bool setExclusiveControl(bool exclusive);
static void classInit();
protected:
class StatsMonitor: public QThread
{
@ -62,6 +64,14 @@ protected:
bool clearPromisc_;
static QList<LinuxPort*> allPorts_;
static StatsMonitor *monitor_; // rx/tx stats for ALL ports
private:
void populateInterfaceInfo();
static struct nl_sock *netSock_;
static struct nl_cache *linkCache_;
static struct nl_cache *addressCache_;
static struct nl_cache *routeCache_;
};
#endif

View File

@ -53,6 +53,8 @@ PortManager::PortManager()
#if defined(Q_OS_WIN32)
WinPcapPort::populateAdapterList();
#elif defined(Q_OS_LINUX)
LinuxPort::classInit();
#endif
txRateAccuracy = rateAccuracy();