Changeset View
Changeset View
Standalone View
Standalone View
plugins/process/network/helper/Capture.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * This file is part of KSysGuard. | ||||
3 | * Copyright 2019 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
4 | * | ||||
5 | * This program is free software; you can redistribute it and/or | ||||
6 | * modify it under the terms of the GNU General Public License as | ||||
7 | * published by the Free Software Foundation; either version 2 of | ||||
8 | * the License or (at your option) version 3 or any later version | ||||
9 | * accepted by the membership of KDE e.V. (or its successor approved | ||||
10 | * by the membership of KDE e.V.), which shall act as a proxy | ||||
11 | * defined in Section 14 of version 3 of the license. | ||||
12 | * | ||||
13 | * This program is distributed in the hope that it will be useful, | ||||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
16 | * GNU General Public License for more details. | ||||
17 | * | ||||
18 | * You should have received a copy of the GNU General Public License | ||||
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
20 | */ | ||||
21 | | ||||
22 | #include "Capture.h" | ||||
23 | | ||||
24 | #include <string> | ||||
25 | #include <iostream> | ||||
26 | | ||||
27 | #include <pcap/pcap.h> | ||||
28 | | ||||
29 | #include "Packet.h" | ||||
30 | #include "TimeStamps.h" | ||||
31 | | ||||
32 | using namespace std::string_literals; | ||||
33 | | ||||
34 | void pcapDispatchCallback(uint8_t *user, const struct pcap_pkthdr *h, const uint8_t *bytes) | ||||
35 | { | ||||
36 | reinterpret_cast<Capture *>(user)->handlePacket(h, bytes); | ||||
37 | } | ||||
38 | | ||||
39 | Capture::Capture(const std::string &interface) | ||||
40 | { | ||||
41 | m_interface = interface; | ||||
42 | } | ||||
43 | | ||||
44 | Capture::~Capture() | ||||
45 | { | ||||
46 | if (m_pcap) { | ||||
47 | if (m_active) { | ||||
48 | stop(); | ||||
49 | } | ||||
50 | | ||||
51 | pcap_close(m_pcap); | ||||
52 | } | ||||
53 | } | ||||
54 | | ||||
55 | bool Capture::start() | ||||
56 | { | ||||
57 | auto device = m_interface.empty() ? (const char *)nullptr : m_interface.c_str(); | ||||
58 | | ||||
59 | char errorBuffer[PCAP_ERRBUF_SIZE]; | ||||
60 | m_pcap = pcap_create(device, errorBuffer); | ||||
61 | if (!m_pcap) { | ||||
62 | m_error = std::string(errorBuffer, PCAP_ERRBUF_SIZE); | ||||
63 | return false; | ||||
64 | } | ||||
65 | | ||||
66 | pcap_set_timeout(m_pcap, 500); | ||||
67 | pcap_set_snaplen(m_pcap, 100); | ||||
68 | pcap_set_promisc(m_pcap, 0); | ||||
69 | pcap_set_datalink(m_pcap, DLT_LINUX_SLL); | ||||
70 | | ||||
71 | if (checkError(pcap_activate(m_pcap))) | ||||
72 | return false; | ||||
73 | | ||||
74 | struct bpf_program filter; | ||||
75 | if (checkError(pcap_compile(m_pcap, &filter, "tcp or udp", 1, PCAP_NETMASK_UNKNOWN))) { | ||||
76 | pcap_freecode(&filter); | ||||
77 | return false; | ||||
78 | } | ||||
79 | | ||||
80 | if (checkError(pcap_setfilter(m_pcap, &filter))) { | ||||
81 | pcap_freecode(&filter); | ||||
82 | return false; | ||||
83 | } | ||||
84 | | ||||
85 | pcap_freecode(&filter); | ||||
86 | | ||||
87 | m_thread = std::thread { &Capture::loop, this }; | ||||
88 | | ||||
89 | return true; | ||||
90 | } | ||||
91 | | ||||
92 | void Capture::stop() | ||||
93 | { | ||||
94 | pcap_breakloop(m_pcap); | ||||
95 | if (m_thread.joinable()) { | ||||
96 | m_thread.join(); | ||||
97 | } | ||||
98 | } | ||||
99 | | ||||
100 | std::string Capture::lastError() const | ||||
101 | { | ||||
102 | return m_error; | ||||
103 | } | ||||
104 | | ||||
105 | void Capture::reportStatistics() | ||||
106 | { | ||||
107 | pcap_stat stats; | ||||
108 | pcap_stats(m_pcap, &stats); | ||||
109 | | ||||
110 | std::cout << "Packet Statistics: " << std::endl; | ||||
111 | std::cout << " " << stats.ps_recv << " received" << std::endl; | ||||
112 | std::cout << " " << stats.ps_drop << " dropped (full)" << std::endl; | ||||
113 | std::cout << " " << stats.ps_ifdrop << " dropped (iface)" << std::endl; | ||||
114 | std::cout << " " << m_packetCount << " processed" << std::endl; | ||||
115 | } | ||||
116 | | ||||
117 | Packet Capture::nextPacket() | ||||
118 | { | ||||
119 | std::unique_lock<std::mutex> lock(m_mutex); | ||||
120 | m_condition.wait(lock, [this]() { return m_queue.size() > 0; }); | ||||
121 | | ||||
122 | auto packet = std::move(m_queue.front()); | ||||
123 | m_queue.pop_front(); | ||||
124 | return packet; | ||||
125 | } | ||||
126 | | ||||
127 | void Capture::loop() | ||||
128 | { | ||||
129 | pcap_loop(m_pcap, -1, &pcapDispatchCallback, reinterpret_cast<uint8_t *>(this)); | ||||
130 | } | ||||
131 | | ||||
132 | bool Capture::checkError(int result) | ||||
133 | { | ||||
134 | switch (result) { | ||||
135 | case PCAP_ERROR_ACTIVATED: | ||||
136 | m_error = "The handle has already been activated"s; | ||||
137 | return true; | ||||
138 | case PCAP_ERROR_NO_SUCH_DEVICE: | ||||
139 | m_error = "The capture source specified when the handle was created doesn't exist"s; | ||||
140 | return true; | ||||
141 | case PCAP_ERROR_PERM_DENIED: | ||||
142 | m_error = "The process doesn't have permission to open the capture source"s; | ||||
143 | return true; | ||||
144 | case PCAP_ERROR_PROMISC_PERM_DENIED: | ||||
145 | m_error = "The process has permission to open the capture source but doesn't have permission to put it into promiscuous mode"s; | ||||
146 | return true; | ||||
147 | case PCAP_ERROR_RFMON_NOTSUP: | ||||
148 | m_error = "Monitor mode was specified but the capture source doesn't support monitor mode"s; | ||||
149 | return true; | ||||
150 | case PCAP_ERROR_IFACE_NOT_UP: | ||||
151 | m_error = "The capture source device is not up"s; | ||||
152 | return true; | ||||
153 | case PCAP_ERROR: | ||||
154 | m_error = std::string(pcap_geterr(m_pcap)); | ||||
155 | return true; | ||||
156 | } | ||||
157 | | ||||
158 | return false; | ||||
159 | } | ||||
160 | | ||||
161 | void Capture::handlePacket(const struct pcap_pkthdr *header, const uint8_t *data) | ||||
162 | { | ||||
163 | 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 }); | ||||
164 | | ||||
165 | m_packetCount++; | ||||
166 | { | ||||
167 | std::lock_guard<std::mutex> lock { m_mutex }; | ||||
168 | m_queue.emplace_back(timeStamp, data, header->caplen, header->len); | ||||
169 | } | ||||
170 | | ||||
171 | m_condition.notify_all(); | ||||
172 | } |