HostDev: Added Linux host device code
This commit is contained in:
parent
f58c4e309c
commit
73043f6fe6
35
common/qtport.h
Normal file
35
common/qtport.h
Normal 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
|
@ -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_;
|
||||
|
||||
return arpLookup(tgtIp);
|
||||
mac = arpLookup(tgtIp);
|
||||
qDebug("tgtIp: %08x, mac: %012llx", tgtIp, mac);
|
||||
}
|
||||
}
|
||||
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_;
|
||||
|
||||
return ndpLookup(tgtIp);
|
||||
mac = ndpLookup(tgtIp);
|
||||
qDebug("tgtIp %s, mac: %012llx",
|
||||
qPrintable(QHostAddress(dstIp.toArray()).toString()),
|
||||
mac);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mac;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
274
server/linuxhostdevice.cpp
Normal 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
51
server/linuxhostdevice.h
Normal 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
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -53,6 +53,8 @@ PortManager::PortManager()
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
WinPcapPort::populateAdapterList();
|
||||
#elif defined(Q_OS_LINUX)
|
||||
LinuxPort::classInit();
|
||||
#endif
|
||||
|
||||
txRateAccuracy = rateAccuracy();
|
||||
|
Loading…
Reference in New Issue
Block a user