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:
parent
f1cfaa6e89
commit
682d0cc5c9
@ -241,3 +241,25 @@ bool SignProtocol::packetGuid(const uchar *pkt, int pktLen, uint *guid)
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ public:
|
||||
|
||||
static quint32 magic();
|
||||
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 quint8 kTypeLenTtagPlaceholder = 0x22;
|
||||
|
@ -56,6 +56,7 @@ SOURCES += \
|
||||
pcaprxstats.cpp \
|
||||
pcaptxstats.cpp \
|
||||
pcaptxthread.cpp \
|
||||
pcaptxttagstats.cpp \
|
||||
bsdhostdevice.cpp \
|
||||
bsdport.cpp \
|
||||
linuxhostdevice.cpp \
|
||||
|
@ -34,6 +34,7 @@ PcapPort::PcapPort(int id, const char *device)
|
||||
transmitter_ = new PcapTransmitter(device, streamStats_);
|
||||
capturer_ = new PortCapturer(device);
|
||||
emulXcvr_ = new EmulationTransceiver(device, deviceManager_);
|
||||
txTtagStatsPoller_ = new PcapTxTtagStats(device, id);
|
||||
rxStatsPoller_ = new PcapRxStats(device, streamStats_, id);
|
||||
|
||||
if (!monitorRx_->handle() || !monitorTx_->handle())
|
||||
@ -85,6 +86,9 @@ PcapPort::~PcapPort()
|
||||
if (monitorTx_)
|
||||
monitorTx_->stop();
|
||||
|
||||
txTtagStatsPoller_->stop();
|
||||
delete txTtagStatsPoller_;
|
||||
|
||||
rxStatsPoller_->stop();
|
||||
delete rxStatsPoller_;
|
||||
|
||||
@ -145,8 +149,10 @@ bool PcapPort::setRateAccuracy(AbstractPort::Accuracy accuracy)
|
||||
void PcapPort::updateStreamStats()
|
||||
{
|
||||
// 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",
|
||||
id(), qUtf8Printable(rxStatsPoller_->debugStats()));
|
||||
}
|
||||
@ -170,6 +176,8 @@ bool PcapPort::startStreamStatsTracking()
|
||||
{
|
||||
if (!transmitter_->setStreamStatsTracking(true))
|
||||
goto _tx_fail;
|
||||
if (!txTtagStatsPoller_->start())
|
||||
goto _tx_ttag_fail;
|
||||
if (!rxStatsPoller_->start())
|
||||
goto _rx_fail;
|
||||
/*
|
||||
@ -183,22 +191,28 @@ bool PcapPort::startStreamStatsTracking()
|
||||
return true;
|
||||
|
||||
_rx_fail:
|
||||
txTtagStatsPoller_->stop();
|
||||
_tx_ttag_fail:
|
||||
transmitter_->setStreamStatsTracking(false);
|
||||
_tx_fail:
|
||||
qWarning("failed to start stream stats tracking");
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: stop everything possible, don't revert in case of error
|
||||
bool PcapPort::stopStreamStatsTracking()
|
||||
{
|
||||
if (!transmitter_->setStreamStatsTracking(false))
|
||||
goto _tx_fail;
|
||||
if (!txTtagStatsPoller_->stop())
|
||||
goto _tx_ttag_fail;
|
||||
if (!rxStatsPoller_->stop())
|
||||
goto _rx_fail;
|
||||
return true;
|
||||
|
||||
_rx_fail:
|
||||
transmitter_->setStreamStatsTracking(true);
|
||||
_tx_ttag_fail:
|
||||
transmitter_->setStreamStatsTracking(true); // FIXME: needed?
|
||||
_tx_fail:
|
||||
qWarning("failed to stop stream stats tracking");
|
||||
return false;
|
||||
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "abstractport.h"
|
||||
#include "pcapextra.h"
|
||||
#include "pcaprxstats.h"
|
||||
#include "pcaptxttagstats.h"
|
||||
#include "pcapsession.h"
|
||||
#include "pcaptransmitter.h"
|
||||
|
||||
@ -177,6 +178,7 @@ private:
|
||||
PcapTransmitter *transmitter_;
|
||||
PortCapturer *capturer_;
|
||||
EmulationTransceiver *emulXcvr_;
|
||||
PcapTxTtagStats *txTtagStatsPoller_;
|
||||
PcapRxStats *rxStatsPoller_;
|
||||
|
||||
static pcap_if_t *deviceList_;
|
||||
|
234
server/pcaptxttagstats.cpp
Normal file
234
server/pcaptxttagstats.cpp
Normal 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
55
server/pcaptxttagstats.h
Normal 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
|
Loading…
Reference in New Issue
Block a user