db66d7c5ea
For now both winpcap and npcap are supported with the latter being experimentally supported till we get some feedback from users and confirm that things are all working fine with npcap. OID for link state has been changed to one that supports both. To check which is being used, run 'drone -v'. Fixes #236
355 lines
10 KiB
C++
355 lines
10 KiB
C++
/*
|
|
Copyright (C) 2010 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 "winpcapport.h"
|
|
|
|
#include "interfaceinfo.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QProcess>
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#include <ntddndis.h>
|
|
#include <ws2ipdef.h>
|
|
|
|
PIP_ADAPTER_ADDRESSES WinPcapPort::adapterList_ = NULL;
|
|
|
|
WinPcapPort::WinPcapPort(int id, const char *device, const char *description)
|
|
: PcapPort(id, device)
|
|
{
|
|
populateInterfaceInfo();
|
|
|
|
monitorRx_->stop();
|
|
monitorTx_->stop();
|
|
monitorRx_->wait();
|
|
monitorTx_->wait();
|
|
|
|
delete monitorRx_;
|
|
delete monitorTx_;
|
|
|
|
monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_);
|
|
monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_);
|
|
|
|
data_.set_description(description);
|
|
|
|
adapter_ = PacketOpenAdapter((CHAR*)device);
|
|
if (!adapter_)
|
|
qFatal("Unable to open adapter %s", device);
|
|
linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) +
|
|
sizeof(NDIS_LINK_STATE));
|
|
if (!linkStateOid_)
|
|
qFatal("failed to alloc oidData");
|
|
|
|
data_.set_is_exclusive_control(hasExclusiveControl());
|
|
minPacketSetSize_ = 256;
|
|
}
|
|
|
|
WinPcapPort::~WinPcapPort()
|
|
{
|
|
}
|
|
|
|
OstProto::LinkState WinPcapPort::linkState()
|
|
{
|
|
memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(NDIS_LINK_STATE));
|
|
|
|
linkStateOid_->Oid = OID_GEN_LINK_STATE;
|
|
linkStateOid_->Length = sizeof(NDIS_LINK_STATE);
|
|
|
|
// TODO: migrate to the npcap-only pcap_oid_get_request() when Ostinato
|
|
// stops supporting WinPcap
|
|
if (PacketRequest(adapter_, 0, linkStateOid_))
|
|
{
|
|
uint state;
|
|
|
|
if (linkStateOid_->Length == sizeof(NDIS_LINK_STATE))
|
|
{
|
|
memcpy((void*)&state,
|
|
(void*)(linkStateOid_->Data+sizeof(NDIS_OBJECT_HEADER)),
|
|
sizeof(state));
|
|
//qDebug("%s: state = %d", data_.description().c_str(), state);
|
|
if (state == 0)
|
|
linkState_ = OstProto::LinkStateUnknown;
|
|
else if (state == 1)
|
|
linkState_ = OstProto::LinkStateUp;
|
|
else if (state == 2)
|
|
linkState_ = OstProto::LinkStateDown;
|
|
}
|
|
else {
|
|
//qDebug("%s: link state fail", data_.description().c_str());
|
|
}
|
|
}
|
|
else {
|
|
//qDebug("%s: link state request fail", data_.description().c_str());
|
|
}
|
|
|
|
return linkState_;
|
|
}
|
|
|
|
bool WinPcapPort::hasExclusiveControl()
|
|
{
|
|
QString portName(adapter_->Name + strlen("\\Device\\NPF_"));
|
|
QString bindConfigFilePath(QCoreApplication::applicationDirPath()
|
|
+ "/bindconfig.exe");
|
|
int exitCode;
|
|
|
|
qDebug("%s: %s", __FUNCTION__, qPrintable(portName));
|
|
|
|
if (!QFile::exists(bindConfigFilePath))
|
|
return false;
|
|
|
|
exitCode = QProcess::execute(bindConfigFilePath,
|
|
QStringList() << "comp" << portName);
|
|
|
|
qDebug("%s: exit code %d", __FUNCTION__, exitCode);
|
|
|
|
if (exitCode == 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool WinPcapPort::setExclusiveControl(bool exclusive)
|
|
{
|
|
QString portName(adapter_->Name + strlen("\\Device\\NPF_"));
|
|
QString bindConfigFilePath(QCoreApplication::applicationDirPath()
|
|
+ "/bindconfig.exe");
|
|
QString status;
|
|
|
|
qDebug("%s: %s", __FUNCTION__, qPrintable(portName));
|
|
|
|
if (!QFile::exists(bindConfigFilePath))
|
|
return false;
|
|
|
|
status = exclusive ? "disable" : "enable";
|
|
|
|
QProcess::execute(bindConfigFilePath,
|
|
QStringList() << "comp" << portName << status);
|
|
|
|
updateNotes();
|
|
|
|
return (exclusive == hasExclusiveControl());
|
|
}
|
|
|
|
WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction,
|
|
AbstractPort::PortStats *stats)
|
|
: PcapPort::PortMonitor(device, direction, stats)
|
|
{
|
|
if (handle())
|
|
pcap_setmode(handle(), MODE_STAT);
|
|
}
|
|
|
|
void WinPcapPort::PortMonitor::run()
|
|
{
|
|
struct timeval lastTs;
|
|
quint64 lastTxPkts = 0;
|
|
quint64 lastTxBytes = 0;
|
|
|
|
qDebug("in %s", __PRETTY_FUNCTION__);
|
|
|
|
lastTs.tv_sec = 0;
|
|
lastTs.tv_usec = 0;
|
|
|
|
while (!stop_)
|
|
{
|
|
int ret;
|
|
struct pcap_pkthdr *hdr;
|
|
const uchar *data;
|
|
|
|
ret = pcap_next_ex(handle(), &hdr, &data);
|
|
switch (ret)
|
|
{
|
|
case 1:
|
|
{
|
|
quint64 pkts = *((quint64*)(data + 0));
|
|
quint64 bytes = *((quint64*)(data + 8));
|
|
|
|
// TODO: is it 12 or 16?
|
|
bytes -= pkts * 12;
|
|
|
|
uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 +
|
|
(hdr->ts.tv_usec - lastTs.tv_usec);
|
|
|
|
switch (direction())
|
|
{
|
|
case kDirectionRx:
|
|
stats_->rxPkts += pkts;
|
|
stats_->rxBytes += bytes;
|
|
stats_->rxPps = qRound64(pkts * 1e6 / usec);
|
|
stats_->rxBps = qRound64(bytes * 1e6 / usec);
|
|
break;
|
|
|
|
case kDirectionTx:
|
|
if (isDirectional())
|
|
{
|
|
stats_->txPkts += pkts;
|
|
stats_->txBytes += bytes;
|
|
}
|
|
else
|
|
{
|
|
// Assuming stats_->txXXX are updated externally
|
|
quint64 txPkts = stats_->txPkts;
|
|
quint64 txBytes = stats_->txBytes;
|
|
|
|
pkts = txPkts - lastTxPkts;
|
|
bytes = txBytes - lastTxBytes;
|
|
|
|
lastTxPkts = txPkts;
|
|
lastTxBytes = txBytes;
|
|
}
|
|
stats_->txPps = qRound64(pkts * 1e6 / usec);
|
|
stats_->txBps = qRound64(bytes * 1e6 / usec);
|
|
break;
|
|
|
|
default:
|
|
Q_ASSERT(false);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 0:
|
|
//qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__);
|
|
continue;
|
|
case -1:
|
|
qWarning("%s: error reading packet (%d): %s",
|
|
__PRETTY_FUNCTION__, ret, pcap_geterr(handle()));
|
|
break;
|
|
case -2:
|
|
qWarning("%s: error reading packet (%d): %s",
|
|
__PRETTY_FUNCTION__, ret, pcap_geterr(handle()));
|
|
break;
|
|
default:
|
|
qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret);
|
|
}
|
|
lastTs.tv_sec = hdr->ts.tv_sec;
|
|
lastTs.tv_usec = hdr->ts.tv_usec;
|
|
if (!stop_)
|
|
QThread::msleep(1000);
|
|
}
|
|
}
|
|
|
|
void WinPcapPort::populateInterfaceInfo()
|
|
{
|
|
if (!adapterList_) {
|
|
qWarning("Adapter List not available");
|
|
return;
|
|
}
|
|
|
|
PIP_ADAPTER_ADDRESSES adapter = adapterList_;
|
|
while (adapter && !QString(name()).endsWith(QString(adapter->AdapterName)))
|
|
adapter = adapter->Next;
|
|
|
|
if (!adapter) {
|
|
qWarning("Adapter info not found for %s", name());
|
|
return;
|
|
}
|
|
|
|
interfaceInfo_ = new InterfaceInfo;
|
|
if (adapter->PhysicalAddressLength == 6) {
|
|
interfaceInfo_->mac = qFromBigEndian<quint64>(
|
|
adapter->PhysicalAddress) >> 16;
|
|
}
|
|
else
|
|
interfaceInfo_->mac = 0;
|
|
|
|
#define SOCKET_ADDRESS_FAMILY(x) \
|
|
(x.lpSockaddr->sa_family)
|
|
|
|
#define SOCKET_ADDRESS_IP4(x) \
|
|
(qFromBigEndian<quint32>(((sockaddr_in*)(x.lpSockaddr))->sin_addr.S_un.S_addr));
|
|
|
|
#define SOCKET_ADDRESS_IP6(x) \
|
|
(UInt128(((PSOCKADDR_IN6)(x.lpSockaddr))->sin6_addr.u.Byte));
|
|
|
|
// We may have multiple gateways - use the first for each family
|
|
quint32 ip4Gateway = 0;
|
|
PIP_ADAPTER_GATEWAY_ADDRESS gateway = adapter->FirstGatewayAddress;
|
|
while (gateway) {
|
|
if (SOCKET_ADDRESS_FAMILY(gateway->Address) == AF_INET) {
|
|
ip4Gateway = SOCKET_ADDRESS_IP4(gateway->Address);
|
|
break;
|
|
}
|
|
gateway = gateway->Next;
|
|
}
|
|
UInt128 ip6Gateway(0, 0);
|
|
gateway = adapter->FirstGatewayAddress;
|
|
while (gateway) {
|
|
if (SOCKET_ADDRESS_FAMILY(gateway->Address) == AF_INET6) {
|
|
ip6Gateway = SOCKET_ADDRESS_IP6(gateway->Address);
|
|
break;
|
|
}
|
|
gateway = gateway->Next;
|
|
}
|
|
|
|
PIP_ADAPTER_UNICAST_ADDRESS ucast = adapter->FirstUnicastAddress;
|
|
while (ucast) {
|
|
if (SOCKET_ADDRESS_FAMILY(ucast->Address) == AF_INET) {
|
|
Ip4Config ip;
|
|
ip.address = SOCKET_ADDRESS_IP4(ucast->Address);
|
|
ip.prefixLength = ucast->OnLinkPrefixLength;
|
|
ip.gateway = ip4Gateway;
|
|
interfaceInfo_->ip4.append(ip);
|
|
}
|
|
else if (SOCKET_ADDRESS_FAMILY(ucast->Address) == AF_INET6) {
|
|
Ip6Config ip;
|
|
ip.address = SOCKET_ADDRESS_IP6(ucast->Address);
|
|
ip.prefixLength = ucast->OnLinkPrefixLength;
|
|
ip.gateway = ip6Gateway;
|
|
interfaceInfo_->ip6.append(ip);
|
|
}
|
|
ucast = ucast->Next;
|
|
}
|
|
#undef SOCKET_ADDRESS_FAMILY
|
|
#undef SOCKET_ADDRESS_IP4
|
|
#undef SOCKET_ADDRESS_IP6
|
|
}
|
|
|
|
void WinPcapPort::fetchHostNetworkInfo()
|
|
{
|
|
DWORD ret;
|
|
ULONG bufLen = 15*1024; // MS recommended starting size
|
|
|
|
while (1) {
|
|
adapterList_ = (IP_ADAPTER_ADDRESSES *) malloc(bufLen);
|
|
ret = GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_INCLUDE_ALL_INTERFACES
|
|
| GAA_FLAG_INCLUDE_GATEWAYS,
|
|
0, adapterList_, &bufLen);
|
|
if (ret == ERROR_BUFFER_OVERFLOW) {
|
|
free(adapterList_);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ret != NO_ERROR) {
|
|
free(adapterList_);
|
|
adapterList_ = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void WinPcapPort::freeHostNetworkInfo()
|
|
{
|
|
free(adapterList_);
|
|
adapterList_ = NULL;
|
|
}
|
|
|
|
#endif
|