summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt28
-rw-r--r--collectors/apps.plugin/apps_plugin.c3
-rw-r--r--collectors/network-viewer.plugin/network-viewer.c3
-rw-r--r--collectors/network-viewer.plugin/viewer.html221
-rw-r--r--collectors/plugins.d/local-sockets.h389
-rw-r--r--collectors/plugins.d/local_listeners.c20
-rw-r--r--config.cmake.h.in1
7 files changed, 487 insertions, 178 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 378aa5945b..dff002aef6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1638,6 +1638,12 @@ target_link_libraries(libnetdata PUBLIC ${OPENSSL_LDFLAGS})
# h2o
target_link_libraries(libnetdata PUBLIC "$<$<BOOL:${ENABLE_H2O}>:h2o>")
+# mnl
+pkg_check_modules(MNL libmnl)
+if(MNL_FOUND)
+ set(HAVE_LIBMNL True)
+endif()
+
#
# helper function to build protos
#
@@ -1781,8 +1787,11 @@ if(ENABLE_PLUGIN_FREEIPMI)
endif()
if(ENABLE_PLUGIN_NFACCT)
+ if (NOT MNL_FOUND)
+ message(FATAL_ERROR "Can not build nfacct.plugin because MNL library could not be found.")
+ endif()
+
pkg_check_modules(NFACCT REQUIRED libnetfilter_acct)
- pkg_check_modules(MNL REQUIRED libmnl)
set(NFACCT_PLUGIN_FILES collectors/nfacct.plugin/plugin_nfacct.c)
@@ -1979,7 +1988,13 @@ if(ENABLE_PLUGIN_LOCAL_LISTENERS)
)
add_executable(local-listeners ${LOCAL_LISTENERS_FILES})
- target_link_libraries(local-listeners libnetdata)
+
+ target_compile_options(local-listeners PRIVATE
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_CFLAGS_OTHER}>")
+ target_include_directories(local-listeners PRIVATE
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_INCLUDE_DIRS}>")
+ target_link_libraries(local-listeners libnetdata
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_LIBRARIES}>")
install(TARGETS local-listeners
COMPONENT local_listeners
@@ -1993,7 +2008,14 @@ if(ENABLE_PLUGIN_NETWORK_VIEWER)
)
add_executable(network-viewer.plugin ${NETWORK_VIEWER_FILES})
- target_link_libraries(network-viewer.plugin libnetdata)
+
+ target_compile_options(network-viewer.plugin PRIVATE
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_CFLAGS_OTHER}>")
+ target_include_directories(network-viewer.plugin PRIVATE
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_INCLUDE_DIRS}>")
+ target_link_libraries(network-viewer.plugin libnetdata
+ "$<$<BOOL:${MNL_FOUND}>:${MNL_LIBRARIES}>")
+
install(TARGETS network-viewer.plugin
COMPONENT network_viewer_plugin
diff --git a/collectors/apps.plugin/apps_plugin.c b/collectors/apps.plugin/apps_plugin.c
index fdaac0a3cf..4144787edc 100644
--- a/collectors/apps.plugin/apps_plugin.c
+++ b/collectors/apps.plugin/apps_plugin.c
@@ -4483,7 +4483,7 @@ static void function_processes(const char *transaction, char *function __maybe_u
unsigned int io_divisor = 1024 * RATES_DETAIL;
BUFFER *wb = buffer_create(4096, NULL);
- buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
buffer_json_member_add_string(wb, "type", "table");
buffer_json_member_add_time_t(wb, "update_every", update_every);
@@ -5264,7 +5264,6 @@ static bool apps_plugin_exit = false;
int main(int argc, char **argv) {
clocks_init();
nd_log_initialize_for_external_plugins("apps.plugin");
- for_each_open_fd(OPEN_FD_ACTION_CLOSE, OPEN_FD_EXCLUDE_STDIN|OPEN_FD_EXCLUDE_STDOUT|OPEN_FD_EXCLUDE_STDERR);
pagesize = (size_t)sysconf(_SC_PAGESIZE);
diff --git a/collectors/network-viewer.plugin/network-viewer.c b/collectors/network-viewer.plugin/network-viewer.c
index fb82ba0721..39f6205ce7 100644
--- a/collectors/network-viewer.plugin/network-viewer.c
+++ b/collectors/network-viewer.plugin/network-viewer.c
@@ -106,7 +106,7 @@ void network_viewer_function(const char *transaction, char *function __maybe_unu
CLEAN_BUFFER *wb = buffer_create(0, NULL);
buffer_flush(wb);
wb->content_type = CT_APPLICATION_JSON;
- buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
buffer_json_member_add_string(wb, "type", "table");
@@ -125,6 +125,7 @@ void network_viewer_function(const char *transaction, char *function __maybe_unu
.udp4 = true,
.udp6 = true,
.pid = true,
+ .uid = true,
.cmdline = true,
.comm = true,
.namespaces = true,
diff --git a/collectors/network-viewer.plugin/viewer.html b/collectors/network-viewer.plugin/viewer.html
index 2ebd4ad4f4..b66f920ae1 100644
--- a/collectors/network-viewer.plugin/viewer.html
+++ b/collectors/network-viewer.plugin/viewer.html
@@ -209,83 +209,162 @@
return `rgba(${r}, ${g}, ${b}, 0.5)`;
}
+ function getRgbColor(hex, opacity = 1) {
+ if (hex.length !== 7 || hex[0] !== "#") throw new Error("Invalid hex color format");
+
+ // Parse the hex color components (red, green, blue)
+ const r = parseInt(hex.slice(1, 3), 16);
+ const g = parseInt(hex.slice(3, 5), 16);
+ const b = parseInt(hex.slice(5, 7), 16);
+
+ // Ensure opacity is within the valid range (0 to 1)
+ const validOpacity = Math.min(1, Math.max(0, opacity));
+
+ // Return the RGBA color
+ return `rgba(${r}, ${g}, ${b}, ${validOpacity})`;
+ }
+
function drawInitialChart(svg, data, w, h, borderPadding, theme) {
const cw = w / 2;
const ch = h / 2;
document.body.style.backgroundColor = theme.backgroundColor;
- svg.append('rect')
- .attr('x', 0)
- .attr('y', 0)
- .attr('width', '100%')
- .attr('height', borderPadding / 2)
- .style('fill', hexToHalfOpacityRGBA(theme.clientColor));
-
- svg.append('text')
- .text('Clients')
- .attr('x', '50%')
- .attr('y', borderPadding / 2 - 4)
- .attr('text-anchor', 'middle')
- .style('font-family', theme.borderFontFamily)
- .style('font-size', theme.borderFontSize)
- .style('font-weight', theme.borderFontWeight)
- .style('fill', theme.borderFontColor);
-
- svg.append('rect')
- .attr('x', 0)
- .attr('y', h - borderPadding / 2)
- .attr('width', '100%')
- .attr('height', borderPadding / 2)
- .style('fill', hexToHalfOpacityRGBA(theme.serverColor));
-
- svg.append('text')
- .text('Servers')
- .attr('x', '50%')
- .attr('y', h - borderPadding / 2 + 16)
- .attr('text-anchor', 'middle')
- .style('font-family', theme.borderFontFamily)
- .style('font-size', theme.borderFontSize)
- .style('font-weight', theme.borderFontWeight)
- .style('fill', theme.borderFontColor);
-
- svg.append('rect')
- .attr('x', w - borderPadding / 2)
- .attr('y', 0)
- .attr('width', borderPadding / 2)
- .attr('height', '100%')
- .style('fill', hexToHalfOpacityRGBA(theme.publicColor));
-
- svg.append('text')
- .text('Public')
- .attr('x', w - (borderPadding / 2))
- .attr('y', ch - 10)
- .attr('text-anchor', 'middle')
- .attr('dominant-baseline', 'middle')
- .attr('transform', `rotate(90, ${w - (borderPadding / 2)}, ${ch})`)
- .style('font-family', theme.borderFontFamily)
- .style('font-size', theme.borderFontSize)
- .style('font-weight', theme.borderFontWeight)
- .style('fill', theme.borderFontColor);
-
- svg.append('rect')
- .attr('x', 0)
- .attr('y', 0)
- .attr('width', borderPadding / 2)
- .attr('height', '100%')
- .style('fill', hexToHalfOpacityRGBA(theme.privateColor));
-
- svg.append('text')
- .text('Private')
- .attr('x', borderPadding / 2)
- .attr('y', ch)
- .attr('text-anchor', 'middle')
- .attr('dominant-baseline', 'middle')
- .attr('transform', `rotate(-90, ${borderPadding / 2 - 10}, ${ch})`)
- .style('font-family', theme.borderFontFamily)
- .style('font-size', theme.borderFontSize)
- .style('font-weight', theme.borderFontWeight)
- .style('fill', theme.borderFontColor);
+ const clientsGradient = svg.append("defs")
+ .append("linearGradient")
+ .attr("id", "clientsGradient")
+ .attr("x1", "0%")
+ .attr("y1", "0%")
+ .attr("x2", "0%")
+ .attr("y2", "100%");
+
+ clientsGradient.append("stop")
+ .attr("offset", "0%")
+ .style("stop-color", getRgbColor(theme.clientColor, 1));
+
+ clientsGradient.append("stop")
+ .attr("offset", "100%")
+ .style("stop-color", getRgbColor(theme.clientColor, 0));
+
+ svg.append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", "100%")
+ .attr("height", borderPadding / 2)
+ .style("fill", "url(#clientsGradient)");
+
+ svg.append("text")
+ .text("Clients")
+ .attr("x", "50%")
+ .attr("y", borderPadding / 2 - 4)
+ .attr("text-anchor", "middle")
+ .style("font-family", theme.borderFontFamily)
+ .style("font-size", theme.borderFontSize)
+ .style("font-weight", theme.borderFontWeight)
+ .style("fill", theme.borderFontColor);
+
+ const serversGradient = svg.append("defs")
+ .append("linearGradient")
+ .attr("id", "serversGradient")
+ .attr("x1", "0%")
+ .attr("y1", "100%") // Start from the bottom
+ .attr("x2", "0%")
+ .attr("y2", "0%") // End at the top
+
+ serversGradient.append("stop")
+ .attr("offset", "0%")
+ .style("stop-color", getRgbColor(theme.serverColor, 1));
+
+ serversGradient.append("stop")
+ .attr("offset", "100%")
+ .style("stop-color", getRgbColor(theme.serverColor, 0));
+
+ svg.append("rect")
+ .attr("x", 0)
+ .attr("y", h - borderPadding / 2)
+ .attr("width", "100%")
+ .attr("height", borderPadding / 2)
+ .style("fill", "url(#serversGradient)"); // Use the reversed gradient fill
+
+ svg.append("text")
+ .text("Servers")
+ .attr("x", "50%")
+ .attr("y", h - borderPadding / 2 + 16)
+ .attr("text-anchor", "middle")
+ .style("font-family", theme.borderFontFamily)
+ .style("font-size", theme.borderFontSize)
+ .style("font-weight", theme.borderFontWeight)
+ .style("fill", theme.borderFontColor);
+
+ const publicGradient = svg.append("defs")
+ .append("linearGradient")
+ .attr("id", "publicGradient")
+ .attr("x1", "100%") // Start from the right
+ .attr("y1", "0%")
+ .attr("x2", "0%") // End at the left
+ .attr("y2", "0%");
+
+ publicGradient.append("stop")
+ .attr("offset", "0%")
+ .style("stop-color", getRgbColor(theme.publicColor, 1));
+
+ publicGradient.append("stop")
+ .attr("offset", "100%")
+ .style("stop-color", getRgbColor(theme.publicColor, 0));
+
+ svg.append("rect")
+ .attr("x", w - borderPadding / 2)
+ .attr("y", 0)
+ .attr("width", borderPadding / 2)
+ .attr("height", "100%")
+ .style("fill", "url(#publicGradient)");
+
+ svg.append("text")
+ .text("Public")
+ .attr("x", w - (borderPadding / 2))
+ .attr("y", ch - 10)
+ .attr("text-anchor", "middle")
+ .attr("dominant-baseline", "middle")
+ .attr("transform", `rotate(90, ${w - (borderPadding / 2)}, ${ch})`)
+ .style("font-family", theme.borderFontFamily)
+ .style("font-size", theme.borderFontSize)
+ .style("font-weight", theme.borderFontWeight)
+ .style("fill", theme.borderFontColor);
+
+ const privateGradient = svg.append("defs")
+ .append("linearGradient")
+ .attr("id", "privateGradient")
+ .attr("x1", "0%") // Start from the left
+ .attr("y1", "0%")
+ .attr("x2", "100%") // End at the right
+ .attr("y2", "0%");
+
+ privateGradient.append("stop")
+ .attr("offset", "0%")
+ .style("stop-color", getRgbColor(theme.privateColor, 1));
+
+ privateGradient.append("stop")
+ .attr("offset", "100%")
+ .style("stop-color", getRgbColor(theme.privateColor, 0));
+
+ svg.append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", borderPadding / 2)
+ .attr("height", "100%")
+ .style("fill", "url(#privateGradient)");
+
+ svg.append("text")
+ .text("Private")
+ .attr("x", borderPadding / 2)
+ .attr("y", ch)
+ .attr("text-anchor", "middle")
+ .attr("dominant-baseline", "middle")
+ .attr("transform", `rotate(-90, ${borderPadding / 2 - 10}, ${ch})`)
+ .style("font-family", theme.borderFontFamily)
+ .style("font-size", theme.borderFontSize)
+ .style("font-weight", theme.borderFontWeight)
+ .style("fill", theme.borderFontColor);
}
let positionsMap = new Map();
diff --git a/collectors/plugins.d/local-sockets.h b/collectors/plugins.d/local-sockets.h
index 4958594fe5..3a21eef4ac 100644
--- a/collectors/plugins.d/local-sockets.h
+++ b/collectors/plugins.d/local-sockets.h
@@ -5,6 +5,19 @@
#include "libnetdata/libnetdata.h"
+// disable libmnl for the moment
+#undef HAVE_LIBMNL
+
+#ifdef HAVE_LIBMNL
+#include <linux/inet_diag.h>
+#include <linux/sock_diag.h>
+#include <linux/unix_diag.h>
+#include <linux/netlink.h>
+#include <libmnl/libmnl.h>
+#endif
+
+#define UID_UNSET (uid_t)(UINT32_MAX)
+
// --------------------------------------------------------------------------------------------------------------------
// hashtable for keeping the namespaces
// key and value is the namespace inode
@@ -67,6 +80,7 @@ typedef struct local_socket_state {
bool pid;
bool cmdline;
bool comm;
+ bool uid;
bool namespaces;
size_t max_errors;
@@ -84,6 +98,12 @@ typedef struct local_socket_state {
size_t errors_encountered;
} stats;
+#ifdef HAVE_LIBMNL
+ bool use_nl;
+ struct mnl_socket *nl;
+ uint16_t tmp_protocol;
+#endif
+
uint64_t proc_self_net_ns_inode;
SIMPLE_HASHTABLE_NET_NS ns_hashtable;
@@ -110,6 +130,7 @@ typedef enum __attribute__((packed)) {
struct pid_socket {
uint64_t inode;
pid_t pid;
+ uid_t uid;
uint64_t net_ns_inode;
char *cmdline;
char comm[TASK_COMM_LEN];
@@ -155,6 +176,13 @@ typedef struct local_socket {
SOCKET_DIRECTION direction;
+ uint8_t timer;
+ uint8_t retransmits;
+ uint32_t expires;
+ uint32_t rqueue;
+ uint32_t wqueue;
+ uid_t uid;
+
char comm[TASK_COMM_LEN];
char *cmdline;
@@ -305,6 +333,7 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
continue;
}
net_ns_inode = 0;
+ uid_t uid = UID_UNSET;
struct dirent *fd_entry;
while ((fd_entry = readdir(fd_dir)) != NULL) {
@@ -319,6 +348,22 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode, &inode, true);
struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl);
if(!ps || (ps->pid == 1 && pid != 1)) {
+ if(uid == UID_UNSET && ls->config.uid) {
+ char status_buf[512];
+ snprintfz(filename, sizeof(filename), "%s/%s/status", proc_filename, proc_entry->d_name);
+ if (read_txt_file(filename, status_buf, sizeof(status_buf)))
+ local_sockets_log(ls, "cannot open file: %s\n", filename);
+ else {
+ char *u = strstr(status_buf, "Uid:");
+ if(u) {
+ u += 4;
+ while(isspace(*u)) u++; // skip spaces
+ while(*u >= '0' && *u <= '9') u++; // skip the first number (real uid)
+ while(isspace(*u)) u++; // skip spaces again
+ uid = strtol(u, NULL, 10); // parse the 2nd number (effective uid)
+ }
+ }
+ }
if(!comm[0] && ls->config.comm) {
snprintfz(filename, sizeof(filename), "%s/%s/comm", proc_filename, proc_entry->d_name);
if (read_txt_file(filename, comm, sizeof(comm)))
@@ -351,6 +396,7 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
ps->inode = inode;
ps->pid = pid;
+ ps->uid = uid;
ps->net_ns_inode = net_ns_inode;
strncpyz(ps->comm, comm, sizeof(ps->comm) - 1);
@@ -502,6 +548,193 @@ static inline void local_sockets_index_listening_port(LS_STATE *ls, LOCAL_SOCKET
}
}
+static inline bool local_sockets_add_socket(LS_STATE *ls, LOCAL_SOCKET *tmp) {
+ if(!tmp->inode) return false;
+
+ SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, tmp->inode, &tmp->inode, true);
+ LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
+ if(n) {
+ local_sockets_log(ls, "inode %" PRIu64" already exists in hashtable - ignoring duplicate", tmp->inode);
+ return false;
+ }
+
+ n = (LOCAL_SOCKET *)callocz(1, sizeof(LOCAL_SOCKET));
+ *n = *tmp; // copy all contents
+
+ // fix the key
+ n->local_port_key.port = n->local.port;
+ n->local_port_key.family = n->local.family;
+ n->local_port_key.protocol = n->local.protocol;
+ n->local_port_key.net_ns_inode = ls->proc_self_net_ns_inode;
+
+ n->local_ip_hash = XXH3_64bits(&n->local.ip, sizeof(n->local.ip));
+ n->remote_ip_hash = XXH3_64bits(&n->remote.ip, sizeof(n->remote.ip));
+ n->local_port_hash = XXH3_64bits(&n->local_port_key, sizeof(n->local_port_key));
+
+ // --- look up a pid for it -----------------------------------------------------------------------------------
+
+ SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, n->inode, &n->inode, false);
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
+ if(ps) {
+ n->net_ns_inode = ps->net_ns_inode;
+ n->pid = ps->pid;
+
+ if(ps->uid != UID_UNSET && n->uid == UID_UNSET)
+ n->uid = ps->uid;
+
+ if(ps->cmdline)
+ n->cmdline = strdupz(ps->cmdline);
+ strncpyz(n->comm, ps->comm, sizeof(n->comm) - 1);
+ }
+
+ // --- index it -----------------------------------------------------------------------------------------------
+
+ simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
+
+ if(!local_sockets_is_zero_address(&n->local)) {
+ // put all the local IPs into the local_ips hashtable
+ // so, we learn all local IPs the system has
+
+ SIMPLE_HASHTABLE_SLOT_LOCAL_IP *sl_ip =
+ simple_hashtable_get_slot_LOCAL_IP(&ls->local_ips_hashtable, n->local_ip_hash, &n->local.ip, true);
+
+ union ipv46 *ip = SIMPLE_HASHTABLE_SLOT_DATA(sl_ip);
+ if(!ip)
+ simple_hashtable_set_slot_LOCAL_IP(&ls->local_ips_hashtable, sl_ip, n->local_ip_hash, &n->local.ip);
+ }
+
+ // --- 1st phase for direction detection ----------------------------------------------------------------------
+
+ if((n->local.protocol == IPPROTO_TCP && n->state == TCP_LISTEN) ||
+ local_sockets_is_zero_address(&n->local) ||
+ local_sockets_is_zero_address(&n->remote)) {
+ // the socket is either in a TCP LISTEN, or
+ // the remote address is zero
+ n->direction |= SOCKET_DIRECTION_LISTEN;
+ }
+ else if(
+ local_sockets_is_loopback_address(&n->local) ||
+ local_sockets_is_loopback_address(&n->remote)) {
+ // the local IP address is loopback
+ n->direction |= SOCKET_DIRECTION_LOCAL;
+ }
+ else {
+ // we can't say yet if it is inbound or outboud
+ // so, mark it as both inbound and outbound
+ n->direction |= SOCKET_DIRECTION_INBOUND | SOCKET_DIRECTION_OUTBOUND;
+ }
+
+ // --- index it in LISTENING_PORT -----------------------------------------------------------------------------
+
+ local_sockets_index_listening_port(ls, n);
+
+ return true;
+}
+
+#ifdef HAVE_LIBMNL
+
+static inline void local_sockets_netlink_init(LS_STATE *ls) {
+ ls->use_nl = true;
+ ls->nl = mnl_socket_open(NETLINK_INET_DIAG);
+ if (!ls->nl) {
+ local_sockets_log(ls, "cannot open netlink socket");
+ ls->use_nl = false;
+ }
+
+ if (mnl_socket_bind(ls->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ local_sockets_log(ls, "cannot bind netlink socket");
+ ls->use_nl = false;
+ }
+}
+
+static inline void local_sockets_netlink_cleanup(LS_STATE *ls) {
+ if(ls->nl) {
+ mnl_socket_close(ls->nl);
+ ls->nl = NULL;
+ }
+}
+
+static inline int local_sockets_netlink_cb_data(const struct nlmsghdr *nlh, void *data) {
+ LS_STATE *ls = data;
+
+ struct inet_diag_msg *diag_msg = mnl_nlmsg_get_payload(nlh);
+
+ LOCAL_SOCKET n = {
+ .inode = diag_msg->idiag_inode,
+ .direction = SOCKET_DIRECTION_NONE,
+ .state = diag_msg->idiag_state,
+ .local = {
+ .protocol = ls->tmp_protocol,
+ .family = diag_msg->idiag_family,
+ .port = diag_msg->id.idiag_sport,
+ },
+ .remote = {
+ .protocol = ls->tmp_protocol,
+ .family = diag_msg->idiag_family,
+ .port = diag_msg->id.idiag_dport,
+ },
+ .timer = diag_msg->idiag_timer,
+ .retransmits = diag_msg->idiag_retrans,
+ .expires = diag_msg->idiag_expires,
+ .rqueue = diag_msg->idiag_rqueue,
+ .wqueue = diag_msg->idiag_wqueue,
+ .uid = diag_msg->idiag_uid,
+ };
+
+ if (diag_msg->idiag_family == AF_INET) {
+ memcpy(&n.local.ip.ipv4, diag_msg->id.idiag_src, sizeof(n.local.ip.ipv4));
+ memcpy(&n.remote.ip.ipv4, diag_msg->id.idiag_dst, sizeof(n.remote.ip.ipv4));
+ }
+ else if (diag_msg->idiag_family == AF_INET6) {
+ memcpy(&n.local.ip.ipv6, diag_msg->id.idiag_src, sizeof(n.local.ip.ipv6));
+ memcpy(&n.remote.ip.ipv6, diag_msg->id.idiag_dst, sizeof(n.remote.ip.ipv6));
+ }
+
+ local_sockets_add_socket(ls, &n);
+
+ return MNL_CB_OK;
+}
+
+static inline bool local_sockets_netlink_get_sockets(LS_STATE *ls, uint16_t family, uint16_t protocol) {
+ ls->tmp_protocol = protocol;
+
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct inet_diag_req_v2 req;
+ unsigned int seq, portid = mnl_socket_get_portid(ls->nl);
+
+ memset(&req, 0, sizeof(req));
+ req.sdiag_family = family;
+ req.sdiag_protocol = protocol;
+ req.idiag_states = -1;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ nlh->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+ nlh->nlmsg_seq = seq = time(NULL);
+ mnl_nlmsg_put_extra_header(nlh, sizeof(req));
+ memcpy(mnl_nlmsg_get_payload(nlh), &req, sizeof(req));
+
+ if (mnl_socket_sendto(ls->nl, nlh, nlh->nlmsg_len) < 0) {
+ local_sockets_log(ls, "mnl_socket_send failed");
+ return false;
+ }
+
+ ssize_t ret;
+ while ((ret = mnl_socket_recvfrom(ls->nl, buf, sizeof(buf))) > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, local_sockets_netlink_cb_data, ls);
+ if (ret <= MNL_CB_STOP)
+ break;
+ }
+ if (ret == -1) {
+ local_sockets_log(ls, "mnl_socket_recvfrom");
+ return false;
+ }
+
+ return true;
+}
+#endif // HAVE_LIBMNL
+
static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
if(family != AF_INET && family != AF_INET6)
return false;
@@ -545,109 +778,34 @@ static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filen
continue;
}
}
- if(!inode) continue;
-
- SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, inode, &inode, true);
- LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
- if(n) {
- local_sockets_log(
- ls,
- "inode %" PRIu64
- " given on line %zu of filename '%s', already exists in hashtable - ignoring duplicate",
- inode,
- counter,
- filename);
- continue;
- }
-
- // allocate a new socket and index it
- n = (LOCAL_SOCKET *)callocz(1, sizeof(LOCAL_SOCKET));
-
- // --- initialize it ------------------------------------------------------------------------------------------
+ LOCAL_SOCKET n = {
+ .inode = inode,
+ .direction = SOCKET_DIRECTION_NONE,
+ .state = (int)state,
+ .local = {
+ .family = family,
+ .protocol = protocol,
+ .port = local_port,
+ },
+ .remote = {
+ .family = family,
+ .protocol = protocol,
+ .port = remote_port,
+ },
+ .uid = UID_UNSET,
+ };
if(family == AF_INET) {
- n->local.ip.ipv4 = local_address;
- n->remote.ip.ipv4 = remote_address;
+ n.local.ip.ipv4 = local_address;
+ n.remote.ip.ipv4 = remote_address;
}
else if(family == AF_INET6) {
- ipv6_to_in6_addr(local_address6, &n->local.ip.ipv6);
- ipv6_to_in6_addr(remote_address6, &n->remote.ip.ipv6);
- }
-
- n->direction = 0;
- n->state = (int)state;
- n->inode = inode;
-
- n->local.family = family;
- n->local.protocol = protocol;
- n->local.port = local_port;
-
- n->remote.family = family;
- n->remote.protocol = protocol;
- n->remote.port = remote_port;
-
- n->local_port_key.port = n->local.port;
- n->local_port_key.family = family;
- n->local_port_key.protocol = protocol;
- n->local_port_key.net_ns_inode = ls->proc_self_net_ns_inode;
-
- n->local_ip_hash = XXH3_64bits(&n->local.ip, sizeof(n->local.ip));
- n->remote_ip_hash = XXH3_64bits(&n->remote.ip, sizeof(n->remote.ip));
- n->local_port_hash = XXH3_64bits(&n->local_port_key, sizeof(n->local_port_key));
-
- // --- look up a pid for it -----------------------------------------------------------------------------------
-
- SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode, &inode, false);
- struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
- if(ps) {
- n->net_ns_inode = ps->net_ns_inode;
- n->pid = ps->pid;
- if(ps->cmdline)
- n->cmdline = strdupz(ps->cmdline);
- strncpyz(n->comm, ps->comm, sizeof(n->comm) - 1);
- }
-
- // --- index it -----------------------------------------------------------------------------------------------
-
- simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, inode, n);
-
- if(!local_sockets_is_zero_address(&n->local)) {
- // put all the local IPs into the local_ips hashtable
- // so, we learn all local IPs the system has
-
- SIMPLE_HASHTABLE_SLOT_LOCAL_IP *sl_ip =
- simple_hashtable_get_slot_LOCAL_IP(&ls->local_ips_hashtable, n->local_ip_hash, &n->local.ip, true);
-
- union ipv46 *ip = SIMPLE_HASHTABLE_SLOT_DATA(sl_ip);
- if(!ip)
- simple_hashtable_set_slot_LOCAL_IP(&ls->local_ips_hashtable, sl_ip, n->local_ip_hash, &n->local.ip);
- }
-
- // --- 1st phase for direction detection ----------------------------------------------------------------------
-
- if((n->local.protocol == IPPROTO_TCP && n->state == TCP_LISTEN) ||
- local_sockets_is_zero_address(&n->local) ||
- local_sockets_is_zero_address(&n->remote)) {
- // the socket is either in a TCP LISTEN, or
- // the remote address is zero
- n->direction |= SOCKET_DIRECTION_LISTEN;
- }
- else if(
- local_sockets_is_loopback_address(&n->local) ||
- local_sockets_is_loopback_address(&n->remote)) {
- // the local IP address is loopback
- n->direction |= SOCKET_DIRECTION_LOCAL;
- }
- else {
- // we can't say yet if it is inbound or outboud
- // so, mark it as both inbound and outbound
- n->direction |= SOCKET_DIRECTION_INBOUND | SOCKET_DIRECTION_OUTBOUND;
+ ipv6_to_in6_addr(local_address6, &n.local.ip.ipv6);
+ ipv6_to_in6_addr(remote_address6, &n.remote.ip.ipv6);
}
- // --- index it in LISTENING_PORT -----------------------------------------------------------------------------
-
- local_sockets_index_listening_port(ls, n);
+ local_sockets_add_socket(ls, &n);
}
fclose(fp);
@@ -744,6 +902,19 @@ static inline void local_sockets_cleanup(LS_STATE *ls) {
// --------------------------------------------------------------------------------------------------------------------
+static inline void local_sockets_do_family_protocol(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
+#ifdef HAVE_LIBMNL
+ if(ls->use_nl) {
+ ls->use_nl = local_sockets_netlink_get_sockets(ls, family, protocol);
+
+ if(ls->use_nl)
+ return;
+ }
+#endif
+
+ local_sockets_read_proc_net_x(ls, filename, family, protocol);
+}
+
static inline void local_sockets_read_sockets_from_proc(LS_STATE *ls) {
char path[FILENAME_MAX + 1];
@@ -759,22 +930,22 @@ static inline void local_sockets_read_sockets_from_proc(LS_STATE *ls) {
if(ls->config.tcp4) {
snprintfz(path, sizeof(path), "%s/proc/net/tcp", ls->config.host_prefix);
- local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_TCP);
+ local_sockets_do_family_protocol(ls, path, AF_INET, IPPROTO_TCP);
}
if(ls->config.udp4) {
snprintfz(path, sizeof(path), "%s/proc/net/udp", ls->config.host_prefix);
- local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_UDP);
+ local_sockets_do_family_protocol(ls, path, AF_INET, IPPROTO_UDP);
}
if(ls->config.tcp6) {
snprintfz(path, sizeof(path), "%s/proc/net/tcp6", ls->config.host_prefix);
- local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_TCP);
+ local_sockets_do_family_protocol(ls, path, AF_INET6, IPPROTO_TCP);
}
if(ls->config.udp6) {
snprintfz(path, sizeof(path), "%s/proc/net/udp6", ls->config.host_prefix);
- local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_UDP);
+ local_sockets_do_family_protocol(ls, path, AF_INET6, IPPROTO_UDP);
}
}
@@ -865,6 +1036,11 @@ static inline bool local_sockets_get_namespace_sockets(LS_STATE *ls, struct pid_
exit(EXIT_FAILURE);
}
+#ifdef HAVE_LIBMNL
+ local_sockets_netlink_cleanup(ls);
+ local_sockets_netlink_init(ls);
+#endif
+
// read all sockets from /proc
local_sockets_read_sockets_from_proc(ls);
@@ -877,6 +1053,10 @@ static inline bool local_sockets_get_namespace_sockets(LS_STATE *ls, struct pid_
};
local_sockets_send_to_parent(ls, &zero, &cw);
+#ifdef HAVE_LIBMNL
+ local_sockets_netlink_cleanup(ls);
+#endif
+
close(pipefd[1]); // Close write end of pipe
exit(EXIT_SUCCESS);
}
@@ -985,6 +1165,11 @@ static inline void local_sockets_namespaces(LS_STATE *ls) {
// --------------------------------------------------------------------------------------------------------------------
static inline void local_sockets_process(LS_STATE *ls) {
+
+#ifdef HAVE_LIBMNL
+ local_sockets_netlink_init(ls);
+#endif
+
ls->config.host_prefix = netdata_configured_host_prefix;
// initialize our hashtables
@@ -1006,6 +1191,10 @@ static inline void local_sockets_process(LS_STATE *ls) {
// free all memory
local_sockets_cleanup(ls);
+
+#ifdef HAVE_LIBMNL
+ local_sockets_netlink_cleanup(ls);
+#endif
}
static inline void ipv6_address_to_txt(struct in6_addr *in6_addr, char *dst) {
diff --git a/collectors/plugins.d/local_listeners.c b/collectors/plugins.d/local_listeners.c
index adebeb86fc..4591b42c5c 100644
--- a/collectors/plugins.d/local_listeners.c
+++ b/collectors/plugins.d/local_listeners.c
@@ -55,7 +55,7 @@ static void print_local_listeners_debug(LS_STATE *ls __maybe_unused, LOCAL_SOCKE
ipv6_address_to_txt(&n->remote.ip.ipv6, remote_address);
}
- printf("%s, direction=%s%s%s%s%s pid=%d, state=0x%0x, ns=%"PRIu64", local=%s[:%u], remote=%s[:%u], comm=%s\n",
+ printf("%s, direction=%s%s%s%s%s pid=%d, state=0x%0x, ns=%"PRIu64", local=%s[:%u], remote=%s[:%u], uid=%u, comm=%s\n",
protocol_name(n),
(n->direction & SOCKET_DIRECTION_LISTEN) ? "LISTEN," : "",
(n->direction & SOCKET_DIRECTION_INBOUND) ? "INBOUND," : "",
@@ -67,12 +67,17 @@ static void print_local_listeners_debug(LS_STATE *ls __maybe_unused, LOCAL_SOCKE
n->net_ns_inode,
local_address, n-&g