Split the PCAP callback into 2 - one for Rx and one for Tx using pcap_setdirection()

This commit is contained in:
Srivats P. 2009-02-02 10:08:57 +00:00
parent 9ac311f80f
commit 0c70668e56
2 changed files with 224 additions and 54 deletions

View File

@ -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)
{

View File

@ -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;