Commit 264b5ec1 authored by David Redondo's avatar David Redondo 🏎 Committed by Arjen Hiemstra
Browse files

Use sock_diag netlink interface to get sockets information

Saves parsing all these files in /proc/net. For now the old code path is
retained as fallback when there is some error communicating with the kernel.
parent ded732c4
add_definitions(-DTRANSLATION_DOMAIN=\"ksysguard_plugins_process\")
if (libpcap_FOUND)
if (BUILD_NETWORK_PLUGIN)
add_subdirectory(network)
endif()
add_subdirectory(nvidia)
......@@ -8,8 +8,8 @@ set(ksgrd_network_helper_SRCS
)
add_executable(ksgrd_network_helper ${ksgrd_network_helper_SRCS})
target_include_directories(ksgrd_network_helper PUBLIC ${PCAP_INCLUDE_DIR})
target_link_libraries(ksgrd_network_helper ${PCAP_LIBRARY})
target_include_directories(ksgrd_network_helper PUBLIC ${PCAP_INCLUDE_DIR} ${NL_INCLUDE_DIRS})
target_link_libraries(ksgrd_network_helper ${PCAP_LIBRARY} ${NL_LIBRARIES})
kde_target_enable_exceptions(ksgrd_network_helper PUBLIC)
set_target_properties(ksgrd_network_helper PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED TRUE)
......
/*
* This file is part of KSysGuard.
* Copyright 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
* Copyright 2020 David Redondo <kde@david-redondo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -28,6 +29,13 @@
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/inet_diag.h>
#include <linux/sock_diag.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
using namespace std::string_literals;
// Convert /proc/net/tcp's mangled big-endian notation to a host-endian int32'
......@@ -41,7 +49,34 @@ uint32_t tcpToInt(const std::string &part)
return result;
}
int parseInetDiagMesg(struct nl_msg *msg, void *arg)
{
auto self = static_cast<ConnectionMapping*>(arg);
struct nlmsghdr *nlh = nlmsg_hdr(msg);
auto inetDiagMsg = static_cast<inet_diag_msg*>(nlmsg_data(nlh));
Packet::Address localAddress;
if (inetDiagMsg->idiag_family == AF_INET) {
// I expected to need ntohl calls here and bewlow for src but comparing to values gathered
// by parsing proc they are not needed and even produce wrong results
localAddress.address[3] = inetDiagMsg->id.idiag_src[0];
} else if (inetDiagMsg->id.idiag_src[0] == 0 && inetDiagMsg->id.idiag_src[1] == 0 && inetDiagMsg->id.idiag_src[2] == 0xffff0000) {
// 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] = inetDiagMsg->id.idiag_src[3];
} else {
std::memcpy(localAddress.address.data(), inetDiagMsg->id.idiag_src, sizeof(localAddress.address));
}
localAddress.port = ntohs(inetDiagMsg->id.idiag_sport);
self->m_localToINode[localAddress] = inetDiagMsg->idiag_inode;
self->m_inodes.insert(inetDiagMsg->idiag_inode);
return NL_OK;
}
ConnectionMapping::ConnectionMapping()
: m_socket(nl_socket_alloc(), nl_socket_free)
{
m_socketFileMatch =
// Format of /proc/net/tcp is:
......@@ -51,7 +86,9 @@ ConnectionMapping::ConnectionMapping()
// 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();
nl_connect(m_socket.get(), NETLINK_SOCK_DIAG);
nl_socket_modify_cb(m_socket.get(), NL_CB_VALID, NL_CB_CUSTOM, &parseInetDiagMesg, this);
getSocketInfo();
}
ConnectionMapping::PacketResult ConnectionMapping::pidForPacket(const Packet &packet)
......@@ -62,7 +99,7 @@ ConnectionMapping::PacketResult ConnectionMapping::pidForPacket(const Packet &pa
auto destInode = m_localToINode.find(packet.destinationAddress());
if (sourceInode == m_localToINode.end() && destInode == m_localToINode.end()) {
parseProc();
getSocketInfo();
sourceInode = m_localToINode.find(packet.sourceAddress());
destInode = m_localToINode.find(packet.destinationAddress());
......@@ -90,29 +127,56 @@ ConnectionMapping::PacketResult ConnectionMapping::pidForPacket(const Packet &pa
return result;
}
void ConnectionMapping::parseProc()
void ConnectionMapping::getSocketInfo()
{
//TODO: Consider using INET_DIAG netlink protocol for retrieving socket information.
if (parseSockets())
auto oldInodes = m_inodes;
m_inodes.clear();
m_localToINode.clear();
if (!dumpSockets()) {
// There was some error with sock_diag
parseSockets();
}
if (m_inodes != oldInodes) {
parsePid();
}
}
bool ConnectionMapping::parseSockets()
bool ConnectionMapping::dumpSockets()
{
auto oldInodes = m_inodes;
for (auto family : {AF_INET, AF_INET6}) {
for (auto protocol : {IPPROTO_TCP, IPPROTO_UDP}) {
if (!dumpSockets(family, protocol)) {
return false;
}
}
}
return true;
}
m_inodes.clear();
m_localToINode.clear();
bool ConnectionMapping::dumpSockets(int inet_family, int protocol)
{
inet_diag_req_v2 inet_request;
inet_request.id = {};
inet_request.sdiag_family = inet_family;
inet_request.sdiag_protocol = protocol;
inet_request.idiag_states = -1;
if (nl_send_simple(m_socket.get(), SOCK_DIAG_BY_FAMILY, NLM_F_DUMP | NLM_F_REQUEST, &inet_request, sizeof(inet_diag_req_v2)) < 0) {
return false;
}
if (nl_recvmsgs_default(m_socket.get()) != 0) {
return false;
}
return true;
}
void ConnectionMapping::parseSockets()
{
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()
......
......@@ -26,10 +26,12 @@
#include <unordered_set>
#include <regex>
// #include <QRegularExpression>
#include <netlink/socket.h>
#include "Packet.h"
struct nl_msg;
/**
* @todo write docs
*/
......@@ -43,12 +45,13 @@ public:
ConnectionMapping();
void parseProc();
PacketResult pidForPacket(const Packet &packet);
private:
bool parseSockets();
void getSocketInfo();
bool dumpSockets();
bool dumpSockets(int inet_family, int protocol);
void parseSockets();
void parsePid();
void parseSocketFile(const char* fileName);
......@@ -56,9 +59,10 @@ private:
std::unordered_map<int, int> m_inodeToPid;
std::unordered_set<int> m_inodes;
std::unordered_set<int> m_pids;
// QRegularExpression m_socketFileMatch;
std::unique_ptr<nl_sock, decltype(&nl_socket_free)> m_socket;
std::regex m_socketFileMatch;
friend int parseInetDiagMesg(struct nl_msg *msg, void *arg);
};
#endif // CONNECTIONMAPPING_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment