Create a Tx Ttag tracker thread

For now we are just debug printing timestamp with T-TagId and GUID. We
need to store this tuple and compare when we Rx the same - this will be
in a upcoming commit.
This commit is contained in:
Srivats P 2023-03-22 16:32:41 +05:30
parent f1cfaa6e89
commit 682d0cc5c9
7 changed files with 331 additions and 2 deletions

View File

@ -241,3 +241,25 @@ bool SignProtocol::packetGuid(const uchar *pkt, int pktLen, uint *guid)
} }
return false; return false;
} }
bool SignProtocol::packetTtagId(const uchar *pkt, int pktLen, uint *ttagId, uint *guid)
{
bool ret = false;
const uchar *p = pkt + pktLen - sizeof(kSignMagic);
quint32 magic = qFromBigEndian<quint32>(p);
if (magic != kSignMagic)
return ret;
*guid = 0xffffffff; // invalid GUID
p--;
while (*p != kTypeLenEnd) {
if (*p == kTypeLenTtag) {
*ttagId = *(p - 1);
ret = true;
} else if (*p == kTypeLenGuid) {
*guid = qFromBigEndian<quint32>(p - 3) >> 8;
}
p -= 1 + (*p >> 5); // move to next TLV
}
return ret;
}

View File

@ -86,6 +86,7 @@ public:
static quint32 magic(); static quint32 magic();
static bool packetGuid(const uchar *pkt, int pktLen, uint *guid); static bool packetGuid(const uchar *pkt, int pktLen, uint *guid);
static bool packetTtagId(const uchar *pkt, int pktLen, uint *ttagId, uint *guid);
static const quint32 kMaxGuid = 0x00ffffff; static const quint32 kMaxGuid = 0x00ffffff;
static const quint8 kTypeLenTtagPlaceholder = 0x22; static const quint8 kTypeLenTtagPlaceholder = 0x22;

View File

@ -56,6 +56,7 @@ SOURCES += \
pcaprxstats.cpp \ pcaprxstats.cpp \
pcaptxstats.cpp \ pcaptxstats.cpp \
pcaptxthread.cpp \ pcaptxthread.cpp \
pcaptxttagstats.cpp \
bsdhostdevice.cpp \ bsdhostdevice.cpp \
bsdport.cpp \ bsdport.cpp \
linuxhostdevice.cpp \ linuxhostdevice.cpp \

View File

@ -34,6 +34,7 @@ PcapPort::PcapPort(int id, const char *device)
transmitter_ = new PcapTransmitter(device, streamStats_); transmitter_ = new PcapTransmitter(device, streamStats_);
capturer_ = new PortCapturer(device); capturer_ = new PortCapturer(device);
emulXcvr_ = new EmulationTransceiver(device, deviceManager_); emulXcvr_ = new EmulationTransceiver(device, deviceManager_);
txTtagStatsPoller_ = new PcapTxTtagStats(device, id);
rxStatsPoller_ = new PcapRxStats(device, streamStats_, id); rxStatsPoller_ = new PcapRxStats(device, streamStats_, id);
if (!monitorRx_->handle() || !monitorTx_->handle()) if (!monitorRx_->handle() || !monitorTx_->handle())
@ -85,6 +86,9 @@ PcapPort::~PcapPort()
if (monitorTx_) if (monitorTx_)
monitorTx_->stop(); monitorTx_->stop();
txTtagStatsPoller_->stop();
delete txTtagStatsPoller_;
rxStatsPoller_->stop(); rxStatsPoller_->stop();
delete rxStatsPoller_; delete rxStatsPoller_;
@ -145,8 +149,10 @@ bool PcapPort::setRateAccuracy(AbstractPort::Accuracy accuracy)
void PcapPort::updateStreamStats() void PcapPort::updateStreamStats()
{ {
// XXX: PcapTxThread already does this at the end of transmit; we // XXX: PcapTxThread already does this at the end of transmit; we
// just dump rx stats poller debug stats here // just dump tx/rx stats poller debug stats here
qDebug("port %d txTtagStatsPoller: %s",
id(), qUtf8Printable(txTtagStatsPoller_->debugStats()));
qDebug("port %d rxStatsPoller: %s", qDebug("port %d rxStatsPoller: %s",
id(), qUtf8Printable(rxStatsPoller_->debugStats())); id(), qUtf8Printable(rxStatsPoller_->debugStats()));
} }
@ -170,6 +176,8 @@ bool PcapPort::startStreamStatsTracking()
{ {
if (!transmitter_->setStreamStatsTracking(true)) if (!transmitter_->setStreamStatsTracking(true))
goto _tx_fail; goto _tx_fail;
if (!txTtagStatsPoller_->start())
goto _tx_ttag_fail;
if (!rxStatsPoller_->start()) if (!rxStatsPoller_->start())
goto _rx_fail; goto _rx_fail;
/* /*
@ -183,22 +191,28 @@ bool PcapPort::startStreamStatsTracking()
return true; return true;
_rx_fail: _rx_fail:
txTtagStatsPoller_->stop();
_tx_ttag_fail:
transmitter_->setStreamStatsTracking(false); transmitter_->setStreamStatsTracking(false);
_tx_fail: _tx_fail:
qWarning("failed to start stream stats tracking"); qWarning("failed to start stream stats tracking");
return false; return false;
} }
// FIXME: stop everything possible, don't revert in case of error
bool PcapPort::stopStreamStatsTracking() bool PcapPort::stopStreamStatsTracking()
{ {
if (!transmitter_->setStreamStatsTracking(false)) if (!transmitter_->setStreamStatsTracking(false))
goto _tx_fail; goto _tx_fail;
if (!txTtagStatsPoller_->stop())
goto _tx_ttag_fail;
if (!rxStatsPoller_->stop()) if (!rxStatsPoller_->stop())
goto _rx_fail; goto _rx_fail;
return true; return true;
_rx_fail: _rx_fail:
transmitter_->setStreamStatsTracking(true); _tx_ttag_fail:
transmitter_->setStreamStatsTracking(true); // FIXME: needed?
_tx_fail: _tx_fail:
qWarning("failed to stop stream stats tracking"); qWarning("failed to stop stream stats tracking");
return false; return false;

View File

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "abstractport.h" #include "abstractport.h"
#include "pcapextra.h" #include "pcapextra.h"
#include "pcaprxstats.h" #include "pcaprxstats.h"
#include "pcaptxttagstats.h"
#include "pcapsession.h" #include "pcapsession.h"
#include "pcaptransmitter.h" #include "pcaptransmitter.h"
@ -177,6 +178,7 @@ private:
PcapTransmitter *transmitter_; PcapTransmitter *transmitter_;
PortCapturer *capturer_; PortCapturer *capturer_;
EmulationTransceiver *emulXcvr_; EmulationTransceiver *emulXcvr_;
PcapTxTtagStats *txTtagStatsPoller_;
PcapRxStats *rxStatsPoller_; PcapRxStats *rxStatsPoller_;
static pcap_if_t *deviceList_; static pcap_if_t *deviceList_;

234
server/pcaptxttagstats.cpp Normal file
View File

@ -0,0 +1,234 @@
/*
Copyright (C) 2023 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 "pcaptxttagstats.h"
#include "pcapextra.h"
#include "../common/sign.h"
#define Xnotify qWarning // FIXME
PcapTxTtagStats::PcapTxTtagStats(const char *device, int id)
: id_(id)
{
setObjectName(QString("TxTtagStats:%1").arg(device));
device_ = QString::fromLatin1(device);
}
void PcapTxTtagStats::run()
{
int flags = PCAP_OPENFLAG_PROMISCUOUS;
char errbuf[PCAP_ERRBUF_SIZE] = "";
struct bpf_program bpf;
const int optimize = 1;
QString capture_filter = QString("(ether[len - 5:5] == 0x%1%2)")
.arg(SignProtocol::kTypeLenTtag, 0, BASE_HEX)
.arg(SignProtocol::magic(), 0, BASE_HEX);
qDebug("In %s", __PRETTY_FUNCTION__);
qDebug("pcap-filter: %s", qPrintable(capture_filter));
handle_ = pcap_open_live(qPrintable(device_), 65535,
flags, 100 /* ms */, errbuf);
if (!handle_) {
if (flags && QString(errbuf).contains("promiscuous")) {
Xnotify("Unable to set promiscuous mode on <%s> - "
"stream stats time tracking will not work", qPrintable(device_));
goto _exit;
}
else {
Xnotify("Unable to open <%s> [%s] - stream stats rx will not work",
qPrintable(device_), errbuf);
goto _exit;
}
}
#ifdef Q_OS_WIN32
// pcap_setdirection() API is not supported in Windows.
// NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1
// but since we would like to work with previous versions of WinPcap
// also, we assume the API does not exist
isDirectional_ = false;
#else
if (pcap_setdirection(handle_, PCAP_D_OUT) < 0) {
qDebug("TxTtagStats: Error setting OUT direction %s: %s\n",
qPrintable(device_), pcap_geterr(handle_));
isDirectional_ = false;
}
#endif
if (pcap_compile(handle_, &bpf, qPrintable(capture_filter),
optimize, 0) < 0) {
qWarning("%s: error compiling filter: %s", qPrintable(device_),
pcap_geterr(handle_));
goto _skip_filter;
}
if (pcap_setfilter(handle_, &bpf) < 0) {
qWarning("%s: error setting filter: %s", qPrintable(device_),
pcap_geterr(handle_));
goto _skip_filter;
}
_skip_filter:
PcapSession::preRun();
state_ = kRunning;
while (1) {
int ret;
struct pcap_pkthdr *hdr;
const uchar *data;
ret = pcap_next_ex(handle_, &hdr, &data);
switch (ret) {
case 1: {
uint ttagId;
uint guid;
if (SignProtocol::packetTtagId(data, hdr->caplen,
&ttagId, &guid)) {
// TODO: store packet timestamp with ttag/guid
qDebug("XXXXX %ld:%ld ttag %u guid %u",
hdr->ts.tv_sec, hdr->ts.tv_usec, ttagId, guid);
}
break;
}
case 0:
// timeout: just go back to the loop
break;
case -1:
qWarning("%s: error reading packet (%d): %s",
__PRETTY_FUNCTION__, ret, pcap_geterr(handle_));
break;
case -2:
qDebug("%s: Loop/signal break or some other error",
__PRETTY_FUNCTION__);
break;
default:
qWarning("%s: Unexpected return value %d",
__PRETTY_FUNCTION__, ret);
stop_ = true;
}
if (stop_) {
qDebug("user requested txTtagStats stop");
break;
}
}
PcapSession::postRun();
pcap_close(handle_);
handle_ = NULL;
stop_ = false;
_exit:
state_ = kFinished;
}
bool PcapTxTtagStats::start()
{
if (state_ == kRunning) {
qWarning("TxTtagStats start requested but is already running!");
goto _exit;
}
state_ = kNotStarted;
PcapSession::start();
while (state_ == kNotStarted)
QThread::msleep(10);
_exit:
return true;
}
bool PcapTxTtagStats::stop()
{
if (state_ == kRunning) {
stop_ = true;
PcapSession::stop(handle_);
while (state_ == kRunning)
QThread::msleep(10);
}
else
qWarning("TxTtagStats stop requested but is not running!");
return true;
}
bool PcapTxTtagStats::isRunning()
{
return (state_ == kRunning);
}
bool PcapTxTtagStats::isDirectional()
{
return isDirectional_;
}
// FIXME: move to PcapSession
// XXX: Implemented as reset on read
QString PcapTxTtagStats::debugStats()
{
QString dbgStats;
#ifdef Q_OS_WIN32
static_assert(sizeof(struct pcap_stat) == 6*sizeof(uint),
"pcap_stat has less or more than 6 values");
int size;
struct pcap_stat incPcapStats;
struct pcap_stat *pcapStats = pcap_stats_ex(handle_, &size);
if (pcapStats && (uint(size) >= 6*sizeof(uint))) {
incPcapStats.ps_recv = pcapStats->ps_recv - lastPcapStats_.ps_recv;
incPcapStats.ps_drop = pcapStats->ps_drop - lastPcapStats_.ps_drop;
incPcapStats.ps_ifdrop = pcapStats->ps_ifdrop - lastPcapStats_.ps_ifdrop;
incPcapStats.ps_capt = pcapStats->ps_capt - lastPcapStats_.ps_capt;
incPcapStats.ps_sent = pcapStats->ps_sent - lastPcapStats_.ps_sent;
incPcapStats.ps_netdrop = pcapStats->ps_netdrop - lastPcapStats_.ps_netdrop;
dbgStats = QString("recv: %1 drop: %2 ifdrop: %3 "
"capt: %4 sent: %5 netdrop: %6")
.arg(incPcapStats.ps_recv)
.arg(incPcapStats.ps_drop)
.arg(incPcapStats.ps_ifdrop)
.arg(incPcapStats.ps_capt)
.arg(incPcapStats.ps_sent)
.arg(incPcapStats.ps_netdrop);
lastPcapStats_ = *pcapStats;
} else {
dbgStats = QString("error reading pcap stats: %1")
.arg(pcap_geterr(handle_));
}
#else
struct pcap_stat pcapStats;
struct pcap_stat incPcapStats;
int ret = pcap_stats(handle_, &pcapStats);
if (ret == 0) {
incPcapStats.ps_recv = pcapStats.ps_recv - lastPcapStats_.ps_recv;
incPcapStats.ps_drop = pcapStats.ps_drop - lastPcapStats_.ps_drop;
incPcapStats.ps_ifdrop = pcapStats.ps_ifdrop - lastPcapStats_.ps_ifdrop;
dbgStats = QString("recv: %1 drop: %2 ifdrop: %3")
.arg(incPcapStats.ps_recv)
.arg(incPcapStats.ps_drop)
.arg(incPcapStats.ps_ifdrop);
lastPcapStats_ = pcapStats;
} else {
dbgStats = QString("error reading pcap stats: %1")
.arg(pcap_geterr(handle_));
}
#endif
return dbgStats;
}

55
server/pcaptxttagstats.h Normal file
View File

@ -0,0 +1,55 @@
/*
Copyright (C) 2023 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 _PCAP_TX_TTAG_H
#define _PCAP_TX_TTAG_H
#include "pcapsession.h"
class PcapTxTtagStats: public PcapSession
{
public:
PcapTxTtagStats(const char *device, int id);
void run();
bool start();
bool stop();
bool isRunning();
bool isDirectional();
QString debugStats();
private:
enum State {
kNotStarted,
kRunning,
kFinished
};
QString device_;
bool isDirectional_{true};
pcap_t *handle_{nullptr};
volatile State state_{kNotStarted};
volatile bool stop_{false};
int id_; // FIXME: needed?
struct pcap_stat lastPcapStats_;
};
#endif