Use interface stats as port stats on Windows

Initial working changes. More changes in subsequent commits
This commit is contained in:
Srivats P 2023-09-26 15:06:06 +05:30
parent ca956c3c18
commit 6d312764e8
2 changed files with 226 additions and 3 deletions

View File

@ -1,4 +1,5 @@
/* /*
*
Copyright (C) 2010 Srivats P. Copyright (C) 2010 Srivats P.
This file is part of "Ostinato" This file is part of "Ostinato"
@ -23,13 +24,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QCoreApplication> #include <QCoreApplication>
#include <QProcess> #include <QProcess>
#include <QTime>
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
#include <ifdef.h>
#include <ntddndis.h> #include <ntddndis.h>
#include <ws2ipdef.h>
static const quint64 kMaxValue64 = 0xffffffffffffffffULL;
PIP_ADAPTER_ADDRESSES WinPcapPort::adapterList_ = NULL; PIP_ADAPTER_ADDRESSES WinPcapPort::adapterList_ = NULL;
QList<WinPcapPort*> WinPcapPort::allPorts_;
WinPcapPort::StatsMonitor *WinPcapPort::monitor_ = NULL;
bool WinPcapPort::internalPortStats_ = false;
// FIXME: duplicated from winhostdevice - remove duplicate
static WCHAR errBuf[256];
static inline QString errStr(ulong err)
{
return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
errBuf, sizeof(errBuf)-1, NULL) > 0 ?
QString("error 0x%1 %2").arg(err, 0, 16)
.arg(QString().fromWCharArray(errBuf)) :
QString("error 0x%1").arg(err, 0, 16);
}
WinPcapPort::WinPcapPort(int id, const char *device, const char *description) WinPcapPort::WinPcapPort(int id, const char *device, const char *description)
: PcapPort(id, device) : PcapPort(id, device)
@ -43,12 +61,21 @@ WinPcapPort::WinPcapPort(int id, const char *device, const char *description)
delete monitorRx_; delete monitorRx_;
delete monitorTx_; delete monitorTx_;
monitorRx_ = monitorTx_ = NULL;
if (internalPortStats_) {
// One monitor each for rx and tx for each port
monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_);
monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_);
} else {
// By default, we have one monitor for both Rx/Tx of all ports
if (!monitor_)
monitor_ = new StatsMonitor();
}
data_.set_description(description); data_.set_description(description);
// XXX: luid_ is already populated by populateInterfaceInfo() call above
adapter_ = PacketOpenAdapter((CHAR*)device); adapter_ = PacketOpenAdapter((CHAR*)device);
if (!adapter_) if (!adapter_)
qFatal("Unable to open adapter %s", device); qFatal("Unable to open adapter %s", device);
@ -59,10 +86,39 @@ WinPcapPort::WinPcapPort(int id, const char *device, const char *description)
data_.set_is_exclusive_control(hasExclusiveControl()); data_.set_is_exclusive_control(hasExclusiveControl());
minPacketSetSize_ = 256; minPacketSetSize_ = 256;
qDebug("adding dev to all ports list <%s>", device);
allPorts_.append(this);
} }
WinPcapPort::~WinPcapPort() WinPcapPort::~WinPcapPort()
{ {
if (monitor_ && monitor_->isRunning()) {
monitor_->stop();
monitor_->wait();
delete monitor_;
monitor_ = nullptr;
}
}
void WinPcapPort::init()
{
if (monitor_) {
if (!monitor_->isRunning())
monitor_->start();
monitor_->waitForSetupFinished();
}
#if 0 // TODO
if (!isPromisc_)
addNote("Non Promiscuous Mode");
#endif
if (monitor_)
AbstractPort::init();
else
PcapPort::init();
} }
OstProto::LinkState WinPcapPort::linkState() OstProto::LinkState WinPcapPort::linkState()
@ -244,6 +300,146 @@ void WinPcapPort::PortMonitor::run()
} }
} }
WinPcapPort::StatsMonitor::StatsMonitor()
: QThread()
{
setObjectName("StatsMon");
stop_ = false;
setupDone_ = false;
}
WinPcapPort::StatsMonitor::~StatsMonitor()
{
}
void WinPcapPort::StatsMonitor::run()
{
struct PortStatsAndState {
NET_LUID luid;
PortStats *stats;
OstProto::LinkState *linkState;
bool firstError;
};
QList<PortStatsAndState> portList;
int count = 0;
//
// We first setup the port list to be polled
//
for (WinPcapPort* port : allPorts_) {
if (port->luid_.Value) {
portList.append(
{port->luid_, &(port->stats_), &(port->linkState_), false});
} else {
qWarning("No LUID for port %s - stats will not be available",
port->name());
}
count++;
}
qDebug("stats port count = %d\n", count);
if (count <= 0) {
qWarning("No ports with valid LUID - no stats will be available");
return;
}
qDebug("stats for %d ports setup", count);
setupDone_ = true;
//
// We are all set - Let's start polling for stats!
//
while (!stop_) {
for (auto &port : portList) {
MIB_IF_ROW2 ifInfo;
ifInfo.InterfaceLuid.Value = port.luid.Value;
ulong status = GetIfEntry2(&ifInfo);
if (status != NO_ERROR) {
if (!port.firstError) {
qWarning("Failed to fetch stats for Luid 0x%016llx (%s)"
" - suppressing further stats error messages "
"for this port",
port.luid.Value, qPrintable(errStr(status)));
port.firstError = true;
}
continue;
}
switch (ifInfo.OperStatus) {
case IfOperStatusUp:
*(port.linkState) = OstProto::LinkStateUp; break;
case IfOperStatusDown:
*(port.linkState) = OstProto::LinkStateDown; break;
default:
*(port.linkState) = OstProto::LinkStateUnknown;
}
quint64 inPkts = ifInfo.InUcastPkts + ifInfo.InNUcastPkts;
port.stats->rxPps =
((inPkts >= port.stats->rxPkts) ?
inPkts - port.stats->rxPkts :
inPkts + (kMaxValue64 - port.stats->rxPkts))
/ kRefreshFreq_;
port.stats->rxBps =
((ifInfo.InOctets >= port.stats->rxBytes) ?
ifInfo.InOctets - port.stats->rxBytes :
ifInfo.InOctets + (kMaxValue64 - port.stats->rxBytes))
/ kRefreshFreq_;
port.stats->rxPkts = inPkts;
port.stats->rxBytes = ifInfo.InOctets;
quint64 outPkts = ifInfo.OutUcastPkts + ifInfo.OutNUcastPkts;
port.stats->txPps =
((outPkts >= port.stats->txPkts) ?
outPkts - port.stats->txPkts :
outPkts + (kMaxValue64 - port.stats->txPkts))
/ kRefreshFreq_;
port.stats->txBps =
((ifInfo.OutOctets >= port.stats->txBytes) ?
ifInfo.OutOctets - port.stats->txBytes :
ifInfo.OutOctets + (kMaxValue64 - port.stats->txBytes))
/ kRefreshFreq_;
port.stats->txPkts = outPkts;
port.stats->txBytes = ifInfo.OutOctets;
port.stats->rxDrops = ifInfo.InDiscards;
port.stats->rxErrors = ifInfo.InErrors + ifInfo.InUnknownProtos;
// XXX: Ostinato stats not available in Win
// - rxFifoErrors
// - rxFrameErrors
// XXX: Win stats not available in Ostinato
// - OutDiscards
// - OutErrors
}
QThread::sleep(kRefreshFreq_);
}
portList.clear();
}
void WinPcapPort::StatsMonitor::stop()
{
stop_ = true;
}
bool WinPcapPort::StatsMonitor::waitForSetupFinished(int msecs)
{
QTime t;
t.start();
while (!setupDone_)
{
if (t.elapsed() > msecs)
return false;
QThread::msleep(10);
}
return true;
}
void WinPcapPort::populateInterfaceInfo() void WinPcapPort::populateInterfaceInfo()
{ {
if (!adapterList_) { if (!adapterList_) {
@ -257,9 +453,12 @@ void WinPcapPort::populateInterfaceInfo()
if (!adapter) { if (!adapter) {
qWarning("Adapter info not found for %s", name()); qWarning("Adapter info not found for %s", name());
luid_.Value = 0;
return; return;
} }
luid_ = adapter->Luid;
interfaceInfo_ = new InterfaceInfo; interfaceInfo_ = new InterfaceInfo;
interfaceInfo_->speed = adapter->TransmitLinkSpeed != quint64(-1) ? interfaceInfo_->speed = adapter->TransmitLinkSpeed != quint64(-1) ?

View File

@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <packet32.h> #include <packet32.h>
#include <ws2ipdef.h>
#include <iphlpapi.h> #include <iphlpapi.h>
class WinPcapPort : public PcapPort class WinPcapPort : public PcapPort
@ -36,6 +37,7 @@ public:
WinPcapPort(int id, const char *device, const char *description); WinPcapPort(int id, const char *device, const char *description);
~WinPcapPort(); ~WinPcapPort();
void init();
virtual OstProto::LinkState linkState(); virtual OstProto::LinkState linkState();
virtual bool hasExclusiveControl(); virtual bool hasExclusiveControl();
virtual bool setExclusiveControl(bool exclusive); virtual bool setExclusiveControl(bool exclusive);
@ -51,10 +53,32 @@ protected:
AbstractPort::PortStats *stats); AbstractPort::PortStats *stats);
void run(); void run();
}; };
class StatsMonitor: public QThread
{
public:
StatsMonitor();
~StatsMonitor();
void run();
void stop();
bool waitForSetupFinished(int msecs = 10000);
private:
// TODO: int setPromisc(const char* portName);
static const int kRefreshFreq_ = 1; // in seconds
bool stop_;
bool setupDone_;
};
static QList<WinPcapPort*> allPorts_;
static StatsMonitor *monitor_; // rx/tx stats for ALL ports
static bool internalPortStats_;
private: private:
void populateInterfaceInfo(); void populateInterfaceInfo();
LPADAPTER adapter_; LPADAPTER adapter_;
NET_LUID luid_;
PPACKET_OID_DATA linkStateOid_ ; PPACKET_OID_DATA linkStateOid_ ;
static PIP_ADAPTER_ADDRESSES adapterList_; static PIP_ADAPTER_ADDRESSES adapterList_;