Local sockets for network namespaces (#16867)
* local-sockets now reads proc before reading sockets, do detect also the namespaces available * new version of local-sockets that is namespace aware
Costa Tsaousis committed
Jan 29, 2024 at 19:20 UTC
e37b12901539579aaaabc4e45fe5b0b306db1934
2 files changed
+524
-140
collectors/plugins.d/local-sockets.h
+511
-136
@@ -5,21 +5,52 @@
5
6
#include "libnetdata/libnetdata.h"
7
8
+// --------------------------------------------------------------------------------------------------------------------
9
+// hashtable for keeping the namespaces
10
+// key and value is the namespace inode
11
+
12
+#define SIMPLE_HASHTABLE_VALUE_TYPE uint64_t
13
+#define SIMPLE_HASHTABLE_NAME _NET_NS
14
+#include "libnetdata/simple_hashtable.h"
15
+
16
+// --------------------------------------------------------------------------------------------------------------------
17
+// hashtable for keeping the sockets of PIDs
18
+// key is the inode
19
+
20
+struct pid_socket;
21
+#define SIMPLE_HASHTABLE_VALUE_TYPE struct pid_socket
22
+#define SIMPLE_HASHTABLE_NAME _PID_SOCKET
23
+#include "libnetdata/simple_hashtable.h"
24
+
25
+// --------------------------------------------------------------------------------------------------------------------
26
+// hashtable for keeping all the sockets
27
+// key is the inode
28
+
29
struct local_socket;
30
#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_socket
31
#define SIMPLE_HASHTABLE_NAME _LOCAL_SOCKET
32
#include "libnetdata/simple_hashtable.h"
33
34
+// --------------------------------------------------------------------------------------------------------------------
35
+// hashtable for keeping all local IPs
36
+// key is XXH3_64bits hash of the IP
37
+
38
union ipv46;
39
#define SIMPLE_HASHTABLE_VALUE_TYPE union ipv46
40
#define SIMPLE_HASHTABLE_NAME _LOCAL_IP
41
#include "libnetdata/simple_hashtable.h"
42
43
+// --------------------------------------------------------------------------------------------------------------------
44
+// hashtable for keeping all listening ports
45
+// key is XXH3_64bits hash of the family, protocol, port number, namespace
46
+
47
struct local_port;
48
#define SIMPLE_HASHTABLE_VALUE_TYPE struct local_port
20
-#define SIMPLE_HASHTABLE_NAME _LOCAL_PORT
49
+#define SIMPLE_HASHTABLE_NAME _LISTENING_PORT
50
#include "libnetdata/simple_hashtable.h"
51
52
+// --------------------------------------------------------------------------------------------------------------------
53
+
54
struct local_socket_state;
55
typedef void (*local_sockets_cb_t)(struct local_socket_state *state, struct local_socket *n, void *data);
56
@@ -36,21 +67,30 @@ typedef struct local_socket_state {
67
bool pid;
68
bool cmdline;
69
bool comm;
70
+ bool namespaces;
71
size_t max_errors;
72
73
local_sockets_cb_t cb;
74
void *data;
75
+
76
+ const char *host_prefix;
77
} config;
78
79
struct {
80
size_t pid_fds_processed;
47
- size_t pid_fds_failed;
81
+ size_t pid_fds_opendir_failed;
82
+ size_t pid_fds_readlink_failed;
83
+ size_t pid_fds_parse_failed;
84
size_t errors_encountered;
85
} stats;
86
87
+ uint64_t proc_self_net_ns_inode;
88
+
89
+ SIMPLE_HASHTABLE_NET_NS ns_hashtable;
90
+ SIMPLE_HASHTABLE_PID_SOCKET pid_sockets_hashtable;
91
SIMPLE_HASHTABLE_LOCAL_SOCKET sockets_hashtable;
92
SIMPLE_HASHTABLE_LOCAL_IP local_ips_hashtable;
53
- SIMPLE_HASHTABLE_LOCAL_PORT listening_ports_hashtable;
93
+ SIMPLE_HASHTABLE_LISTENING_PORT listening_ports_hashtable;
94
} LS_STATE;
95
96
// --------------------------------------------------------------------------------------------------------------------
@@ -66,6 +106,14 @@ typedef enum __attribute__((packed)) {
106
#define TASK_COMM_LEN 16
107
#endif
108
109
+struct pid_socket {
110
+ uint64_t inode;
111
+ pid_t pid;
112
+ uint64_t net_ns_inode;
113
+ char *cmdline;
114
+ char comm[TASK_COMM_LEN];
115
+};
116
+
117
union ipv46 {
118
uint32_t ipv4;
119
struct in6_addr ipv6;
@@ -75,6 +123,7 @@ struct local_port {
123
uint16_t protocol;
124
uint16_t family;
125
uint16_t port;
126
+ uint64_t net_ns_inode;
127
};
128
129
struct socket_endpoint {
@@ -93,7 +142,8 @@ static inline void ipv6_to_in6_addr(const char *ipv6_str, struct in6_addr *d) {
142
}
143
144
typedef struct local_socket {
96
- unsigned int inode;
145
+ uint64_t inode;
146
+ uint64_t net_ns_inode;
147
148
uint16_t protocol;
149
uint16_t family;
@@ -116,9 +166,14 @@ typedef struct local_socket {
166
167
// --------------------------------------------------------------------------------------------------------------------
168
119
-static inline void ll_log(LS_STATE *ls, const char *format, ...) __attribute__ ((format(__printf__, 2, 3)));
120
-static inline void ll_log(LS_STATE *ls, const char *format, ...) {
121
- if(++ls->stats.errors_encountered >= ls->config.max_errors)
169
+static inline void local_sockets_log(LS_STATE *ls, const char *format, ...) __attribute__ ((format(__printf__, 2, 3)));
170
+static inline void local_sockets_log(LS_STATE *ls, const char *format, ...) {
171
+ if(++ls->stats.errors_encountered == ls->config.max_errors) {
172
+ nd_log(NDLS_COLLECTORS, NDLP_ERR, "LOCAL-LISTENERS: max number of logs reached. Not logging anymore");
173
+ return;
174
+ }
175
+
176
+ if(ls->stats.errors_encountered > ls->config.max_errors)
177
return;
178
179
char buf[16384];
@@ -132,9 +187,10 @@ static inline void ll_log(LS_STATE *ls, const char *format, ...) {
187
188
// --------------------------------------------------------------------------------------------------------------------
189
135
-static void foreach_local_socket_call_cb_and_cleanup(LS_STATE *ls) {
136
- for (unsigned int i = 0; i < ls->sockets_hashtable.size; i++) {
137
- SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = &ls->sockets_hashtable.hashtable[i];
190
+static void local_sockets_foreach_local_socket_call_cb(LS_STATE *ls) {
191
+ for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable);
192
+ sl;
193
+ sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) {
194
LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
195
if(!n) continue;
196
@@ -147,15 +203,12 @@ static void foreach_local_socket_call_cb_and_cleanup(LS_STATE *ls) {
203
if (ls->config.cb)
204
ls->config.cb(ls, n, ls->config.data);
205
}
150
-
151
- freez(n->cmdline);
152
- freez(n);
206
}
207
}
208
209
// --------------------------------------------------------------------------------------------------------------------
210
158
-static inline void fix_cmdline(char* str) {
211
+static inline void local_sockets_fix_cmdline(char* str) {
212
char *s = str;
213
214
// map invalid characters to underscores
@@ -165,113 +218,146 @@ static inline void fix_cmdline(char* str) {
218
}
219
}
220
168
-static inline bool associate_inode_with_pid(LS_STATE *ls, unsigned int inode, pid_t pid) {
169
- SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, inode, &inode, false);
170
- LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
171
- if(!n) return false;
221
+// ----------------------------------------------------------------------------
222
173
- n->pid = pid;
223
+static inline bool
224
+local_sockets_read_proc_inode_link(LS_STATE *ls, const char *filename, uint64_t *inode, const char *type) {
225
+ char link_target[FILENAME_MAX + 1];
226
175
- if(ls->config.cmdline || ls->config.comm) {
176
- char cmdline[8192] = "";
177
- char filename[FILENAME_MAX + 1];
178
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, pid);
227
+ *inode = 0;
228
180
- if(ls->config.cmdline) {
181
- if (read_proc_cmdline(filename, cmdline, sizeof(cmdline)))
182
- ll_log(ls, "cannot open file: %s\n", filename);
183
- else {
184
- fix_cmdline(cmdline);
229
+ ssize_t len = readlink(filename, link_target, sizeof(link_target) - 1);
230
+ if (len == -1) {
231
+ local_sockets_log(ls, "cannot read '%s' link '%s'", type, filename);
232
186
- char *s = trim(cmdline);
233
+ ls->stats.pid_fds_readlink_failed++;
234
+ return false;
235
+ }
236
+ link_target[len] = '\0';
237
188
- if(s) {
189
- // replace it
190
- freez(n->cmdline);
191
- n->cmdline = strdupz(s);
192
- }
193
- }
194
- }
238
+ len = strlen(type);
239
+ if(strncmp(link_target, type, len) == 0 && link_target[len] == ':' && link_target[len + 1] == '[' && isdigit(link_target[len + 2])) {
240
+ *inode = strtoull(&link_target[len + 2], NULL, 10);
241
+ // ll_log(ls, "read link of type '%s' '%s' from '%s', inode = %"PRIu64, type, link_target, filename, *inode);
242
+ return true;
243
+ }
244
+ else {
245
+ // ll_log(ls, "cannot read '%s' link '%s' from '%s'", type, link_target, filename);
246
+ ls->stats.pid_fds_processed++;
247
+ return false;
248
+ }
249
+}
250
196
- if(ls->config.comm) {
197
- n->comm[0] = '\0';
198
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/comm", netdata_configured_host_prefix, pid);
199
- if (read_txt_file(filename, n->comm, sizeof(n->comm)))
200
- ll_log(ls, "cannot open file: %s\n", filename);
201
- else {
202
- size_t len = strlen(n->comm);
203
- if(n->comm[len - 1] == '\n')
204
- n->comm[len - 1] = '\0';
205
- }
206
- }
251
+static inline bool local_sockets_is_path_a_pid(const char *s) {
252
+ if(!s || !*s) return false;
253
+
254
+ while(*s) {
255
+ if(!isdigit(*s++))
256
+ return false;
257
}
258
259
return true;
260
}
261
212
-// ----------------------------------------------------------------------------
213
-
214
-static inline bool find_all_sockets_in_proc(LS_STATE *ls, const char *proc_filename) {
215
- DIR *proc_dir, *fd_dir;
216
- struct dirent *proc_entry, *fd_entry;
217
- char path_buffer[FILENAME_MAX + 1];
262
+static inline bool local_sockets_find_all_sockets_in_proc(LS_STATE *ls, const char *proc_filename) {
263
+ DIR *proc_dir;
264
+ struct dirent *proc_entry;
265
+ char filename[FILENAME_MAX + 1];
266
+ char comm[TASK_COMM_LEN];
267
+ char cmdline[8192];
268
+ const char *cmdline_trimmed;
269
+ uint64_t net_ns_inode;
270
271
proc_dir = opendir(proc_filename);
272
if (proc_dir == NULL) {
221
- ll_log(ls, "cannot opendir() '%s'", proc_filename);
222
- ls->stats.pid_fds_failed++;
273
+ local_sockets_log(ls, "cannot opendir() '%s'", proc_filename);
274
+ ls->stats.pid_fds_readlink_failed++;
275
return false;
276
}
277
278
while ((proc_entry = readdir(proc_dir)) != NULL) {
227
- // Check if directory entry is a PID by seeing if the name is made up of digits only
228
- int is_pid = 1;
229
- for (char *c = proc_entry->d_name; *c != '\0'; c++) {
230
- if (*c < '0' || *c > '9') {
231
- is_pid = 0;
232
- break;
233
- }
234
- }
279
+ if(proc_entry->d_type != DT_DIR)
280
+ continue;
281
236
- if (!is_pid)
282
+ if(!strcmp(proc_entry->d_name, ".") || !strcmp(proc_entry->d_name, ".."))
283
continue;
284
239
- // Build the path to the fd directory of the process
240
- snprintfz(path_buffer, FILENAME_MAX, "%s/%s/fd/", proc_filename, proc_entry->d_name);
285
+ if(!local_sockets_is_path_a_pid(proc_entry->d_name))
286
+ continue;
287
242
- fd_dir = opendir(path_buffer);
288
+ // Build the path to the fd directory of the process
289
+ snprintfz(filename, FILENAME_MAX, "%s/%s/fd/", proc_filename, proc_entry->d_name);
290
+ DIR *fd_dir = opendir(filename);
291
if (fd_dir == NULL) {
244
- ll_log(ls, "cannot opendir() '%s'", path_buffer);
292
+ local_sockets_log(ls, "cannot opendir() '%s'", filename);
293
+ ls->stats.pid_fds_opendir_failed++;
294
+ continue;
295
+ }
296
246
- ls->stats.pid_fds_failed++;
297
+ comm[0] = '\0';
298
+ cmdline[0] = '\0';
299
+ cmdline_trimmed = NULL;
300
+ pid_t pid = (pid_t)strtoul(proc_entry->d_name, NULL, 10);
301
+ if(!pid) {
302
+ local_sockets_log(ls, "cannot parse pid of '%s'", proc_entry->d_name);
303
continue;
304
}
305
+ net_ns_inode = 0;
306
307
+ struct dirent *fd_entry;
308
while ((fd_entry = readdir(fd_dir)) != NULL) {
251
- if(!strcmp(fd_entry->d_name, ".") || !strcmp(fd_entry->d_name, ".."))
309
+ if(fd_entry->d_type != DT_LNK)
310
continue;
311
254
- char link_path[FILENAME_MAX + 1];
255
- char link_target[FILENAME_MAX + 1];
256
- unsigned inode;
312
+ snprintfz(filename, sizeof(filename), "%s/%s/fd/%s", proc_filename, proc_entry->d_name, fd_entry->d_name);
313
+ uint64_t inode = 0;
314
+ if(!local_sockets_read_proc_inode_link(ls, filename, &inode, "socket"))
315
+ continue;
316
258
- // Build the path to the file descriptor link
259
- snprintfz(link_path, FILENAME_MAX, "%s/%s", path_buffer, fd_entry->d_name);
317
+ SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode, &inode, true);
318
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl);
319
+ if(!ps || (ps->pid == 1 && pid != 1)) {
320
+ if(!comm[0] && ls->config.comm) {
321
+ snprintfz(filename, sizeof(filename), "%s/%s/comm", proc_filename, proc_entry->d_name);
322
+ if (read_txt_file(filename, comm, sizeof(comm)))
323
+ local_sockets_log(ls, "cannot open file: %s\n", filename);
324
+ else {
325
+ size_t clen = strlen(comm);
326
+ if(comm[clen - 1] == '\n')
327
+ comm[clen - 1] = '\0';
328
+ }
329
+ }
330
+ if(!cmdline[0] && ls->config.cmdline) {
331
+ snprintfz(filename, sizeof(filename), "%s/%s/cmdline", proc_filename, proc_entry->d_name);
332
+ if (read_proc_cmdline(filename, cmdline, sizeof(cmdline)))
333
+ local_sockets_log(ls, "cannot open file: %s\n", filename);
334
+ else {
335
+ local_sockets_fix_cmdline(cmdline);
336
+ cmdline_trimmed = trim(cmdline);
337
+ }
338
+ }
339
+ if(!net_ns_inode && ls->config.namespaces) {
340
+ snprintfz(filename, sizeof(filename), "%s/%s/ns/net", proc_filename, proc_entry->d_name);
341
+ if(local_sockets_read_proc_inode_link(ls, filename, &net_ns_inode, "net")) {
342
+ 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);
343
+ simple_hashtable_set_slot_NET_NS(&ls->ns_hashtable, sl_ns, net_ns_inode, (uint64_t *)net_ns_inode);
344
+ }
345
+ }
346
261
- ssize_t len = readlink(link_path, link_target, sizeof(link_target) - 1);
262
- if (len == -1) {
263
- ll_log(ls, "cannot read link '%s'", link_path);
347
+ if(!ps)
348
+ ps = callocz(1, sizeof(*ps));
349
265
- ls->stats.pid_fds_failed++;
266
- continue;
267
- }
268
- link_target[len] = '\0';
350
+ ps->inode = inode;
351
+ ps->pid = pid;
352
+ ps->net_ns_inode = net_ns_inode;
353
+ strncpyz(ps->comm, comm, sizeof(ps->comm) - 1);
354
270
- ls->stats.pid_fds_processed++;
355
+ if(ps->cmdline)
356
+ freez(ps->cmdline);
357
272
- // If the link target indicates a socket, print its inode number
273
- if (sscanf(link_target, "socket:[%u]", &inode) == 1)
274
- associate_inode_with_pid(ls, inode, (pid_t)strtoul(proc_entry->d_name, NULL, 10));
358
+ ps->cmdline = cmdline_trimmed ? strdupz(cmdline_trimmed) : NULL;
359
+ simple_hashtable_set_slot_PID_SOCKET(&ls->pid_sockets_hashtable, sl, inode, ps);
360
+ }
361
}
362
363
closedir(fd_dir);
@@ -283,13 +369,13 @@ static inline bool find_all_sockets_in_proc(LS_STATE *ls, const char *proc_filen
369
370
// ----------------------------------------------------------------------------
371
286
-static bool is_ipv4_mapped_ipv6_address(const struct in6_addr *addr) {
372
+static bool local_sockets_is_ipv4_mapped_ipv6_address(const struct in6_addr *addr) {
373
// An IPv4-mapped IPv6 address starts with 80 bits of zeros followed by 16 bits of ones
374
static const unsigned char ipv4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
375
return memcmp(addr->s6_addr, ipv4_mapped_prefix, 12) == 0;
376
}
377
292
-static bool is_loopback_address(const void *ip, uint16_t family) {
378
+static bool local_sockets_is_loopback_address(const void *ip, uint16_t family) {
379
if (family == AF_INET) {
380
// For IPv4, loopback addresses are in the 127.0.0.0/8 range
381
const uint32_t addr = ntohl(*((const uint32_t *)ip)); // Convert to host byte order for comparison
@@ -297,7 +383,7 @@ static bool is_loopback_address(const void *ip, uint16_t family) {
383
} else if (family == AF_INET6) {
384
// Check if the address is an IPv4-mapped IPv6 address
385
const struct in6_addr *ipv6_addr = (const struct in6_addr *)ip;
300
- if (is_ipv4_mapped_ipv6_address(ipv6_addr)) {
386
+ if (local_sockets_is_ipv4_mapped_ipv6_address(ipv6_addr)) {
387
// Extract the last 32 bits (IPv4 address) and check if it's in the 127.0.0.0/8 range
388
const uint32_t ipv4_addr = ntohl(*((const uint32_t *)(ipv6_addr->s6_addr + 12)));
389
return (ipv4_addr >> 24) == 127;
@@ -311,7 +397,7 @@ static bool is_loopback_address(const void *ip, uint16_t family) {
397
return false;
398
}
399
314
-static bool is_zero_address(const void *ip, uint16_t family) {
400
+static bool local_sockets_is_zero_address(const void *ip, uint16_t family) {
401
if (family == AF_INET) {
402
// For IPv4, check if the address is not 0.0.0.0
403
const uint32_t zero_ipv4 = 0; // Zero address in network byte order
@@ -325,7 +411,21 @@ static bool is_zero_address(const void *ip, uint16_t family) {
411
return false;
412
}
413
328
-static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
414
+static inline void local_sockets_index_listening_port(LS_STATE *ls, LOCAL_SOCKET *n) {
415
+ if(n->direction & SOCKET_DIRECTION_LISTEN) {
416
+ // for the listening sockets, keep a hashtable with all the local ports
417
+ // so that we will be able to detect INBOUND sockets
418
+
419
+ SIMPLE_HASHTABLE_SLOT_LISTENING_PORT *sl_port =
420
+ simple_hashtable_get_slot_LISTENING_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, true);
421
+
422
+ struct local_port *port = SIMPLE_HASHTABLE_SLOT_DATA(sl_port);
423
+ if(!port)
424
+ simple_hashtable_set_slot_LISTENING_PORT(&ls->listening_ports_hashtable, sl_port, n->local_port_hash, &n->local_port_key);
425
+ }
426
+}
427
+
428
+static inline bool local_sockets_read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t family, uint16_t protocol) {
429
if(family != AF_INET && family != AF_INET6)
430
return false;
431
@@ -346,24 +446,25 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
446
if(counter++ == 0) continue; // skip the first line
447
448
if(read < min_line_length) {
349
- ll_log(ls, "too small line No %zu of filename '%s': %s", counter, filename, line);
449
+ local_sockets_log(ls, "too small line No %zu of filename '%s': %s", counter, filename, line);
450
continue;
451
}
452
353
- unsigned int local_address, local_port, state, remote_address, remote_port, inode = 0;
453
+ unsigned int local_address, local_port, state, remote_address, remote_port;
454
+ uint64_t inode = 0;
455
char local_address6[33], remote_address6[33];
456
457
if(family == AF_INET) {
357
- if (sscanf(line, "%*d: %X:%X %X:%X %X %*X:%*X %*X:%*X %*X %*d %*d %u",
458
+ if (sscanf(line, "%*d: %X:%X %X:%X %X %*X:%*X %*X:%*X %*X %*d %*d %"PRIu64,
459
&local_address, &local_port, &remote_address, &remote_port, &state, &inode) != 6) {
359
- ll_log(ls, "cannot parse ipv4 line No %zu of filename '%s': %s", counter, filename, line);
460
+ local_sockets_log(ls, "cannot parse ipv4 line No %zu of filename '%s': %s", counter, filename, line);
461
continue;
462
}
463
}
464
else if(family == AF_INET6) {
364
- if(sscanf(line, "%*d: %32[0-9A-Fa-f]:%X %32[0-9A-Fa-f]:%X %X %*X:%*X %*X:%*X %*X %*d %*d %u",
465
+ if(sscanf(line, "%*d: %32[0-9A-Fa-f]:%X %32[0-9A-Fa-f]:%X %X %*X:%*X %*X:%*X %*X %*d %*d %"PRIu64,
466
local_address6, &local_port, remote_address6, &remote_port, &state, &inode) != 6) {
366
- ll_log(ls, "cannot parse ipv6 line No %zu of filename '%s': %s", counter, filename, line);
467
+ local_sockets_log(ls, "cannot parse ipv6 line No %zu of filename '%s': %s", counter, filename, line);
468
continue;
469
}
470
}
@@ -372,7 +473,13 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
473
SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, inode, &inode, true);
474
LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
475
if(n) {
375
- ll_log(ls, "inode %u given on line %zu of filename '%s', already exists in hashtable - ignoring duplicate", inode, counter, filename);
476
+ local_sockets_log(
477
+ ls,
478
+ "inode %" PRIu64
479
+ " given on line %zu of filename '%s', already exists in hashtable - ignoring duplicate",
480
+ inode,
481
+ counter,
482
+ filename);
483
continue;
484
}
485
@@ -380,6 +487,8 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
487
488
n = (LOCAL_SOCKET *)callocz(1, sizeof(LOCAL_SOCKET));
489
490
+ // --- initialize it ------------------------------------------------------------------------------------------
491
+
492
if(family == AF_INET) {
493
n->local.ip.ipv4 = local_address;
494
n->remote.ip.ipv4 = remote_address;
@@ -401,14 +510,29 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
510
n->local_port_key.port = n->local.port;
511
n->local_port_key.family = n->family;
512
n->local_port_key.protocol = n->protocol;
513
+ n->local_port_key.net_ns_inode = ls->proc_self_net_ns_inode;
514
515
n->local_ip_hash = XXH3_64bits(&n->local.ip, sizeof(n->local.ip));
516
n->remote_ip_hash = XXH3_64bits(&n->remote.ip, sizeof(n->remote.ip));
517
n->local_port_hash = XXH3_64bits(&n->local_port_key, sizeof(n->local_port_key));
518
519
+ // --- look up a pid for it -----------------------------------------------------------------------------------
520
+
521
+ SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_get_slot_PID_SOCKET(&ls->pid_sockets_hashtable, inode, &inode, false);
522
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
523
+ if(ps) {
524
+ n->net_ns_inode = ps->net_ns_inode;
525
+ n->pid = ps->pid;
526
+ if(ps->cmdline)
527
+ n->cmdline = strdupz(ps->cmdline);
528
+ strncpyz(n->comm, ps->comm, sizeof(n->comm) - 1);
529
+ }
530
+
531
+ // --- index it -----------------------------------------------------------------------------------------------
532
+
533
simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, inode, n);
534
411
- if(!is_zero_address(&n->local.ip, n->family)) {
535
+ if(!local_sockets_is_zero_address(&n->local.ip, n->family)) {
536
// put all the local IPs into the local_ips hashtable
537
// so, we learn all local IPs the system has
538
@@ -420,12 +544,18 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
544
simple_hashtable_set_slot_LOCAL_IP(&ls->local_ips_hashtable, sl_ip, n->local_ip_hash, &n->local.ip);
545
}
546
423
- if((n->protocol == IPPROTO_TCP && n->state == TCP_LISTEN) || is_zero_address(&n->local.ip, n->family) || is_zero_address(&n->remote.ip, n->family)) {
547
+ // --- 1st phase for direction detection ----------------------------------------------------------------------
548
+
549
+ if((n->protocol == IPPROTO_TCP && n->state == TCP_LISTEN) ||
550
+ local_sockets_is_zero_address(&n->local.ip, n->family) ||
551
+ local_sockets_is_zero_address(&n->remote.ip, n->family)) {
552
// the socket is either in a TCP LISTEN, or
553
// the remote address is zero
554
n->direction |= SOCKET_DIRECTION_LISTEN;
555
}
428
- else if(is_loopback_address(&n->local.ip, n->family) || is_loopback_address(&n->remote.ip, n->family)) {
556
+ else if(
557
+ local_sockets_is_loopback_address(&n->local.ip, n->family) ||
558
+ local_sockets_is_loopback_address(&n->remote.ip, n->family)) {
559
// the local IP address is loopback
560
n->direction |= SOCKET_DIRECTION_LOCAL;
561
}
@@ -435,17 +565,9 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
565
n->direction |= SOCKET_DIRECTION_INBOUND | SOCKET_DIRECTION_OUTBOUND;
566
}
567
438
- if(n->direction & SOCKET_DIRECTION_LISTEN) {
439
- // for the listening sockets, keep a hashtable with all the local ports
440
- // so that we will be able to detect INBOUND sockets
568
+ // --- index it in LISTENING_PORT -----------------------------------------------------------------------------
569
442
- SIMPLE_HASHTABLE_SLOT_LOCAL_PORT *sl_port =
443
- simple_hashtable_get_slot_LOCAL_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, true);
444
-
445
- struct local_port *port = SIMPLE_HASHTABLE_SLOT_DATA(sl_port);
446
- if(!port)
447
- simple_hashtable_set_slot_LOCAL_PORT(&ls->listening_ports_hashtable, sl_port, n->local_port_hash, &n->local_port_key);
448
- }
570
+ local_sockets_index_listening_port(ls, n);
571
}
572
573
fclose(fp);
@@ -459,8 +581,9 @@ static inline bool read_proc_net_x(LS_STATE *ls, const char *filename, uint16_t
581
// --------------------------------------------------------------------------------------------------------------------
582
583
static inline void local_sockets_detect_directions(LS_STATE *ls) {
462
- for (unsigned int i = 0; i < ls->sockets_hashtable.size; i++) {
463
- SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = &ls->sockets_hashtable.hashtable[i];
584
+ for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable);
585
+ sl ;
586
+ sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) {
587
LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
588
if (!n) continue;
589
@@ -484,8 +607,8 @@ static inline void local_sockets_detect_directions(LS_STATE *ls) {
607
608
// check if the local port is one of our listening ports
609
{
487
- SIMPLE_HASHTABLE_SLOT_LOCAL_PORT *sl_port =
488
- simple_hashtable_get_slot_LOCAL_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, false);
610
+ SIMPLE_HASHTABLE_SLOT_LISTENING_PORT *sl_port =
611
+ simple_hashtable_get_slot_LISTENING_PORT(&ls->listening_ports_hashtable, n->local_port_hash, &n->local_port_key, false);
612
613
struct local_port *port = SIMPLE_HASHTABLE_SLOT_DATA(sl_port); // do not reference this pointer - is invalid
614
if(port) {
@@ -500,49 +623,301 @@ static inline void local_sockets_detect_directions(LS_STATE *ls) {
623
624
// --------------------------------------------------------------------------------------------------------------------
625
503
-static inline void local_sockets_process(LS_STATE *ls) {
626
+static inline void local_sockets_init(LS_STATE *ls) {
627
+ simple_hashtable_init_NET_NS(&ls->ns_hashtable, 1024);
628
+ simple_hashtable_init_PID_SOCKET(&ls->pid_sockets_hashtable, 65535);
629
+ simple_hashtable_init_LOCAL_SOCKET(&ls->sockets_hashtable, 65535);
630
+ simple_hashtable_init_LOCAL_IP(&ls->local_ips_hashtable, 4096);
631
+ simple_hashtable_init_LISTENING_PORT(&ls->listening_ports_hashtable, 4096);
632
+}
633
+
634
+static inline void local_sockets_cleanup(LS_STATE *ls) {
635
+ // free the sockets hashtable data
636
+ for(SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_first_read_only_LOCAL_SOCKET(&ls->sockets_hashtable);
637
+ sl;
638
+ sl = simple_hashtable_next_read_only_LOCAL_SOCKET(&ls->sockets_hashtable, sl)) {
639
+ LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
640
+ if(!n) continue;
641
+
642
+ freez(n->cmdline);
643
+ freez(n);
644
+ }
645
+
646
+ // free the pid_socket hashtable data
647
+ for(SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl = simple_hashtable_first_read_only_PID_SOCKET(&ls->pid_sockets_hashtable);
648
+ sl;
649
+ sl = simple_hashtable_next_read_only_PID_SOCKET(&ls->pid_sockets_hashtable, sl)) {
650
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl);
651
+ if(!ps) continue;
652
+
653
+ freez(ps->cmdline);
654
+ freez(ps);
655
+ }
656
+
657
+ // free the hashtable
658
+ simple_hashtable_destroy_NET_NS(&ls->ns_hashtable);
659
+ simple_hashtable_destroy_PID_SOCKET(&ls->pid_sockets_hashtable);
660
+ simple_hashtable_destroy_LISTENING_PORT(&ls->listening_ports_hashtable);
661
+ simple_hashtable_destroy_LOCAL_IP(&ls->local_ips_hashtable);
662
+ simple_hashtable_destroy_LOCAL_SOCKET(&ls->sockets_hashtable);
663
+}
664
+
665
+// --------------------------------------------------------------------------------------------------------------------
666
+
667
+static inline void local_sockets_read_sockets_from_proc(LS_STATE *ls) {
668
char path[FILENAME_MAX + 1];
669
506
- simple_hashtable_init_LOCAL_SOCKET(&ls->sockets_hashtable, 65535);
507
- simple_hashtable_init_LOCAL_IP(&ls->local_ips_hashtable, 1024);
508
- simple_hashtable_init_LOCAL_PORT(&ls->listening_ports_hashtable, 1024);
670
+ if(ls->config.namespaces) {
671
+ snprintfz(path, sizeof(path), "%s/proc/self/ns/net", ls->config.host_prefix);
672
+ local_sockets_read_proc_inode_link(ls, path, &ls->proc_self_net_ns_inode, "net");
673
+ }
674
+
675
+ if(ls->config.cmdline || ls->config.comm || ls->config.pid || ls->config.namespaces) {
676
+ snprintfz(path, sizeof(path), "%s/proc", ls->config.host_prefix);
677
+ local_sockets_find_all_sockets_in_proc(ls, path);
678
+ }
679
680
if(ls->config.tcp4) {
511
- snprintfz(path, FILENAME_MAX, "%s/proc/net/tcp", netdata_configured_host_prefix);
512
- read_proc_net_x(ls, path, AF_INET, IPPROTO_TCP);
681
+ snprintfz(path, sizeof(path), "%s/proc/net/tcp", ls->config.host_prefix);
682
+ local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_TCP);
683
}
684
685
if(ls->config.udp4) {
516
- snprintfz(path, FILENAME_MAX, "%s/proc/net/udp", netdata_configured_host_prefix);
517
- read_proc_net_x(ls, path, AF_INET, IPPROTO_UDP);
686
+ snprintfz(path, sizeof(path), "%s/proc/net/udp", ls->config.host_prefix);
687
+ local_sockets_read_proc_net_x(ls, path, AF_INET, IPPROTO_UDP);
688
}
689
690
if(ls->config.tcp6) {
521
- snprintfz(path, FILENAME_MAX, "%s/proc/net/tcp6", netdata_configured_host_prefix);
522
- read_proc_net_x(ls, path, AF_INET6, IPPROTO_TCP);
691
+ snprintfz(path, sizeof(path), "%s/proc/net/tcp6", ls->config.host_prefix);
692
+ local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_TCP);
693
}
694
695
if(ls->config.udp6) {
526
- snprintfz(path, FILENAME_MAX, "%s/proc/net/udp6", netdata_configured_host_prefix);
527
- read_proc_net_x(ls, path, AF_INET6, IPPROTO_UDP);
696
+ snprintfz(path, sizeof(path), "%s/proc/net/udp6", ls->config.host_prefix);
697
+ local_sockets_read_proc_net_x(ls, path, AF_INET6, IPPROTO_UDP);
698
+ }
699
+}
700
+
701
+// --------------------------------------------------------------------------------------------------------------------
702
+
703
+struct local_sockets_child_work {
704
+ int fd;
705
+ uint64_t net_ns_inode;
706
+};
707
+
708
+static inline void local_sockets_send_to_parent(struct local_socket_state *ls __maybe_unused, struct local_socket *n, void *data) {
709
+ struct local_sockets_child_work *cw = data;
710
+ int fd = cw->fd;
711
+
712
+ if(n->net_ns_inode != cw->net_ns_inode)
713
+ return;
714
+
715
+ // local_sockets_log(ls, "child is sending inode %"PRIu64" of namespace %"PRIu64, n->inode, n->net_ns_inode);
716
+
717
+ if(write(fd, n, sizeof(*n)) != sizeof(*n))
718
+ local_sockets_log(ls, "failed to write local socket to pipe");
719
+
720
+ size_t len = n->cmdline ? strlen(n->cmdline) + 1 : 0;
721
+ if(write(fd, &len, sizeof(len)) != sizeof(len))
722
+ local_sockets_log(ls, "failed to write cmdline length to pipe");
723
+
724
+ if(len)
725
+ if(write(fd, n->cmdline, len) != (ssize_t)len)
726
+ local_sockets_log(ls, "failed to write cmdline to pipe");
727
+}
728
+
729
+static inline bool local_sockets_get_namespace_sockets(LS_STATE *ls, struct pid_socket *ps, pid_t *pid) {
730
+ char filename[1024];
731
+ snprintfz(filename, sizeof(filename), "%s/proc/%d/ns/net", ls->config.host_prefix, ps->pid);
732
+
733
+ // verify the pid is in the target namespace
734
+ struct stat statbuf;
735
+ if (stat(filename, &statbuf) == -1 || statbuf.st_ino != ps->net_ns_inode) {
736
+ local_sockets_log(ls, "pid %d is not in the wanted network namespace", ps->pid);
737
+ return false;
738
+ }
739
+
740
+ int fd = open(filename, O_RDONLY);
741
+ if(!fd) {
742
+ local_sockets_log(ls, "cannot open file '%s'", filename);
743
+ return false;
744
+ }
745
+
746
+ int pipefd[2];
747
+ if (pipe(pipefd) != 0) {
748
+ local_sockets_log(ls, "cannot create pipe");
749
+ return false;
750
+ }
751
+
752
+ *pid = fork();
753
+ if (*pid == 0) {
754
+ // Child process
755
+ close(pipefd[0]);
756
+
757
+ // local_sockets_log(ls, "child is here for inode %"PRIu64" and namespace %"PRIu64, ps->inode, ps->net_ns_inode);
758
+
759
+ struct local_sockets_child_work cw = {
760
+ .net_ns_inode = ps->net_ns_inode,
761
+ .fd = pipefd[1],
762
+ };
763
+
764
+ ls->config.host_prefix = ""; // we need the /proc of the container
765
+ ls->config.cb = local_sockets_send_to_parent;
766
+ ls->config.data = &cw;
767
+ ls->config.cmdline = false; // we have these already
768
+ ls->config.comm = false; // we have these already
769
+ ls->config.pid = false; // we have these already
770
+ ls->config.namespaces = false;
771
+ ls->proc_self_net_ns_inode = ps->net_ns_inode;
772
+
773
+
774
+ // switch namespace
775
+ if (setns(fd, CLONE_NEWNET) == -1) {
776
+ local_sockets_log(ls, "failed to switch network namespace at child process");
777
+ exit(EXIT_FAILURE);
778
+ }
779
+
780
+ // read all sockets from /proc
781
+ local_sockets_read_sockets_from_proc(ls);
782
+
783
+ // send all sockets to parent
784
+ local_sockets_foreach_local_socket_call_cb(ls);
785
+
786
+ // send the terminating socket
787
+ struct local_socket zero = {
788
+ .net_ns_inode = ps->net_ns_inode,
789
+ };
790
+ local_sockets_send_to_parent(ls, &zero, &cw);
791
+
792
+ close(pipefd[1]); // Close write end of pipe
793
+ exit(EXIT_SUCCESS);
794
+ }
795
+ // parent
796
+
797
+ close(fd);
798
+ close(pipefd[1]);
799
+
800
+ size_t received = 0;
801
+ struct local_socket buf;
802
+ while(read(pipefd[0], &buf, sizeof(buf)) == sizeof(buf)) {
803
+ size_t len = 0;
804
+ if(read(pipefd[0], &len, sizeof(len)) != sizeof(len))
805
+ local_sockets_log(ls, "failed to read cmdline length from pipe");
806
+
807
+ if(len) {
808
+ buf.cmdline = mallocz(len);
809
+ if(read(pipefd[0], buf.cmdline, len) != (ssize_t)len)
810
+ local_sockets_log(ls, "failed to read cmdline from pipe");
811
+ }
812
+ else
813
+ buf.cmdline = NULL;
814
+
815
+ received++;
816
+
817
+ struct local_socket zero = {
818
+ .net_ns_inode = ps->net_ns_inode,
819
+ };
820
+ if(memcmp(&buf, &zero, sizeof(buf)) == 0) {
821
+ // the terminator
822
+ break;
823
+ }
824
+
825
+ SIMPLE_HASHTABLE_SLOT_LOCAL_SOCKET *sl = simple_hashtable_get_slot_LOCAL_SOCKET(&ls->sockets_hashtable, buf.inode, &buf, true);
826
+ LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
827
+ if(n) {
828
+ if(buf.cmdline)
829
+ freez(buf.cmdline);
830
+
831
+// local_sockets_log(ls,
832
+// "ns inode %" PRIu64" (comm: '%s', pid: %u, ns: %"PRIu64") already exists in hashtable (comm: '%s', pid: %u, ns: %"PRIu64") - ignoring duplicate",
833
+// buf.inode, buf.comm, buf.pid, buf.net_ns_inode, n->comm, n->pid, n->net_ns_inode);
834
+ continue;
835
+ }
836
+ else {
837
+ n = mallocz(sizeof(*n));
838
+ memcpy(n, &buf, sizeof(*n));
839
+ simple_hashtable_set_slot_LOCAL_SOCKET(&ls->sockets_hashtable, sl, n->inode, n);
840
+
841
+ local_sockets_index_listening_port(ls, n);
842
+ }
843
}
844
530
- if(ls->config.cmdline || ls->config.comm || ls->config.pid) {
531
- snprintfz(path, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
532
- find_all_sockets_in_proc(ls, path);
845
+ close(pipefd[0]);
846
+
847
+ return received > 0;
848
+}
849
+
850
+static inline void local_socket_waitpid(LS_STATE *ls, pid_t pid) {
851
+ if(!pid) return;
852
+
853
+ int status;
854
+ waitpid(pid, &status, 0);
855
+
856
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
857
+ local_sockets_log(ls, "Child exited with status %d", WEXITSTATUS(status));
858
+ else if (WIFSIGNALED(status))
859
+ local_sockets_log(ls, "Child terminated by signal %d", WTERMSIG(status));
860
+}
861
+
862
+static inline void local_sockets_namespaces(LS_STATE *ls) {
863
+ pid_t children[5] = { 0 };
864
+ size_t last_child = 0;
865
+
866
+ for(SIMPLE_HASHTABLE_SLOT_NET_NS *sl = simple_hashtable_first_read_only_NET_NS(&ls->ns_hashtable);
867
+ sl;
868
+ sl = simple_hashtable_next_read_only_NET_NS(&ls->ns_hashtable, sl)) {
869
+ uint64_t inode = (uint64_t)SIMPLE_HASHTABLE_SLOT_DATA(sl);
870
+
871
+ if(inode == ls->proc_self_net_ns_inode)
872
+ continue;
873
+
874
+ // find a pid_socket that has this namespace
875
+ for(SIMPLE_HASHTABLE_SLOT_PID_SOCKET *sl_pid = simple_hashtable_first_read_only_PID_SOCKET(&ls->pid_sockets_hashtable) ;
876
+ sl_pid ;
877
+ sl_pid = simple_hashtable_next_read_only_PID_SOCKET(&ls->pid_sockets_hashtable, sl_pid)) {
878
+ struct pid_socket *ps = SIMPLE_HASHTABLE_SLOT_DATA(sl_pid);
879
+ if(!ps || ps->net_ns_inode != inode) continue;
880
+
881
+ if(++last_child >= 5)
882
+ last_child = 0;
883
+
884
+ local_socket_waitpid(ls, children[last_child]);
885
+ children[last_child] = 0;
886
+
887
+ // now we have a pid that has the same namespace inode
888
+ if(local_sockets_get_namespace_sockets(ls, ps, &children[last_child]))
889
+ break;
890
+ }
891
}
892
893
+ for(size_t i = 0; i < 5 ;i++)
894
+ local_socket_waitpid(ls, children[i]);
895
+}
896
+
897
+// --------------------------------------------------------------------------------------------------------------------
898
+
899
+static inline void local_sockets_process(LS_STATE *ls) {
900
+ ls->config.host_prefix = netdata_configured_host_prefix;
901
+
902
+ // initialize our hashtables
903
+ local_sockets_init(ls);
904
+
905
+ // read all sockets from /proc
906
+ local_sockets_read_sockets_from_proc(ls);
907
+
908
+ // check all socket namespaces
909
+ if(ls->config.namespaces)
910
+ local_sockets_namespaces(ls);
911
+
912
// detect the directions of the sockets
913
if(ls->config.inbound || ls->config.outbound || ls->config.local)
914
local_sockets_detect_directions(ls);
915
539
- // this will call the callback for each socket and free the memory we use
540
- foreach_local_socket_call_cb_and_cleanup(ls);
916
+ // call the callback for each socket
917
+ local_sockets_foreach_local_socket_call_cb(ls);
918
542
- // free the hashtable
543
- simple_hashtable_destroy_LOCAL_PORT(&ls->listening_ports_hashtable);
544
- simple_hashtable_destroy_LOCAL_IP(&ls->local_ips_hashtable);
545
- simple_hashtable_destroy_LOCAL_SOCKET(&ls->sockets_hashtable);
919
+ // free all memory
920
+ local_sockets_cleanup(ls);
921
}
922
923
static inline void ipv6_address_to_txt(struct in6_addr *in6_addr, char *dst) {
collectors/plugins.d/local_listeners.c
+13
-4
@@ -55,7 +55,7 @@ static void print_local_listeners_debug(LS_STATE *ls __maybe_unused, LOCAL_SOCKE
55
ipv6_address_to_txt(&n->remote.ip.ipv6, remote_address);
56
}
57
58
- printf("%s, direction=%s%s%s%s%s pid=%d, state=0x%0x, local=%s[:%u], remote=%s[:%u], comm=%s\n",
58
+ printf("%s, direction=%s%s%s%s%s pid=%d, state=0x%0x, ns=%"PRIu64", local=%s[:%u], remote=%s[:%u], comm=%s\n",
59
protocol_name(n),
60
(n->direction & SOCKET_DIRECTION_LISTEN) ? "LISTEN," : "",
61
(n->direction & SOCKET_DIRECTION_INBOUND) ? "INBOUND," : "",
@@ -64,6 +64,7 @@ static void print_local_listeners_debug(LS_STATE *ls __maybe_unused, LOCAL_SOCKE
64
(n->direction == 0) ? "NONE," : "",
65
n->pid,
66
n->state,
67
+ n->net_ns_inode,
68
local_address, n->local.port,
69
remote_address, n->remote.port,
70
n->comm);
@@ -85,6 +86,7 @@ int main(int argc, char **argv) {
86
.pid = false,
87
.cmdline = true,
88
.comm = false,
89
+ .namespaces = true,
90
91
.max_errors = 10,
92
@@ -123,7 +125,7 @@ int main(int argc, char **argv) {
125
"\n"
126
" while:\n"
127
"\n"
126
- " listening, local, inbound, outbound\n"
128
+ " listening, local, inbound, outbound, namespaces\n"
129
"\n"
130
" filter the output based on the direction of the sockets.\n"
131
"\n"
@@ -131,7 +133,7 @@ int main(int argc, char **argv) {
133
"\n"
134
" Current options:\n"
135
"\n"
134
- " %s %s %s %s %s %s %s %s\n"
136
+ " %s %s %s %s %s %s %s %s %s\n"
137
"\n"
138
" Option 'debug' enables all sources and all directions and provides\n"
139
" a full dump of current sockets.\n"
@@ -180,6 +182,7 @@ int main(int argc, char **argv) {
182
, ls.config.local ? "local" : "no-local"
183
, ls.config.inbound ? "inbound" : "no-inbound"
184
, ls.config.outbound ? "outbound" : "no-outbound"
185
+ , ls.config.namespaces ? "namespaces" : "no-namespaces"
186
);
187
exit(1);
188
}
@@ -201,7 +204,9 @@ int main(int argc, char **argv) {
204
ls.config.outbound = true;
205
ls.config.pid = true;
206
ls.config.comm = true;
204
- ls.config.cmdline = false;
207
+ ls.config.cmdline = true;
208
+ ls.config.namespaces = true;
209
+ ls.config.max_errors = SIZE_MAX;
210
ls.config.cb = print_local_listeners_debug;
211
}
212
else if (strcmp("tcp", s) == 0) {
@@ -252,6 +257,10 @@ int main(int argc, char **argv) {
257
ls.config.outbound = positive;
258
// fprintf(stderr, "%s outbound\n", positive ? "enabling" : "disabling");
259
}
260
+ else if (strcmp("namespaces", s) == 0 || strcmp("ns", s) == 0) {
261
+ ls.config.namespaces = positive;
262
+ // fprintf(stderr, "%s namespaces\n", positive ? "enabling" : "disabling");
263
+ }
264
else {
265
fprintf(stderr, "Unknown parameter %s\n", s);
266
exit(1);