Use interface stats as port stats on Windows
Initial working changes. More changes in subsequent commits
This commit is contained in:
parent
ca956c3c18
commit
6d312764e8
@ -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) ?
|
||||||
|
@ -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_;
|
||||||
|
Loading…
Reference in New Issue
Block a user