File indexing completed on 2024-06-23 05:27:53

0001 /*
0002     SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "Capture.h"
0008 
0009 #include <iostream>
0010 #include <string>
0011 
0012 #include <pcap/pcap.h>
0013 
0014 #include "TimeStamps.h"
0015 
0016 using namespace std::string_literals;
0017 
0018 // Limit the amount of entries waiting in the queue to this size, to prevent
0019 // the queue from getting too full.
0020 static const int MaximumQueueSize = 1000;
0021 
0022 void pcapDispatchCallback(uint8_t *user, const struct pcap_pkthdr *h, const uint8_t *bytes)
0023 {
0024     reinterpret_cast<Capture *>(user)->handlePacket(h, bytes);
0025 }
0026 
0027 Capture::Capture(const std::string &interface)
0028     : m_interface(interface)
0029 {
0030 }
0031 
0032 Capture::~Capture()
0033 {
0034     if (m_pcap) {
0035         pcap_breakloop(m_pcap);
0036         if (m_thread.joinable()) {
0037             m_thread.join();
0038         }
0039         pcap_close(m_pcap);
0040     }
0041 }
0042 
0043 bool Capture::start()
0044 {
0045     auto device = m_interface.empty() ? (const char *)nullptr : m_interface.c_str();
0046 
0047     char errorBuffer[PCAP_ERRBUF_SIZE];
0048     m_pcap = pcap_create(device, errorBuffer);
0049     if (!m_pcap) {
0050         m_error = std::string(errorBuffer, PCAP_ERRBUF_SIZE);
0051         return false;
0052     }
0053 
0054     pcap_set_timeout(m_pcap, 500);
0055     pcap_set_snaplen(m_pcap, 100);
0056     pcap_set_promisc(m_pcap, 0);
0057     pcap_set_datalink(m_pcap, DLT_LINUX_SLL);
0058 
0059     if (checkError(pcap_activate(m_pcap))) {
0060         return false;
0061     }
0062 
0063     struct bpf_program filter;
0064     if (checkError(pcap_compile(m_pcap, &filter, "tcp or udp", 1, PCAP_NETMASK_UNKNOWN))) {
0065         return false;
0066     }
0067 
0068     if (checkError(pcap_setfilter(m_pcap, &filter))) {
0069         pcap_freecode(&filter);
0070         return false;
0071     }
0072 
0073     pcap_freecode(&filter);
0074 
0075     m_thread = std::thread{&Capture::loop, this};
0076 
0077     return true;
0078 }
0079 
0080 std::string Capture::lastError() const
0081 {
0082     return m_error;
0083 }
0084 
0085 void Capture::reportStatistics()
0086 {
0087     pcap_stat stats;
0088     pcap_stats(m_pcap, &stats);
0089 
0090     std::cout << "Packet Statistics: " << std::endl;
0091     std::cout << "  " << stats.ps_recv << " received" << std::endl;
0092     std::cout << "  " << stats.ps_drop << " dropped (full)" << std::endl;
0093     std::cout << "  " << stats.ps_ifdrop << " dropped (iface)" << std::endl;
0094     std::cout << "  " << m_packetCount << " processed" << std::endl;
0095     std::cout << "  " << m_droppedPackets << " dropped (capture)" << std::endl;
0096 }
0097 
0098 Packet Capture::nextPacket()
0099 {
0100     std::unique_lock<std::mutex> lock(m_mutex);
0101     m_condition.wait(lock, [this]() {
0102         return m_queue.size() > 0;
0103     });
0104 
0105     auto packet = std::move(m_queue.front());
0106     m_queue.pop_front();
0107     return packet;
0108 }
0109 
0110 void Capture::loop()
0111 {
0112     pcap_loop(m_pcap, -1, &pcapDispatchCallback, reinterpret_cast<uint8_t *>(this));
0113 }
0114 
0115 bool Capture::checkError(int result)
0116 {
0117     switch (result) {
0118     case PCAP_ERROR_ACTIVATED:
0119         m_error = "The handle has already been activated"s;
0120         return true;
0121     case PCAP_ERROR_NO_SUCH_DEVICE:
0122         m_error = "The capture source specified when the handle was created doesn't exist"s;
0123         return true;
0124     case PCAP_ERROR_PERM_DENIED:
0125         m_error = "The process doesn't have permission to open the capture source"s;
0126         return true;
0127     case PCAP_ERROR_PROMISC_PERM_DENIED:
0128         m_error = "The process has permission to open the capture source but doesn't have permission to put it into promiscuous mode"s;
0129         return true;
0130     case PCAP_ERROR_RFMON_NOTSUP:
0131         m_error = "Monitor mode was specified but the capture source doesn't support monitor mode"s;
0132         return true;
0133     case PCAP_ERROR_IFACE_NOT_UP:
0134         m_error = "The capture source device is not up"s;
0135         return true;
0136     case PCAP_ERROR:
0137         m_error = std::string(pcap_geterr(m_pcap));
0138         return true;
0139     }
0140 
0141     return false;
0142 }
0143 
0144 void Capture::handlePacket(const struct pcap_pkthdr *header, const uint8_t *data)
0145 {
0146     auto timeStamp = std::chrono::time_point_cast<TimeStamp::MicroSeconds::duration>(std::chrono::system_clock::from_time_t(header->ts.tv_sec)
0147                                                                                      + std::chrono::microseconds{header->ts.tv_usec});
0148 
0149     {
0150         std::lock_guard<std::mutex> lock{m_mutex};
0151 
0152         m_packetCount++;
0153         if (m_queue.size() < MaximumQueueSize) {
0154             m_queue.emplace_back(timeStamp, data, header->caplen, header->len);
0155         } else {
0156             m_droppedPackets++;
0157         }
0158     }
0159 
0160     m_condition.notify_all();
0161 }