summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/local-sockets/local-sockets.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libnetdata/local-sockets/local-sockets.h (renamed from src/libnetdata/maps/local-sockets.h)822
1 files changed, 612 insertions, 210 deletions
diff --git a/src/libnetdata/maps/local-sockets.h b/src/libnetdata/local-sockets/local-sockets.h
index 6f2ffd81a..06ac08767 100644
--- a/src/libnetdata/maps/local-sockets.h
+++ b/src/libnetdata/local-sockets/local-sockets.h
@@ -5,7 +5,14 @@
#include "libnetdata/libnetdata.h"
-#ifdef HAVE_LIBMNL
+#ifndef _countof
+#define _countof(x) (sizeof(x) / sizeof(*(x)))
+#endif
+
+#define LOCAL_SOCKETS_USE_SETNS
+#define USE_LIBMNL_AFTER_SETNS
+
+#if defined(HAVE_LIBMNL)
#include <linux/rtnetlink.h>
#include <linux/inet_diag.h>
#include <linux/sock_diag.h>
@@ -22,7 +29,7 @@
#define SIMPLE_HASHTABLE_VALUE_TYPE uint64_t
#define SIMPLE_HASHTABLE_NAME _NET_NS
-#include "libnetdata/simple_hashtable.h"
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
// --------------------------------------------------------------------------------------------------------------------
// hashtable for keeping the sockets of PIDs
@@ -31,7 +38,7 @@
struct pid_socket;
#define SIMPLE_HASHTABLE_VALUE_TYPE struct pid_socket
#define SIMPLE_HASHTABLE_NAME _PID_SOCKET
-#include "libnetdata/simple_hashtable.h"
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
// --------------------------------------------------------------------------------------------------------------------
// hashtable for keeping all the sockets
@@ -40,7 +47,7 @@ struct pid_socket;
struct local_socket;
#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_socket
#define SIMPLE_HASHTABLE_NAME _LOCAL_SOCKET
-#include "libnetdata/simple_hashtable.h"
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
// --------------------------------------------------------------------------------------------------------------------
// hashtable for keeping all local IPs
@@ -49,7 +56,7 @@ struct local_socket;
union ipv46;
#define SIMPLE_HASHTABLE_VALUE_TYPE union ipv46
#define SIMPLE_HASHTABLE_NAME _LOCAL_IP
-#include "libnetdata/simple_hashtable.h"
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
// --------------------------------------------------------------------------------------------------------------------
// hashtable for keeping all listening ports
@@ -58,12 +65,12 @@ union ipv46;
struct local_port;
#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_port
#define SIMPLE_HASHTABLE_NAME _LISTENING_PORT
-#include "libnetdata/simple_hashtable.h"
+#include "libnetdata/simple_hashtable/simple_hashtable.h"
// --------------------------------------------------------------------------------------------------------------------
struct local_socket_state;
-typedef void (*local_sockets_cb_t)(struct local_socket_state *state, struct local_socket *n, void *data);
+typedef void (*local_sockets_cb_t)(struct local_socket_state *state, const struct local_socket *n, void *data);
struct local_sockets_config {
bool listening;
@@ -80,6 +87,9 @@ struct local_sockets_config {
bool uid;
bool namespaces;
bool tcp_info;
+ bool no_mnl;
+ bool procfile;
+ bool report;
size_t max_errors;
size_t max_concurrent_namespaces;
@@ -88,34 +98,68 @@ struct local_sockets_config {
void *data;
const char *host_prefix;
+};
- // internal use
+struct local_sockets_state {
+ uint32_t nl_seq;
uint64_t net_ns_inode;
+ pid_t net_ns_pid;
+};
+
+struct timing_work {
+ usec_t start_ut;
+ usec_t end_ut;
+ const char *name;
+};
+
+struct local_sockets_ns_req {
+ struct local_sockets_config config;
+ struct local_sockets_state ns_state;
};
typedef struct local_socket_state {
struct local_sockets_config config;
+ struct local_sockets_state ns_state;
struct {
size_t mnl_sends;
- size_t namespaces_found;
size_t tcp_info_received;
size_t pid_fds_processed;
size_t pid_fds_opendir_failed;
size_t pid_fds_readlink_failed;
size_t pid_fds_parse_failed;
size_t errors_encountered;
+
+ size_t sockets_added;
+
+ size_t namespaces_found;
+ size_t namespaces_absent;
+ size_t namespaces_invalid;
+#if defined(LOCAL_SOCKETS_USE_SETNS)
+ size_t namespaces_forks_attempted;
+ size_t namespaces_forks_failed;
+ size_t namespaces_forks_unresponsive;
+ size_t namespaces_sockets_new;
+ size_t namespaces_sockets_existing;
+#endif
+
+ struct procfile_stats ff;
} stats;
+ size_t timings_idx;
+ struct timing_work timings[30];
+
+#if defined(LOCAL_SOCKETS_USE_SETNS)
bool spawn_server_is_mine;
SPAWN_SERVER *spawn_server;
+#endif
-#ifdef HAVE_LIBMNL
- bool use_nl;
- struct mnl_socket *nl;
+#if defined(HAVE_LIBMNL)
uint16_t tmp_protocol;
#endif
+ procfile *ff;
+
ARAL *local_socket_aral;
ARAL *pid_socket_aral;
SPINLOCK spinlock; // for namespaces
@@ -223,7 +267,9 @@ typedef struct local_socket {
#endif
} LOCAL_SOCKET;
-static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request);
+#if defined(LOCAL_SOCKETS_USE_SETNS)
+static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request);
+#endif
// --------------------------------------------------------------------------------------------------------------------
@@ -254,7 +300,7 @@ static bool local_sockets_is_ipv4_mapped_ipv6_address(const struct in6_addr *add
return memcmp(addr->s6_addr, ipv4_mapped_prefix, 12) == 0;
}
-static bool local_sockets_is_loopback_address(struct socket_endpoint *se) {
+static bool local_sockets_is_loopback_address(const struct socket_endpoint *se) {
if (se->family == AF_INET) {
// For IPv4, loopback addresses are in the 127.0.0.0/8 range
return (ntohl(se->ip.ipv4) >> 24) == 127; // Check if the first byte is 127
@@ -288,7 +334,7 @@ static inline bool local_sockets_is_ipv4_reserved_address(uint32_t ip) {
);
}
-static inline bool local_sockets_is_private_address(struct socket_endpoint *se) {
+static inline bool local_sockets_is_private_address(const struct socket_endpoint *se) {
if (se->family == AF_INET) {
return local_sockets_is_ipv4_reserved_address(se->ip.ipv4);
}
@@ -322,7 +368,7 @@ static inline bool local_sockets_is_private_address(struct socket_endpoint *se)
return false;
}
-static bool local_sockets_is_multicast_address(struct socket_endpoint *se) {
+static bool local_sockets_is_multicast_address(const struct socket_endpoint *se) {
if (se->family == AF_INET) {
// For IPv4, check if the address is 0.0.0.0
uint32_t ip = htonl(se->ip.ipv4);
@@ -337,7 +383,7 @@ static bool local_sockets_is_multicast_address(struct socket_endpoint *se) {
return false;
}
-static bool local_sockets_is_zero_address(struct socket_endpoint *se) {
+static bool local_sockets_is_zero_address(const struct socket_endpoint *se) {
if (se->family == AF_INET) {
// For IPv4, check if the address is 0.0.0.0
return se->ip.ipv4 == 0;
@@ -350,7 +396,7 @@ static bool local_sockets_is_zero_address(struct socket_endpoint *se) {
return false;
}
-static inline const char *local_sockets_address_space(struct socket_endpoint *se) {
+static inline const char *local_sockets_address_space(const struct socket_endpoint *se) {
if(local_sockets_is_zero_address(se))
return "zero";
else if(local_sockets_is_loopback_address(se))
@@ -363,14 +409,94 @@ static inline const char *local_sockets_address_space(struct socket_endpoint *se
return "public";
}
-// --------------------------------------------------------------------------------------------------------------------
+static inline void ipv6_address_to_txt(const struct in6_addr *in6_addr, char *dst) {
+ struct sockaddr_in6 sa = { 0 };
+
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(0);
+ sa.sin6_addr = *in6_addr;
+
+ // Convert to human-readable format
+ if (inet_ntop(AF_INET6, &(sa.sin6_addr), dst, INET6_ADDRSTRLEN) == NULL)
+ *dst = '\0';
+}
+
+static inline void ipv4_address_to_txt(uint32_t ip, char *dst) {
+ uint8_t octets[4];
+ octets[0] = ip & 0xFF;
+ octets[1] = (ip >> 8) & 0xFF;
+ octets[2] = (ip >> 16) & 0xFF;
+ octets[3] = (ip >> 24) & 0xFF;
+ sprintf(dst, "%u.%u.%u.%u", octets[0], octets[1], octets[2], octets[3]);
+}
-static inline bool is_local_socket_ipv46(LOCAL_SOCKET *n) {
+static inline bool is_local_socket_ipv46(const LOCAL_SOCKET *n) {
return n->local.family == AF_INET6 &&
- n->direction == SOCKET_DIRECTION_LISTEN &&
- local_sockets_is_zero_address(&n->local) &&
- n->ipv6ony.checked &&
- n->ipv6ony.ipv46;
+ n->direction == SOCKET_DIRECTION_LISTEN &&
+ local_sockets_is_zero_address(&n->local) &&
+ n->ipv6ony.checked &&
+ n->ipv6ony.ipv46;
+}
+
+static inline const char *local_sockets_protocol_name(LOCAL_SOCKET *n) {
+ if(n->local.family == AF_INET) {
+ if(n->local.protocol == IPPROTO_TCP)
+ return "TCP";
+ else if(n->local.protocol == IPPROTO_UDP)
+ return "UDP";
+ else
+ return "UNKNOWN_IPV4";
+ }
+ else if(is_local_socket_ipv46(n)) {
+ if (n->local.protocol == IPPROTO_TCP)
+ return "TCP46";
+ else if(n->local.protocol == IPPROTO_UDP)
+ return "UDP46";
+ else
+ return "UNKNOWN_IPV46";
+ }
+ else if(n->local.family == AF_INET6) {
+ if (n->local.protocol == IPPROTO_TCP)
+ return "TCP6";
+ else if(n->local.protocol == IPPROTO_UDP)
+ return "UDP6";
+ else
+ return "UNKNOWN_IPV6";
+ }
+ else
+ return "UNKNOWN";
+}
+
+static inline void local_listeners_print_socket(LS_STATE *ls __maybe_unused, const LOCAL_SOCKET *nn, void *data __maybe_unused) {
+ LOCAL_SOCKET *n = (LOCAL_SOCKET *)nn;
+
+ char local_address[INET6_ADDRSTRLEN];
+ char remote_address[INET6_ADDRSTRLEN];
+
+ if(n->local.family == AF_INET) {
+ ipv4_address_to_txt(n->local.ip.ipv4, local_address);
+ ipv4_address_to_txt(n->remote.ip.ipv4, remote_address);
+ }
+ else if(n->local.family == AF_INET6) {
+ ipv6_address_to_txt(&n->local.ip.ipv6, local_address);
+ 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], uid=%u, inode=%"PRIu64", comm=%s\n",
+ local_sockets_protocol_name(n),
+ (n->direction & SOCKET_DIRECTION_LISTEN) ? "LISTEN," : "",
+ (n->direction & SOCKET_DIRECTION_INBOUND) ? "INBOUND," : "",
+ (n->direction & SOCKET_DIRECTION_OUTBOUND) ? "OUTBOUND," : "",
+ (n->direction & (SOCKET_DIRECTION_LOCAL_INBOUND|SOCKET_DIRECTION_LOCAL_OUTBOUND)) ? "LOCAL," : "",
+ (n->direction == 0) ? "NONE," : "",
+ n->pid,
+ (unsigned int)n->state,
+ n->net_ns_inode,
+ local_address, n->local.port,
+ remote_address, n->remote.port,
+ n->uid,
+ n->inode,
+ n->comm);
}
// --------------------------------------------------------------------------------------------------------------------
@@ -504,7 +630,9 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
if(!local_sockets_read_proc_inode_link(ls, filename, &inode, "socket"))
continue;
- SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode, &inode, true);
+ // fprintf(stderr, "%d: PID %d is using socket inode %"PRIu64"\n", gettid_uncached(), pid, inode);
+ XXH64_hash_t inode_hash = XXH3_64bits(&inode, sizeof(inode));
+ SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode_hash, &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) {
@@ -545,7 +673,8 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
if(!net_ns_inode && ls->config.namespaces) {
snprintfz(filename, sizeof(filename), "%s/%s/ns/net", proc_filename, proc_entry->d_name);
if(local_sockets_read_proc_inode_link(ls, filename, &net_ns_inode, "net")) {
- SIMPLE_HASHTABLE_SLOT_NET_NS *sl_ns = simple_hashtable_get_slot_NET_NS(&ls->ns_hashtable, net_ns_inode, (uint64_t *)net_ns_inode, true);
+ XXH64_hash_t net_ns_inode_hash = XXH3_64bits(&net_ns_inode, sizeof(net_ns_inode));
+ SIMPLE_HASHTABLE_SLOT_NET_NS *sl_ns = simple_hashtable_get_slot_NET_NS(&ls->ns_hashtable, net_ns_inode_hash, (uint64_t *)net_ns_inode, true);
simple_hashtable_set_slot_NET_NS(&ls->ns_hashtable, sl_ns, net_ns_inode, (uint64_t *)net_ns_inode);
}
}
@@ -563,7 +692,8 @@ static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const ch
freez(ps->cmdline);
ps->cmdline = cmdline_trimmed ? strdupz(cmdline_trimmed) : NULL;
- simple_hashtable_set_slot_PID_SOCKET(&ls->pid_sockets_hashtable, sl, inode, ps);
+ simple_hashtable_set_slot_PID_SOCKET(&ls->pid_sockets_hashtable, sl, inode_hash, ps);
+ // fprintf(stderr, "%d: PID %d indexed for using socket inode %"PRIu64"\n", gettid_uncached(), pid, inode);
}
}
@@ -593,13 +723,16 @@ 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);
+ XXH64_hash_t inode_hash = XXH3_64bits(&tmp->inode, sizeof(tmp->inode));
+ SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, inode_hash, &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;
}
+ ls->stats.sockets_added++;
+
n = aral_mallocz(ls->local_socket_aral);
*n = *tmp; // copy all contents
@@ -615,7 +748,7 @@ static inline bool local_sockets_add_socket(LS_STATE *ls, LOCAL_SOCKET *tmp) {
// --- 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);
+ SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode_hash, &n->inode, false);
struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
if(ps) {
n->net_ns_inode = ps->net_ns_inode;
@@ -624,15 +757,19 @@ static inline bool local_sockets_add_socket(LS_STATE *ls, LOCAL_SOCKET *tmp) {
if(ps->uid != UID_UNSET && n->uid == UID_UNSET)
n->uid = ps->uid;
- if(ps->cmdline)
+ if(ps->cmdline) {
+ if(n->cmdline) string_freez(n->cmdline);
n->cmdline = string_strdupz(ps->cmdline);
+ }
strncpyz(n->comm, ps->comm, sizeof(n->comm) - 1);
}
+// else
+// fprintf(stderr, "%d: No PID found for inode %"PRIu64"\n", gettid_uncached(), n->inode);
// --- index it -----------------------------------------------------------------------------------------------
- simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
+ simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, inode_hash, n);
if(!local_sockets_is_zero_address(&n->local)) {
// put all the local IPs into the local_ips hashtable
@@ -668,31 +805,7 @@ static inline bool local_sockets_add_socket(LS_STATE *ls, LOCAL_SOCKET *tmp) {
return true;
}
-#ifdef HAVE_LIBMNL
-
-static inline void local_sockets_libmnl_init(LS_STATE *ls) {
- ls->nl = mnl_socket_open(NETLINK_INET_DIAG);
- if (ls->nl == NULL) {
- local_sockets_log(ls, "cannot open libmnl netlink socket");
- ls->use_nl = false;
- }
- else if (mnl_socket_bind(ls->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- local_sockets_log(ls, "cannot bind libmnl netlink socket");
- mnl_socket_close(ls->nl);
- ls->nl = NULL;
- ls->use_nl = false;
- }
- else
- ls->use_nl = true;
-}
-
-static inline void local_sockets_libmnl_cleanup(LS_STATE *ls) {
- if(ls->nl) {
- mnl_socket_close(ls->nl);
- ls->nl = NULL;
- ls->use_nl = false;
- }
-}
+#if defined(HAVE_LIBMNL)
static inline int local_sockets_libmnl_cb_data(const struct nlmsghdr *nlh, void *data) {
LS_STATE *ls = data;
@@ -767,16 +880,30 @@ static inline int local_sockets_libmnl_cb_data(const struct nlmsghdr *nlh, void
static inline bool local_sockets_libmnl_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);
+ struct mnl_socket *nl = mnl_socket_open(NETLINK_INET_DIAG);
+ if (nl == NULL) {
+ local_sockets_log(ls, "mnl_socket_open() failed");
+ return false;
+ }
- memset(&req, 0, sizeof(req));
- req.sdiag_family = family;
- req.sdiag_protocol = protocol;
- req.idiag_states = -1;
- req.idiag_ext = 0;
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ local_sockets_log(ls, "mnl_socket_bind() failed");
+ mnl_socket_close(nl);
+ return false;
+ }
+
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = ls->ns_state.nl_seq ? ls->ns_state.nl_seq++ : time(NULL);
+
+ struct inet_diag_req_v2 req = {
+ .sdiag_family = family,
+ .sdiag_protocol = protocol,
+ .idiag_states = ~0, // Request all socket states
+ .idiag_ext = 0,
+ };
if(family == AF_INET6)
req.idiag_ext |= 1 << (INET_DIAG_SKV6ONLY - 1);
@@ -784,35 +911,106 @@ static inline bool local_sockets_libmnl_get_sockets(LS_STATE *ls, uint16_t famil
if(protocol == IPPROTO_TCP && ls->config.tcp_info)
req.idiag_ext |= 1 << (INET_DIAG_INFO - 1);
- nlh = mnl_nlmsg_put_header(buf);
- nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
- nlh->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | 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));
ls->stats.mnl_sends++;
- if (mnl_socket_sendto(ls->nl, nlh, nlh->nlmsg_len) < 0) {
- local_sockets_log(ls, "mnl_socket_send failed");
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ local_sockets_log(ls, "mnl_socket_sendto() failed");
+ mnl_socket_close(nl);
return false;
}
+ bool rc = true;
+ size_t received = 0;
ssize_t ret;
- while ((ret = mnl_socket_recvfrom(ls->nl, buf, sizeof(buf))) > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, local_sockets_libmnl_cb_data, ls);
- if (ret <= MNL_CB_STOP)
+ while ((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, local_sockets_libmnl_cb_data, ls);
+ if (ret == MNL_CB_ERROR) {
+ local_sockets_log(ls, "mnl_cb_run() failed");
+ rc = false;
+ break;
+ }
+ else if (ret <= MNL_CB_STOP)
break;
+
+ received++;
}
+ mnl_socket_close(nl);
+
if (ret == -1) {
- local_sockets_log(ls, "mnl_socket_recvfrom");
+ local_sockets_log(ls, "mnl_socket_recvfrom() failed");
+ rc = false;
+ }
+
+ return rc;
+}
+#endif // HAVE_LIBMNL
+
+static inline bool local_sockets_process_proc_line(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol, size_t line, char **words, size_t num_words) {
+ // char *sl_txt = get_word(words, num_words, 0);
+ char *local_ip_txt = get_word(words, num_words, 1);
+ char *local_port_txt = get_word(words, num_words, 2);
+ char *remote_ip_txt = get_word(words, num_words, 3);
+ char *remote_port_txt = get_word(words, num_words, 4);
+ char *state_txt = get_word(words, num_words, 5);
+ char *tx_queue_txt = get_word(words, num_words, 6);
+ char *rx_queue_txt = get_word(words, num_words, 7);
+ char *tr_txt = get_word(words, num_words, 8);
+ char *tm_when_txt = get_word(words, num_words, 9);
+ char *retrans_txt = get_word(words, num_words, 10);
+ char *uid_txt = get_word(words, num_words, 11);
+ // char *timeout_txt = get_word(words, num_words, 12);
+ char *inode_txt = get_word(words, num_words, 13);
+
+ if(!local_ip_txt || !local_port_txt || !remote_ip_txt || !remote_port_txt || !state_txt ||
+ !tx_queue_txt || !rx_queue_txt || !tr_txt || !tm_when_txt || !retrans_txt || !uid_txt || !inode_txt) {
+ local_sockets_log(ls, "cannot parse ipv4 line No %zu of filename '%s'", line, filename);
return false;
}
+ LOCAL_SOCKET n = {
+ .direction = SOCKET_DIRECTION_NONE,
+ .ipv6ony = {
+ .checked = false,
+ .ipv46 = false,
+ },
+ .local = {
+ .family = family,
+ .protocol = protocol,
+ },
+ .remote = {
+ .family = family,
+ .protocol = protocol,
+ },
+ .uid = UID_UNSET,
+ };
+
+ n.local.port = str2uint32_hex(local_port_txt, NULL);
+ n.remote.port = str2uint32_hex(remote_port_txt, NULL);
+ n.state = str2uint32_hex(state_txt, NULL);
+ n.wqueue = str2uint32_hex(tx_queue_txt, NULL);
+ n.rqueue = str2uint32_hex(rx_queue_txt, NULL);
+ n.timer = str2uint32_hex(tr_txt, NULL);
+ n.expires = str2uint32_hex(tm_when_txt, NULL);
+ n.retransmits = str2uint32_hex(retrans_txt, NULL);
+ n.uid = str2uint32_t(uid_txt, NULL);
+ n.inode = str2uint64_t(inode_txt, NULL);
+
+ if(family == AF_INET) {
+ n.local.ip.ipv4 = str2uint32_hex(local_ip_txt, NULL);
+ n.remote.ip.ipv4 = str2uint32_hex(remote_ip_txt, NULL);
+ }
+ else if(family == AF_INET6) {
+ ipv6_to_in6_addr(local_ip_txt, &n.local.ip.ipv6);
+ ipv6_to_in6_addr(remote_ip_txt, &n.remote.ip.ipv6);
+ }
+
+ local_sockets_add_socket(ls, &n);
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) {
+static inline bool local_sockets_read_proc_net_x_getline(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
static bool is_space[256] = {
[':'] = true,
[' '] = true,
@@ -846,67 +1044,9 @@ static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filen
continue;
}
- LOCAL_SOCKET n = {
- .direction = SOCKET_DIRECTION_NONE,
- .ipv6ony = {
- .checked = false,
- .ipv46 = false,
- },
- .local = {
- .family = family,
- .protocol = protocol,
- },
- .remote = {
- .family = family,
- .protocol = protocol,
- },
- .uid = UID_UNSET,
- };
-
char *words[32];
size_t num_words = quoted_strings_splitter(line, words, 32, is_space);
- // char *sl_txt = get_word(words, num_words, 0);
- char *local_ip_txt = get_word(words, num_words, 1);
- char *local_port_txt = get_word(words, num_words, 2);
- char *remote_ip_txt = get_word(words, num_words, 3);
- char *remote_port_txt = get_word(words, num_words, 4);
- char *state_txt = get_word(words, num_words, 5);
- char *tx_queue_txt = get_word(words, num_words, 6);
- char *rx_queue_txt = get_word(words, num_words, 7);
- char *tr_txt = get_word(words, num_words, 8);
- char *tm_when_txt = get_word(words, num_words, 9);
- char *retrans_txt = get_word(words, num_words, 10);
- char *uid_txt = get_word(words, num_words, 11);
- // char *timeout_txt = get_word(words, num_words, 12);
- char *inode_txt = get_word(words, num_words, 13);
-
- if(!local_ip_txt || !local_port_txt || !remote_ip_txt || !remote_port_txt || !state_txt ||
- !tx_queue_txt || !rx_queue_txt || !tr_txt || !tm_when_txt || !retrans_txt || !uid_txt || !inode_txt) {
- local_sockets_log(ls, "cannot parse ipv4 line No %zu of filename '%s'", counter, filename);
- continue;
- }
-
- n.local.port = str2uint32_hex(local_port_txt, NULL);
- n.remote.port = str2uint32_hex(remote_port_txt, NULL);
- n.state = str2uint32_hex(state_txt, NULL);
- n.wqueue = str2uint32_hex(tx_queue_txt, NULL);
- n.rqueue = str2uint32_hex(rx_queue_txt, NULL);
- n.timer = str2uint32_hex(tr_txt, NULL);
- n.expires = str2uint32_hex(tm_when_txt, NULL);
- n.retransmits = str2uint32_hex(retrans_txt, NULL);
- n.uid = str2uint32_t(uid_txt, NULL);
- n.inode = str2uint64_t(inode_txt, NULL);
-
- if(family == AF_INET) {
- n.local.ip.ipv4 = str2uint32_hex(local_ip_txt, NULL);
- n.remote.ip.ipv4 = str2uint32_hex(remote_ip_txt, NULL);
- }
- else if(family == AF_INET6) {
- ipv6_to_in6_addr(local_ip_txt, &n.local.ip.ipv6);
- ipv6_to_in6_addr(remote_ip_txt, &n.remote.ip.ipv6);
- }
-
- local_sockets_add_socket(ls, &n);
+ local_sockets_process_proc_line(ls, filename, family, protocol, counter, words, num_words);
}
fclose(fp);
@@ -917,6 +1057,59 @@ static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filen
return true;
}
+#define INITIALLY_EXPECTED_PROC_NET_LINES 16384
+#define PROC_NET_BYTES_PER_LINE 155 // 105 for IPv4, 155 for IPv6
+#define PROC_NET_WORDS_PER_LINE 22
+#define INITIALLY_EXPECTED_PROC_NET_WORDS (INITIALLY_EXPECTED_PROC_NET_LINES * PROC_NET_WORDS_PER_LINE)
+#define INITIALLY_EXPECTED_PROC_NET_BYTES (INITIALLY_EXPECTED_PROC_NET_LINES * PROC_NET_BYTES_PER_LINE)
+
+static inline bool local_sockets_read_proc_net_x_procfile(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
+ if(family != AF_INET && family != AF_INET6)
+ return false;
+
+ procfile_set_adaptive_allocation(true, INITIALLY_EXPECTED_PROC_NET_BYTES, INITIALLY_EXPECTED_PROC_NET_LINES, INITIALLY_EXPECTED_PROC_NET_WORDS);
+
+ bool copy_initial_ff_stats = ls->ff == NULL && ls->stats.ff.memory > 0;
+ ls->ff = procfile_reopen(ls->ff, filename, ls->ff ? NULL :" :", PROCFILE_FLAG_DEFAULT);
+
+ // we just created ff, copy our old stats to it
+ if(ls->ff && copy_initial_ff_stats) ls->ff->stats = ls->stats.ff;
+
+ ls->ff = procfile_readall(ls->ff);
+ if(!ls->ff) return false;
+
+ // get the latest stats from ff;
+ ls->stats.ff = ls->ff->stats;
+
+ for(size_t l = 1; l < procfile_lines(ls->ff) ;l++) {
+ size_t w = procfile_linewords(ls->ff, l);
+ if(!w) continue;
+ if(w < 14) {
+ local_sockets_log(ls, "too small line No %zu of filename '%s' (has %zu words)", l, filename, w);
+ continue;
+ }
+
+ char *words[14] = { 0 };
+ words[0] = procfile_lineword(ls->ff, l, 0);
+ words[1] = procfile_lineword(ls->ff, l, 1);
+ words[2] = procfile_lineword(ls->ff, l, 2);
+ words[3] = procfile_lineword(ls->ff, l, 3);
+ words[4] = procfile_lineword(ls->ff, l, 4);
+ words[5] = procfile_lineword(ls->ff, l, 5);
+ words[6] = procfile_lineword(ls->ff, l, 6);
+ words[7] = procfile_lineword(ls->ff, l, 7);
+ words[8] = procfile_lineword(ls->ff, l, 8);
+ words[9] = procfile_lineword(ls->ff, l, 9);
+ words[10] = procfile_lineword(ls->ff, l, 10);
+ words[11] = procfile_lineword(ls->ff, l, 11);
+ words[12] = procfile_lineword(ls->ff, l, 12);
+ words[13] = procfile_lineword(ls->ff, l, 13);
+ local_sockets_process_proc_line(ls, filename, family, protocol, l, words, _countof(words));
+ }
+
+ return true;
+}
+
// --------------------------------------------------------------------------------------------------------------------
static inline void local_sockets_detect_directions(LS_STATE *ls) {
@@ -1008,31 +1201,33 @@ static inline void local_sockets_init(LS_STATE *ls) {
memset(&ls->stats, 0, sizeof(ls->stats));
-#ifdef HAVE_LIBMNL
- ls->use_nl = false;
- ls->nl = NULL;
+#if defined(HAVE_LIBMNL)
ls->tmp_protocol = 0;
- local_sockets_libmnl_init(ls);
#endif
+#if defined(LOCAL_SOCKETS_USE_SETNS)
if(ls->config.namespaces && ls->spawn_server == NULL) {
ls->spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, NULL, local_sockets_spawn_server_callback, 0, NULL);
ls->spawn_server_is_mine = true;
}
else
ls->spawn_server_is_mine = false;
+#endif
}
static inline void local_sockets_cleanup(LS_STATE *ls) {
+ if(ls->ff) {
+ ls->stats.ff = ls->ff->stats;
+ procfile_close(ls->ff);
+ ls->ff = NULL;
+ }
+#if defined(LOCAL_SOCKETS_USE_SETNS)
if(ls->spawn_server_is_mine) {
spawn_server_destroy(ls->spawn_server);
ls->spawn_server = NULL;
ls->spawn_server_is_mine = false;
}
-
-#ifdef HAVE_LIBMNL
- local_sockets_libmnl_cleanup(ls);
#endif
// free the sockets hashtable data
@@ -1070,28 +1265,93 @@ 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->nl && ls->use_nl) {
- ls->use_nl = local_sockets_libmnl_get_sockets(ls, family, protocol);
+static inline void local_sockets_track_time(LS_STATE *ls, const char *name) {
+ if(!ls->config.report || ls->timings_idx >= _countof(ls->timings))
+ return;
+
+ usec_t now_ut = now_monotonic_usec();
+
+ if(ls->timings_idx == 0 && !ls->timings[0].start_ut) {
+ ls->timings[0].start_ut = now_ut;
+ ls->timings[0].name = name;
+ }
+ else if(ls->timings_idx + 1 < _countof(ls->timings)) {
+ ls->timings[ls->timings_idx].end_ut = now_ut;
+ ls->timings_idx++;
+ ls->timings[ls->timings_idx].start_ut = now_ut;
+ ls->timings[ls->timings_idx].name = name;
+ }
+ else if(ls->timings_idx + 1 == _countof(ls->timings)) {
+ ls->timings[ls->timings_idx].end_ut = now_ut;
+ ls->timings_idx++; // out of bounds
+ }
+}
+
+static void local_sockets_track_time_by_protocol(LS_STATE *ls, bool mnl, uint16_t family, uint16_t protocol) {
+ if(mnl) {
+ if(family == AF_INET) {
+ if(protocol == IPPROTO_TCP)
+ local_sockets_track_time(ls, "mnl_read_tcp4");
+ else if(protocol == IPPROTO_UDP)
+ local_sockets_track_time(ls, "mnl_read_udp4");
+ }
+ else if(family == AF_INET6) {
+ if(protocol == IPPROTO_TCP)
+ local_sockets_track_time(ls, "mnl_read_tcp6");
+ else if(protocol == IPPROTO_UDP)
+ local_sockets_track_time(ls, "mnl_read_udp6");
+ }
+ else
+ local_sockets_track_time(ls, "mnl_read_unknown");
+ }
+ else {
+ if(family == AF_INET) {
+ if(protocol == IPPROTO_TCP)
+ local_sockets_track_time(ls, "proc_read_tcp4");
+ else if(protocol == IPPROTO_UDP)
+ local_sockets_track_time(ls, "proc_read_udp4");
+ }
+ else if(family == AF_INET6) {
+ if(protocol == IPPROTO_TCP)
+ local_sockets_track_time(ls, "proc_read_tcp6");
+ else if(protocol == IPPROTO_UDP)
+ local_sockets_track_time(ls, "proc_read_udp6");
+ }
+ else
+ local_sockets_track_time(ls, "proc_read_unknown");
+ }
+}
- if(ls->use_nl)
+static inline void local_sockets_do_family_protocol(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
+#if defined(HAVE_LIBMNL)
+ if(!ls->config.no_mnl) {
+ local_sockets_track_time_by_protocol(ls, true, family, protocol);
+ if(local_sockets_libmnl_get_sockets(ls, family, protocol))
return;
+
+ // else, do proc
}
#endif
- local_sockets_read_proc_net_x(ls, filename, family, protocol);
+ local_sockets_track_time_by_protocol(ls, false, family, protocol);
+
+ if(ls->config.procfile)
+ local_sockets_read_proc_net_x_procfile(ls, filename, family, protocol);
+ else
+ local_sockets_read_proc_net_x_getline(ls, filename, family, protocol);
}
static inline void local_sockets_read_all_system_sockets(LS_STATE *ls) {
char path[FILENAME_MAX + 1];
if(ls->config.namespaces) {
+ local_sockets_track_time(ls, "read_namespaces");
snprintfz(path, sizeof(path), "%s/proc/self/ns/net", ls->config.host_prefix);
local_sockets_read_proc_inode_link(ls, path, &ls->proc_self_net_ns_inode, "net");
}
if(ls->config.cmdline || ls->config.comm || ls->config.pid || ls->config.namespaces) {
+ local_sockets_track_time(ls, "proc_read_pids");
snprintfz(path, sizeof(path), "%s/proc", ls->config.host_prefix);
local_sockets_find_all_sockets_in_proc(ls, path);
}
@@ -1118,20 +1378,41 @@ static inline void local_sockets_read_all_system_sockets(LS_STATE *ls) {
}
// --------------------------------------------------------------------------------------------------------------------
+// switch namespaces to read namespace sockets
+
+#if defined(LOCAL_SOCKETS_USE_SETNS)
struct local_sockets_child_work {
int fd;
uint64_t net_ns_inode;
};
-static inline void local_sockets_send_to_parent(struct local_socket_state *ls __maybe_unused, struct local_socket *n, void *data) {
+#define LOCAL_SOCKET_TERMINATOR (struct local_socket) { \
+ .expires = UINT32_MAX, \
+ .timer = UINT8_MAX, \
+ .inode = UINT64_MAX, \
+ .net_ns_inode = UINT64_MAX, \
+}
+
+static inline bool local_socket_is_terminator(const struct local_socket *n) {
+ static const struct local_socket t = LOCAL_SOCKET_TERMINATOR;
+ return (n->expires == t.expires &&
+ n->timer == t.timer &&
+ n->inode == t.inode &&
+ n->net_ns_inode == t.net_ns_inode);
+}
+
+static inline void local_sockets_send_to_parent(struct local_socket_state *ls, const struct local_socket *n, void *data) {
struct local_sockets_child_work *cw = data;
int fd = cw->fd;
- if(n->net_ns_inode != cw->net_ns_inode)
- return;
-
- // local_sockets_log(ls, "child is sending inode %"PRIu64" of namespace %"PRIu64, n->inode, n->net_ns_inode);
+ if(!local_socket_is_terminator(n)) {
+ ls->stats.errors_encountered = 0;
+// local_sockets_log(
+// ls,
+// "child is sending inode %"PRIu64" of namespace %"PRIu64", from namespace %"PRIu64" for pid %d",
+// n->inode, n->net_ns_inode, ls->proc_self_net_ns_inode, ls->ns_state.net_ns_pid);
+ }
if(write(fd, n, sizeof(*n)) != sizeof(*n))
local_sockets_log(ls, "failed to write local socket to pipe");
@@ -1145,9 +1426,15 @@ static inline void local_sockets_send_to_parent(struct local_socket_state *ls __
local_sockets_log(ls, "failed to write cmdline to pipe");
}
-static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
+static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
+ static const struct local_socket terminator = LOCAL_SOCKET_TERMINATOR;
+
+ struct local_sockets_ns_req *req = (struct local_sockets_ns_req *)request->data;
+
LS_STATE ls = { 0 };
- ls.config = *((struct local_sockets_config *)request->data);
+ ls.config = req->config;
+ ls.ns_state = req->ns_state;
+ ls.ns_state.nl_seq += gettid_uncached() * 10;
// we don't need these inside namespaces
ls.config.cmdline = false;
@@ -1155,9 +1442,13 @@ static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
ls.config.pid = false;
ls.config.namespaces = false;
+#if !defined(USE_LIBMNL_AFTER_SETNS)
+ ls.config.no_mnl = true; // disable mnl since this collects all sockets from the entire system
+#endif
+
// initialize local sockets
local_sockets_init(&ls);
-
+ ls.proc_self_net_ns_inode = ls.ns_state.net_ns_inode;
ls.config.host_prefix = ""; // we need the /proc of the container
struct local_sockets_child_work cw = {
@@ -1167,14 +1458,16 @@ static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
ls.config.cb = local_sockets_send_to_parent;
ls.config.data = &cw;
- ls.proc_self_net_ns_inode = ls.config.net_ns_inode;
// switch namespace using the custom fd passed via the spawn server
if (setns(request->fds[3], CLONE_NEWNET) == -1) {
local_sockets_log(&ls, "failed to switch network namespace at child process using fd %d", request->fds[3]);
- exit(EXIT_FAILURE);
+ return EXIT_FAILURE;
}
+ // close the custom fd
+ close(request->fds[3]); request->fds[3] = -1;
+
// read all sockets from /proc
local_sockets_read_all_system_sockets(&ls);
@@ -1182,12 +1475,11 @@ static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
local_sockets_foreach_local_socket_call_cb(&ls);
// send the terminating socket
- struct local_socket zero = {
- .net_ns_inode = ls.config.net_ns_inode,
- };
- local_sockets_send_to_parent(&ls, &zero, &cw);
+ local_sockets_send_to_parent(&ls, &terminator, &cw);
+
+ local_sockets_cleanup(&ls);
- exit(EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, struct pid_socket *ps) {
@@ -1198,6 +1490,8 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
int fd = open(filename, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
local_sockets_log(ls, "cannot open file '%s'", filename);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
return false;
}
@@ -1205,28 +1499,46 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
if (fstat(fd, &statbuf) == -1) {
close(fd);
local_sockets_log(ls, "failed to get file statistics for '%s'", filename);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
return false;
}
if (statbuf.st_ino != ps->net_ns_inode) {
close(fd);
local_sockets_log(ls, "pid %d is not in the wanted network namespace", ps->pid);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_invalid, 1, __ATOMIC_RELAXED);
return false;
}
if(ls->spawn_server == NULL) {
close(fd);
local_sockets_log(ls, "spawn server is not available");
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_forks_failed, 1, __ATOMIC_RELAXED);
return false;
}
- struct local_sockets_config config = ls->config;
- config.net_ns_inode = ps->net_ns_inode;
- SPAWN_INSTANCE *si = spawn_server_exec(ls->spawn_server, STDERR_FILENO, fd, NULL, &config, sizeof(config), SPAWN_INSTANCE_TYPE_CALLBACK);
+ struct local_sockets_ns_req req = {
+ .config = ls->config,
+ .ns_state = ls->ns_state,
+ };
+ req.ns_state.net_ns_pid = ps->pid;
+ req.ns_state.net_ns_inode = ps->net_ns_inode;
+
+ SPAWN_INSTANCE *si = spawn_server_exec(ls->spawn_server, STDERR_FILENO, fd, NULL, &req, sizeof(req), SPAWN_INSTANCE_TYPE_CALLBACK);
close(fd); fd = -1;
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_forks_attempted, 1, __ATOMIC_RELAXED);
+
if(si == NULL) {
local_sockets_log(ls, "cannot create spawn instance");
+
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_forks_failed, 1, __ATOMIC_RELAXED);
+
return false;
}
@@ -1251,36 +1563,35 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
received++;
- struct local_socket zero = {
- .net_ns_inode = ps->net_ns_inode,
- };
- if(memcmp(&buf, &zero, sizeof(buf)) == 0) {
- // the terminator
+ if(local_socket_is_terminator(&buf))
+ // the child finished
break;
- }
+
+ // overwrite the net_ns_inode we receive
+ buf.net_ns_inode = ps->net_ns_inode;
spinlock_lock(&ls->spinlock);
- SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, buf.inode, &buf, true);
- LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
- if(n) {
+ if(!local_sockets_add_socket(ls, &buf)) {
+ // fprintf(stderr, "Failed to add duplicate namespace socket inode %"PRIu64"\n", buf.inode);
string_freez(buf.cmdline);
-// local_sockets_log(ls,
-// "ns inode %" PRIu64" (comm: '%s', pid: %u, ns: %"PRIu64") already exists in hashtable (comm: '%s', pid: %u, ns: %"PRIu64") - ignoring duplicate",
-// buf.inode, buf.comm, buf.pid, buf.net_ns_inode, n->comm, n->pid, n->net_ns_inode);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_sockets_existing, 1, __ATOMIC_RELAXED);
}
else {
- n = aral_mallocz(ls->local_socket_aral);
- memcpy(n, &buf, sizeof(*n));
- simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
-
- local_sockets_index_listening_port(ls, n);
+ // fprintf(stderr, "Added namespace socket inode %"PRIu64"\n", buf.inode);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_sockets_new, 1, __ATOMIC_RELAXED);
}
spinlock_unlock(&ls->spinlock);
}
spawn_server_exec_kill(ls->spawn_server, si);
+
+ if(ls->config.report && received == 0)
+ __atomic_add_fetch(&ls->stats.namespaces_forks_unresponsive, 1, __ATOMIC_RELAXED);
+
return received > 0;
}
@@ -1289,7 +1600,7 @@ struct local_sockets_namespace_worker {
uint64_t inode;
};
-static inline void *local_sockets_get_namespace_sockets(void *arg) {
+static inline void *local_sockets_get_namespace_sockets_worker(void *arg) {
struct local_sockets_namespace_worker *data = arg;
LS_STATE *ls = data->ls;
const uint64_t inode = data->inode;
@@ -1337,6 +1648,7 @@ static inline void local_sockets_namespaces(LS_STATE *ls) {
const uint64_t inode = (uint64_t)SIMPLE_HASHTABLE_SLOT_DATA(sl);
if(inode == ls->proc_self_net_ns_inode)
+ // skip our own namespace, we already have them
continue;
spinlock_unlock(&ls->spinlock);
@@ -1356,8 +1668,10 @@ static inline void local_sockets_namespaces(LS_STATE *ls) {
workers_data[last_thread].ls = ls;
workers_data[last_thread].inode = inode;
workers[last_thread] = nd_thread_create(
- "local-sockets-worker", NETDATA_THREAD_OPTION_JOINABLE,
- local_sockets_get_namespace_sockets, &workers_data[last_thread]);
+ "local-sockets-worker",
+ NETDATA_THREAD_OPTION_JOINABLE,
+ local_sockets_get_namespace_sockets_worker,
+ &workers_data[last_thread]);
spinlock_lock(&ls->spinlock);
}
@@ -1371,49 +1685,137 @@ static inline void local_sockets_namespaces(LS_STATE *ls) {
}
}
+#endif // LOCAL_SOCKETS_USE_SETNS
+
+// --------------------------------------------------------------------------------------------------------------------
+// read namespace sockets from the host's /proc
+
+#if !defined(LOCAL_SOCKETS_USE_SETNS)
+
+static inline bool local_sockets_namespaces_from_proc_with_pid(LS_STATE *ls, struct pid_socket *ps) {
+ char filename[1024];
+ snprintfz(filename, sizeof(filename), "%s/proc/%d/ns/net", ls->config.host_prefix, ps->pid);
+
+ // verify the pid is in the target namespace
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ local_sockets_log(ls, "cannot open file '%s'", filename);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
+ return false;
+ }
+
+ struct stat statbuf;
+ if (fstat(fd, &statbuf) == -1) {
+ close(fd);
+ local_sockets_log(ls, "failed to get file statistics for '%s'", filename);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
+ return false;
+ }
+
+ if (statbuf.st_ino != ps->net_ns_inode) {
+ close(fd);
+ local_sockets_log(ls, "pid %d is not in the wanted network namespace", ps->pid);
+ if(ls->config.report)
+ __atomic_add_fetch(&ls->stats.namespaces_invalid, 1, __ATOMIC_RELAXED);
+ return false;
+ }
+
+ char path[FILENAME_MAX + 1];
+
+ if(ls->config.tcp4) {
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/tcp", ls->config.host_prefix, ps->pid);
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_TCP))
+ return false;
+ }
+
+ if(ls->config.udp4) {
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/udp", ls->config.host_prefix, ps->pid);
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_UDP))
+ return false;
+ }
+
+ if(ls->config.tcp6) {
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/tcp6", ls->config.host_prefix, ps->pid);
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_TCP))
+ return false;
+ }
+
+ if(ls->config.udp6) {
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/udp6", ls->config.host_prefix, ps->pid);
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_UDP))
+ return false;
+ }
+
+ return true;
+}
+
+static inline void local_sockets_namespaces_from_proc(LS_STATE *ls) {
+ for(SIMPLE_HASHTABLE_SLOT_NET_NS *sl = simple_hashtable_first_read_only_NET_NS(&ls->ns_hashtable);
+ sl;
+ sl = simple_hashtable_next_read_only_NET_NS(&ls->ns_hashtable, sl)) {
+ const uint64_t inode = (uint64_t)SIMPLE_HASHTABLE_SLOT_DATA(sl);
+
+ if (inode == ls->proc_self_net_ns_inode)
+ // skip our own namespace, we already have them
+ continue;
+
+ ls->stats.namespaces_found++;
+
+ for(SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_first_read_only_PID_SOCKET(&ls->pid_sockets_hashtable) ;
+ sl_pid ;
+ sl_pid = simple_hashtable_next_read_only_PID_SOCKET(&ls->pid_sockets_hashtable, sl_pid)) {
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
+ if(!ps || ps->net_ns_inode != inode) continue;
+
+ // now we have a pid that has the same namespace inode
+
+ if(local_sockets_namespaces_from_proc_with_pid(ls, ps))
+ break;
+ }
+ }
+}
+
+#endif
+
// --------------------------------------------------------------------------------------------------------------------
static inline void local_sockets_process(LS_STATE *ls) {
+ ls->timings_idx = 0;
+ local_sockets_track_time(ls, "init");
+
// initialize our hashtables
local_sockets_init(ls);
+ local_sockets_track_time(ls, "all_sockets");
+
// read all sockets from /proc
local_sockets_read_all_system_sockets(ls);
// check all socket namespaces
- if(ls->config.namespaces)
+ if(ls->config.namespaces) {
+ local_sockets_track_time(ls, "switch_namespaces");
+#if defined(LOCAL_SOCKETS_USE_SETNS)
local_sockets_namespaces(ls);
+#else
+ local_sockets_namespaces_from_proc(ls);
+#endif
+ }
// detect the directions of the sockets
- if(ls->config.inbound || ls->config.outbound || ls->config.local)
+ if(ls->config.inbound || ls->config.outbound || ls->config.local) {
+ local_sockets_track_time(ls, "detect_direction");
local_sockets_detect_directions(ls);
+ }
// call the callback for each socket
+ local_sockets_track_time(ls, "output");
local_sockets_foreach_local_socket_call_cb(ls);
// free all memory
+ local_sockets_track_time(ls, "cleanup");
local_sockets_cleanup(ls);
}
-static inline void ipv6_address_to_txt(struct in6_addr *in6_addr, char *dst) {
- struct sockaddr_in6 sa = { 0 };
-
- sa.sin6_family = AF_INET6;
- sa.sin6_port = htons(0);
- sa.sin6_addr = *in6_addr;
-
- // Convert to human-readable format
- if (inet_ntop(AF_INET6, &(sa.sin6_addr), dst, INET6_ADDRSTRLEN) == NULL)
- *dst = '\0';
-}
-
-static inline void ipv4_address_to_txt(uint32_t ip, char *dst) {
- uint8_t octets[4];
- octets[0] = ip & 0xFF;
- octets[1] = (ip >> 8) & 0xFF;
- octets[2] = (ip >> 16) & 0xFF;
- octets[3] = (ip >> 24) & 0xFF;
- sprintf(dst, "%u.%u.%u.%u", octets[0], octets[1], octets[2], octets[3]);
-}
-
#endif //NETDATA_LOCAL_SOCKETS_H