/* 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 */ #include "winpcapport.h" #include "interfaceinfo.h" #include #include #ifdef Q_OS_WIN32 #include #include 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( adapter->PhysicalAddress) >> 16; } else interfaceInfo_->mac = 0; #define SOCKET_ADDRESS_FAMILY(x) \ (x.lpSockaddr->sa_family) #define SOCKET_ADDRESS_IP4(x) \ (qFromBigEndian(((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