ostinato/server/winpcapport.cpp
2021-11-07 19:05:11 +05:30

360 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;
interfaceInfo_->speed = adapter->TransmitLinkSpeed != quint64(-1) ?
adapter->TransmitLinkSpeed/1e6 : 0;
interfaceInfo_->mtu = adapter->Mtu;
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