diff options
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 |