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
|
// We expect pktBuf to point to EthType on entry
|
||||||
quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
||||||
{
|
{
|
||||||
|
quint64 mac = 0;
|
||||||
const uchar *pktData = pktBuf->data();
|
const uchar *pktData = pktBuf->data();
|
||||||
quint16 ethType = qFromBigEndian<quint16>(pktData);
|
quint16 ethType = qFromBigEndian<quint16>(pktData);
|
||||||
|
|
||||||
@ -295,17 +296,19 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|||||||
if (pktBuf->length() < ipHdrLen) {
|
if (pktBuf->length() < ipHdrLen) {
|
||||||
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
qDebug("incomplete IPv4 header: expected %d, actual %d",
|
||||||
ipHdrLen, pktBuf->length());
|
ipHdrLen, pktBuf->length());
|
||||||
return false;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
|
dstIp = qFromBigEndian<quint32>(pktData + ipHdrLen - 4);
|
||||||
if ((dstIp & 0xF0000000) == 0xE0000000) { // Mcast IP?
|
if ((dstIp & 0xF0000000) == 0xE0000000) { // Mcast IP?
|
||||||
qDebug("mcast dst %x", dstIp);
|
mac = (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF);
|
||||||
return (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
|
else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6
|
||||||
UInt128 dstIp, tgtIp;
|
UInt128 dstIp, tgtIp;
|
||||||
@ -313,21 +316,26 @@ quint64 Device::neighborMac(const PacketBuffer *pktBuf)
|
|||||||
if (pktBuf->length() < (kIp6HdrLen+2)) {
|
if (pktBuf->length() < (kIp6HdrLen+2)) {
|
||||||
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
qDebug("incomplete IPv6 header: expected %d, actual %d",
|
||||||
kIp6HdrLen, pktBuf->length()-2);
|
kIp6HdrLen, pktBuf->length()-2);
|
||||||
return false;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
dstIp = qFromBigEndian<UInt128>(pktData + 24);
|
||||||
if (dstIp.toArray()[0] == 0xFF) { // Mcast IP?
|
if (dstIp.toArray()[0] == 0xFF) { // Mcast IP?
|
||||||
qDebug("mcast dst %s",
|
mac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
|
||||||
qPrintable(QHostAddress(dstIp.toArray()).toString()));
|
qDebug("mcast dst %s, mac: %012llx",
|
||||||
return (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,6 +31,10 @@ win32 {
|
|||||||
LIBS += -L"../rpc" -lpbrpc
|
LIBS += -L"../rpc" -lpbrpc
|
||||||
POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a"
|
POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a"
|
||||||
}
|
}
|
||||||
|
linux-g++ {
|
||||||
|
INCLUDEPATH += "/usr/include/libnl3"
|
||||||
|
LIBS += -lnl-3 -lnl-route-3
|
||||||
|
}
|
||||||
LIBS += -lm
|
LIBS += -lm
|
||||||
LIBS += -lprotobuf
|
LIBS += -lprotobuf
|
||||||
HEADERS += drone.h \
|
HEADERS += drone.h \
|
||||||
@ -50,6 +54,7 @@ SOURCES += \
|
|||||||
pcaptxstats.cpp \
|
pcaptxstats.cpp \
|
||||||
pcaptxthread.cpp \
|
pcaptxthread.cpp \
|
||||||
bsdport.cpp \
|
bsdport.cpp \
|
||||||
|
linuxhostdevice.cpp \
|
||||||
linuxport.cpp \
|
linuxport.cpp \
|
||||||
winhostdevice.cpp \
|
winhostdevice.cpp \
|
||||||
winpcapport.cpp
|
winpcapport.cpp
|
||||||
|
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
#ifndef _HOST_DEVICE_H
|
#ifndef _HOST_DEVICE_H
|
||||||
#define _HOST_DEVICE_H
|
#define _HOST_DEVICE_H
|
||||||
|
|
||||||
|
#include "linuxhostdevice.h"
|
||||||
#include "winhostdevice.h"
|
#include "winhostdevice.h"
|
||||||
|
|
||||||
class DeviceManager;
|
class DeviceManager;
|
||||||
@ -32,8 +33,10 @@ class HostDevice
|
|||||||
public:
|
public:
|
||||||
static Device* create(QString portName, DeviceManager *deviceManager)
|
static Device* create(QString portName, DeviceManager *deviceManager)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
#if defined(Q_OS_WIN32)
|
||||||
return new WindowsHostDevice(portName, deviceManager);
|
return new WindowsHostDevice(portName, deviceManager);
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
return new LinuxHostDevice(portName, deviceManager);
|
||||||
#else
|
#else
|
||||||
(void)portName; // squelch unused warning
|
(void)portName; // squelch unused warning
|
||||||
(void)deviceManager; // 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 "linuxport.h"
|
||||||
|
|
||||||
|
#include "interfaceinfo.h"
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
|
||||||
|
#include "../common/qtport.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.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/ioctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.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;
|
typedef struct rtnl_link_stats x_rtnl_link_stats;
|
||||||
#endif
|
#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)
|
LinuxPort::LinuxPort(int id, const char *device)
|
||||||
: PcapPort(id, device)
|
: PcapPort(id, device)
|
||||||
{
|
{
|
||||||
isPromisc_ = true;
|
isPromisc_ = true;
|
||||||
clearPromisc_ = false;
|
clearPromisc_ = false;
|
||||||
|
|
||||||
|
populateInterfaceInfo();
|
||||||
|
|
||||||
// We don't need per port Rx/Tx monitors for Linux
|
// We don't need per port Rx/Tx monitors for Linux
|
||||||
delete monitorRx_;
|
delete monitorRx_;
|
||||||
delete monitorTx_;
|
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()
|
void LinuxPort::init()
|
||||||
{
|
{
|
||||||
if (!monitor_->isRunning())
|
if (!monitor_->isRunning())
|
||||||
@ -124,6 +166,8 @@ void LinuxPort::init()
|
|||||||
|
|
||||||
if (!isPromisc_)
|
if (!isPromisc_)
|
||||||
addNote("Non Promiscuous Mode");
|
addNote("Non Promiscuous Mode");
|
||||||
|
|
||||||
|
AbstractPort::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
OstProto::LinkState LinuxPort::linkState()
|
OstProto::LinkState LinuxPort::linkState()
|
||||||
@ -143,6 +187,116 @@ bool LinuxPort::setExclusiveControl(bool /*exclusive*/)
|
|||||||
return false;
|
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()
|
LinuxPort::StatsMonitor::StatsMonitor()
|
||||||
: QThread()
|
: QThread()
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,8 @@ public:
|
|||||||
virtual bool hasExclusiveControl();
|
virtual bool hasExclusiveControl();
|
||||||
virtual bool setExclusiveControl(bool exclusive);
|
virtual bool setExclusiveControl(bool exclusive);
|
||||||
|
|
||||||
|
static void classInit();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class StatsMonitor: public QThread
|
class StatsMonitor: public QThread
|
||||||
{
|
{
|
||||||
@ -62,6 +64,14 @@ protected:
|
|||||||
bool clearPromisc_;
|
bool clearPromisc_;
|
||||||
static QList<LinuxPort*> allPorts_;
|
static QList<LinuxPort*> allPorts_;
|
||||||
static StatsMonitor *monitor_; // rx/tx stats for ALL ports
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ PortManager::PortManager()
|
|||||||
|
|
||||||
#if defined(Q_OS_WIN32)
|
#if defined(Q_OS_WIN32)
|
||||||
WinPcapPort::populateAdapterList();
|
WinPcapPort::populateAdapterList();
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
LinuxPort::classInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
txRateAccuracy = rateAccuracy();
|
txRateAccuracy = rateAccuracy();
|
||||||
|
Loading…
Reference in New Issue
Block a user