diff --git a/plugins/process/network/helper/Accumulator.cpp b/plugins/process/network/helper/Accumulator.cpp index 50defe7d..efebc72e 100644 --- a/plugins/process/network/helper/Accumulator.cpp +++ b/plugins/process/network/helper/Accumulator.cpp @@ -1,91 +1,95 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "Accumulator.h" #include "Capture.h" #include "ConnectionMapping.h" #include "Packet.h" using namespace std::chrono_literals; Accumulator::Accumulator(std::shared_ptr capture, std::shared_ptr mapping) { m_capture = capture; m_mapping = mapping; m_running = true; m_thread = std::thread { &Accumulator::loop, this }; } Accumulator::PidDataCounterHash Accumulator::data() { + std::lock_guard lock{m_mutex}; + auto tmp = m_data; auto toErase = std::vector{}; for (auto &entry : m_data) { if (entry.second.first == 0 && entry.second.second == 0) { toErase.push_back(entry.first); } else { entry.second.first = 0; entry.second.second = 0; } } std::for_each(toErase.cbegin(), toErase.cend(), [this](int pid) { m_data.erase(pid); }); return tmp; } void Accumulator::stop() { m_running = false; if (m_thread.joinable()) { m_thread.join(); } } void Accumulator::loop() { while (m_running) { auto packet = m_capture->nextPacket(); auto result = m_mapping->pidForPacket(packet); if (result.pid == 0) continue; addData(result.direction, packet, result.pid); } } void Accumulator::addData(Packet::Direction direction, const Packet &packet, int pid) { + std::lock_guard lock{m_mutex}; + auto itr = m_data.find(pid); if (itr == m_data.end()) { m_data.emplace(pid, InboundOutboundData{0, 0}); } if (direction == Packet::Direction::Inbound) { m_data[pid].first += packet.size(); } else { m_data[pid].second += packet.size(); }; } diff --git a/plugins/process/network/helper/Accumulator.h b/plugins/process/network/helper/Accumulator.h index 02c84ad4..7d701710 100644 --- a/plugins/process/network/helper/Accumulator.h +++ b/plugins/process/network/helper/Accumulator.h @@ -1,63 +1,65 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 . */ #ifndef ACCUMULATOR_H #define ACCUMULATOR_H #include #include +#include #include #include #include "TimeStamps.h" #include "Packet.h" class Capture; class ConnectionMapping; class Packet; class Accumulator { public: using InboundOutboundData = std::pair; using PidDataCounterHash = std::unordered_map; Accumulator(std::shared_ptr capture, std::shared_ptr mapping); PidDataCounterHash data(); void stop(); private: void addData(Packet::Direction direction, const Packet &packet, int pid); void loop(); std::shared_ptr m_capture; std::shared_ptr m_mapping; std::thread m_thread; std::atomic_bool m_running; + std::mutex m_mutex; PidDataCounterHash m_data; }; #endif // ACCUMULATOR_H diff --git a/plugins/process/network/helper/Capture.cpp b/plugins/process/network/helper/Capture.cpp index 054e8079..4bcf7514 100644 --- a/plugins/process/network/helper/Capture.cpp +++ b/plugins/process/network/helper/Capture.cpp @@ -1,172 +1,180 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "Capture.h" #include #include #include #include "Packet.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(user)->handlePacket(h, bytes); } Capture::Capture(const std::string &interface) { m_interface = interface; } Capture::~Capture() { if (m_pcap) { - if (m_active) { - stop(); - } - - pcap_close(m_pcap); + 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))) + 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))) { - pcap_freecode(&filter); return false; } if (checkError(pcap_setfilter(m_pcap, &filter))) { pcap_freecode(&filter); return false; } pcap_freecode(&filter); m_thread = std::thread { &Capture::loop, this }; return true; } void Capture::stop() { pcap_breakloop(m_pcap); if (m_thread.joinable()) { m_thread.join(); } + pcap_close(m_pcap); + m_pcap = nullptr; } 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 lock(m_mutex); m_condition.wait(lock, [this]() { return m_queue.size() > 0; }); auto packet = std::move(m_queue.front()); m_queue.pop_front(); return packet; } void Capture::loop() { pcap_loop(m_pcap, -1, &pcapDispatchCallback, reinterpret_cast(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(std::chrono::system_clock::from_time_t(header->ts.tv_sec) + std::chrono::microseconds { header->ts.tv_usec }); - m_packetCount++; { std::lock_guard lock { m_mutex }; - m_queue.emplace_back(timeStamp, data, header->caplen, header->len); + + m_packetCount++; + if (m_queue.size() < MaximumQueueSize) { + m_queue.emplace_back(timeStamp, data, header->caplen, header->len); + } else { + m_droppedPackets++; + } } m_condition.notify_all(); } diff --git a/plugins/process/network/helper/Capture.h b/plugins/process/network/helper/Capture.h index 1ca6f6c6..3cbfb2e9 100644 --- a/plugins/process/network/helper/Capture.h +++ b/plugins/process/network/helper/Capture.h @@ -1,66 +1,66 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 . */ #ifndef CAPTURE_H #define CAPTURE_H #include #include #include #include #include #include "Packet.h" class pcap; class Capture { public: Capture(const std::string &interface = std::string{}); ~Capture(); bool start(); void stop(); std::string lastError() const; void reportStatistics(); Packet nextPacket(); void handlePacket(const struct pcap_pkthdr *header, const uint8_t *data); private: void loop(); bool checkError(int result); std::string m_interface; std::string m_error; - std::atomic_bool m_active; std::thread m_thread; std::mutex m_mutex; std::condition_variable m_condition; std::deque m_queue; int m_packetCount = 0; + int m_droppedPackets = 0; - pcap *m_pcap; + pcap *m_pcap = nullptr; }; #endif // CAPTURE_H diff --git a/plugins/process/network/helper/ConnectionMapping.cpp b/plugins/process/network/helper/ConnectionMapping.cpp index e06e67e3..4ff18572 100644 --- a/plugins/process/network/helper/ConnectionMapping.cpp +++ b/plugins/process/network/helper/ConnectionMapping.cpp @@ -1,191 +1,191 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "ConnectionMapping.h" #include #include #include #include #include using namespace std::string_literals; // Convert /proc/net/tcp's mangled big-endian notation to a host-endian int32' uint32_t tcpToInt(const std::string &part) { uint32_t result = 0; result |= std::stoi(part.substr(0, 2), 0, 16) << 24; result |= std::stoi(part.substr(2, 2), 0, 16) << 16; result |= std::stoi(part.substr(4, 2), 0, 16) << 8; result |= std::stoi(part.substr(6, 2), 0, 16) << 0; return result; } ConnectionMapping::ConnectionMapping() { m_socketFileMatch = // Format of /proc/net/tcp is: // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode // 0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 31896 ... // Where local_address is a hex representation of the IP Address and port, in big endian notation. // Since we care only about local address, local port and inode we ignore the middle 70 characters. std::regex("\\s*\\d+: (?:(\\w{8})|(\\w{32})):([A-F0-9]{4}) (.{94}|.{70}) (\\d+) .*", std::regex::ECMAScript | std::regex::optimize); parseProc(); } ConnectionMapping::PacketResult ConnectionMapping::pidForPacket(const Packet &packet) { PacketResult result; auto sourceInode = m_localToINode.find(packet.sourceAddress()); auto destInode = m_localToINode.find(packet.destinationAddress()); if (sourceInode == m_localToINode.end() && destInode == m_localToINode.end()) { parseProc(); sourceInode = m_localToINode.find(packet.sourceAddress()); destInode = m_localToINode.find(packet.destinationAddress()); if (sourceInode == m_localToINode.end() && destInode == m_localToINode.end()) { return result; } } auto inode = m_localToINode.end(); if (sourceInode != m_localToINode.end()) { result.direction = Packet::Direction::Outbound; inode = sourceInode; } else { result.direction = Packet::Direction::Inbound; inode = destInode; } auto pid = m_inodeToPid.find((*inode).second); if (pid == m_inodeToPid.end()) { result.pid = -1; } else { result.pid = (*pid).second; } return result; } void ConnectionMapping::parseProc() { //TODO: Consider using INET_DIAG netlink protocol for retrieving socket information. if (parseSockets()) parsePid(); } bool ConnectionMapping::parseSockets() { auto oldInodes = m_inodes; m_inodes.clear(); m_localToINode.clear(); parseSocketFile("/proc/net/tcp"); parseSocketFile("/proc/net/udp"); parseSocketFile("/proc/net/tcp6"); parseSocketFile("/proc/net/udp6"); if (m_inodes == oldInodes) { return false; } return true; } void ConnectionMapping::parsePid() { std::unordered_set pids; auto dir = opendir("/proc"); dirent *entry = nullptr; while ((entry = readdir(dir))) if (entry->d_name[0] >= '0' && entry->d_name[0] <= '9') pids.insert(std::stoi(entry->d_name)); closedir(dir); char buffer[100] = { "\0" }; m_inodeToPid.clear(); for (auto pid : pids) { auto fdPath = "/proc/%/fd"s.replace(6, 1, std::to_string(pid)); auto dir = opendir(fdPath.data()); if (dir == NULL) { continue; } dirent *fd = nullptr; while ((fd = readdir(dir))) { memset(buffer, 0, 100); - readlinkat(dirfd(dir), fd->d_name, buffer, 100); + readlinkat(dirfd(dir), fd->d_name, buffer, 99); auto target = std::string(buffer); if (target.find("socket:") == std::string::npos) continue; auto inode = std::stoi(target.substr(8)); m_inodeToPid.insert(std::make_pair(inode, pid)); } closedir(dir); } } void ConnectionMapping::parseSocketFile(const char *fileName) { std::ifstream file { fileName }; if (!file.is_open()) return; std::string data; while (std::getline(file, data)) { std::smatch match; if (!std::regex_match(data, match, m_socketFileMatch)) { continue; } Packet::Address localAddress; if (!match.str(1).empty()) { localAddress.address[3] = tcpToInt(match.str(1)); } else { auto ipv6 = match.str(2); if (ipv6.compare(0, 24, "0000000000000000FFFF0000")) { // Some applications (like Steam) use ipv6 sockets with ipv4. // This results in ipv4 addresses that end up in the tcp6 file. // They seem to start with 0000000000000000FFFF0000, so if we // detect that, assume it is ipv4-over-ipv6. localAddress.address[3] = tcpToInt(ipv6.substr(24,8)); } else { localAddress.address[0] = tcpToInt(ipv6.substr(0, 8)); localAddress.address[1] = tcpToInt(ipv6.substr(8, 8)); localAddress.address[2] = tcpToInt(ipv6.substr(16, 8)); localAddress.address[3] = tcpToInt(ipv6.substr(24, 8)); } } localAddress.port = std::stoi(match.str(3), 0, 16); auto inode = std::stoi(match.str(5)); m_localToINode.insert(std::make_pair(localAddress, inode)); m_inodes.insert(inode); } } diff --git a/plugins/process/network/helper/Packet.cpp b/plugins/process/network/helper/Packet.cpp index e8ac5375..65ea4916 100644 --- a/plugins/process/network/helper/Packet.cpp +++ b/plugins/process/network/helper/Packet.cpp @@ -1,150 +1,167 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "Packet.h" #include #include #include #include #include #include #include #include #include #include +uint32_t u8Tou32(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) +{ + return uint32_t(first) << 24 | uint32_t(second) << 16 | uint32_t(third) << 8 | uint32_t(fourth); +} + Packet::Packet() { } Packet::Packet(const TimeStamp::MicroSeconds &timeStamp, const uint8_t *data, uint32_t dataLength, uint32_t packetSize) { m_timeStamp = timeStamp; m_size = packetSize; const sll_header *header = reinterpret_cast(data); switch (ntohs(header->sll_protocol)) { case ETHERTYPE_IP: m_networkProtocol = NetworkProtocolType::IPv4; if (sizeof(sll_header) <= dataLength) { - parseIPv4(data + sizeof(sll_header)); + parseIPv4(data + sizeof(sll_header), dataLength - sizeof(sll_header)); } break; case ETHERTYPE_IPV6: m_networkProtocol = NetworkProtocolType::IPv6; if (sizeof(sll_header) <= dataLength) { - parseIPv6(data + sizeof(sll_header)); + parseIPv6(data + sizeof(sll_header), dataLength - sizeof(sll_header)); } break; default: m_networkProtocol = NetworkProtocolType::Unknown; break; } } Packet::~Packet() { } unsigned int Packet::size() const { return m_size; } TimeStamp::MicroSeconds Packet::timeStamp() const { return m_timeStamp; } Packet::NetworkProtocolType Packet::networkProtocol() const { return m_networkProtocol; } Packet::TransportProtocolType Packet::transportProtocol() const { return m_transportProtocol; } Packet::Address Packet::sourceAddress() const { return m_sourceAddress; } Packet::Address Packet::destinationAddress() const { return m_destinationAddress; } -void Packet::parseIPv4(const uint8_t *data) +void Packet::parseIPv4(const uint8_t *data, int32_t dataLength) { + if (dataLength < int32_t(sizeof(ip))) { + return; + } + const ip *header = reinterpret_cast(data); m_sourceAddress.address[3] = header->ip_src.s_addr; m_destinationAddress.address[3] = header->ip_dst.s_addr; - parseTransport(header->ip_p, data + sizeof(ip)); + parseTransport(header->ip_p, data + sizeof(ip), dataLength - sizeof(ip)); } -void Packet::parseIPv6(const uint8_t *data) +void Packet::parseIPv6(const uint8_t *data, int32_t dataLength) { + if (dataLength < int32_t(sizeof(ip6_hdr))) { + return; + } + const ip6_hdr *header = reinterpret_cast(data); m_sourceAddress.address = { - uint32_t(header->ip6_src.s6_addr[0] << 24 & header->ip6_src.s6_addr[1] << 16 & header->ip6_src.s6_addr[2] << 8 & header->ip6_src.s6_addr[3]), - uint32_t(header->ip6_src.s6_addr[4] << 24 & header->ip6_src.s6_addr[5] << 16 & header->ip6_src.s6_addr[6] << 8 & header->ip6_src.s6_addr[7]), - uint32_t(header->ip6_src.s6_addr[8] << 24 & header->ip6_src.s6_addr[9] << 16 & header->ip6_src.s6_addr[10] << 8 & header->ip6_src.s6_addr[11]), - uint32_t(header->ip6_src.s6_addr[12] << 24 & header->ip6_src.s6_addr[13] << 16 & header->ip6_src.s6_addr[14] << 8 & header->ip6_src.s6_addr[15]) + u8Tou32(header->ip6_src.s6_addr[0], header->ip6_src.s6_addr[1], header->ip6_src.s6_addr[2], header->ip6_src.s6_addr[3]), + u8Tou32(header->ip6_src.s6_addr[4], header->ip6_src.s6_addr[5], header->ip6_src.s6_addr[6], header->ip6_src.s6_addr[7]), + u8Tou32(header->ip6_src.s6_addr[8], header->ip6_src.s6_addr[9], header->ip6_src.s6_addr[10], header->ip6_src.s6_addr[11]), + u8Tou32(header->ip6_src.s6_addr[12], header->ip6_src.s6_addr[13], header->ip6_src.s6_addr[14], header->ip6_src.s6_addr[15]) }; m_destinationAddress.address = { - uint32_t(header->ip6_dst.s6_addr[0] << 24 & header->ip6_dst.s6_addr[1] << 16 & header->ip6_dst.s6_addr[2] << 8 & header->ip6_dst.s6_addr[3]), - uint32_t(header->ip6_dst.s6_addr[4] << 24 & header->ip6_dst.s6_addr[5] << 16 & header->ip6_dst.s6_addr[6] << 8 & header->ip6_dst.s6_addr[7]), - uint32_t(header->ip6_dst.s6_addr[8] << 24 & header->ip6_dst.s6_addr[9] << 16 & header->ip6_dst.s6_addr[10] << 8 & header->ip6_dst.s6_addr[11]), - uint32_t(header->ip6_dst.s6_addr[12] << 24 & header->ip6_dst.s6_addr[13] << 16 & header->ip6_dst.s6_addr[14] << 8 & header->ip6_dst.s6_addr[15]) + u8Tou32(header->ip6_dst.s6_addr[0], header->ip6_dst.s6_addr[1], header->ip6_dst.s6_addr[2], header->ip6_dst.s6_addr[3]), + u8Tou32(header->ip6_dst.s6_addr[4], header->ip6_dst.s6_addr[5], header->ip6_dst.s6_addr[6], header->ip6_dst.s6_addr[7]), + u8Tou32(header->ip6_dst.s6_addr[8], header->ip6_dst.s6_addr[9], header->ip6_dst.s6_addr[10], header->ip6_dst.s6_addr[11]), + u8Tou32(header->ip6_dst.s6_addr[12], header->ip6_dst.s6_addr[13], header->ip6_dst.s6_addr[14], header->ip6_dst.s6_addr[15]) }; - parseTransport(header->ip6_nxt, data + sizeof(ip6_hdr)); + parseTransport(header->ip6_nxt, data + sizeof(ip6_hdr), dataLength - sizeof(ip6_hdr)); } -void Packet::parseTransport(uint8_t type, const uint8_t *data) +void Packet::parseTransport(uint8_t type, const uint8_t *data, int32_t dataLength) { switch (type) { case IPPROTO_TCP: { m_transportProtocol = TransportProtocolType::Tcp; - const tcphdr *tcpHeader = reinterpret_cast(data); - m_sourceAddress.port = ntohs(tcpHeader->th_sport); - m_destinationAddress.port = ntohs(tcpHeader->th_dport); + if (dataLength >= int32_t(sizeof(tcphdr))) { + const tcphdr *tcpHeader = reinterpret_cast(data); + m_sourceAddress.port = ntohs(tcpHeader->th_sport); + m_destinationAddress.port = ntohs(tcpHeader->th_dport); + } break; } case IPPROTO_UDP: { m_transportProtocol = TransportProtocolType::Udp; - const udphdr *udpHeader = reinterpret_cast(data); - m_sourceAddress.port = ntohs(udpHeader->uh_sport); - m_destinationAddress.port = ntohs(udpHeader->uh_dport); + if (dataLength >= int32_t(sizeof(udphdr))) { + const udphdr *udpHeader = reinterpret_cast(data); + m_sourceAddress.port = ntohs(udpHeader->uh_sport); + m_destinationAddress.port = ntohs(udpHeader->uh_dport); + } break; } default: m_transportProtocol = TransportProtocolType::Unknown; break; } } diff --git a/plugins/process/network/helper/Packet.h b/plugins/process/network/helper/Packet.h index 2719175c..ee7c96c8 100644 --- a/plugins/process/network/helper/Packet.h +++ b/plugins/process/network/helper/Packet.h @@ -1,110 +1,110 @@ /* * This file is part of KSysGuard. * Copyright 2019 Arjen Hiemstra * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 . */ #ifndef PACKET_H #define PACKET_H #include #include #include #include #include "TimeStamps.h" class Packet { public: enum class NetworkProtocolType { Unknown, IPv4, IPv6, }; enum class TransportProtocolType { Unknown, Tcp, Udp, }; enum class Direction { Inbound, Outbound, }; struct Address { std::array address = { 0 }; uint32_t port = 0; inline bool operator==(const Address &other) const { return address == other.address && port == other.port; } }; Packet(); Packet(const TimeStamp::MicroSeconds &timeStamp, const uint8_t *data, uint32_t dataLength, uint32_t packetSize); ~Packet(); Packet(const Packet &other) = delete; Packet(Packet &&other) = default; TimeStamp::MicroSeconds timeStamp() const; unsigned int size() const; NetworkProtocolType networkProtocol() const; TransportProtocolType transportProtocol() const; Address sourceAddress() const; Address destinationAddress() const; private: - void parseIPv4(const uint8_t *data); - void parseIPv6(const uint8_t *data); - void parseTransport(uint8_t type, const uint8_t *data); + void parseIPv4(const uint8_t* data, int32_t dataLength); + void parseIPv6(const uint8_t* data, int32_t dataLength); + void parseTransport(uint8_t type, const uint8_t *data, int32_t dataLength); TimeStamp::MicroSeconds m_timeStamp; unsigned int m_size = 0; NetworkProtocolType m_networkProtocol = NetworkProtocolType::Unknown; TransportProtocolType m_transportProtocol = TransportProtocolType::Unknown; Address m_sourceAddress; Address m_destinationAddress; }; namespace std { template <> struct hash { using argument_type = Packet::Address; using result_type = std::size_t; inline result_type operator()(argument_type const& address) const noexcept { return std::hash{}(address.address[0]) ^ (std::hash{}(address.address[1]) << 1) ^ (std::hash{}(address.address[2]) << 2) ^ (std::hash{}(address.address[3]) << 3) ^ (std::hash{}(address.port) << 4); } }; } #endif // PACKET_H