/* Copyright (C) 2016 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 "pcaprxstats.h" #include "pcapextra.h" #include "../common/sign.h" #define Xnotify qWarning // FIXME PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats) : streamStats_(portStreamStats) { device_ = QString::fromLatin1(device); stop_ = false; state_ = kNotStarted; isDirectional_ = true; handle_ = NULL; } pcap_t* PcapRxStats::handle() { return handle_; } void PcapRxStats::run() { int flags = PCAP_OPENFLAG_PROMISCUOUS; char errbuf[PCAP_ERRBUF_SIZE] = ""; struct bpf_program bpf; const int optimize = 1; QString capture_filter = QString("(ether[len - 4:4] == 0x%1)").arg( SignProtocol::magic(), 0, BASE_HEX); // XXX: Exclude ICMP packets which contain an embedded signed packet // For now we check upto 4 vlan tags capture_filter.append( "and not (" "icmp or " "(vlan and icmp) or " "(vlan and icmp) or " "(vlan and icmp) or " "(vlan and icmp) " ")"); qDebug("In %s", __PRETTY_FUNCTION__); handle_ = pcap_open_live(qPrintable(device_), 65535, flags, 100 /* ms */, errbuf); if (handle_ == NULL) { if (flags && QString(errbuf).contains("promiscuous")) { Xnotify("Unable to set promiscuous mode on <%s> - " "stream stats rx will not work", qPrintable(device_)); goto _exit; } else { Xnotify("Unable to open <%s> [%s] - stream stats rx will not work", qPrintable(device_), errbuf); goto _exit; } } #ifdef Q_OS_WIN32 // pcap_setdirection() API is not supported in Windows. // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 // but since we would like to work with previous versions of WinPcap // also, we assume the API does not exist isDirectional_ = false; #else if (pcap_setdirection(handle_, PCAP_D_IN) < 0) { qDebug("RxStats: Error setting IN direction %s: %s\n", qPrintable(device_), pcap_geterr(handle_)); isDirectional_ = false; } #endif if (pcap_compile(handle_, &bpf, qPrintable(capture_filter), optimize, 0) < 0) { qWarning("%s: error compiling filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } if (pcap_setfilter(handle_, &bpf) < 0) { qWarning("%s: error setting filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } _skip_filter: PcapSession::preRun(); state_ = kRunning; while (1) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle_, &hdr, &data); switch (ret) { case 1: { uint guid; if (SignProtocol::packetGuid(data, hdr->caplen, &guid)) { streamStats_[guid].rx_pkts++; streamStats_[guid].rx_bytes += hdr->caplen; } break; } case 0: // timeout: just go back to the loop break; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: qDebug("Loop/signal break or some other error"); break; default: qWarning("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); stop_ = true; } if (stop_) { qDebug("user requested rxstats stop"); break; } } PcapSession::postRun(); pcap_close(handle_); handle_ = NULL; stop_ = false; _exit: state_ = kFinished; } bool PcapRxStats::start() { if (state_ == kRunning) { qWarning("RxStats start requested but is already running!"); goto _exit; } state_ = kNotStarted; PcapSession::start(); while (state_ == kNotStarted) QThread::msleep(10); _exit: return true; } bool PcapRxStats::stop() { if (state_ == kRunning) { stop_ = true; PcapSession::stop(handle_); while (state_ == kRunning) QThread::msleep(10); } else qWarning("RxStats stop requested but is not running!"); return true; } bool PcapRxStats::isRunning() { return (state_ == kRunning); } bool PcapRxStats::isDirectional() { return isDirectional_; }