diff options
Diffstat (limited to 'src/libnetdata/maps/local-sockets.h')
-rw-r--r-- | src/libnetdata/maps/local-sockets.h | 1419 |
1 files changed, 0 insertions, 1419 deletions
diff --git a/src/libnetdata/maps/local-sockets.h b/src/libnetdata/maps/local-sockets.h deleted file mode 100644 index 6f2ffd81a..000000000 --- a/src/libnetdata/maps/local-sockets.h +++ /dev/null @@ -1,1419 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_LOCAL_SOCKETS_H -#define NETDATA_LOCAL_SOCKETS_H - -#include "libnetdata/libnetdata.h" - -#ifdef HAVE_LIBMNL -#include <linux/rtnetlink.h> -#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 - -#define SIMPLE_HASHTABLE_VALUE_TYPE uint64_t -#define SIMPLE_HASHTABLE_NAME _NET_NS -#include "libnetdata/simple_hashtable.h" - -// -------------------------------------------------------------------------------------------------------------------- -// hashtable for keeping the sockets of PIDs -// key is the inode - -struct pid_socket; -#define SIMPLE_HASHTABLE_VALUE_TYPE struct pid_socket -#define SIMPLE_HASHTABLE_NAME _PID_SOCKET -#include "libnetdata/simple_hashtable.h" - -// -------------------------------------------------------------------------------------------------------------------- -// hashtable for keeping all the sockets -// key is the inode - -struct local_socket; -#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_socket -#define SIMPLE_HASHTABLE_NAME _LOCAL_SOCKET -#include "libnetdata/simple_hashtable.h" - -// -------------------------------------------------------------------------------------------------------------------- -// hashtable for keeping all local IPs -// key is XXH3_64bits hash of the IP - -union ipv46; -#define SIMPLE_HASHTABLE_VALUE_TYPE union ipv46 -#define SIMPLE_HASHTABLE_NAME _LOCAL_IP -#include "libnetdata/simple_hashtable.h" - -// -------------------------------------------------------------------------------------------------------------------- -// hashtable for keeping all listening ports -// key is XXH3_64bits hash of the family, protocol, port number, namespace - -struct local_port; -#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_port -#define SIMPLE_HASHTABLE_NAME _LISTENING_PORT -#include "libnetdata/simple_hashtable.h" - -// -------------------------------------------------------------------------------------------------------------------- - -struct local_socket_state; -typedef void (*local_sockets_cb_t)(struct local_socket_state *state, struct local_socket *n, void *data); - -struct local_sockets_config { - bool listening; - bool inbound; - bool outbound; - bool local; - bool tcp4; - bool tcp6; - bool udp4; - bool udp6; - bool pid; - bool cmdline; - bool comm; - bool uid; - bool namespaces; - bool tcp_info; - - size_t max_errors; - size_t max_concurrent_namespaces; - - local_sockets_cb_t cb; - void *data; - - const char *host_prefix; - - // internal use - uint64_t net_ns_inode; -}; - -typedef struct local_socket_state { - struct local_sockets_config config; - - 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; - } stats; - - bool spawn_server_is_mine; - SPAWN_SERVER *spawn_server; - -#ifdef HAVE_LIBMNL - bool use_nl; - struct mnl_socket *nl; - uint16_t tmp_protocol; -#endif - - ARAL *local_socket_aral; - ARAL *pid_socket_aral; - SPINLOCK spinlock; // for namespaces - - uint64_t proc_self_net_ns_inode; - - SIMPLE_HASHTABLE_NET_NS ns_hashtable; - SIMPLE_HASHTABLE_PID_SOCKET pid_sockets_hashtable; - SIMPLE_HASHTABLE_LOCAL_SOCKET sockets_hashtable; - SIMPLE_HASHTABLE_LOCAL_IP local_ips_hashtable; - SIMPLE_HASHTABLE_LISTENING_PORT listening_ports_hashtable; -} LS_STATE; - -// -------------------------------------------------------------------------------------------------------------------- - -typedef enum __attribute__((packed)) { - SOCKET_DIRECTION_NONE = 0, - SOCKET_DIRECTION_LISTEN = (1 << 0), // a listening socket - SOCKET_DIRECTION_INBOUND = (1 << 1), // an inbound socket connecting a remote system to a local listening socket - SOCKET_DIRECTION_OUTBOUND = (1 << 2), // a socket initiated by this system, connecting to another system - SOCKET_DIRECTION_LOCAL_INBOUND = (1 << 3), // the socket connecting 2 localhost applications - SOCKET_DIRECTION_LOCAL_OUTBOUND = (1 << 4), // the socket connecting 2 localhost applications -} SOCKET_DIRECTION; - -#ifndef TASK_COMM_LEN -#define TASK_COMM_LEN 16 -#endif - -struct pid_socket { - uint64_t inode; - pid_t pid; - uid_t uid; - uint64_t net_ns_inode; - char *cmdline; - char comm[TASK_COMM_LEN]; -}; - -struct local_port { - uint16_t protocol; - uint16_t family; - uint16_t port; - uint64_t net_ns_inode; -}; - -union ipv46 { - uint32_t ipv4; - struct in6_addr ipv6; -}; - -struct socket_endpoint { - uint16_t protocol; - uint16_t family; - uint16_t port; - union ipv46 ip; -}; - -static inline void ipv6_to_in6_addr(const char *ipv6_str, struct in6_addr *d) { - char buf[9]; - - for (size_t k = 0; k < 4; ++k) { - memcpy(buf, ipv6_str + (k * 8), 8); - buf[sizeof(buf) - 1] = '\0'; - d->s6_addr32[k] = str2uint32_hex(buf, NULL); - } -} - -typedef struct local_socket { - uint64_t inode; - uint64_t net_ns_inode; - - int state; - struct socket_endpoint local; - struct socket_endpoint remote; - pid_t pid; - - SOCKET_DIRECTION direction; - - uint8_t timer; - uint8_t retransmits; // the # of packets currently queued for retransmission (not yet acknowledged) - uint32_t expires; - uint32_t rqueue; - uint32_t wqueue; - uid_t uid; - - struct { - bool checked; - bool ipv46; - } ipv6ony; - - union { - struct tcp_info tcp; - } info; - - char comm[TASK_COMM_LEN]; - STRING *cmdline; - - struct local_port local_port_key; - - XXH64_hash_t local_ip_hash; - XXH64_hash_t remote_ip_hash; - XXH64_hash_t local_port_hash; - -#ifdef LOCAL_SOCKETS_EXTENDED_MEMBERS - LOCAL_SOCKETS_EXTENDED_MEMBERS -#endif -} LOCAL_SOCKET; - -static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request); - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_log(LS_STATE *ls, const char *format, ...) PRINTFLIKE(2, 3); -static inline void local_sockets_log(LS_STATE *ls, const char *format, ...) { - if(ls && ++ls->stats.errors_encountered == ls->config.max_errors) { - nd_log(NDLS_COLLECTORS, NDLP_ERR, "LOCAL-SOCKETS: max number of logs reached. Not logging anymore"); - return; - } - - if(ls && ls->stats.errors_encountered > ls->config.max_errors) - return; - - char buf[16384]; - va_list args; - va_start(args, format); - vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - - nd_log(NDLS_COLLECTORS, NDLP_ERR, "LOCAL-SOCKETS: %s", buf); -} - -// -------------------------------------------------------------------------------------------------------------------- - -static bool local_sockets_is_ipv4_mapped_ipv6_address(const struct in6_addr *addr) { - // An IPv4-mapped IPv6 address starts with 80 bits of zeros followed by 16 bits of ones - static const unsigned char ipv4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }; - return memcmp(addr->s6_addr, ipv4_mapped_prefix, 12) == 0; -} - -static bool local_sockets_is_loopback_address(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 - } else if (se->family == AF_INET6) { - // Check if the address is an IPv4-mapped IPv6 address - if (local_sockets_is_ipv4_mapped_ipv6_address(&se->ip.ipv6)) { - // Extract the last 32 bits (IPv4 address) and check if it's in the 127.0.0.0/8 range - uint8_t *ip6 = (uint8_t *)&se->ip.ipv6; - const uint32_t ipv4_addr = *((const uint32_t *)(ip6 + 12)); - return (ntohl(ipv4_addr) >> 24) == 127; - } - - // For IPv6, loopback address is ::1 - return memcmp(&se->ip.ipv6, &in6addr_loopback, sizeof(se->ip.ipv6)) == 0; - } - - return false; -} - -static inline bool local_sockets_is_ipv4_reserved_address(uint32_t ip) { - // Check for the reserved address ranges - ip = ntohl(ip); - return ( - (ip >> 24 == 10) || // Private IP range (A class) - (ip >> 20 == (172 << 4) + 1) || // Private IP range (B class) - (ip >> 16 == (192 << 8) + 168) || // Private IP range (C class) - (ip >> 24 == 127) || // Loopback address (127.0.0.0) - (ip >> 24 == 0) || // Reserved (0.0.0.0) - (ip >> 24 == 169 && (ip >> 16) == 254) || // Link-local address (169.254.0.0) - (ip >> 16 == (192 << 8) + 0) // Test-Net (192.0.0.0) - ); -} - -static inline bool local_sockets_is_private_address(struct socket_endpoint *se) { - if (se->family == AF_INET) { - return local_sockets_is_ipv4_reserved_address(se->ip.ipv4); - } - else if (se->family == AF_INET6) { - uint8_t *ip6 = (uint8_t *)&se->ip.ipv6; - - // Check if the address is an IPv4-mapped IPv6 address - if (local_sockets_is_ipv4_mapped_ipv6_address(&se->ip.ipv6)) { - // Extract the last 32 bits (IPv4 address) and check if it's in the 127.0.0.0/8 range - const uint32_t ipv4_addr = *((const uint32_t *)(ip6 + 12)); - return local_sockets_is_ipv4_reserved_address(ipv4_addr); - } - - // Check for link-local addresses (fe80::/10) - if ((ip6[0] == 0xFE) && ((ip6[1] & 0xC0) == 0x80)) - return true; - - // Check for Unique Local Addresses (ULA) (fc00::/7) - if ((ip6[0] & 0xFE) == 0xFC) - return true; - - // Check for multicast addresses (ff00::/8) - if (ip6[0] == 0xFF) - return true; - - // For IPv6, loopback address is :: or ::1 - return memcmp(&se->ip.ipv6, &in6addr_any, sizeof(se->ip.ipv6)) == 0 || - memcmp(&se->ip.ipv6, &in6addr_loopback, sizeof(se->ip.ipv6)) == 0; - } - - return false; -} - -static bool local_sockets_is_multicast_address(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); - return (ip >= 0xE0000000 && ip <= 0xEFFFFFFF); // Multicast address range (224.0.0.0/4) - } - else if (se->family == AF_INET6) { - // For IPv6, check if the address is ff00::/8 - uint8_t *ip6 = (uint8_t *)&se->ip.ipv6; - return ip6[0] == 0xff; - } - - return false; -} - -static bool local_sockets_is_zero_address(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; - } - else if (se->family == AF_INET6) { - // For IPv6, check if the address is :: - return memcmp(&se->ip.ipv6, &in6addr_any, sizeof(se->ip.ipv6)) == 0; - } - - return false; -} - -static inline const char *local_sockets_address_space(struct socket_endpoint *se) { - if(local_sockets_is_zero_address(se)) - return "zero"; - else if(local_sockets_is_loopback_address(se)) - return "loopback"; - else if(local_sockets_is_multicast_address(se)) - return "multicast"; - else if(local_sockets_is_private_address(se)) - return "private"; - else - return "public"; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline bool is_local_socket_ipv46(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; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static void local_sockets_foreach_local_socket_call_cb(LS_STATE *ls) { - for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable); - sl; - sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) { - LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl); - if(!n) continue; - - if((ls->config.listening && n->direction & SOCKET_DIRECTION_LISTEN) || - (ls->config.local && n->direction & (SOCKET_DIRECTION_LOCAL_INBOUND|SOCKET_DIRECTION_LOCAL_OUTBOUND)) || - (ls->config.inbound && n->direction & SOCKET_DIRECTION_INBOUND) || - (ls->config.outbound && n->direction & SOCKET_DIRECTION_OUTBOUND) - ) { - // we have to call the callback for this socket - if (ls->config.cb) - ls->config.cb(ls, n, ls->config.data); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_fix_cmdline(char* str) { - char *s = str; - - // map invalid characters to underscores - while(*s) { - if(*s == '|' || iscntrl(*s)) *s = '_'; - s++; - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline bool -local_sockets_read_proc_inode_link(LS_STATE *ls, const char *filename, uint64_t *inode, const char *type) { - char link_target[FILENAME_MAX + 1]; - - *inode = 0; - - ssize_t len = readlink(filename, link_target, sizeof(link_target) - 1); - if (len == -1) { - local_sockets_log(ls, "cannot read '%s' link '%s'", type, filename); - - ls->stats.pid_fds_readlink_failed++; - return false; - } - link_target[len] = '\0'; - - len = strlen(type); - if(strncmp(link_target, type, len) == 0 && link_target[len] == ':' && link_target[len + 1] == '[' && isdigit(link_target[len + 2])) { - *inode = strtoull(&link_target[len + 2], NULL, 10); - // ll_log(ls, "read link of type '%s' '%s' from '%s', inode = %"PRIu64, type, link_target, filename, *inode); - return true; - } - else { - // ll_log(ls, "cannot read '%s' link '%s' from '%s'", type, link_target, filename); - ls->stats.pid_fds_processed++; - return false; - } -} - -static inline bool local_sockets_is_path_a_pid(const char *s) { - if(!s || !*s) return false; - - while(*s) { - if(!isdigit(*s++)) - return false; - } - - return true; -} - -static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const char *proc_filename) { - DIR *proc_dir; - struct dirent *proc_entry; - char filename[FILENAME_MAX + 1]; - char comm[TASK_COMM_LEN]; - char cmdline[8192]; - const char *cmdline_trimmed; - uint64_t net_ns_inode; - - proc_dir = opendir(proc_filename); - if (proc_dir == NULL) { - local_sockets_log(ls, "cannot opendir() '%s'", proc_filename); - ls->stats.pid_fds_readlink_failed++; - return false; - } - - while ((proc_entry = readdir(proc_dir)) != NULL) { - if(proc_entry->d_type != DT_DIR) - continue; - - if(!strcmp(proc_entry->d_name, ".") || !strcmp(proc_entry->d_name, "..")) - continue; - - if(!local_sockets_is_path_a_pid(proc_entry->d_name)) - continue; - - // Build the path to the fd directory of the process - snprintfz(filename, FILENAME_MAX, "%s/%s/fd/", proc_filename, proc_entry->d_name); - DIR *fd_dir = opendir(filename); - if (fd_dir == NULL) { - local_sockets_log(ls, "cannot opendir() '%s'", filename); - ls->stats.pid_fds_opendir_failed++; - continue; - } - - comm[0] = '\0'; - cmdline[0] = '\0'; - cmdline_trimmed = NULL; - pid_t pid = (pid_t)strtoul(proc_entry->d_name, NULL, 10); - if(!pid) { - local_sockets_log(ls, "cannot parse pid of '%s'", proc_entry->d_name); - closedir(fd_dir); - continue; - } - net_ns_inode = 0; - uid_t uid = UID_UNSET; - - struct dirent *fd_entry; - while ((fd_entry = readdir(fd_dir)) != NULL) { - if(fd_entry->d_type != DT_LNK) - continue; - - snprintfz(filename, sizeof(filename), "%s/%s/fd/%s", proc_filename, proc_entry->d_name, fd_entry->d_name); - uint64_t inode = 0; - 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); - 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))) - local_sockets_log(ls, "cannot open file: %s\n", filename); - else { - size_t clen = strlen(comm); - if(comm[clen - 1] == '\n') - comm[clen - 1] = '\0'; - } - } - if(!cmdline[0] && ls->config.cmdline) { - snprintfz(filename, sizeof(filename), "%s/%s/cmdline", proc_filename, proc_entry->d_name); - if (read_proc_cmdline(filename, cmdline, sizeof(cmdline))) - local_sockets_log(ls, "cannot open file: %s\n", filename); - else { - local_sockets_fix_cmdline(cmdline); - cmdline_trimmed = trim(cmdline); - } - } - 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); - simple_hashtable_set_slot_NET_NS(&ls->ns_hashtable, sl_ns, net_ns_inode, (uint64_t *)net_ns_inode); - } - } - - if(!ps) - ps = aral_callocz(ls->pid_socket_aral); - - ps->inode = inode; - ps->pid = pid; - ps->uid = uid; - ps->net_ns_inode = net_ns_inode; - strncpyz(ps->comm, comm, sizeof(ps->comm) - 1); - - if(ps->cmdline) - freez(ps->cmdline); - - ps->cmdline = cmdline_trimmed ? strdupz(cmdline_trimmed) : NULL; - simple_hashtable_set_slot_PID_SOCKET(&ls->pid_sockets_hashtable, sl, inode, ps); - } - } - - closedir(fd_dir); - } - - closedir(proc_dir); - return true; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_index_listening_port(LS_STATE *ls, LOCAL_SOCKET *n) { - if(n->direction & SOCKET_DIRECTION_LISTEN) { - // for the listening sockets, keep a hashtable with all the local ports - // so that we will be able to detect INBOUND sockets - - SIMPLE_HASHTABLE_SLOT_LISTENING_PORT *sl_port = - simple_hashtable_get_slot_LISTENING_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, true); - - struct local_port *port = SIMPLE_HASHTABLE_SLOT_DATA(sl_port); - if(!port) - simple_hashtable_set_slot_LISTENING_PORT(&ls->listening_ports_hashtable, sl_port, n->local_port_hash, &n->local_port_key); - } -} - -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 = aral_mallocz(ls->local_socket_aral); - *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 = string_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 { - // 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_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; - } -} - -static inline int local_sockets_libmnl_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, - .ipv6ony = { - .checked = false, - .ipv46 = false, - }, - .local = { - .protocol = ls->tmp_protocol, - .family = diag_msg->idiag_family, - .port = ntohs(diag_msg->id.idiag_sport), - }, - .remote = { - .protocol = ls->tmp_protocol, - .family = diag_msg->idiag_family, - .port = ntohs(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)); - } - - struct rtattr *attr = (struct rtattr *)(diag_msg + 1); - int rtattrlen = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*diag_msg)); - for (; !n.ipv6ony.checked && RTA_OK(attr, rtattrlen); attr = RTA_NEXT(attr, rtattrlen)) { - switch (attr->rta_type) { - case INET_DIAG_INFO: { - if(ls->tmp_protocol == IPPROTO_TCP) { - struct tcp_info *info = (struct tcp_info *)RTA_DATA(attr); - n.info.tcp = *info; - ls->stats.tcp_info_received++; - } - } - break; - - case INET_DIAG_SKV6ONLY: { - n.ipv6ony.checked = true; - int ipv6only = *(int *)RTA_DATA(attr); - n.ipv6ony.ipv46 = !ipv6only; - } - break; - - default: - break; - } - } - - local_sockets_add_socket(ls, &n); - - return MNL_CB_OK; -} - -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); - - memset(&req, 0, sizeof(req)); - req.sdiag_family = family; - req.sdiag_protocol = protocol; - req.idiag_states = -1; - req.idiag_ext = 0; - - if(family == AF_INET6) - req.idiag_ext |= 1 << (INET_DIAG_SKV6ONLY - 1); - - 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"); - 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_libmnl_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) { - static bool is_space[256] = { - [':'] = true, - [' '] = true, - }; - - if(family != AF_INET && family != AF_INET6) - return false; - - FILE *fp = fopen(filename, "r"); - if (fp == NULL) - return false; - - char *line = malloc(1024); // no mallocz() here because getline() may resize - if(!line) { - fclose(fp); - return false; - } - - size_t len = 1024; - ssize_t read; - - ssize_t min_line_length = (family == AF_INET) ? 105 : 155; - size_t counter = 0; - - // Read line by line - while ((read = getline(&line, &len, fp)) != -1) { - if(counter++ == 0) continue; // skip the first line - - if(read < min_line_length) { - local_sockets_log(ls, "too small line No %zu of filename '%s': %s", counter, filename, line); - 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); - } - - fclose(fp); - - if (line) - free(line); // no freez() here because getline() may resize - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_detect_directions(LS_STATE *ls) { - for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable); - sl ; - sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) { - LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl); - if (!n) continue; - - if ((n->direction & (SOCKET_DIRECTION_INBOUND|SOCKET_DIRECTION_OUTBOUND)) != - (SOCKET_DIRECTION_INBOUND|SOCKET_DIRECTION_OUTBOUND)) - continue; - - // check if the local port is one of our listening ports - { - SIMPLE_HASHTABLE_SLOT_LISTENING_PORT *sl_port = - simple_hashtable_get_slot_LISTENING_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, false); - - struct local_port *port = SIMPLE_HASHTABLE_SLOT_DATA(sl_port); // do not reference this pointer - is invalid - if(port) { - // the local port of this socket is a port we listen to - n->direction &= ~SOCKET_DIRECTION_OUTBOUND; - } - else - n->direction &= ~SOCKET_DIRECTION_INBOUND; - } - - // check if the remote IP is one of our local IPs - { - SIMPLE_HASHTABLE_SLOT_LOCAL_IP *sl_ip = - simple_hashtable_get_slot_LOCAL_IP(&ls->local_ips_hashtable, n->remote_ip_hash, &n->remote.ip, false); - - union ipv46 *d = SIMPLE_HASHTABLE_SLOT_DATA(sl_ip); - if (d) { - // the remote IP of this socket is one of our local IPs - if(n->direction & SOCKET_DIRECTION_INBOUND) { - n->direction &= ~SOCKET_DIRECTION_INBOUND; - n->direction |= SOCKET_DIRECTION_LOCAL_INBOUND; - } - else if(n->direction & SOCKET_DIRECTION_OUTBOUND) { - n->direction &= ~SOCKET_DIRECTION_OUTBOUND; - n->direction |= SOCKET_DIRECTION_LOCAL_OUTBOUND; - } - continue; - } - } - - if (local_sockets_is_loopback_address(&n->local) || - local_sockets_is_loopback_address(&n->remote)) { - // both IP addresses are loopback - if(n->direction & SOCKET_DIRECTION_INBOUND) { - n->direction &= ~SOCKET_DIRECTION_INBOUND; - n->direction |= SOCKET_DIRECTION_LOCAL_INBOUND; - } - else if(n->direction & SOCKET_DIRECTION_OUTBOUND) { - n->direction &= ~SOCKET_DIRECTION_OUTBOUND; - n->direction |= SOCKET_DIRECTION_LOCAL_OUTBOUND; - } - } - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_init(LS_STATE *ls) { - ls->config.host_prefix = netdata_configured_host_prefix; - - spinlock_init(&ls->spinlock); - - simple_hashtable_init_NET_NS(&ls->ns_hashtable, 1024); - simple_hashtable_init_PID_SOCKET(&ls->pid_sockets_hashtable, 65535); - simple_hashtable_init_LOCAL_SOCKET(&ls->sockets_hashtable, 65535); - simple_hashtable_init_LOCAL_IP(&ls->local_ips_hashtable, 4096); - simple_hashtable_init_LISTENING_PORT(&ls->listening_ports_hashtable, 4096); - - ls->local_socket_aral = aral_create( - "local-sockets", - sizeof(LOCAL_SOCKET), - 65536, - 65536, - NULL, NULL, NULL, false, true); - - ls->pid_socket_aral = aral_create( - "pid-sockets", - sizeof(struct pid_socket), - 65536, - 65536, - NULL, NULL, NULL, false, true); - - memset(&ls->stats, 0, sizeof(ls->stats)); - -#ifdef HAVE_LIBMNL - ls->use_nl = false; - ls->nl = NULL; - ls->tmp_protocol = 0; - local_sockets_libmnl_init(ls); -#endif - - 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; -} - -static inline void local_sockets_cleanup(LS_STATE *ls) { - - 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 - for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable); - sl; - sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) { - LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl); - if(!n) continue; - - string_freez(n->cmdline); - aral_freez(ls->local_socket_aral, n); - } - - // free the pid_socket hashtable data - for(SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_first_read_only_PID_SOCKET(&ls->pid_sockets_hashtable); - sl; - sl = simple_hashtable_next_read_only_PID_SOCKET(&ls->pid_sockets_hashtable, sl)) { - struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl); - if(!ps) continue; - - freez(ps->cmdline); - aral_freez(ls->pid_socket_aral, ps); - } - - // free the hashtable - simple_hashtable_destroy_NET_NS(&ls->ns_hashtable); - simple_hashtable_destroy_PID_SOCKET(&ls->pid_sockets_hashtable); - simple_hashtable_destroy_LISTENING_PORT(&ls->listening_ports_hashtable); - simple_hashtable_destroy_LOCAL_IP(&ls->local_ips_hashtable); - simple_hashtable_destroy_LOCAL_SOCKET(&ls->sockets_hashtable); - - aral_destroy(ls->local_socket_aral); - aral_destroy(ls->pid_socket_aral); -} - -// -------------------------------------------------------------------------------------------------------------------- - -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); - - if(ls->use_nl) - return; - } -#endif - - local_sockets_read_proc_net_x(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) { - 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) { - snprintfz(path, sizeof(path), "%s/proc", ls->config.host_prefix); - local_sockets_find_all_sockets_in_proc(ls, path); - } - - if(ls->config.tcp4) { - snprintfz(path, sizeof(path), "%s/proc/net/tcp", ls->config.host_prefix); - 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_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_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_do_family_protocol(ls, path, AF_INET6, IPPROTO_UDP); - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -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) { - 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(write(fd, n, sizeof(*n)) != sizeof(*n)) - local_sockets_log(ls, "failed to write local socket to pipe"); - - size_t len = n->cmdline ? string_strlen(n->cmdline) + 1 : 0; - if(write(fd, &len, sizeof(len)) != sizeof(len)) - local_sockets_log(ls, "failed to write cmdline length to pipe"); - - if(len) - if(write(fd, string2str(n->cmdline), len) != (ssize_t)len) - local_sockets_log(ls, "failed to write cmdline to pipe"); -} - -static inline void local_sockets_spawn_server_callback(SPAWN_REQUEST *request) { - LS_STATE ls = { 0 }; - ls.config = *((struct local_sockets_config *)request->data); - - // we don't need these inside namespaces - ls.config.cmdline = false; - ls.config.comm = false; - ls.config.pid = false; - ls.config.namespaces = false; - - // initialize local sockets - local_sockets_init(&ls); - - ls.config.host_prefix = ""; // we need the /proc of the container - - struct local_sockets_child_work cw = { - .net_ns_inode = ls.proc_self_net_ns_inode, - .fd = request->fds[1], // stdout - }; - - 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); - } - - // read all sockets from /proc - local_sockets_read_all_system_sockets(&ls); - - // send all sockets to parent - 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); - - exit(EXIT_SUCCESS); -} - -static inline bool local_sockets_get_namespace_sockets_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); - return false; - } - - struct stat statbuf; - if (fstat(fd, &statbuf) == -1) { - close(fd); - local_sockets_log(ls, "failed to get file statistics for '%s'", filename); - 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); - return false; - } - - if(ls->spawn_server == NULL) { - close(fd); - local_sockets_log(ls, "spawn server is not available"); - 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); - close(fd); fd = -1; - - if(si == NULL) { - local_sockets_log(ls, "cannot create spawn instance"); - return false; - } - - size_t received = 0; - struct local_socket buf; - while(read(spawn_server_instance_read_fd(si), &buf, sizeof(buf)) == sizeof(buf)) { - size_t len = 0; - if(read(spawn_server_instance_read_fd(si), &len, sizeof(len)) != sizeof(len)) - local_sockets_log(ls, "failed to read cmdline length from pipe"); - - if(len) { - char cmdline[len + 1]; - if(read(spawn_server_instance_read_fd(si), cmdline, len) != (ssize_t)len) - local_sockets_log(ls, "failed to read cmdline from pipe"); - else { - cmdline[len] = '\0'; - buf.cmdline = string_strdupz(cmdline); - } - } - else - buf.cmdline = NULL; - - received++; - - struct local_socket zero = { - .net_ns_inode = ps->net_ns_inode, - }; - if(memcmp(&buf, &zero, sizeof(buf)) == 0) { - // the terminator - break; - } - - 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) { - 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); - } - 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); - } - - spinlock_unlock(&ls->spinlock); - } - - spawn_server_exec_kill(ls->spawn_server, si); - return received > 0; -} - -struct local_sockets_namespace_worker { - LS_STATE *ls; - uint64_t inode; -}; - -static inline void *local_sockets_get_namespace_sockets(void *arg) { - struct local_sockets_namespace_worker *data = arg; - LS_STATE *ls = data->ls; - const uint64_t inode = data->inode; - - spinlock_lock(&ls->spinlock); - - // find a pid_socket that has this namespace - 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 - - spinlock_unlock(&ls->spinlock); - const bool worked = local_sockets_get_namespace_sockets_with_pid(ls, ps); - spinlock_lock(&ls->spinlock); - - if(worked) - break; - } - - spinlock_unlock(&ls->spinlock); - - return NULL; -} - -static inline void local_sockets_namespaces(LS_STATE *ls) { - size_t threads = ls->config.max_concurrent_namespaces; - if(threads == 0) threads = 5; - if(threads > 100) threads = 100; - - size_t last_thread = 0; - ND_THREAD *workers[threads]; - struct local_sockets_namespace_worker workers_data[threads]; - memset(workers, 0, sizeof(workers)); - memset(workers_data, 0, sizeof(workers_data)); - - spinlock_lock(&ls->spinlock); - - 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) - continue; - - spinlock_unlock(&ls->spinlock); - - ls->stats.namespaces_found++; - - if(workers[last_thread] != NULL) { - if(++last_thread >= threads) - last_thread = 0; - - if(workers[last_thread]) { - nd_thread_join(workers[last_thread]); - workers[last_thread] = NULL; - } - } - - 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]); - - spinlock_lock(&ls->spinlock); - } - - spinlock_unlock(&ls->spinlock); - - // wait all the threads running - for(size_t i = 0; i < threads ;i++) { - if(workers[i]) - nd_thread_join(workers[i]); - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline void local_sockets_process(LS_STATE *ls) { - // initialize our hashtables - local_sockets_init(ls); - - // read all sockets from /proc - local_sockets_read_all_system_sockets(ls); - - // check all socket namespaces - if(ls->config.namespaces) - local_sockets_namespaces(ls); - - // detect the directions of the sockets - if(ls->config.inbound || ls->config.outbound || ls->config.local) - local_sockets_detect_directions(ls); - - // call the callback for each socket - local_sockets_foreach_local_socket_call_cb(ls); - - // free all memory - 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 |