Split the PCAP callback into 2 - one for Rx and one for Tx using pcap_setdirection()
This commit is contained in:
parent
9ac311f80f
commit
0c70668e56
@ -482,33 +482,57 @@ int StreamInfo::makePacket(uchar *buf, int bufMaxSize, int n)
|
||||
// ------------------ PortInfo --------------------
|
||||
//
|
||||
PortInfo::PortInfo(uint id, pcap_if_t *dev)
|
||||
: monitor(this)
|
||||
: monitorRx(this), monitorTx(this)
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
this->dev = dev;
|
||||
devHandle = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS ,
|
||||
|
||||
/*
|
||||
* Get 2 device handles - one for rx and one for tx. If we use only
|
||||
* one handle for both rx and tx anythin that we tx using the single
|
||||
* handle is not received back to us
|
||||
*/
|
||||
devHandleRx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS ,
|
||||
1000 /*ms*/, errbuf);
|
||||
if (devHandle == NULL)
|
||||
if (devHandleRx == NULL)
|
||||
{
|
||||
qDebug("Error opening port %s: %s\n",
|
||||
dev->name, pcap_geterr(devHandle));
|
||||
dev->name, pcap_geterr(devHandleRx));
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (pcap_setdirection(devHandle, PCAP_D_IN)<0)
|
||||
if (pcap_setdirection(devHandleRx, PCAP_D_IN)<0)
|
||||
{
|
||||
qDebug("[%s] Error setting direction inbound only\n", dev->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* By default, put the interface in statistics mode */
|
||||
if (pcap_setmode(devHandle, MODE_STAT)<0)
|
||||
if (pcap_setmode(devHandleRx, MODE_STAT)<0)
|
||||
{
|
||||
qDebug("Error setting statistics mode.\n");
|
||||
}
|
||||
|
||||
devHandleTx = pcap_open_live(dev->name, 0, PCAP_OPENFLAG_PROMISCUOUS ,
|
||||
1000 /*ms*/, errbuf);
|
||||
if (devHandleTx == NULL)
|
||||
{
|
||||
qDebug("Error opening port %s: %s\n",
|
||||
dev->name, pcap_geterr(devHandleTx));
|
||||
}
|
||||
|
||||
if (pcap_setdirection(devHandleTx, PCAP_D_OUT)<0)
|
||||
{
|
||||
qDebug("[%s] Error setting direction outbound only\n", dev->name);
|
||||
}
|
||||
|
||||
/* By default, put the interface in statistics mode */
|
||||
if (pcap_setmode(devHandleTx, MODE_STAT)<0)
|
||||
{
|
||||
qDebug("Error setting statistics mode.\n");
|
||||
}
|
||||
|
||||
d.mutable_port_id()->set_id(id);
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
d.set_name(QString("if%1").arg(id).toAscii().constData());
|
||||
#else
|
||||
@ -517,6 +541,8 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev)
|
||||
else
|
||||
d.set_name(QString("if%1").arg(id).toAscii().constData());
|
||||
#endif
|
||||
d.set_name(d.name()+pcap_datalink_val_to_name(pcap_datalink(devHandleRx)));
|
||||
|
||||
if (dev->description)
|
||||
d.set_description(dev->description);
|
||||
d.set_is_enabled(true); // FIXME(MED):check
|
||||
@ -534,7 +560,8 @@ PortInfo::PortInfo(uint id, pcap_if_t *dev)
|
||||
isSendQueueDirty=true;
|
||||
|
||||
// Start the monitor thread
|
||||
monitor.start();
|
||||
monitorRx.start();
|
||||
monitorTx.start();
|
||||
}
|
||||
|
||||
void PortInfo::update()
|
||||
@ -612,11 +639,13 @@ void PortInfo::startTransmit()
|
||||
uint bytes, pkts;
|
||||
|
||||
// TODO(HI): Stream Mode - one pass/continuous
|
||||
bytes = pcap_sendqueue_transmit(devHandle, sendQueue, false);
|
||||
// NOTE: Transmit on the Rx Handle so that we can receive it back
|
||||
// on the Tx Handle to do stats
|
||||
bytes = pcap_sendqueue_transmit(devHandleRx, sendQueue, false);
|
||||
if (bytes < sendQueue->len)
|
||||
{
|
||||
qDebug("port %d: sent (%d/%d) error %s. TxStats may be inconsistent",
|
||||
id(), bytes, sendQueue->len, pcap_geterr(devHandle));
|
||||
id(), bytes, sendQueue->len, pcap_geterr(devHandleTx));
|
||||
|
||||
// parse sendqueue using 'bytes' to get actual pkts sent
|
||||
#if 0
|
||||
@ -649,11 +678,6 @@ void PortInfo::startTransmit()
|
||||
// together
|
||||
pcapExtra.txPkts += pkts;
|
||||
pcapExtra.txBytes += bytes;
|
||||
#else
|
||||
// We don't have a regular stats callback function here, so update
|
||||
// Port TxStats directly here
|
||||
stats.txPkts += pkts;
|
||||
stats.txBytes += bytes;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -670,7 +694,27 @@ void PortInfo::resetStats()
|
||||
// ------------------ PortMonitor -------------------
|
||||
//
|
||||
|
||||
PortInfo::PortMonitor::PortMonitor(PortInfo *port)
|
||||
PortInfo::PortMonitorRx::PortMonitorRx(PortInfo *port)
|
||||
{
|
||||
this->port = port;
|
||||
#ifdef Q_OS_WIN32
|
||||
{
|
||||
int sz = sizeof(PACKET_OID_DATA) + sizeof(quint64) + 4;
|
||||
//oidData = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,
|
||||
//sizeof(PACKET_OID_DATA) + sizeof(quint64) - 1);
|
||||
oidData = (PPACKET_OID_DATA) malloc(sz);
|
||||
if (oidData)
|
||||
{
|
||||
memset(oidData, 0, sz);
|
||||
oidData->Length=sizeof(quint64);
|
||||
}
|
||||
else
|
||||
qFatal("failed to alloc oidData");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PortInfo::PortMonitorTx::PortMonitorTx(PortInfo *port)
|
||||
{
|
||||
this->port = port;
|
||||
#ifdef Q_OS_WIN32
|
||||
@ -691,7 +735,60 @@ PortInfo::PortMonitor::PortMonitor(PortInfo *port)
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
void PortInfo::PortMonitor::callback(u_char *state,
|
||||
void PortInfo::PortMonitorRx::callbackRx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data)
|
||||
{
|
||||
// This is the WinPcap Callback - which is a 'stats mode' callback
|
||||
|
||||
uint usec;
|
||||
PortInfo *port = (PortInfo*) state;
|
||||
|
||||
quint64 pkts;
|
||||
quint64 bytes;
|
||||
|
||||
// Update RxStats and RxRates using PCAP data
|
||||
pkts = *((quint64*)(pkt_data + 0));
|
||||
bytes = *((quint64*)(pkt_data + 8));
|
||||
|
||||
// Note: PCAP reported bytes includes ETH_FRAME_HDR_SIZE - adjust for it
|
||||
bytes -= pkts * ETH_FRAME_HDR_SIZE;
|
||||
|
||||
usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 +
|
||||
(header->ts.tv_usec - port->lastTsRx.tv_usec);
|
||||
port->stats.rxPps = (pkts * 1000000) / usec;
|
||||
port->stats.rxBps = (bytes * 1000000) / usec;
|
||||
|
||||
port->stats.rxPkts += pkts;
|
||||
port->stats.rxBytes += bytes;
|
||||
|
||||
// Store curr timestamp as last timestamp
|
||||
port->lastTsRx.tv_sec = header->ts.tv_sec;
|
||||
port->lastTsRx.tv_usec = header->ts.tv_usec;
|
||||
|
||||
#if 0
|
||||
for (int i=0; i < 16; i++)
|
||||
{
|
||||
qDebug("%02x ", pkt_data[i]);
|
||||
}
|
||||
qDebug("{%d: %llu, %llu}\n", port->id(),
|
||||
pkts, bytes);
|
||||
qDebug("[%d: pkts : %llu]\n", port->id(), port->stats.rxPkts);
|
||||
qDebug("[%d: bytes: %llu]\n", port->id(), port->stats.rxBytes);
|
||||
#endif
|
||||
|
||||
// Retreive NIC stats
|
||||
#ifdef Q_OS_WIN32
|
||||
port->monitorRx.oidData->Oid = OID_GEN_RCV_OK;
|
||||
if (PacketRequest(port->devHandleRx->adapter, 0, port->monitorRx.oidData))
|
||||
{
|
||||
if (port->monitorRx.oidData->Length <= sizeof(port->stats.rxPktsNic))
|
||||
memcpy((void*)&port->stats.rxPktsNic,
|
||||
(void*)port->monitorRx.oidData->Data,
|
||||
port->monitorRx.oidData->Length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void PortInfo::PortMonitorTx::callbackTx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data)
|
||||
{
|
||||
// This is the WinPcap Callback - which is a 'stats mode' callback
|
||||
@ -717,15 +814,25 @@ void PortInfo::PortMonitor::callback(u_char *state,
|
||||
port->stats.rxPkts += pkts;
|
||||
port->stats.rxBytes += bytes;
|
||||
|
||||
// Update TxStats from PcapExtra
|
||||
// Since WinPCAP (due to NDIS limitation) cannot distinguish between
|
||||
// rx/tx packets, pcap stats are not of much use - for the tx stats
|
||||
// update from PcapExtra
|
||||
|
||||
pkts = port->pcapExtra.txPkts - port->stats.txPkts;
|
||||
bytes = port->pcapExtra.txBytes - port->stats.txBytes;
|
||||
|
||||
// Use the pcap timestamp for rate calculation though
|
||||
usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 +
|
||||
(header->ts.tv_usec - port->lastTs.tv_usec);
|
||||
port->stats.txPps = (pkts * 1000000) / usec;
|
||||
port->stats.txBps = (bytes * 1000000) / usec;
|
||||
|
||||
port->stats.txPkts = port->pcapExtra.txPkts;
|
||||
port->stats.txBytes = port->pcapExtra.txBytes;
|
||||
|
||||
//! \TODO TxRates
|
||||
|
||||
// Store curr timestamp as last timestamp
|
||||
port->lastTs.tv_sec = header->ts.tv_sec;
|
||||
port->lastTs.tv_usec = header->ts.tv_usec;
|
||||
port->lastTsTx.tv_sec = header->ts.tv_sec;
|
||||
port->lastTsTx.tv_usec = header->ts.tv_usec;
|
||||
|
||||
#if 0
|
||||
for (int i=0; i < 16; i++)
|
||||
@ -740,26 +847,18 @@ void PortInfo::PortMonitor::callback(u_char *state,
|
||||
|
||||
// Retreive NIC stats
|
||||
#ifdef Q_OS_WIN32
|
||||
port->monitor.oidData->Oid = OID_GEN_RCV_OK;
|
||||
if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData))
|
||||
port->monitorTx.oidData->Oid = OID_GEN_XMIT_OK;
|
||||
if (PacketRequest(port->devHandleTx->adapter, 0, port->monitorTx.oidData))
|
||||
{
|
||||
if (port->monitor.oidData->Length <= sizeof(port->stats.rxPktsNic))
|
||||
memcpy((void*)&port->stats.rxPktsNic,
|
||||
(void*)port->monitor.oidData->Data,
|
||||
port->monitor.oidData->Length);
|
||||
}
|
||||
port->monitor.oidData->Oid = OID_GEN_XMIT_OK;
|
||||
if (PacketRequest(port->devHandle->adapter, 0, port->monitor.oidData))
|
||||
{
|
||||
if (port->monitor.oidData->Length <= sizeof(port->stats.txPktsNic))
|
||||
if (port->monitorTx.oidData->Length <= sizeof(port->stats.txPktsNic))
|
||||
memcpy((void*)&port->stats.txPktsNic,
|
||||
(void*)port->monitor.oidData->Data,
|
||||
port->monitor.oidData->Length);
|
||||
(void*)port->monitorTx.oidData->Data,
|
||||
port->monitorTx.oidData->Length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void PortInfo::PortMonitor::callback(u_char *state,
|
||||
void PortInfo::PortMonitorRx::callbackRx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data)
|
||||
{
|
||||
// This is the LibPcap Callback - which is a 'capture mode' callback
|
||||
@ -772,8 +871,8 @@ void PortInfo::PortMonitor::callback(u_char *state,
|
||||
quint64 bytes;
|
||||
|
||||
// Update RxStats and RxRates using PCAP data
|
||||
usec = (header->ts.tv_sec - port->lastTs.tv_sec) * 1000000 +
|
||||
(header->ts.tv_usec - port->lastTs.tv_usec);
|
||||
usec = (header->ts.tv_sec - port->lastTsRx.tv_sec) * 1000000 +
|
||||
(header->ts.tv_usec - port->lastTsRx.tv_usec);
|
||||
// TODO(rate)
|
||||
#if 0
|
||||
port->stats.rxPps = (pkts * 1000000) / usec;
|
||||
@ -785,25 +884,78 @@ void PortInfo::PortMonitor::callback(u_char *state,
|
||||
port->stats.rxPkts++;
|
||||
port->stats.rxBytes += header->len;
|
||||
|
||||
// NOTE: Port TxStats are updated by Port Transmit Function itself
|
||||
// since this callback is called only when a packet is received
|
||||
// Store curr timestamp as last timestamp
|
||||
port->lastTsRx.tv_sec = header->ts.tv_sec;
|
||||
port->lastTsRx.tv_usec = header->ts.tv_usec;
|
||||
}
|
||||
|
||||
//! \TODO TxRates
|
||||
void PortInfo::PortMonitorTx::callbackTx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data)
|
||||
{
|
||||
// This is the LibPcap Callback - which is a 'capture mode' callback
|
||||
// This callback is called once for EVERY packet
|
||||
|
||||
uint usec;
|
||||
PortInfo *port = (PortInfo*) state;
|
||||
|
||||
quint64 pkts;
|
||||
quint64 bytes;
|
||||
|
||||
// Update TxStats and TxRates using PCAP data
|
||||
usec = (header->ts.tv_sec - port->lastTsTx.tv_sec) * 1000000 +
|
||||
(header->ts.tv_usec - port->lastTsTx.tv_usec);
|
||||
// TODO(rate)
|
||||
#if 0
|
||||
port->stats.txPps = (pkts * 1000000) / usec;
|
||||
port->stats.txBps = (bytes * 1000000) / usec;
|
||||
#endif
|
||||
|
||||
// Note: For a 'capture callback' PCAP reported bytes DOES NOT include
|
||||
// ETH_FRAME_HDR_SIZE - so don't adjust for it
|
||||
|
||||
port->stats.txPkts++;
|
||||
port->stats.txBytes += header->len;
|
||||
|
||||
// Store curr timestamp as last timestamp
|
||||
port->lastTs.tv_sec = header->ts.tv_sec;
|
||||
port->lastTs.tv_usec = header->ts.tv_usec;
|
||||
port->lastTsTx.tv_sec = header->ts.tv_sec;
|
||||
port->lastTsTx.tv_usec = header->ts.tv_usec;
|
||||
}
|
||||
#endif
|
||||
void PortInfo::PortMonitor::run()
|
||||
void PortInfo::PortMonitorRx::run()
|
||||
{
|
||||
int ret;
|
||||
|
||||
qDebug("before pcap_loop\n");
|
||||
qDebug("before pcap_loop rx \n");
|
||||
|
||||
/* Start the main loop */
|
||||
ret = pcap_loop(port->devHandle, -1, &PortInfo::PortMonitor::callback,
|
||||
(u_char*) port);
|
||||
ret = pcap_loop(port->devHandleRx, -1,
|
||||
&PortInfo::PortMonitorRx::callbackRx, (u_char*) port);
|
||||
|
||||
switch(ret)
|
||||
{
|
||||
case 0:
|
||||
qDebug("Unexpected return from pcap_loop()\n");
|
||||
break;
|
||||
case -1:
|
||||
qDebug("Unsolicited (error) return from pcap_loop()\n");
|
||||
break;
|
||||
case -2:
|
||||
qDebug("Solicited return from pcap_loop()\n");
|
||||
break;
|
||||
default:
|
||||
qDebug("Unknown return value from pcap_loop()\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PortInfo::PortMonitorTx::run()
|
||||
{
|
||||
int ret;
|
||||
|
||||
qDebug("before pcap_loopTx\n");
|
||||
|
||||
/* Start the main loop */
|
||||
ret = pcap_loop(port->devHandleTx, -1,
|
||||
&PortInfo::PortMonitorTx::callbackTx, (u_char*) port);
|
||||
|
||||
switch(ret)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ class PortInfo
|
||||
{
|
||||
friend class MyService;
|
||||
|
||||
class PortMonitor: public QThread
|
||||
class PortMonitorRx: public QThread
|
||||
{
|
||||
friend class PortInfo;
|
||||
|
||||
@ -62,8 +62,23 @@ class PortInfo
|
||||
PPACKET_OID_DATA oidData;
|
||||
#endif
|
||||
public:
|
||||
PortMonitor(PortInfo *port);
|
||||
static void callback(u_char *state,
|
||||
PortMonitorRx(PortInfo *port);
|
||||
static void callbackRx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data);
|
||||
void run();
|
||||
};
|
||||
|
||||
class PortMonitorTx: public QThread
|
||||
{
|
||||
friend class PortInfo;
|
||||
|
||||
PortInfo *port;
|
||||
#ifdef Q_OS_WIN32
|
||||
PPACKET_OID_DATA oidData;
|
||||
#endif
|
||||
public:
|
||||
PortMonitorTx(PortInfo *port);
|
||||
static void callbackTx(u_char *state,
|
||||
const struct pcap_pkthdr *header, const u_char *pkt_data);
|
||||
void run();
|
||||
};
|
||||
@ -104,15 +119,18 @@ class PortInfo
|
||||
};
|
||||
|
||||
pcap_if_t *dev;
|
||||
pcap_t *devHandle;
|
||||
pcap_t *devHandleRx;
|
||||
pcap_t *devHandleTx;
|
||||
pcap_send_queue *sendQueue;
|
||||
bool isSendQueueDirty;
|
||||
PcapExtra pcapExtra;
|
||||
PortMonitor monitor;
|
||||
PortMonitorRx monitorRx;
|
||||
PortMonitorTx monitorTx;
|
||||
|
||||
struct PortStats epochStats;
|
||||
struct PortStats stats;
|
||||
struct timeval lastTs; //! used for Rate Stats calculations
|
||||
struct timeval lastTsRx; //! used for Rate Stats calculations
|
||||
struct timeval lastTsTx; //! used for Rate Stats calculations
|
||||
|
||||
/*! StreamInfo::d::stream_id and index into streamList[] are NOT same! */
|
||||
QList<StreamInfo> streamList;
|
||||
|
Loading…
Reference in New Issue
Block a user