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 }