1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
/*
SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "Capture.h"
#include <iostream>
#include <string>
#include <pcap/pcap.h>
#include "TimeStamps.h"
using namespace std::string_literals;
// Limit the amount of entries waiting in the queue to this size, to prevent
// the queue from getting too full.
static const int MaximumQueueSize = 1000;
void pcapDispatchCallback(uint8_t *user, const struct pcap_pkthdr *h, const uint8_t *bytes)
{
reinterpret_cast<Capture *>(user)->handlePacket(h, bytes);
}
Capture::Capture(const std::string &interface)
: m_interface(interface)
{
}
Capture::~Capture()
{
stop();
}
bool Capture::start()
{
auto device = m_interface.empty() ? (const char *)nullptr : m_interface.c_str();
char errorBuffer[PCAP_ERRBUF_SIZE];
m_pcap = pcap_create(device, errorBuffer);
if (!m_pcap) {
m_error = std::string(errorBuffer, PCAP_ERRBUF_SIZE);
return false;
}
pcap_set_timeout(m_pcap, 500);
pcap_set_snaplen(m_pcap, 100);
pcap_set_promisc(m_pcap, 0);
pcap_set_datalink(m_pcap, DLT_LINUX_SLL);
if (checkError(pcap_activate(m_pcap))) {
return false;
}
struct bpf_program filter;
if (checkError(pcap_compile(m_pcap, &filter, "tcp or udp", 1, PCAP_NETMASK_UNKNOWN))) {
return false;
}
if (checkError(pcap_setfilter(m_pcap, &filter))) {
pcap_freecode(&filter);
return false;
}
pcap_freecode(&filter);
m_thread = std::thread{&Capture::loop, this};
pthread_setname_np(m_thread.native_handle(), "capture");
m_running = true;
return true;
}
void Capture::stop()
{
if (!m_running) {
return;
}
m_running = false;
if (m_pcap) {
pcap_breakloop(m_pcap);
if (m_thread.joinable()) {
m_thread.join();
}
pcap_close(m_pcap);
m_pcap = nullptr;
}
m_condition.notify_all();
}
std::string Capture::lastError() const
{
return m_error;
}
void Capture::reportStatistics()
{
pcap_stat stats;
pcap_stats(m_pcap, &stats);
std::cout << "Packet Statistics: " << std::endl;
std::cout << " " << stats.ps_recv << " received" << std::endl;
std::cout << " " << stats.ps_drop << " dropped (full)" << std::endl;
std::cout << " " << stats.ps_ifdrop << " dropped (iface)" << std::endl;
std::cout << " " << m_packetCount << " processed" << std::endl;
std::cout << " " << m_droppedPackets << " dropped (capture)" << std::endl;
}
Packet Capture::nextPacket()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_condition.wait(lock, [this]() {
return m_queue.size() > 0 || !m_running;
});
if (m_queue.size() == 0) {
return Packet{};
}
auto packet = std::move(m_queue.front());
m_queue.pop_front();
return packet;
}
void Capture::loop()
{
pcap_loop(m_pcap, -1, &pcapDispatchCallback, reinterpret_cast<uint8_t *>(this));
}
bool Capture::checkError(int result)
{
switch (result) {
case PCAP_ERROR_ACTIVATED:
m_error = "The handle has already been activated"s;
return true;
case PCAP_ERROR_NO_SUCH_DEVICE:
m_error = "The capture source specified when the handle was created doesn't exist"s;
return true;
case PCAP_ERROR_PERM_DENIED:
m_error = "The process doesn't have permission to open the capture source"s;
return true;
case PCAP_ERROR_PROMISC_PERM_DENIED:
m_error = "The process has permission to open the capture source but doesn't have permission to put it into promiscuous mode"s;
return true;
case PCAP_ERROR_RFMON_NOTSUP:
m_error = "Monitor mode was specified but the capture source doesn't support monitor mode"s;
return true;
case PCAP_ERROR_IFACE_NOT_UP:
m_error = "The capture source device is not up"s;
return true;
case PCAP_ERROR:
m_error = std::string(pcap_geterr(m_pcap));
return true;
}
return false;
}
void Capture::handlePacket(const struct pcap_pkthdr *header, const uint8_t *data)
{
auto timeStamp = std::chrono::time_point_cast<TimeStamp::MicroSeconds::duration>(std::chrono::system_clock::from_time_t(header->ts.tv_sec)
+ std::chrono::microseconds{header->ts.tv_usec});
{
std::lock_guard<std::mutex> lock{m_mutex};
m_packetCount++;
if (m_queue.size() < MaximumQueueSize) {
m_queue.emplace_back(timeStamp, data, header->caplen, header->len);
} else {
m_droppedPackets++;
}
}
m_condition.notify_all();
}
|