9
#define _countof(x) (sizeof(x) / sizeof(*(x)))
10
#endif
11
12
-#ifdef HAVE_LIBMNL
12
+#define LOCAL_SOCKETS_USE_SETNS
13
+#define USE_LIBMNL_AFTER_SETNS
14
+
15
+#if defined(HAVE_LIBMNL)
16
#include <linux/rtnetlink.h>
17
#include <linux/inet_diag.h>
18
#include <linux/sock_diag.h>
70
// --------------------------------------------------------------------------------------------------------------------
71
72
struct local_socket_state;
70
-typedef void (*local_sockets_cb_t)(struct local_socket_state *state, struct local_socket *n, void *data);
73
+typedef void (*local_sockets_cb_t)(struct local_socket_state *state, const struct local_socket *n, void *data);
74
75
struct local_sockets_config {
76
bool listening;
88
bool namespaces;
89
bool tcp_info;
90
bool no_mnl;
91
+ bool procfile;
92
bool report;
93
94
size_t max_errors;
98
void *data;
99
100
const char *host_prefix;
101
+};
102
98
- // internal use
103
+struct local_sockets_state {
104
+ uint32_t nl_seq;
105
uint64_t net_ns_inode;
106
+ pid_t net_ns_pid;
107
};
108
109
struct timing_work {
112
const char *name;
113
};
114
115
+struct local_sockets_ns_req {
116
+ struct local_sockets_config config;
117
+ struct local_sockets_state ns_state;
118
+};
119
+
120
typedef struct local_socket_state {
121
struct local_sockets_config config;
122
+ struct local_sockets_state ns_state;
123
124
struct {
125
size_t mnl_sends;
113
- size_t namespaces_found;
126
size_t tcp_info_received;
127
size_t pid_fds_processed;
128
size_t pid_fds_opendir_failed;
129
size_t pid_fds_readlink_failed;
130
size_t pid_fds_parse_failed;
131
size_t errors_encountered;
132
+
133
+ size_t sockets_added;
134
+
135
+ size_t namespaces_found;
136
+ size_t namespaces_absent;
137
+ size_t namespaces_invalid;
138
+#if defined(LOCAL_SOCKETS_USE_SETNS)
139
+ size_t namespaces_forks_attempted;
140
+ size_t namespaces_forks_failed;
141
+ size_t namespaces_forks_unresponsive;
142
+ size_t namespaces_sockets_new;
143
+ size_t namespaces_sockets_existing;
144
+#endif
145
+
146
+ struct procfile_stats ff;
147
} stats;
148
149
size_t timings_idx;
123
- struct timing_work timings[20];
150
+ struct timing_work timings[30];
151
152
+#if defined(LOCAL_SOCKETS_USE_SETNS)
153
bool spawn_server_is_mine;
154
SPAWN_SERVER *spawn_server;
155
+#endif
156
128
-#ifdef HAVE_LIBMNL
129
- bool use_mnl;
130
- struct mnl_socket *nl;
157
+#if defined(HAVE_LIBMNL)
158
uint16_t tmp_protocol;
159
#endif
160
161
+ procfile *ff;
162
+
163
ARAL *local_socket_aral;
164
ARAL *pid_socket_aral;
165
SPINLOCK spinlock; // for namespaces
267
#endif
268
} LOCAL_SOCKET;
269
270
+#if defined(LOCAL_SOCKETS_USE_SETNS)
271
static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request);
272
+#endif
273
274
// --------------------------------------------------------------------------------------------------------------------
275
300
return memcmp(addr->s6_addr, ipv4_mapped_prefix, 12) == 0;
301
}
302
272
-static bool local_sockets_is_loopback_address(struct socket_endpoint *se) {
303
+static bool local_sockets_is_loopback_address(const struct socket_endpoint *se) {
304
if (se->family == AF_INET) {
305
// For IPv4, loopback addresses are in the 127.0.0.0/8 range
306
return (ntohl(se->ip.ipv4) >> 24) == 127; // Check if the first byte is 127
334
);
335
}
336
306
-static inline bool local_sockets_is_private_address(struct socket_endpoint *se) {
337
+static inline bool local_sockets_is_private_address(const struct socket_endpoint *se) {
338
if (se->family == AF_INET) {
339
return local_sockets_is_ipv4_reserved_address(se->ip.ipv4);
340
}
368
return false;
369
}
370
340
-static bool local_sockets_is_multicast_address(struct socket_endpoint *se) {
371
+static bool local_sockets_is_multicast_address(const struct socket_endpoint *se) {
372
if (se->family == AF_INET) {
373
// For IPv4, check if the address is 0.0.0.0
374
uint32_t ip = htonl(se->ip.ipv4);
383
return false;
384
}
385
355
-static bool local_sockets_is_zero_address(struct socket_endpoint *se) {
386
+static bool local_sockets_is_zero_address(const struct socket_endpoint *se) {
387
if (se->family == AF_INET) {
388
// For IPv4, check if the address is 0.0.0.0
389
return se->ip.ipv4 == 0;
396
return false;
397
}
398
368
-static inline const char *local_sockets_address_space(struct socket_endpoint *se) {
399
+static inline const char *local_sockets_address_space(const struct socket_endpoint *se) {
400
if(local_sockets_is_zero_address(se))
401
return "zero";
402
else if(local_sockets_is_loopback_address(se))
411
412
// --------------------------------------------------------------------------------------------------------------------
413
383
-static inline bool is_local_socket_ipv46(LOCAL_SOCKET *n) {
414
+static inline bool is_local_socket_ipv46(const LOCAL_SOCKET *n) {
415
return n->local.family == AF_INET6 &&
416
n->direction == SOCKET_DIRECTION_LISTEN &&
417
local_sockets_is_zero_address(&n->local) &&
646
return false;
647
}
648
649
+ ls->stats.sockets_added++;
650
+
651
n = aral_mallocz(ls->local_socket_aral);
652
*n = *tmp; // copy all contents
653
716
return true;
717
}
718
686
-#ifdef HAVE_LIBMNL
687
-
688
-static inline void local_sockets_libmnl_init(LS_STATE *ls) {
689
- if(ls->config.no_mnl) return;
690
-
691
- ls->nl = mnl_socket_open(NETLINK_INET_DIAG);
692
- if (ls->nl == NULL) {
693
- local_sockets_log(ls, "cannot open libmnl netlink socket");
694
- ls->use_mnl = false;
695
- }
696
- else if (mnl_socket_bind(ls->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
697
- local_sockets_log(ls, "cannot bind libmnl netlink socket");
698
- mnl_socket_close(ls->nl);
699
- ls->nl = NULL;
700
- ls->use_mnl = false;
701
- }
702
- else
703
- ls->use_mnl = true;
704
-}
705
-
706
-static inline void local_sockets_libmnl_cleanup(LS_STATE *ls) {
707
- if(ls->nl) {
708
- mnl_socket_close(ls->nl);
709
- ls->nl = NULL;
710
- ls->use_mnl = false;
711
- }
712
-}
719
+#if defined(HAVE_LIBMNL)
720
721
static inline int local_sockets_libmnl_cb_data(const struct nlmsghdr *nlh, void *data) {
722
LS_STATE *ls = data;
791
static inline bool local_sockets_libmnl_get_sockets(LS_STATE *ls, uint16_t family, uint16_t protocol) {
792
ls->tmp_protocol = protocol;
793
787
- char buf[MNL_SOCKET_BUFFER_SIZE];
788
- struct nlmsghdr *nlh;
789
- struct inet_diag_req_v2 req;
790
- unsigned int seq, portid = mnl_socket_get_portid(ls->nl);
794
+ struct mnl_socket *nl = mnl_socket_open(NETLINK_INET_DIAG);
795
+ if (nl == NULL) {
796
+ local_sockets_log(ls, "mnl_socket_open() failed");
797
+ return false;
798
+ }
799
792
- memset(&req, 0, sizeof(req));
793
- req.sdiag_family = family;
794
- req.sdiag_protocol = protocol;
795
- req.idiag_states = -1;
796
- req.idiag_ext = 0;
800
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
801
+ local_sockets_log(ls, "mnl_socket_bind() failed");
802
+ mnl_socket_close(nl);
803
+ return false;
804
+ }
805
+
806
+ char buf[MNL_SOCKET_BUFFER_SIZE];
807
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
808
+ nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
809
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
810
+ nlh->nlmsg_seq = ls->ns_state.nl_seq ? ls->ns_state.nl_seq++ : time(NULL);
811
+
812
+ struct inet_diag_req_v2 req = {
813
+ .sdiag_family = family,
814
+ .sdiag_protocol = protocol,
815
+ .idiag_states = ~0, // Request all socket states
816
+ .idiag_ext = 0,
817
+ };
818
819
if(family == AF_INET6)
820
req.idiag_ext |= 1 << (INET_DIAG_SKV6ONLY - 1);
822
if(protocol == IPPROTO_TCP && ls->config.tcp_info)
823
req.idiag_ext |= 1 << (INET_DIAG_INFO - 1);
824
804
- nlh = mnl_nlmsg_put_header(buf);
805
- nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
806
- nlh->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
807
- nlh->nlmsg_seq = seq = time(NULL);
825
mnl_nlmsg_put_extra_header(nlh, sizeof(req));
826
memcpy(mnl_nlmsg_get_payload(nlh), &req, sizeof(req));
827
828
ls->stats.mnl_sends++;
812
- if (mnl_socket_sendto(ls->nl, nlh, nlh->nlmsg_len) < 0) {
813
- local_sockets_log(ls, "mnl_socket_send failed");
829
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
830
+ local_sockets_log(ls, "mnl_socket_sendto() failed");
831
+ mnl_socket_close(nl);
832
return false;
833
}
834
835
+ bool rc = true;
836
+ size_t received = 0;
837
ssize_t ret;
818
- while ((ret = mnl_socket_recvfrom(ls->nl, buf, sizeof(buf))) > 0) {
819
- ret = mnl_cb_run(buf, ret, seq, portid, local_sockets_libmnl_cb_data, ls);
820
- if (ret <= MNL_CB_STOP)
838
+ while ((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
839
+ ret = mnl_cb_run(buf, ret, 0, 0, local_sockets_libmnl_cb_data, ls);
840
+ if (ret == MNL_CB_ERROR) {
841
+ local_sockets_log(ls, "mnl_cb_run() failed");
842
+ rc = false;
843
break;
844
+ }
845
+ else if (ret <= MNL_CB_STOP)
846
+ break;
847
+
848
+ received++;
849
}
850
+ mnl_socket_close(nl);
851
+
852
if (ret == -1) {
824
- local_sockets_log(ls, "mnl_socket_recvfrom");
853
+ local_sockets_log(ls, "mnl_socket_recvfrom() failed");
854
+ rc = false;
855
+ }
856
+
857
+ return rc;
858
+}
859
+#endif // HAVE_LIBMNL
860
+
861
+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) {
862
+ // char *sl_txt = get_word(words, num_words, 0);
863
+ char *local_ip_txt = get_word(words, num_words, 1);
864
+ char *local_port_txt = get_word(words, num_words, 2);
865
+ char *remote_ip_txt = get_word(words, num_words, 3);
866
+ char *remote_port_txt = get_word(words, num_words, 4);
867
+ char *state_txt = get_word(words, num_words, 5);
868
+ char *tx_queue_txt = get_word(words, num_words, 6);
869
+ char *rx_queue_txt = get_word(words, num_words, 7);
870
+ char *tr_txt = get_word(words, num_words, 8);
871
+ char *tm_when_txt = get_word(words, num_words, 9);
872
+ char *retrans_txt = get_word(words, num_words, 10);
873
+ char *uid_txt = get_word(words, num_words, 11);
874
+ // char *timeout_txt = get_word(words, num_words, 12);
875
+ char *inode_txt = get_word(words, num_words, 13);
876
+
877
+ if(!local_ip_txt || !local_port_txt || !remote_ip_txt || !remote_port_txt || !state_txt ||
878
+ !tx_queue_txt || !rx_queue_txt || !tr_txt || !tm_when_txt || !retrans_txt || !uid_txt || !inode_txt) {
879
+ local_sockets_log(ls, "cannot parse ipv4 line No %zu of filename '%s'", line, filename);
880
return false;
881
}
882
883
+ LOCAL_SOCKET n = {
884
+ .direction = SOCKET_DIRECTION_NONE,
885
+ .ipv6ony = {
886
+ .checked = false,
887
+ .ipv46 = false,
888
+ },
889
+ .local = {
890
+ .family = family,
891
+ .protocol = protocol,
892
+ },
893
+ .remote = {
894
+ .family = family,
895
+ .protocol = protocol,
896
+ },
897
+ .uid = UID_UNSET,
898
+ };
899
+
900
+ n.local.port = str2uint32_hex(local_port_txt, NULL);
901
+ n.remote.port = str2uint32_hex(remote_port_txt, NULL);
902
+ n.state = str2uint32_hex(state_txt, NULL);
903
+ n.wqueue = str2uint32_hex(tx_queue_txt, NULL);
904
+ n.rqueue = str2uint32_hex(rx_queue_txt, NULL);
905
+ n.timer = str2uint32_hex(tr_txt, NULL);
906
+ n.expires = str2uint32_hex(tm_when_txt, NULL);
907
+ n.retransmits = str2uint32_hex(retrans_txt, NULL);
908
+ n.uid = str2uint32_t(uid_txt, NULL);
909
+ n.inode = str2uint64_t(inode_txt, NULL);
910
+
911
+ if(family == AF_INET) {
912
+ n.local.ip.ipv4 = str2uint32_hex(local_ip_txt, NULL);
913
+ n.remote.ip.ipv4 = str2uint32_hex(remote_ip_txt, NULL);
914
+ }
915
+ else if(family == AF_INET6) {
916
+ ipv6_to_in6_addr(local_ip_txt, &n.local.ip.ipv6);
917
+ ipv6_to_in6_addr(remote_ip_txt, &n.remote.ip.ipv6);
918
+ }
919
+
920
+ local_sockets_add_socket(ls, &n);
921
return true;
922
}
830
-#endif // HAVE_LIBMNL
923
832
-static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
924
+static inline bool local_sockets_read_proc_net_x_getline(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
925
static bool is_space[256] = {
926
[':'] = true,
927
[' '] = true,
955
continue;
956
}
957
866
- LOCAL_SOCKET n = {
867
- .direction = SOCKET_DIRECTION_NONE,
868
- .ipv6ony = {
869
- .checked = false,
870
- .ipv46 = false,
871
- },
872
- .local = {
873
- .family = family,
874
- .protocol = protocol,
875
- },
876
- .remote = {
877
- .family = family,
878
- .protocol = protocol,
879
- },
880
- .uid = UID_UNSET,
881
- };
882
-
958
char *words[32];
959
size_t num_words = quoted_strings_splitter(line, words, 32, is_space);
885
- // char *sl_txt = get_word(words, num_words, 0);
886
- char *local_ip_txt = get_word(words, num_words, 1);
887
- char *local_port_txt = get_word(words, num_words, 2);
888
- char *remote_ip_txt = get_word(words, num_words, 3);
889
- char *remote_port_txt = get_word(words, num_words, 4);
890
- char *state_txt = get_word(words, num_words, 5);
891
- char *tx_queue_txt = get_word(words, num_words, 6);
892
- char *rx_queue_txt = get_word(words, num_words, 7);
893
- char *tr_txt = get_word(words, num_words, 8);
894
- char *tm_when_txt = get_word(words, num_words, 9);
895
- char *retrans_txt = get_word(words, num_words, 10);
896
- char *uid_txt = get_word(words, num_words, 11);
897
- // char *timeout_txt = get_word(words, num_words, 12);
898
- char *inode_txt = get_word(words, num_words, 13);
899
-
900
- if(!local_ip_txt || !local_port_txt || !remote_ip_txt || !remote_port_txt || !state_txt ||
901
- !tx_queue_txt || !rx_queue_txt || !tr_txt || !tm_when_txt || !retrans_txt || !uid_txt || !inode_txt) {
902
- local_sockets_log(ls, "cannot parse ipv4 line No %zu of filename '%s'", counter, filename);
903
- continue;
904
- }
905
-
906
- n.local.port = str2uint32_hex(local_port_txt, NULL);
907
- n.remote.port = str2uint32_hex(remote_port_txt, NULL);
908
- n.state = str2uint32_hex(state_txt, NULL);
909
- n.wqueue = str2uint32_hex(tx_queue_txt, NULL);
910
- n.rqueue = str2uint32_hex(rx_queue_txt, NULL);
911
- n.timer = str2uint32_hex(tr_txt, NULL);
912
- n.expires = str2uint32_hex(tm_when_txt, NULL);
913
- n.retransmits = str2uint32_hex(retrans_txt, NULL);
914
- n.uid = str2uint32_t(uid_txt, NULL);
915
- n.inode = str2uint64_t(inode_txt, NULL);
916
-
917
- if(family == AF_INET) {
918
- n.local.ip.ipv4 = str2uint32_hex(local_ip_txt, NULL);
919
- n.remote.ip.ipv4 = str2uint32_hex(remote_ip_txt, NULL);
920
- }
921
- else if(family == AF_INET6) {
922
- ipv6_to_in6_addr(local_ip_txt, &n.local.ip.ipv6);
923
- ipv6_to_in6_addr(remote_ip_txt, &n.remote.ip.ipv6);
924
- }
925
-
926
- local_sockets_add_socket(ls, &n);
960
+ local_sockets_process_proc_line(ls, filename, family, protocol, counter, words, num_words);
961
}
962
963
fclose(fp);
968
return true;
969
}
970
971
+#define INITIALLY_EXPECTED_PROC_NET_LINES 16384
972
+#define PROC_NET_BYTES_PER_LINE 155 // 105 for IPv4, 155 for IPv6
973
+#define PROC_NET_WORDS_PER_LINE 22
974
+#define INITIALLY_EXPECTED_PROC_NET_WORDS (INITIALLY_EXPECTED_PROC_NET_LINES * PROC_NET_WORDS_PER_LINE)
975
+#define INITIALLY_EXPECTED_PROC_NET_BYTES (INITIALLY_EXPECTED_PROC_NET_LINES * PROC_NET_BYTES_PER_LINE)
976
+
977
+static inline bool local_sockets_read_proc_net_x_procfile(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
978
+ if(family != AF_INET && family != AF_INET6)
979
+ return false;
980
+
981
+ procfile_set_adaptive_allocation(true, INITIALLY_EXPECTED_PROC_NET_BYTES, INITIALLY_EXPECTED_PROC_NET_LINES, INITIALLY_EXPECTED_PROC_NET_WORDS);
982
+
983
+ bool copy_initial_ff_stats = ls->ff == NULL && ls->stats.ff.memory > 0;
984
+ ls->ff = procfile_reopen(ls->ff, filename, ls->ff ? NULL :" :", PROCFILE_FLAG_DEFAULT);
985
+
986
+ // we just created ff, copy our old stats to it
987
+ if(ls->ff && copy_initial_ff_stats) ls->ff->stats = ls->stats.ff;
988
+
989
+ ls->ff = procfile_readall(ls->ff);
990
+ if(!ls->ff) return false;
991
+
992
+ // get the latest stats from ff;
993
+ ls->stats.ff = ls->ff->stats;
994
+
995
+ for(size_t l = 1; l < procfile_lines(ls->ff) ;l++) {
996
+ size_t w = procfile_linewords(ls->ff, l);
997
+ if(!w) continue;
998
+ if(w < 14) {
999
+ local_sockets_log(ls, "too small line No %zu of filename '%s' (has %zu words)", l, filename, w);
1000
+ continue;
1001
+ }
1002
+
1003
+ char *words[14] = { 0 };
1004
+ words[0] = procfile_lineword(ls->ff, l, 0);
1005
+ words[1] = procfile_lineword(ls->ff, l, 1);
1006
+ words[2] = procfile_lineword(ls->ff, l, 2);
1007
+ words[3] = procfile_lineword(ls->ff, l, 3);
1008
+ words[4] = procfile_lineword(ls->ff, l, 4);
1009
+ words[5] = procfile_lineword(ls->ff, l, 5);
1010
+ words[6] = procfile_lineword(ls->ff, l, 6);
1011
+ words[7] = procfile_lineword(ls->ff, l, 7);
1012
+ words[8] = procfile_lineword(ls->ff, l, 8);
1013
+ words[9] = procfile_lineword(ls->ff, l, 9);
1014
+ words[10] = procfile_lineword(ls->ff, l, 10);
1015
+ words[11] = procfile_lineword(ls->ff, l, 11);
1016
+ words[12] = procfile_lineword(ls->ff, l, 12);
1017
+ words[13] = procfile_lineword(ls->ff, l, 13);
1018
+ local_sockets_process_proc_line(ls, filename, family, protocol, l, words, _countof(words));
1019
+ }
1020
+
1021
+ return true;
1022
+}
1023
+
1024
// --------------------------------------------------------------------------------------------------------------------
1025
1026
static inline void local_sockets_detect_directions(LS_STATE *ls) {
1112
1113
memset(&ls->stats, 0, sizeof(ls->stats));
1114
1028
-#ifdef HAVE_LIBMNL
1029
- ls->use_mnl = false;
1030
- ls->nl = NULL;
1115
+#if defined(HAVE_LIBMNL)
1116
ls->tmp_protocol = 0;
1032
- local_sockets_libmnl_init(ls);
1117
#endif
1118
1119
+#if defined(LOCAL_SOCKETS_USE_SETNS)
1120
if(ls->config.namespaces && ls->spawn_server == NULL) {
1121
ls->spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, NULL, local_sockets_spawn_server_callback, 0, NULL);
1122
ls->spawn_server_is_mine = true;
1123
}
1124
else
1125
ls->spawn_server_is_mine = false;
1126
+#endif
1127
}
1128
1129
static inline void local_sockets_cleanup(LS_STATE *ls) {
1130
+ if(ls->ff) {
1131
+ ls->stats.ff = ls->ff->stats;
1132
+ procfile_close(ls->ff);
1133
+ ls->ff = NULL;
1134
+ }
1135
1136
+#if defined(LOCAL_SOCKETS_USE_SETNS)
1137
if(ls->spawn_server_is_mine) {
1138
spawn_server_destroy(ls->spawn_server);
1139
ls->spawn_server = NULL;
1140
ls->spawn_server_is_mine = false;
1141
}
1050
-
1051
-#ifdef HAVE_LIBMNL
1052
- local_sockets_libmnl_cleanup(ls);
1142
#endif
1143
1144
// free the sockets hashtable data
1176
1177
// --------------------------------------------------------------------------------------------------------------------
1178
1090
-static inline void local_sockets_do_family_protocol(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
1091
-#ifdef HAVE_LIBMNL
1092
- if(!ls->config.no_mnl && ls->nl && ls->use_mnl) {
1093
- ls->use_mnl = local_sockets_libmnl_get_sockets(ls, family, protocol);
1094
-
1095
- if(ls->use_mnl)
1096
- return;
1097
- }
1098
-#endif
1099
-
1100
- local_sockets_read_proc_net_x(ls, filename, family, protocol);
1101
-}
1102
-
1179
static inline void local_sockets_track_time(LS_STATE *ls, const char *name) {
1180
if(!ls->config.report || ls->timings_idx >= _countof(ls->timings))
1181
return;
1198
}
1199
}
1200
1201
+static void local_sockets_track_time_by_protocol(LS_STATE *ls, bool mnl, uint16_t family, uint16_t protocol) {
1202
+ if(mnl) {
1203
+ if(family == AF_INET) {
1204
+ if(protocol == IPPROTO_TCP)
1205
+ local_sockets_track_time(ls, "mnl_read_tcp4");
1206
+ else if(protocol == IPPROTO_UDP)
1207
+ local_sockets_track_time(ls, "mnl_read_udp4");
1208
+ }
1209
+ else if(family == AF_INET6) {
1210
+ if(protocol == IPPROTO_TCP)
1211
+ local_sockets_track_time(ls, "mnl_read_tcp6");
1212
+ else if(protocol == IPPROTO_UDP)
1213
+ local_sockets_track_time(ls, "mnl_read_udp6");
1214
+ }
1215
+ else
1216
+ local_sockets_track_time(ls, "mnl_read_unknown");
1217
+ }
1218
+ else {
1219
+ if(family == AF_INET) {
1220
+ if(protocol == IPPROTO_TCP)
1221
+ local_sockets_track_time(ls, "proc_read_tcp4");
1222
+ else if(protocol == IPPROTO_UDP)
1223
+ local_sockets_track_time(ls, "proc_read_udp4");
1224
+ }
1225
+ else if(family == AF_INET6) {
1226
+ if(protocol == IPPROTO_TCP)
1227
+ local_sockets_track_time(ls, "proc_read_tcp6");
1228
+ else if(protocol == IPPROTO_UDP)
1229
+ local_sockets_track_time(ls, "proc_read_udp6");
1230
+ }
1231
+ else
1232
+ local_sockets_track_time(ls, "proc_read_unknown");
1233
+ }
1234
+}
1235
+
1236
+static inline void local_sockets_do_family_protocol(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
1237
+#if defined(HAVE_LIBMNL)
1238
+ if(!ls->config.no_mnl) {
1239
+ local_sockets_track_time_by_protocol(ls, true, family, protocol);
1240
+ if(local_sockets_libmnl_get_sockets(ls, family, protocol))
1241
+ return;
1242
+
1243
+ // else, do proc
1244
+ }
1245
+#endif
1246
+
1247
+ local_sockets_track_time_by_protocol(ls, false, family, protocol);
1248
+
1249
+ if(ls->config.procfile)
1250
+ local_sockets_read_proc_net_x_procfile(ls, filename, family, protocol);
1251
+ else
1252
+ local_sockets_read_proc_net_x_getline(ls, filename, family, protocol);
1253
+}
1254
+
1255
static inline void local_sockets_read_all_system_sockets(LS_STATE *ls) {
1256
char path[FILENAME_MAX + 1];
1257
1259
local_sockets_track_time(ls, "read_namespaces");
1260
snprintfz(path, sizeof(path), "%s/proc/self/ns/net", ls->config.host_prefix);
1261
local_sockets_read_proc_inode_link(ls, path, &ls->proc_self_net_ns_inode, "net");
1132
-
1262
}
1263
1264
if(ls->config.cmdline || ls->config.comm || ls->config.pid || ls->config.namespaces) {
1136
- local_sockets_track_time(ls, "read_proc_pids");
1265
+ local_sockets_track_time(ls, "proc_read_pids");
1266
snprintfz(path, sizeof(path), "%s/proc", ls->config.host_prefix);
1267
local_sockets_find_all_sockets_in_proc(ls, path);
1268
}
1269
1270
if(ls->config.tcp4) {
1142
- local_sockets_track_time(ls, "read_tcp4");
1271
snprintfz(path, sizeof(path), "%s/proc/net/tcp", ls->config.host_prefix);
1272
local_sockets_do_family_protocol(ls, path, AF_INET, IPPROTO_TCP);
1273
}
1274
1275
if(ls->config.udp4) {
1148
- local_sockets_track_time(ls, "read_udp4");
1276
snprintfz(path, sizeof(path), "%s/proc/net/udp", ls->config.host_prefix);
1277
local_sockets_do_family_protocol(ls, path, AF_INET, IPPROTO_UDP);
1278
}
1279
1280
if(ls->config.tcp6) {
1154
- local_sockets_track_time(ls, "read_tcp6");
1281
snprintfz(path, sizeof(path), "%s/proc/net/tcp6", ls->config.host_prefix);
1282
local_sockets_do_family_protocol(ls, path, AF_INET6, IPPROTO_TCP);
1283
}
1284
1285
if(ls->config.udp6) {
1160
- local_sockets_track_time(ls, "read_udp6");
1286
snprintfz(path, sizeof(path), "%s/proc/net/udp6", ls->config.host_prefix);
1287
local_sockets_do_family_protocol(ls, path, AF_INET6, IPPROTO_UDP);
1288
}
1289
}
1290
1291
// --------------------------------------------------------------------------------------------------------------------
1292
+// switch namespaces to read namespace sockets
1293
+
1294
+#if defined(LOCAL_SOCKETS_USE_SETNS)
1295
1296
struct local_sockets_child_work {
1297
int fd;
1298
uint64_t net_ns_inode;
1299
};
1300
1173
-static inline void local_sockets_send_to_parent(struct local_socket_state *ls __maybe_unused, struct local_socket *n, void *data) {
1301
+#define LOCAL_SOCKET_TERMINATOR (struct local_socket) { \
1302
+ .expires = UINT32_MAX, \
1303
+ .timer = UINT8_MAX, \
1304
+ .inode = UINT64_MAX, \
1305
+ .net_ns_inode = UINT64_MAX, \
1306
+}
1307
+
1308
+static inline bool local_socket_is_terminator(const struct local_socket *n) {
1309
+ static const struct local_socket t = LOCAL_SOCKET_TERMINATOR;
1310
+ return (n->expires == t.expires &&
1311
+ n->timer == t.timer &&
1312
+ n->inode == t.inode &&
1313
+ n->net_ns_inode == t.net_ns_inode);
1314
+}
1315
+
1316
+static inline void local_sockets_send_to_parent(struct local_socket_state *ls, const struct local_socket *n, void *data) {
1317
struct local_sockets_child_work *cw = data;
1318
int fd = cw->fd;
1319
1177
- if(n->net_ns_inode != cw->net_ns_inode)
1178
- return;
1179
-
1180
- // local_sockets_log(ls, "child is sending inode %"PRIu64" of namespace %"PRIu64, n->inode, n->net_ns_inode);
1320
+ if(!local_socket_is_terminator(n)) {
1321
+ ls->stats.errors_encountered = 0;
1322
+// local_sockets_log(
1323
+// ls,
1324
+// "child is sending inode %"PRIu64" of namespace %"PRIu64", from namespace %"PRIu64" for pid %d",
1325
+// n->inode, n->net_ns_inode, ls->proc_self_net_ns_inode, ls->ns_state.net_ns_pid);
1326
+ }
1327
1328
if(write(fd, n, sizeof(*n)) != sizeof(*n))
1329
local_sockets_log(ls, "failed to write local socket to pipe");
1338
}
1339
1340
static inline int local_sockets_spawn_server_callback(SPAWN_REQUEST *request) {
1341
+ static const struct local_socket terminator = LOCAL_SOCKET_TERMINATOR;
1342
+
1343
+ struct local_sockets_ns_req *req = (struct local_sockets_ns_req *)request->data;
1344
+
1345
LS_STATE ls = { 0 };
1196
- ls.config = *((struct local_sockets_config *)request->data);
1346
+ ls.config = req->config;
1347
+ ls.ns_state = req->ns_state;
1348
+ ls.ns_state.nl_seq += gettid_uncached() * 10;
1349
1350
// we don't need these inside namespaces
1351
ls.config.cmdline = false;
1353
ls.config.pid = false;
1354
ls.config.namespaces = false;
1355
1356
+#if !defined(USE_LIBMNL_AFTER_SETNS)
1357
+ ls.config.no_mnl = true; // disable mnl since this collects all sockets from the entire system
1358
+#endif
1359
+
1360
// initialize local sockets
1361
local_sockets_init(&ls);
1206
-
1362
+ ls.proc_self_net_ns_inode = ls.ns_state.net_ns_inode;
1363
ls.config.host_prefix = ""; // we need the /proc of the container
1364
1365
struct local_sockets_child_work cw = {
1369
1370
ls.config.cb = local_sockets_send_to_parent;
1371
ls.config.data = &cw;
1216
- ls.proc_self_net_ns_inode = ls.config.net_ns_inode;
1372
1373
// switch namespace using the custom fd passed via the spawn server
1374
if (setns(request->fds[3], CLONE_NEWNET) == -1) {
1376
return EXIT_FAILURE;
1377
}
1378
1379
+ // close the custom fd
1380
+ close(request->fds[3]); request->fds[3] = -1;
1381
+
1382
// read all sockets from /proc
1383
local_sockets_read_all_system_sockets(&ls);
1384
1386
local_sockets_foreach_local_socket_call_cb(&ls);
1387
1388
// send the terminating socket
1231
- struct local_socket zero = {
1232
- .net_ns_inode = ls.config.net_ns_inode,
1233
- };
1234
- local_sockets_send_to_parent(&ls, &zero, &cw);
1389
+ local_sockets_send_to_parent(&ls, &terminator, &cw);
1390
1391
local_sockets_cleanup(&ls);
1392
1401
int fd = open(filename, O_RDONLY | O_CLOEXEC);
1402
if (fd == -1) {
1403
local_sockets_log(ls, "cannot open file '%s'", filename);
1404
+ if(ls->config.report)
1405
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
1406
return false;
1407
}
1408
1410
if (fstat(fd, &statbuf) == -1) {
1411
close(fd);
1412
local_sockets_log(ls, "failed to get file statistics for '%s'", filename);
1413
+ if(ls->config.report)
1414
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
1415
return false;
1416
}
1417
1418
if (statbuf.st_ino != ps->net_ns_inode) {
1419
close(fd);
1420
local_sockets_log(ls, "pid %d is not in the wanted network namespace", ps->pid);
1421
+ if(ls->config.report)
1422
+ __atomic_add_fetch(&ls->stats.namespaces_invalid, 1, __ATOMIC_RELAXED);
1423
return false;
1424
}
1425
1426
if(ls->spawn_server == NULL) {
1427
close(fd);
1428
local_sockets_log(ls, "spawn server is not available");
1429
+ if(ls->config.report)
1430
+ __atomic_add_fetch(&ls->stats.namespaces_forks_failed, 1, __ATOMIC_RELAXED);
1431
return false;
1432
}
1433
1271
- struct local_sockets_config config = ls->config;
1272
- config.net_ns_inode = ps->net_ns_inode;
1273
- SPAWN_INSTANCE *si = spawn_server_exec(ls->spawn_server, STDERR_FILENO, fd, NULL, &config, sizeof(config), SPAWN_INSTANCE_TYPE_CALLBACK);
1434
+ struct local_sockets_ns_req req = {
1435
+ .config = ls->config,
1436
+ .ns_state = ls->ns_state,
1437
+ };
1438
+ req.ns_state.net_ns_pid = ps->pid;
1439
+ req.ns_state.net_ns_inode = ps->net_ns_inode;
1440
+
1441
+ SPAWN_INSTANCE *si = spawn_server_exec(ls->spawn_server, STDERR_FILENO, fd, NULL, &req, sizeof(req), SPAWN_INSTANCE_TYPE_CALLBACK);
1442
close(fd); fd = -1;
1443
1444
+ if(ls->config.report)
1445
+ __atomic_add_fetch(&ls->stats.namespaces_forks_attempted, 1, __ATOMIC_RELAXED);
1446
+
1447
if(si == NULL) {
1448
local_sockets_log(ls, "cannot create spawn instance");
1449
+
1450
+ if(ls->config.report)
1451
+ __atomic_add_fetch(&ls->stats.namespaces_forks_failed, 1, __ATOMIC_RELAXED);
1452
+
1453
return false;
1454
}
1455
1474
1475
received++;
1476
1302
- struct local_socket zero = {
1303
- .net_ns_inode = ps->net_ns_inode,
1304
- };
1305
- if(memcmp(&buf, &zero, sizeof(buf)) == 0) {
1306
- // the terminator
1477
+ if(local_socket_is_terminator(&buf))
1478
+ // the child finished
1479
break;
1308
- }
1480
+
1481
+ // overwrite the net_ns_inode we receive
1482
+ buf.net_ns_inode = ps->net_ns_inode;
1483
1484
spinlock_lock(&ls->spinlock);
1485
1487
LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
1488
if(n) {
1489
string_freez(buf.cmdline);
1490
+
1491
// local_sockets_log(ls,
1492
// "ns inode %" PRIu64" (comm: '%s', pid: %u, ns: %"PRIu64") already exists in hashtable (comm: '%s', pid: %u, ns: %"PRIu64") - ignoring duplicate",
1493
// buf.inode, buf.comm, buf.pid, buf.net_ns_inode, n->comm, n->pid, n->net_ns_inode);
1494
+
1495
+ if(ls->config.report)
1496
+ __atomic_add_fetch(&ls->stats.namespaces_sockets_existing, 1, __ATOMIC_RELAXED);
1497
}
1498
else {
1499
n = aral_mallocz(ls->local_socket_aral);
1501
simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
1502
1503
local_sockets_index_listening_port(ls, n);
1504
+
1505
+ if(ls->config.report)
1506
+ __atomic_add_fetch(&ls->stats.namespaces_sockets_new, 1, __ATOMIC_RELAXED);
1507
}
1508
1509
spinlock_unlock(&ls->spinlock);
1510
}
1511
1512
spawn_server_exec_kill(ls->spawn_server, si);
1513
+
1514
+ if(ls->config.report && received == 0)
1515
+ __atomic_add_fetch(&ls->stats.namespaces_forks_unresponsive, 1, __ATOMIC_RELAXED);
1516
+
1517
return received > 0;
1518
}
1519
1570
const uint64_t inode = (uint64_t)SIMPLE_HASHTABLE_SLOT_DATA(sl);
1571
1572
if(inode == ls->proc_self_net_ns_inode)
1573
+ // skip our own namespace, we already have them
1574
continue;
1575
1576
spinlock_unlock(&ls->spinlock);
1607
}
1608
}
1609
1610
+#endif // LOCAL_SOCKETS_USE_SETNS
1611
+
1612
+// --------------------------------------------------------------------------------------------------------------------
1613
+// read namespace sockets from the host's /proc
1614
+
1615
+#if !defined(LOCAL_SOCKETS_USE_SETNS)
1616
+
1617
+static inline bool local_sockets_namespaces_from_proc_with_pid(LS_STATE *ls, struct pid_socket *ps) {
1618
+ char filename[1024];
1619
+ snprintfz(filename, sizeof(filename), "%s/proc/%d/ns/net", ls->config.host_prefix, ps->pid);
1620
+
1621
+ // verify the pid is in the target namespace
1622
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
1623
+ if (fd == -1) {
1624
+ local_sockets_log(ls, "cannot open file '%s'", filename);
1625
+ if(ls->config.report)
1626
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
1627
+ return false;
1628
+ }
1629
+
1630
+ struct stat statbuf;
1631
+ if (fstat(fd, &statbuf) == -1) {
1632
+ close(fd);
1633
+ local_sockets_log(ls, "failed to get file statistics for '%s'", filename);
1634
+ if(ls->config.report)
1635
+ __atomic_add_fetch(&ls->stats.namespaces_absent, 1, __ATOMIC_RELAXED);
1636
+ return false;
1637
+ }
1638
+
1639
+ if (statbuf.st_ino != ps->net_ns_inode) {
1640
+ close(fd);
1641
+ local_sockets_log(ls, "pid %d is not in the wanted network namespace", ps->pid);
1642
+ if(ls->config.report)
1643
+ __atomic_add_fetch(&ls->stats.namespaces_invalid, 1, __ATOMIC_RELAXED);
1644
+ return false;
1645
+ }
1646
+
1647
+ char path[FILENAME_MAX + 1];
1648
+
1649
+ if(ls->config.tcp4) {
1650
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/tcp", ls->config.host_prefix, ps->pid);
1651
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_TCP))
1652
+ return false;
1653
+ }
1654
+
1655
+ if(ls->config.udp4) {
1656
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/udp", ls->config.host_prefix, ps->pid);
1657
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_UDP))
1658
+ return false;
1659
+ }
1660
+
1661
+ if(ls->config.tcp6) {
1662
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/tcp6", ls->config.host_prefix, ps->pid);
1663
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_TCP))
1664
+ return false;
1665
+ }
1666
+
1667
+ if(ls->config.udp6) {
1668
+ snprintfz(path, sizeof(path), "%s/proc/%d/net/udp6", ls->config.host_prefix, ps->pid);
1669
+ if(!local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_UDP))
1670
+ return false;
1671
+ }
1672
+
1673
+ return true;
1674
+}
1675
+
1676
+static inline void local_sockets_namespaces_from_proc(LS_STATE *ls) {
1677
+ for(SIMPLE_HASHTABLE_SLOT_NET_NS *sl = simple_hashtable_first_read_only_NET_NS(&ls->ns_hashtable);
1678
+ sl;
1679
+ sl = simple_hashtable_next_read_only_NET_NS(&ls->ns_hashtable, sl)) {
1680
+ const uint64_t inode = (uint64_t)SIMPLE_HASHTABLE_SLOT_DATA(sl);
1681
+
1682
+ if (inode == ls->proc_self_net_ns_inode)
1683
+ // skip our own namespace, we already have them
1684
+ continue;
1685
+
1686
+ ls->stats.namespaces_found++;
1687
+
1688
+ for(SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_first_read_only_PID_SOCKET(&ls->pid_sockets_hashtable) ;
1689
+ sl_pid ;
1690
+ sl_pid = simple_hashtable_next_read_only_PID_SOCKET(&ls->pid_sockets_hashtable, sl_pid)) {
1691
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
1692
+ if(!ps || ps->net_ns_inode != inode) continue;
1693
+
1694
+ // now we have a pid that has the same namespace inode
1695
+
1696
+ if(local_sockets_namespaces_from_proc_with_pid(ls, ps))
1697
+ break;
1698
+ }
1699
+ }
1700
+}
1701
+
1702
+#endif
1703
+
1704
// --------------------------------------------------------------------------------------------------------------------
1705
1706
static inline void local_sockets_process(LS_STATE *ls) {
1718
// check all socket namespaces
1719
if(ls->config.namespaces) {
1720
local_sockets_track_time(ls, "switch_namespaces");
1721
+#if defined(LOCAL_SOCKETS_USE_SETNS)
1722
local_sockets_namespaces(ls);
1723
+#else
1724
+ local_sockets_namespaces_from_proc(ls);
1725
+#endif
1726
}
1727
1728
// detect the directions of the sockets
1740
local_sockets_cleanup(ls);
1741
}
1742
1459
-static inline void ipv6_address_to_txt(struct in6_addr *in6_addr, char *dst) {
1743
+static inline void ipv6_address_to_txt(const struct in6_addr *in6_addr, char *dst) {
1744
struct sockaddr_in6 sa = { 0 };
1745
1746
sa.sin6_family = AF_INET6;