|
@@ -9,7 +9,10 @@
|
|
|
#define _countof(x) (sizeof(x) / sizeof(*(x)))
|
|
|
#endif
|
|
|
|
|
|
-#ifdef HAVE_LIBMNL
|
|
|
+#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>
|
|
@@ -67,7 +70,7 @@ struct local_port;
|
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
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;
|
|
@@ -85,6 +88,7 @@ struct local_sockets_config {
|
|
|
bool namespaces;
|
|
|
bool tcp_info;
|
|
|
bool no_mnl;
|
|
|
+ bool procfile;
|
|
|
bool report;
|
|
|
|
|
|
size_t max_errors;
|
|
@@ -94,9 +98,12 @@ 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 {
|
|
@@ -105,32 +112,54 @@ struct timing_work {
|
|
|
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[20];
|
|
|
+ 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_mnl;
|
|
|
- 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
|
|
@@ -238,7 +267,9 @@ typedef struct local_socket {
|
|
|
#endif
|
|
|
} LOCAL_SOCKET;
|
|
|
|
|
|
+#if defined(LOCAL_SOCKETS_USE_SETNS)
|
|
|
static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request);
|
|
|
+#endif
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
@@ -269,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
|
|
@@ -303,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);
|
|
|
}
|
|
@@ -337,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);
|
|
@@ -352,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;
|
|
@@ -365,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))
|
|
@@ -380,7 +411,7 @@ static inline const char *local_sockets_address_space(struct socket_endpoint *se
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
-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) &&
|
|
@@ -615,6 +646,8 @@ static inline bool local_sockets_add_socket(LS_STATE *ls, LOCAL_SOCKET *tmp) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ ls->stats.sockets_added++;
|
|
|
+
|
|
|
n = aral_mallocz(ls->local_socket_aral);
|
|
|
*n = *tmp; // copy all contents
|
|
|
|
|
@@ -683,33 +716,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) {
|
|
|
- if(ls->config.no_mnl) return;
|
|
|
-
|
|
|
- ls->nl = mnl_socket_open(NETLINK_INET_DIAG);
|
|
|
- if (ls->nl == NULL) {
|
|
|
- local_sockets_log(ls, "cannot open libmnl netlink socket");
|
|
|
- ls->use_mnl = 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_mnl = false;
|
|
|
- }
|
|
|
- else
|
|
|
- ls->use_mnl = true;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void local_sockets_libmnl_cleanup(LS_STATE *ls) {
|
|
|
- if(ls->nl) {
|
|
|
- mnl_socket_close(ls->nl);
|
|
|
- ls->nl = NULL;
|
|
|
- ls->use_mnl = false;
|
|
|
- }
|
|
|
-}
|
|
|
+#if defined(HAVE_LIBMNL)
|
|
|
|
|
|
static inline int local_sockets_libmnl_cb_data(const struct nlmsghdr *nlh, void *data) {
|
|
|
LS_STATE *ls = data;
|
|
@@ -784,16 +791,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);
|
|
@@ -801,35 +822,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,
|
|
@@ -863,67 +955,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);
|
|
@@ -934,6 +968,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) {
|
|
@@ -1025,31 +1112,33 @@ static inline void local_sockets_init(LS_STATE *ls) {
|
|
|
|
|
|
memset(&ls->stats, 0, sizeof(ls->stats));
|
|
|
|
|
|
-#ifdef HAVE_LIBMNL
|
|
|
- ls->use_mnl = 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
|
|
@@ -1087,19 +1176,6 @@ 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->config.no_mnl && ls->nl && ls->use_mnl) {
|
|
|
- ls->use_mnl = local_sockets_libmnl_get_sockets(ls, family, protocol);
|
|
|
-
|
|
|
- if(ls->use_mnl)
|
|
|
- return;
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- local_sockets_read_proc_net_x(ls, filename, 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;
|
|
@@ -1122,6 +1198,60 @@ static inline void local_sockets_track_time(LS_STATE *ls, const char *name) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+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");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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_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];
|
|
|
|
|
@@ -1129,55 +1259,71 @@ static inline void local_sockets_read_all_system_sockets(LS_STATE *ls) {
|
|
|
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, "read_proc_pids");
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
if(ls->config.tcp4) {
|
|
|
- local_sockets_track_time(ls, "read_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) {
|
|
|
- local_sockets_track_time(ls, "read_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) {
|
|
|
- local_sockets_track_time(ls, "read_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) {
|
|
|
- local_sockets_track_time(ls, "read_udp6");
|
|
|
snprintfz(path, sizeof(path), "%s/proc/net/udp6", ls->config.host_prefix);
|
|
|
local_sockets_do_family_protocol(ls, path, AF_INET6, IPPROTO_UDP);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
|
+// 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");
|
|
@@ -1192,8 +1338,14 @@ static inline void local_sockets_send_to_parent(struct local_socket_state *ls __
|
|
|
}
|
|
|
|
|
|
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;
|
|
@@ -1201,9 +1353,13 @@ static inline int 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 = {
|
|
@@ -1213,7 +1369,6 @@ static inline int 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) {
|
|
@@ -1221,6 +1376,9 @@ static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
|
|
|
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);
|
|
|
|
|
@@ -1228,10 +1386,7 @@ static inline int 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);
|
|
|
|
|
@@ -1246,6 +1401,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;
|
|
|
}
|
|
|
|
|
@@ -1253,28 +1410,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;
|
|
|
}
|
|
|
|
|
@@ -1299,13 +1474,12 @@ 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);
|
|
|
|
|
@@ -1313,9 +1487,13 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
|
|
|
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);
|
|
|
+
|
|
|
+ if(ls->config.report)
|
|
|
+ __atomic_add_fetch(&ls->stats.namespaces_sockets_existing, 1, __ATOMIC_RELAXED);
|
|
|
}
|
|
|
else {
|
|
|
n = aral_mallocz(ls->local_socket_aral);
|
|
@@ -1323,12 +1501,19 @@ static inline bool local_sockets_get_namespace_sockets_with_pid(LS_STATE *ls, st
|
|
|
simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
|
|
|
|
|
|
local_sockets_index_listening_port(ls, n);
|
|
|
+
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
@@ -1385,6 +1570,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);
|
|
@@ -1421,6 +1607,100 @@ 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) {
|
|
@@ -1438,7 +1718,11 @@ static inline void local_sockets_process(LS_STATE *ls) {
|
|
|
// check all socket 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
|
|
@@ -1456,7 +1740,7 @@ static inline void local_sockets_process(LS_STATE *ls) {
|
|
|
local_sockets_cleanup(ls);
|
|
|
}
|
|
|
|
|
|
-static inline void ipv6_address_to_txt(struct in6_addr *in6_addr, char *dst) {
|
|
|
+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;
|