master
c 4,963 lines 205 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "collectors/all.h"
4 #include "libnetdata/libnetdata.h"
5
6
7 static SPAWN_SERVER *spawn_srv = NULL;
8
9 #define ENABLE_DETAILED_VIEW
10
11 #define LOCAL_SOCKETS_EXTENDED_MEMBERS struct { \
12 size_t count; \
13 struct { \
14 pid_t pid; \
15 uid_t uid; \
16 SOCKET_DIRECTION direction; \
17 int state; \
18 uint64_t net_ns_inode; \
19 struct socket_endpoint server; \
20 const char *local_address_space; \
21 const char *remote_address_space; \
22 } aggregated_key; \
23 } network_viewer;
24
25 #include "libnetdata/local-sockets/local-sockets.h"
26 #include "libnetdata/os/system-maps/system-services.h"
27
28 #define NETWORK_CONNECTIONS_VIEWER_FUNCTION "network-connections"
29 #define NETWORK_CONNECTIONS_VIEWER_HELP "Shows active network connections with protocol details, states, addresses, ports, and performance metrics."
30 #define NETWORK_TOPOLOGY_VIEWER_FUNCTION "topology:network-connections"
31 #define NETWORK_TOPOLOGY_VIEWER_HELP "Shows live network-connections topology with self/process/endpoint actors and ownership/socket links."
32 #define NETWORK_VIEWER_RESPONSE_UPDATE_EVERY 5
33 #define NETWORK_PROTOCOLS_FUNCTION "network-protocols"
34 #define NETWORK_PROTOCOLS_FUNCTION_HELP "FreeBSD TCP and UDP statistics (IPv4 and IPv6 combined)"
35 // Keep in sync with the topology schema contract used across topology producers.
36 #define NETWORK_TOPOLOGY_SCHEMA_VERSION "netdata.topology.v1"
37 #define NETWORK_TOPOLOGY_SOURCE "network-connections"
38 #define NETWORK_TOPOLOGY_LAYER "network"
39 #define NV_TOPOLOGY_MAX_PPID_DEPTH 64
40
41 #define NV_TOPOLOGY_USERNAME_MAX 128
42 #define NV_TOPOLOGY_CMDLINE_MAX 512
43 #define NV_TOPOLOGY_LABEL_KEY_MAX 96
44 #define NV_TOPOLOGY_LABEL_VALUE_MAX 512
45 #define NV_TOPOLOGY_KEY_MAX 1024
46
47 typedef struct {
48 pid_t pid;
49 pid_t ppid;
50 uid_t uid;
51 uint64_t net_ns_inode;
52 uint64_t sockets;
53 char process[TASK_COMM_LEN + 1];
54 char username[NV_TOPOLOGY_USERNAME_MAX];
55 char namespace_type[16];
56 char local_ip[INET6_ADDRSTRLEN];
57 char local_address_space[16];
58 char cmdline[NV_TOPOLOGY_CMDLINE_MAX];
59 } NV_PROCESS_ACTOR;
60
61 typedef struct {
62 uint64_t sockets;
63 char ip[INET6_ADDRSTRLEN];
64 char address_space[16];
65 } NV_REMOTE_ACTOR;
66
67 typedef struct {
68 uint64_t sockets;
69 char ip[INET6_ADDRSTRLEN];
70 char address_space[16];
71 } NV_LOCAL_IP;
72
73 typedef struct {
74 uint64_t pid;
75 uint64_t ppid;
76 uint64_t uid;
77 uint64_t net_ns_inode;
78 char process[TASK_COMM_LEN + 1];
79 } NV_ENDPOINT_OWNER;
80
81 typedef enum {
82 NV_TOPOLOGY_ENDPOINT_ROLE_NONE = 0,
83 NV_TOPOLOGY_ENDPOINT_ROLE_CLIENT,
84 NV_TOPOLOGY_ENDPOINT_ROLE_SERVER,
85 } NV_TOPOLOGY_ENDPOINT_ROLE;
86
87 typedef struct {
88 uint64_t pid;
89 uint64_t ppid;
90 uint64_t uid;
91 uint64_t net_ns_inode;
92 uint64_t sockets;
93 uint64_t retransmissions;
94 uint32_t max_rtt_usec;
95 uint32_t max_rcv_rtt_usec;
96 uint16_t client_port;
97 uint16_t server_port;
98 uint16_t process_port;
99 uint16_t protocol_id;
100 bool process_is_client;
101 char process[TASK_COMM_LEN + 1];
102 char username[NV_TOPOLOGY_USERNAME_MAX];
103 char namespace_type[16];
104 char protocol[8];
105 char protocol_family[8];
106 char state[32];
107 char client_ip[INET6_ADDRSTRLEN];
108 char server_ip[INET6_ADDRSTRLEN];
109 char client_address_space[16];
110 char server_address_space[16];
111 char cmdline[NV_TOPOLOGY_CMDLINE_MAX];
112 } NV_TOPOLOGY_LINK;
113
114 typedef struct {
115 bool info_only;
116 bool detailed;
117 bool processes_by_pid;
118 bool sockets_listening;
119 bool sockets_inbound;
120 bool sockets_outbound;
121 bool protocols_ipv4_tcp;
122 bool protocols_ipv6_tcp;
123 bool protocols_ipv4_udp;
124 bool protocols_ipv6_udp;
125 } NV_TOPOLOGY_OPTIONS;
126
127 typedef struct {
128 DICTIONARY *process_actors;
129 DICTIONARY *remote_actors;
130 DICTIONARY *local_ips;
131 DICTIONARY *endpoint_owners_exact;
132 DICTIONARY *endpoint_owners_exact_any_ns;
133 DICTIONARY *endpoint_owners_service;
134 DICTIONARY *endpoint_owners_service_any_ns;
135 DICTIONARY *links;
136 usec_t now_ut;
137 uint64_t sockets_total;
138 uint64_t skipped_sockets;
139 char hostname[256];
140 char machine_guid[128];
141 NV_TOPOLOGY_OPTIONS options;
142 } NV_TOPOLOGY_CONTEXT;
143
144 typedef struct {
145 uint64_t ppid;
146 } NV_PPID_CACHE_ENTRY;
147
148 typedef struct {
149 size_t process_actor_count;
150 size_t socket_link_count;
151 size_t local_ip_count;
152 size_t endpoint_actor_count;
153 size_t ownership_link_count;
154 char host_actor_id[NV_TOPOLOGY_KEY_MAX];
155 } NV_TOPOLOGY_RENDER_STATE;
156
157 #define SIMPLE_HASHTABLE_VALUE_TYPE LOCAL_SOCKET *
158 #define SIMPLE_HASHTABLE_NAME _AGGREGATED_SOCKETS
159 #include "libnetdata/simple_hashtable/simple_hashtable.h"
160
161 netdata_mutex_t stdout_mutex;
162 #if defined(OS_FREEBSD)
163 static netdata_mutex_t nv_proto_mutex;
164 #endif
165
166 static void __attribute__((constructor)) init_mutex(void) {
167 netdata_mutex_init(&stdout_mutex);
168 #if defined(OS_FREEBSD)
169 netdata_mutex_init(&nv_proto_mutex);
170 #endif
171 }
172
173 static void __attribute__((destructor)) destroy_mutex(void) {
174 netdata_mutex_destroy(&stdout_mutex);
175 }
176 static bool plugin_should_exit = false;
177 static SERVICENAMES_CACHE *sc;
178
179 ENUM_STR_MAP_DEFINE(SOCKET_DIRECTION) = {
180 { .id = SOCKET_DIRECTION_LISTEN, .name = "listen" },
181 { .id = SOCKET_DIRECTION_LOCAL_INBOUND, .name = "inbound" },
182 { .id = SOCKET_DIRECTION_LOCAL_OUTBOUND, .name = "outbound" },
183 { .id = SOCKET_DIRECTION_INBOUND, .name = "inbound" },
184 { .id = SOCKET_DIRECTION_OUTBOUND, .name = "outbound" },
185
186 // terminator
187 { . id = 0, .name = NULL }
188 };
189 ENUM_STR_DEFINE_FUNCTIONS(SOCKET_DIRECTION, SOCKET_DIRECTION_LISTEN, "unknown");
190
191 typedef int TCP_STATE;
192 ENUM_STR_MAP_DEFINE(TCP_STATE) = {
193 { .id = TCP_ESTABLISHED, .name = "established" },
194 { .id = TCP_SYN_SENT, .name = "syn-sent" },
195 { .id = TCP_SYN_RECV, .name = "syn-received" },
196 { .id = TCP_FIN_WAIT1, .name = "fin-wait1" },
197 { .id = TCP_FIN_WAIT2, .name = "fin-wait2" },
198 { .id = TCP_TIME_WAIT, .name = "time-wait" },
199 { .id = TCP_CLOSE, .name = "close" },
200 { .id = TCP_CLOSE_WAIT, .name = "close-wait" },
201 { .id = TCP_LAST_ACK, .name = "last-ack" },
202 { .id = TCP_LISTEN, .name = "listen" },
203 { .id = TCP_CLOSING, .name = "closing" },
204
205 // terminator
206 { . id = 0, .name = NULL }
207 };
208 ENUM_STR_DEFINE_FUNCTIONS(TCP_STATE, 0, "unknown");
209
210 struct sockets_stats {
211 BUFFER *wb;
212
213 struct {
214 uint32_t tcpi_rtt;
215 uint32_t tcpi_rcv_rtt;
216 uint32_t tcpi_total_retrans;
217 } max;
218 };
219
220 static inline const char *network_viewer_machine_guid(void) {
221 const char *guid = getenv("NETDATA_REGISTRY_UNIQUE_ID");
222 return (guid && *guid) ? guid : NULL;
223 }
224
225 static inline void topology_options_defaults(NV_TOPOLOGY_OPTIONS *opts) {
226 if(!opts)
227 return;
228
229 memset(opts, 0, sizeof(*opts));
230 opts->detailed = false; // default: aggregated graph view
231 opts->processes_by_pid = false; // default: by_name
232 opts->sockets_listening = false;
233 opts->sockets_inbound = true;
234 opts->sockets_outbound = true;
235 opts->protocols_ipv4_tcp = true;
236 opts->protocols_ipv6_tcp = true;
237 opts->protocols_ipv4_udp = true;
238 opts->protocols_ipv6_udp = true;
239 }
240
241 static inline bool topology_sockets_any_enabled(const NV_TOPOLOGY_OPTIONS *opts) {
242 if(!opts)
243 return false;
244
245 return (opts->sockets_listening ||
246 opts->sockets_inbound ||
247 opts->sockets_outbound);
248 }
249
250 static inline bool topology_protocols_any_enabled(const NV_TOPOLOGY_OPTIONS *opts) {
251 if(!opts)
252 return false;
253
254 return (opts->protocols_ipv4_tcp ||
255 opts->protocols_ipv6_tcp ||
256 opts->protocols_ipv4_udp ||
257 opts->protocols_ipv6_udp);
258 }
259
260 static void topology_parse_options(const char *function, NV_TOPOLOGY_OPTIONS *opts) {
261 topology_options_defaults(opts);
262 if(!function || !*function || !opts)
263 return;
264
265 bool protocols_selected_explicitly = false;
266 bool sockets_selected_explicitly = false;
267
268 char *function_copy = strdupz(function);
269 char *words[1024];
270 size_t num_words = quoted_strings_splitter_whitespace(function_copy, words, 1024);
271 for(size_t i = 1; i < num_words; i++) {
272 char *param = get_word(words, num_words, i);
273 if(!param || !*param)
274 continue;
275
276 if(strcmp(param, "info") == 0) {
277 opts->info_only = true;
278 continue;
279 }
280
281 if(strcmp(param, "aggregated") == 0 ||
282 strcmp(param, "mode:aggregated") == 0 ||
283 strcmp(param, "__topology_mode:aggregated") == 0 ||
284 strcmp(param, "__topology_mode=aggregated") == 0 ||
285 strcmp(param, "view:aggregated") == 0) {
286 opts->detailed = false;
287 continue;
288 }
289 if(strcmp(param, "detailed") == 0 ||
290 strcmp(param, "mode:detailed") == 0 ||
291 strcmp(param, "__topology_mode:detailed") == 0 ||
292 strcmp(param, "__topology_mode=detailed") == 0 ||
293 strcmp(param, "view:detailed") == 0) {
294 opts->detailed = true;
295 continue;
296 }
297
298 if(strcmp(param, "processes:by_name") == 0 || strcmp(param, "processes:by-name") == 0) {
299 opts->processes_by_pid = false;
300 continue;
301 }
302 if(strcmp(param, "processes:by_pid") == 0 || strcmp(param, "processes:by-pid") == 0) {
303 opts->processes_by_pid = true;
304 continue;
305 }
306
307 if(strcmp(param, "endpoints:by_ip") == 0 || strcmp(param, "endpoints:by-ip") == 0)
308 continue;
309
310 if(strncmp(param, "sockets:", 8) == 0) {
311 if(!sockets_selected_explicitly) {
312 opts->sockets_listening = false;
313 opts->sockets_inbound = false;
314 opts->sockets_outbound = false;
315 sockets_selected_explicitly = true;
316 }
317
318 char *sockets_copy = strdupz(&param[8]);
319 char *sockets_remaining = sockets_copy;
320 char *socket_kind;
321 while(sockets_remaining && *sockets_remaining &&
322 (socket_kind = strsep_skip_consecutive_separators(&sockets_remaining, ","))) {
323 socket_kind = trim(socket_kind);
324 if(!socket_kind || !*socket_kind)
325 continue;
326
327 if(strcmp(socket_kind, "listening") == 0)
328 opts->sockets_listening = true;
329 else if(strcmp(socket_kind, "inbound") == 0)
330 opts->sockets_inbound = true;
331 else if(strcmp(socket_kind, "outbound") == 0)
332 opts->sockets_outbound = true;
333 }
334 freez(sockets_copy);
335 continue;
336 }
337
338 if(strncmp(param, "protocols:", 10) == 0) {
339 if(!protocols_selected_explicitly) {
340 opts->protocols_ipv4_tcp = false;
341 opts->protocols_ipv6_tcp = false;
342 opts->protocols_ipv4_udp = false;
343 opts->protocols_ipv6_udp = false;
344 protocols_selected_explicitly = true;
345 }
346
347 char *protocols_copy = strdupz(&param[10]);
348 char *protocols_remaining = protocols_copy;
349 char *protocol;
350 while(protocols_remaining && *protocols_remaining &&
351 (protocol = strsep_skip_consecutive_separators(&protocols_remaining, ","))) {
352 protocol = trim(protocol);
353 if(!protocol || !*protocol)
354 continue;
355
356 if(strcmp(protocol, "ipv4_tcp") == 0 || strcmp(protocol, "ipv4-tcp") == 0)
357 opts->protocols_ipv4_tcp = true;
358 else if(strcmp(protocol, "ipv6_tcp") == 0 || strcmp(protocol, "ipv6-tcp") == 0)
359 opts->protocols_ipv6_tcp = true;
360 else if(strcmp(protocol, "ipv4_udp") == 0 || strcmp(protocol, "ipv4-udp") == 0)
361 opts->protocols_ipv4_udp = true;
362 else if(strcmp(protocol, "ipv6_udp") == 0 || strcmp(protocol, "ipv6-udp") == 0)
363 opts->protocols_ipv6_udp = true;
364 }
365 freez(protocols_copy);
366 }
367 }
368 freez(function_copy);
369
370 if(!topology_sockets_any_enabled(opts)) {
371 opts->sockets_listening = false;
372 opts->sockets_inbound = true;
373 opts->sockets_outbound = true;
374 }
375
376 if(!topology_protocols_any_enabled(opts)) {
377 opts->protocols_ipv4_tcp = true;
378 opts->protocols_ipv6_tcp = true;
379 opts->protocols_ipv4_udp = true;
380 opts->protocols_ipv6_udp = true;
381 }
382 }
383
384 static inline const char *socket_protocol_name(uint16_t protocol) {
385 return (protocol == IPPROTO_UDP) ? "udp" : "tcp";
386 }
387
388 static inline const char *socket_protocol_family_name(const LOCAL_SOCKET *n) {
389 if(is_local_socket_ipv46(n))
390 return "ipv46";
391
392 if(n->local.family == AF_INET)
393 return "ipv4";
394
395 if(n->local.family == AF_INET6)
396 return "ipv6";
397
398 return "unknown";
399 }
400
401 static bool socket_endpoint_to_ip_text(const struct socket_endpoint *ep, char *dst) {
402 if(ep->family == AF_INET) {
403 ipv4_address_to_txt(ep->ip.ipv4, dst);
404 return true;
405 }
406
407 if(ep->family == AF_INET6) {
408 ipv6_address_to_txt(&ep->ip.ipv6, dst);
409 return true;
410 }
411
412 dst[0] = '\0';
413 return false;
414 }
415
416 static inline bool topology_ip_is_unspecified(const char *ip) {
417 if(!ip || !*ip)
418 return true;
419
420 return (strcmp(ip, "*") == 0 || strcmp(ip, "0.0.0.0") == 0 || strcmp(ip, "::") == 0);
421 }
422
423 static inline bool topology_ip_is_self_range(const char *ip) {
424 if(!ip || !*ip)
425 return false;
426
427 if(strncmp(ip, "127.", 4) == 0)
428 return true;
429 if(strncmp(ip, "0.", 2) == 0)
430 return true;
431 if(strcmp(ip, "::1") == 0)
432 return true;
433 if(strncmp(ip, "::ffff:127.", 10) == 0)
434 return true;
435
436 return false;
437 }
438
439 static inline bool topology_ip_belongs_to_self(const NV_TOPOLOGY_CONTEXT *ctx, const char *ip, const char *address_space) {
440 if(topology_ip_is_unspecified(ip))
441 return true;
442
443 if(topology_ip_is_self_range(ip))
444 return true;
445
446 if(address_space && *address_space) {
447 if(strcmp(address_space, "loopback") == 0 || strcmp(address_space, "zero") == 0)
448 return true;
449 }
450
451 if(ctx && ctx->local_ips && ip && *ip && dictionary_get(ctx->local_ips, ip))
452 return true;
453
454 return false;
455 }
456
457 static inline void topology_process_parent_lookup_key(
458 char *dst,
459 size_t dst_size,
460 uint64_t pid,
461 uint64_t net_ns_inode,
462 bool include_ns
463 ) {
464 if(!dst || !dst_size)
465 return;
466
467 if(include_ns)
468 snprintf(dst, dst_size, "ns=%llu|pid=%llu",
469 (unsigned long long)net_ns_inode,
470 (unsigned long long)pid);
471 else
472 snprintf(dst, dst_size, "pid=%llu",
473 (unsigned long long)pid);
474 }
475
476 static inline void topology_pid_lookup_key(char *dst, size_t dst_size, uint64_t pid) {
477 if(!dst || !dst_size)
478 return;
479
480 snprintf(dst, dst_size, "pid=%llu", (unsigned long long)pid);
481 }
482
483 #if defined(OS_LINUX)
484 static bool topology_read_proc_ppid(uint64_t pid, uint64_t *ppid) {
485 if(!pid || !ppid)
486 return false;
487
488 char filename[FILENAME_MAX + 1];
489 char status_buf[1024];
490 snprintfz(filename, sizeof(filename), "%s/proc/%llu/status",
491 netdata_configured_host_prefix,
492 (unsigned long long)pid);
493
494 if(read_txt_file(filename, status_buf, sizeof(status_buf)))
495 return false;
496
497 char *p = strstr(status_buf, "PPid:");
498 if(!p)
499 return false;
500
501 p += 5;
502 while(isspace((unsigned char)*p))
503 p++;
504
505 if(*p < '0' || *p > '9')
506 return false;
507
508 uint64_t parent = strtoull(p, NULL, 10);
509 if(parent == pid)
510 parent = 0;
511
512 *ppid = parent;
513 return true;
514 }
515 #elif defined(OS_FREEBSD)
516 #include <sys/types.h>
517 #include <sys/sysctl.h>
518 #include <sys/user.h>
519 #include <netinet/udp.h>
520 #include <netinet/udp_var.h>
521 static bool topology_read_proc_ppid(uint64_t pid, uint64_t *ppid) {
522 if(!pid || !ppid)
523 return false;
524
525 struct kinfo_proc kp;
526 size_t size = sizeof(kp);
527 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid };
528
529 if(sysctl(mib, 4, &kp, &size, NULL, 0) != 0 || size == 0)
530 return false;
531
532 uint64_t parent = (uint64_t)kp.ki_ppid;
533 if(parent == pid)
534 parent = 0;
535
536 *ppid = parent;
537 return true;
538 }
539 #endif
540
541 static uint64_t topology_ppid_cache_get_or_load(DICTIONARY *ppid_cache, uint64_t pid) {
542 if(!ppid_cache || !pid)
543 return 0;
544
545 char pid_key[64];
546 topology_pid_lookup_key(pid_key, sizeof(pid_key), pid);
547
548 NV_PPID_CACHE_ENTRY *cached = dictionary_get(ppid_cache, pid_key);
549 if(cached)
550 return cached->ppid;
551
552 NV_PPID_CACHE_ENTRY tmp = { .ppid = 0 };
553 uint64_t ppid = 0;
554 if(topology_read_proc_ppid(pid, &ppid))
555 tmp.ppid = ppid;
556
557 cached = dictionary_set(ppid_cache, pid_key, &tmp, sizeof(tmp));
558 return cached ? cached->ppid : tmp.ppid;
559 }
560
561 static NV_ENDPOINT_OWNER *topology_find_process_parent_actor(
562 uint64_t ppid,
563 uint64_t net_ns_inode,
564 DICTIONARY *process_parent_ns_lookup,
565 DICTIONARY *process_parent_any_lookup,
566 DICTIONARY *ppid_cache
567 ) {
568 if(!ppid || !process_parent_ns_lookup || !process_parent_any_lookup)
569 return NULL;
570
571 uint64_t current_pid = ppid;
572 for(size_t depth = 0; depth < NV_TOPOLOGY_MAX_PPID_DEPTH && current_pid; depth++) {
573 char parent_key_ns[NV_TOPOLOGY_KEY_MAX];
574 char parent_key_any[NV_TOPOLOGY_KEY_MAX];
575
576 topology_process_parent_lookup_key(parent_key_ns, sizeof(parent_key_ns), current_pid, net_ns_inode, true);
577 NV_ENDPOINT_OWNER *parent = dictionary_get(process_parent_ns_lookup, parent_key_ns);
578 if(!parent) {
579 topology_process_parent_lookup_key(parent_key_any, sizeof(parent_key_any), current_pid, 0, false);
580 parent = dictionary_get(process_parent_any_lookup, parent_key_any);
581 }
582
583 if(parent)
584 return parent;
585
586 if(!ppid_cache)
587 break;
588
589 uint64_t next_ppid = topology_ppid_cache_get_or_load(ppid_cache, current_pid);
590 if(!next_ppid || next_ppid == current_pid)
591 break;
592
593 current_pid = next_ppid;
594 }
595
596 return NULL;
597 }
598
599 static void topology_endpoint_owner_exact_key(char *dst, size_t dst_size, uint64_t net_ns_inode, uint16_t protocol, const char *ip, uint16_t port, bool include_ns) {
600 if(!dst || !dst_size)
601 return;
602
603 if(include_ns)
604 snprintf(dst, dst_size, "ns=%llu|proto=%u|ip=%s|port=%u",
605 (unsigned long long)net_ns_inode, (unsigned)protocol, ip, (unsigned)port);
606 else
607 snprintf(dst, dst_size, "proto=%u|ip=%s|port=%u", (unsigned)protocol, ip, (unsigned)port);
608 }
609
610 static void topology_endpoint_owner_service_key(char *dst, size_t dst_size, uint64_t net_ns_inode, uint16_t protocol, uint16_t port, bool include_ns) {
611 if(!dst || !dst_size)
612 return;
613
614 if(include_ns)
615 snprintf(dst, dst_size, "ns=%llu|proto=%u|port=%u",
616 (unsigned long long)net_ns_inode, (unsigned)protocol, (unsigned)port);
617 else
618 snprintf(dst, dst_size, "proto=%u|port=%u", (unsigned)protocol, (unsigned)port);
619 }
620
621 static void topology_register_endpoint_owner(
622 NV_TOPOLOGY_CONTEXT *ctx,
623 uint64_t net_ns_inode,
624 uint16_t protocol,
625 const char *ip,
626 uint16_t port,
627 const NV_PROCESS_ACTOR *pa,
628 bool service_candidate
629 ) {
630 if(!ctx || !pa || !port)
631 return;
632
633 NV_ENDPOINT_OWNER owner = {
634 .pid = pa->pid,
635 .ppid = pa->ppid,
636 .uid = pa->uid,
637 .net_ns_inode = pa->net_ns_inode,
638 };
639 snprintf(owner.process, sizeof(owner.process), "%s", pa->process);
640
641 char key[NV_TOPOLOGY_KEY_MAX];
642 if(ip && *ip && strcmp(ip, "*") != 0) {
643 if(ctx->endpoint_owners_exact) {
644 topology_endpoint_owner_exact_key(key, sizeof(key), net_ns_inode, protocol, ip, port, true);
645 dictionary_set(ctx->endpoint_owners_exact, key, &owner, sizeof(owner));
646 }
647 if(ctx->endpoint_owners_exact_any_ns) {
648 topology_endpoint_owner_exact_key(key, sizeof(key), 0, protocol, ip, port, false);
649 dictionary_set(ctx->endpoint_owners_exact_any_ns, key, &owner, sizeof(owner));
650 }
651 }
652
653 if(service_candidate) {
654 if(ctx->endpoint_owners_service) {
655 topology_endpoint_owner_service_key(key, sizeof(key), net_ns_inode, protocol, port, true);
656 dictionary_set(ctx->endpoint_owners_service, key, &owner, sizeof(owner));
657 }
658 if(ctx->endpoint_owners_service_any_ns) {
659 topology_endpoint_owner_service_key(key, sizeof(key), 0, protocol, port, false);
660 dictionary_set(ctx->endpoint_owners_service_any_ns, key, &owner, sizeof(owner));
661 }
662 }
663 }
664
665 static NV_ENDPOINT_OWNER *topology_lookup_endpoint_owner(
666 const NV_TOPOLOGY_CONTEXT *ctx,
667 uint64_t net_ns_inode,
668 uint16_t protocol,
669 const char *ip,
670 uint16_t port,
671 bool allow_service_fallback
672 ) {
673 if(!ctx || !port)
674 return NULL;
675
676 char key[NV_TOPOLOGY_KEY_MAX];
677 NV_ENDPOINT_OWNER *owner = NULL;
678
679 if(ip && *ip && strcmp(ip, "*") != 0) {
680 if(ctx->endpoint_owners_exact) {
681 topology_endpoint_owner_exact_key(key, sizeof(key), net_ns_inode, protocol, ip, port, true);
682 owner = dictionary_get(ctx->endpoint_owners_exact, key);
683 if(owner)
684 return owner;
685 }
686
687 if(ctx->endpoint_owners_exact_any_ns) {
688 topology_endpoint_owner_exact_key(key, sizeof(key), 0, protocol, ip, port, false);
689 owner = dictionary_get(ctx->endpoint_owners_exact_any_ns, key);
690 if(owner)
691 return owner;
692 }
693 }
694
695 if(!allow_service_fallback)
696 return NULL;
697
698 if(ctx->endpoint_owners_service) {
699 topology_endpoint_owner_service_key(key, sizeof(key), net_ns_inode, protocol, port, true);
700 owner = dictionary_get(ctx->endpoint_owners_service, key);
701 if(owner)
702 return owner;
703 }
704
705 if(ctx->endpoint_owners_service_any_ns) {
706 topology_endpoint_owner_service_key(key, sizeof(key), 0, protocol, port, false);
707 owner = dictionary_get(ctx->endpoint_owners_service_any_ns, key);
708 if(owner)
709 return owner;
710 }
711
712 return NULL;
713 }
714
715 static void topology_encode_identifier_component(char *dst, size_t dst_size, const char *src) {
716 if(!dst || !dst_size)
717 return;
718
719 const uint8_t *s = (const uint8_t *)((src && *src) ? src : "[unknown]");
720 size_t written = 0;
721
722 while(*s && written + 1 < dst_size) {
723 uint8_t ch = *s++;
724
725 if(isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~') {
726 dst[written++] = (char)ch;
727 continue;
728 }
729
730 if(written + 3 >= dst_size)
731 break;
732
733 dst[written++] = '%';
734 dst[written++] = hex_digits_lower[(ch >> 4) & 0x0F];
735 dst[written++] = hex_digits_lower[ch & 0x0F];
736 }
737
738 dst[written] = '\0';
739 }
740
741 static void topology_actor_id_for_host(const NV_TOPOLOGY_CONTEXT *ctx, char *dst, size_t dst_size) {
742 if(!dst || !dst_size)
743 return;
744
745 if(ctx && ctx->machine_guid[0])
746 snprintf(dst, dst_size, "netdata-machine-guid:%s", ctx->machine_guid);
747 else if(ctx && ctx->hostname[0])
748 snprintf(dst, dst_size, "hostname:%s", ctx->hostname);
749 else
750 snprintf(dst, dst_size, "host:unknown");
751 }
752
753 static void topology_actor_id_for_process(
754 const NV_TOPOLOGY_CONTEXT *ctx,
755 uint64_t pid,
756 uint64_t uid,
757 uint64_t net_ns_inode,
758 const char *process,
759 char *dst,
760 size_t dst_size
761 ) {
762 if(!dst || !dst_size)
763 return;
764
765 const char *node_identity = (ctx && ctx->machine_guid[0]) ? ctx->machine_guid : (ctx && ctx->hostname[0] ? ctx->hostname : "unknown");
766 const char *safe_process = (process && *process) ? process : "[unknown]";
767 if(ctx && !ctx->options.processes_by_pid) {
768 char encoded_process[((TASK_COMM_LEN + 1) * 3) + 1];
769 topology_encode_identifier_component(encoded_process, sizeof(encoded_process), safe_process);
770 snprintf(dst, dst_size, "process:%s|comm=%s", node_identity, encoded_process);
771 }
772 else {
773 snprintf(dst, dst_size, "process:%s|pid=%llu|uid=%llu|ns=%llu",
774 node_identity,
775 (unsigned long long)pid,
776 (unsigned long long)uid,
777 (unsigned long long)net_ns_inode);
778 }
779 }
780
781 static bool topology_process_name_is_unknown(const char *process_name) {
782 return !process_name || !*process_name || strcmp(process_name, "[unknown]") == 0;
783 }
784
785 static void topology_actor_id_for_remote_endpoint(const NV_TOPOLOGY_CONTEXT *ctx, const char *ip, const char *address_space, char *dst, size_t dst_size) {
786 if(!dst || !dst_size)
787 return;
788
789 (void)ctx;
790 (void)address_space;
791 if(ip && *ip)
792 snprintf(dst, dst_size, "ip:%s", ip);
793 else
794 snprintf(dst, dst_size, "ip:unknown");
795 }
796
797 static void topology_process_display_name(
798 const NV_TOPOLOGY_CONTEXT *ctx,
799 const char *process_name,
800 uint64_t pid,
801 char *dst,
802 size_t dst_size
803 ) {
804 if(!dst || !dst_size)
805 return;
806
807 const char *safe_process = (process_name && *process_name) ? process_name : "[unknown]";
808 if(ctx && !ctx->options.processes_by_pid)
809 snprintf(dst, dst_size, "%s", safe_process);
810 else
811 snprintf(dst, dst_size, "%s[%llu]", safe_process, (unsigned long long)pid);
812 }
813
814 static void local_socket_to_json_array(struct sockets_stats *st, const LOCAL_SOCKET *n, uint64_t proc_self_net_ns_inode, bool aggregated) {
815 if(n->direction == SOCKET_DIRECTION_NONE)
816 return;
817
818 BUFFER *wb = st->wb;
819
820 char local_address[INET6_ADDRSTRLEN];
821 char remote_address[INET6_ADDRSTRLEN];
822 char *protocol;
823
824 if(n->local.family == AF_INET) {
825 ipv4_address_to_txt(n->local.ip.ipv4, local_address);
826
827 if(local_sockets_is_zero_address(&n->remote))
828 remote_address[0] = '\0';
829 else
830 ipv4_address_to_txt(n->remote.ip.ipv4, remote_address);
831
832 protocol = n->local.protocol == IPPROTO_TCP ? "tcp4" : "udp4";
833 }
834 else if(is_local_socket_ipv46(n)) {
835 strncpyz(local_address, "*", sizeof(local_address) - 1);
836 remote_address[0] = '\0';
837 protocol = n->local.protocol == IPPROTO_TCP ? "tcp46" : "udp46";
838 }
839 else if(n->local.family == AF_INET6) {
840 ipv6_address_to_txt(&n->local.ip.ipv6, local_address);
841
842 if(local_sockets_is_zero_address(&n->remote))
843 remote_address[0] = '\0';
844 else
845 ipv6_address_to_txt(&n->remote.ip.ipv6, remote_address);
846
847 protocol = n->local.protocol == IPPROTO_TCP ? "tcp6" : "udp6";
848 }
849 else
850 return;
851
852 const char *type;
853 if(n->net_ns_inode == proc_self_net_ns_inode)
854 type = "system";
855 else if(n->net_ns_inode == 0)
856 type = "[unknown]";
857 else
858 type = "container";
859
860 buffer_json_add_array_item_array(wb);
861 {
862 buffer_json_add_array_item_string(wb, SOCKET_DIRECTION_2str(n->direction));
863 buffer_json_add_array_item_string(wb, protocol);
864 buffer_json_add_array_item_string(wb, type); // system or container
865 if(n->local.protocol == IPPROTO_TCP)
866 buffer_json_add_array_item_string(wb, TCP_STATE_2str(n->state));
867 else
868 buffer_json_add_array_item_string(wb, "stateless");
869
870 buffer_json_add_array_item_uint64(wb, n->pid);
871
872 if(!n->comm[0])
873 buffer_json_add_array_item_string(wb, "[unknown]");
874 else
875 buffer_json_add_array_item_string(wb, n->comm);
876
877 // buffer_json_add_array_item_string(wb, string2str(n->cmdline));
878
879 if(n->uid == UID_UNSET) {
880 // buffer_json_add_array_item_uint64(wb, n->uid);
881 buffer_json_add_array_item_string(wb, "[unknown]");
882 }
883 else {
884 // buffer_json_add_array_item_uint64(wb, n->uid);
885 CACHED_USERNAME cu = cached_username_get_by_uid(n->uid);
886 buffer_json_add_array_item_string(wb, string2str(cu.username));
887 cached_username_release(cu);
888 }
889
890 const struct socket_endpoint *server_endpoint;
891 const char *server_address;
892 const char *client_address_space;
893 const char *server_address_space;
894 switch (n->direction) {
895 case SOCKET_DIRECTION_LISTEN:
896 case SOCKET_DIRECTION_INBOUND:
897 case SOCKET_DIRECTION_LOCAL_INBOUND:
898 server_address = local_address;
899 server_address_space = n->network_viewer.aggregated_key.local_address_space;
900 client_address_space = n->network_viewer.aggregated_key.remote_address_space;
901 server_endpoint = &n->local;
902 break;
903
904 case SOCKET_DIRECTION_OUTBOUND:
905 case SOCKET_DIRECTION_LOCAL_OUTBOUND:
906 server_address = remote_address;
907 server_address_space = n->network_viewer.aggregated_key.remote_address_space;
908 client_address_space = n->network_viewer.aggregated_key.local_address_space;
909 server_endpoint = &n->remote;
910 break;
911
912 default:
913 case SOCKET_DIRECTION_NONE:
914 server_address = NULL;
915 client_address_space = NULL;
916 server_address_space = NULL;
917 server_endpoint = NULL;
918 break;
919 }
920
921 if(server_endpoint) {
922 STRING *serv = system_servicenames_cache_lookup(sc, server_endpoint->port, server_endpoint->protocol);
923 buffer_json_add_array_item_string(wb, string2str(serv));
924 }
925 else
926 buffer_json_add_array_item_string(wb, "[unknown]");
927
928 if(!aggregated) {
929 buffer_json_add_array_item_string(wb, local_address);
930 buffer_json_add_array_item_uint64(wb, n->local.port);
931 }
932 buffer_json_add_array_item_string(wb, n->network_viewer.aggregated_key.local_address_space);
933
934 if(!aggregated) {
935 buffer_json_add_array_item_string(wb, remote_address);
936 buffer_json_add_array_item_uint64(wb, n->remote.port);
937 }
938 buffer_json_add_array_item_string(wb, n->network_viewer.aggregated_key.remote_address_space);
939
940 if(aggregated) {
941 buffer_json_add_array_item_string(wb, server_address);
942 }
943
944 buffer_json_add_array_item_uint64(wb, n->network_viewer.aggregated_key.server.port);
945
946 if(aggregated) {
947 buffer_json_add_array_item_string(wb, client_address_space);
948 buffer_json_add_array_item_string(wb, server_address_space);
949 }
950
951 // buffer_json_add_array_item_uint64(wb, n->inode);
952 // buffer_json_add_array_item_uint64(wb, n->net_ns_inode);
953
954 // RTT
955 buffer_json_add_array_item_double(wb, (double)n->info.tcp.tcpi_rtt / (double)USEC_PER_MS);
956 if(st->max.tcpi_rtt < n->info.tcp.tcpi_rtt)
957 st->max.tcpi_rtt = n->info.tcp.tcpi_rtt;
958
959 // Receiver RTT
960 #if defined(OS_LINUX)
961 buffer_json_add_array_item_double(wb, (double)n->info.tcp.tcpi_rcv_rtt / (double)USEC_PER_MS);
962 if(st->max.tcpi_rcv_rtt < n->info.tcp.tcpi_rcv_rtt)
963 st->max.tcpi_rcv_rtt = n->info.tcp.tcpi_rcv_rtt;
964 #else
965 buffer_json_add_array_item_double(wb, 0.0);
966 #endif
967
968 // Retransmissions
969 #if defined(OS_LINUX)
970 buffer_json_add_array_item_uint64(wb, n->info.tcp.tcpi_total_retrans);
971 if(st->max.tcpi_total_retrans < n->info.tcp.tcpi_total_retrans)
972 st->max.tcpi_total_retrans = n->info.tcp.tcpi_total_retrans;
973 #else
974 buffer_json_add_array_item_uint64(wb, 0);
975 #endif
976
977 // count
978 buffer_json_add_array_item_uint64(wb, n->network_viewer.count);
979 }
980 buffer_json_array_close(wb);
981 }
982
983 static void populate_aggregated_key(const LOCAL_SOCKET *nn) {
984 LOCAL_SOCKET *n = (LOCAL_SOCKET *)nn;
985
986 n->network_viewer.count = 1;
987
988 n->network_viewer.aggregated_key.pid = n->pid;
989 n->network_viewer.aggregated_key.uid = n->uid;
990 n->network_viewer.aggregated_key.direction = n->direction;
991 n->network_viewer.aggregated_key.net_ns_inode = n->net_ns_inode;
992 n->network_viewer.aggregated_key.state = n->state;
993
994 switch(n->direction) {
995 case SOCKET_DIRECTION_INBOUND:
996 case SOCKET_DIRECTION_LOCAL_INBOUND:
997 case SOCKET_DIRECTION_LISTEN:
998 n->network_viewer.aggregated_key.server = n->local;
999 break;
1000
1001 case SOCKET_DIRECTION_OUTBOUND:
1002 case SOCKET_DIRECTION_LOCAL_OUTBOUND:
1003 n->network_viewer.aggregated_key.server = n->remote;
1004 break;
1005
1006 case SOCKET_DIRECTION_NONE:
1007 break;
1008 }
1009
1010 n->network_viewer.aggregated_key.local_address_space = local_sockets_address_space(&n->local);
1011 n->network_viewer.aggregated_key.remote_address_space = local_sockets_address_space(&n->remote);
1012 }
1013
1014 static void local_sockets_cb_to_json(LS_STATE *ls, const LOCAL_SOCKET *n, void *data) {
1015 struct sockets_stats *st = data;
1016 populate_aggregated_key(n);
1017 local_socket_to_json_array(st, n, ls->proc_self_net_ns_inode, false);
1018 }
1019
1020 #define KEEP_THE_BIGGER(a, b) (a) = ((a) < (b)) ? (b) : (a)
1021 #define KEEP_THE_SMALLER(a, b) (a) = ((a) > (b)) ? (b) : (a)
1022 #define SUM_THEM_ALL(a, b) (a) += (b)
1023 #define OR_THEM_ALL(a, b) (a) |= (b)
1024
1025 static void local_sockets_cb_to_aggregation(LS_STATE *ls __maybe_unused, const LOCAL_SOCKET *n, void *data) {
1026 SIMPLE_HASHTABLE_AGGREGATED_SOCKETS *ht = data;
1027
1028 populate_aggregated_key(n);
1029 XXH64_hash_t hash = XXH3_64bits(&n->network_viewer.aggregated_key, sizeof(n->network_viewer.aggregated_key));
1030 SIMPLE_HASHTABLE_SLOT_AGGREGATED_SOCKETS *sl = simple_hashtable_get_slot_AGGREGATED_SOCKETS(ht, hash, (LOCAL_SOCKET *)n, true);
1031 LOCAL_SOCKET *t = SIMPLE_HASHTABLE_SLOT_DATA(sl);
1032 if(t) {
1033 t->network_viewer.count++;
1034
1035 KEEP_THE_BIGGER(t->timer, n->timer);
1036 KEEP_THE_BIGGER(t->retransmits, n->retransmits);
1037 KEEP_THE_SMALLER(t->expires, n->expires);
1038 KEEP_THE_BIGGER(t->rqueue, n->rqueue);
1039 KEEP_THE_BIGGER(t->wqueue, n->wqueue);
1040
1041 // The current number of consecutive retransmissions that have occurred for the most recently transmitted segment.
1042 #if defined(OS_LINUX)
1043 SUM_THEM_ALL(t->info.tcp.tcpi_retransmits, n->info.tcp.tcpi_retransmits);
1044 #endif
1045
1046 // The total number of retransmissions that have occurred for the entire connection since it was established.
1047 #if defined(OS_LINUX)
1048 SUM_THEM_ALL(t->info.tcp.tcpi_total_retrans, n->info.tcp.tcpi_total_retrans);
1049 #endif
1050
1051 // The total number of segments that have been retransmitted since the connection was established.
1052 #if defined(OS_LINUX)
1053 SUM_THEM_ALL(t->info.tcp.tcpi_retrans, n->info.tcp.tcpi_retrans);
1054 #endif
1055
1056 // The number of keepalive probes sent
1057 #if defined(OS_LINUX)
1058 SUM_THEM_ALL(t->info.tcp.tcpi_probes, n->info.tcp.tcpi_probes);
1059 #endif
1060
1061 // The number of times the retransmission timeout has been backed off.
1062 #if defined(OS_LINUX)
1063 SUM_THEM_ALL(t->info.tcp.tcpi_backoff, n->info.tcp.tcpi_backoff);
1064 #endif
1065
1066 // A bitmask representing the TCP options currently enabled for the connection, such as SACK and Timestamps.
1067 OR_THEM_ALL(t->info.tcp.tcpi_options, n->info.tcp.tcpi_options);
1068
1069 // The send window scale value used for this connection
1070 KEEP_THE_SMALLER(t->info.tcp.tcpi_snd_wscale, n->info.tcp.tcpi_snd_wscale);
1071
1072 // The receive window scale value used for this connection
1073 KEEP_THE_SMALLER(t->info.tcp.tcpi_rcv_wscale, n->info.tcp.tcpi_rcv_wscale);
1074
1075 // Retransmission timeout in milliseconds
1076 KEEP_THE_SMALLER(t->info.tcp.tcpi_rto, n->info.tcp.tcpi_rto);
1077
1078 // The delayed acknowledgement timeout in milliseconds.
1079 #if defined(OS_LINUX)
1080 KEEP_THE_SMALLER(t->info.tcp.tcpi_ato, n->info.tcp.tcpi_ato);
1081 #endif
1082
1083 // The maximum segment size for sending.
1084 KEEP_THE_SMALLER(t->info.tcp.tcpi_snd_mss, n->info.tcp.tcpi_snd_mss);
1085
1086 // The maximum segment size for receiving.
1087 KEEP_THE_SMALLER(t->info.tcp.tcpi_rcv_mss, n->info.tcp.tcpi_rcv_mss);
1088
1089 // The number of unacknowledged segments
1090 #if defined(OS_LINUX)
1091 SUM_THEM_ALL(t->info.tcp.tcpi_unacked, n->info.tcp.tcpi_unacked);
1092 #endif
1093
1094 // The number of segments that have been selectively acknowledged
1095 #if defined(OS_LINUX)
1096 SUM_THEM_ALL(t->info.tcp.tcpi_sacked, n->info.tcp.tcpi_sacked);
1097 #endif
1098
1099 // The number of lost segments.
1100 #if defined(OS_LINUX)
1101 SUM_THEM_ALL(t->info.tcp.tcpi_lost, n->info.tcp.tcpi_lost);
1102 #endif
1103
1104 // The number of forward acknowledgment segments.
1105 #if defined(OS_LINUX)
1106 SUM_THEM_ALL(t->info.tcp.tcpi_fackets, n->info.tcp.tcpi_fackets);
1107 #endif
1108
1109 // The time in milliseconds since the last data was sent.
1110 #if defined(OS_LINUX)
1111 KEEP_THE_SMALLER(t->info.tcp.tcpi_last_data_sent, n->info.tcp.tcpi_last_data_sent);
1112 #endif
1113
1114 // The time in milliseconds since the last acknowledgment was sent (not tracked in Linux, hence often zero).
1115 #if defined(OS_LINUX)
1116 KEEP_THE_SMALLER(t->info.tcp.tcpi_last_ack_sent, n->info.tcp.tcpi_last_ack_sent);
1117 #endif
1118
1119 // The time in milliseconds since the last data was received.
1120 #if defined(OS_LINUX)
1121 KEEP_THE_SMALLER(t->info.tcp.tcpi_last_data_recv, n->info.tcp.tcpi_last_data_recv);
1122 #endif
1123
1124 // The time in milliseconds since the last acknowledgment was received.
1125 #if defined(OS_LINUX)
1126 KEEP_THE_SMALLER(t->info.tcp.tcpi_last_ack_recv, n->info.tcp.tcpi_last_ack_recv);
1127 #endif
1128
1129 // The path MTU for this connection
1130 #if defined(OS_LINUX)
1131 KEEP_THE_SMALLER(t->info.tcp.tcpi_pmtu, n->info.tcp.tcpi_pmtu);
1132 #endif
1133
1134 // The slow start threshold for receiving
1135 #if defined(OS_LINUX)
1136 KEEP_THE_SMALLER(t->info.tcp.tcpi_rcv_ssthresh, n->info.tcp.tcpi_rcv_ssthresh);
1137 #endif
1138
1139 // The slow start threshold for sending
1140 KEEP_THE_SMALLER(t->info.tcp.tcpi_snd_ssthresh, n->info.tcp.tcpi_snd_ssthresh);
1141
1142 // The round trip time in milliseconds
1143 KEEP_THE_BIGGER(t->info.tcp.tcpi_rtt, n->info.tcp.tcpi_rtt);
1144
1145 // The round trip time variance in milliseconds.
1146 KEEP_THE_BIGGER(t->info.tcp.tcpi_rttvar, n->info.tcp.tcpi_rttvar);
1147
1148 // The size of the sending congestion window.
1149 KEEP_THE_SMALLER(t->info.tcp.tcpi_snd_cwnd, n->info.tcp.tcpi_snd_cwnd);
1150
1151 // The maximum segment size that could be advertised.
1152 #if defined(OS_LINUX)
1153 KEEP_THE_BIGGER(t->info.tcp.tcpi_advmss, n->info.tcp.tcpi_advmss);
1154 #endif
1155
1156 // The reordering metric
1157 #if defined(OS_LINUX)
1158 KEEP_THE_SMALLER(t->info.tcp.tcpi_reordering, n->info.tcp.tcpi_reordering);
1159 #endif
1160
1161 // The receive round trip time in milliseconds.
1162 #if defined(OS_LINUX)
1163 KEEP_THE_BIGGER(t->info.tcp.tcpi_rcv_rtt, n->info.tcp.tcpi_rcv_rtt);
1164 #endif
1165
1166 // The available space in the receive buffer.
1167 KEEP_THE_SMALLER(t->info.tcp.tcpi_rcv_space, n->info.tcp.tcpi_rcv_space);
1168 }
1169 else {
1170 t = mallocz(sizeof(*t));
1171 memcpy(t, n, sizeof(*t));
1172 t->cmdline = string_dup(t->cmdline);
1173 simple_hashtable_set_slot_AGGREGATED_SOCKETS(ht, sl, hash, t);
1174 }
1175 }
1176
1177 static bool topology_socket_direction_selected(const NV_TOPOLOGY_CONTEXT *ctx, SOCKET_DIRECTION direction) {
1178 if(!ctx)
1179 return false;
1180
1181 switch(direction) {
1182 case SOCKET_DIRECTION_LISTEN:
1183 return ctx->options.sockets_listening;
1184 case SOCKET_DIRECTION_INBOUND:
1185 case SOCKET_DIRECTION_LOCAL_INBOUND:
1186 return ctx->options.sockets_inbound;
1187 case SOCKET_DIRECTION_OUTBOUND:
1188 case SOCKET_DIRECTION_LOCAL_OUTBOUND:
1189 return ctx->options.sockets_outbound;
1190 default:
1191 return false;
1192 }
1193 }
1194
1195 static void local_sockets_cb_to_topology(LS_STATE *ls, const LOCAL_SOCKET *n, void *data) {
1196 if(n->direction == SOCKET_DIRECTION_NONE)
1197 return;
1198
1199 NV_TOPOLOGY_CONTEXT *ctx = data;
1200 ctx->sockets_total++;
1201 bool selected_socket = topology_socket_direction_selected(ctx, n->direction);
1202 bool hidden_listen_socket = (n->direction == SOCKET_DIRECTION_LISTEN && !ctx->options.sockets_listening);
1203
1204 char local_ip[INET6_ADDRSTRLEN] = "";
1205 char remote_ip[INET6_ADDRSTRLEN] = "";
1206
1207 if(is_local_socket_ipv46(n))
1208 strncpyz(local_ip, "*", sizeof(local_ip) - 1);
1209 else if(!socket_endpoint_to_ip_text(&n->local, local_ip))
1210 return;
1211
1212 if(!local_sockets_is_zero_address(&n->remote)) {
1213 socket_endpoint_to_ip_text(&n->remote, remote_ip);
1214 }
1215
1216 const char *namespace_type;
1217 if(n->net_ns_inode == ls->proc_self_net_ns_inode)
1218 namespace_type = "system";
1219 else if(n->net_ns_inode == 0)
1220 namespace_type = "unknown";
1221 else
1222 namespace_type = "container";
1223
1224 const char *local_address_space = local_sockets_address_space(&n->local);
1225 const char *remote_address_space = local_sockets_address_space(&n->remote);
1226 const char *process_name = n->comm[0] ? n->comm : "[unknown]";
1227 const char *cmdline = string2str(n->cmdline);
1228
1229 char username[NV_TOPOLOGY_USERNAME_MAX] = "[unknown]";
1230 if(n->uid != UID_UNSET) {
1231 CACHED_USERNAME cu = cached_username_get_by_uid(n->uid);
1232 const char *cached_username = string2str(cu.username);
1233 if(cached_username && *cached_username)
1234 snprintf(username, sizeof(username), "%s", cached_username);
1235 cached_username_release(cu);
1236 }
1237
1238 if(local_ip[0] && !topology_ip_is_unspecified(local_ip)) {
1239 NV_LOCAL_IP *local_actor = dictionary_get(ctx->local_ips, local_ip);
1240 if(!local_actor) {
1241 NV_LOCAL_IP tmp = { 0 };
1242 snprintf(tmp.ip, sizeof(tmp.ip), "%s", local_ip);
1243 snprintf(tmp.address_space, sizeof(tmp.address_space), "%s", local_address_space);
1244 local_actor = dictionary_set(ctx->local_ips, local_ip, &tmp, sizeof(tmp));
1245 }
1246 local_actor->sockets++;
1247 }
1248
1249 if(hidden_listen_socket) {
1250 NV_PROCESS_ACTOR hidden_owner = {
1251 .pid = n->pid,
1252 .ppid = n->ppid,
1253 .uid = n->uid,
1254 .net_ns_inode = n->net_ns_inode,
1255 };
1256 snprintf(hidden_owner.process, sizeof(hidden_owner.process), "%s", process_name);
1257 topology_register_endpoint_owner(ctx, n->net_ns_inode, n->local.protocol, local_ip, n->local.port, &hidden_owner, true);
1258 ctx->skipped_sockets++;
1259 return;
1260 }
1261
1262 if(!selected_socket) {
1263 ctx->skipped_sockets++;
1264 return;
1265 }
1266
1267 char process_key[NV_TOPOLOGY_KEY_MAX];
1268 if(ctx->options.processes_by_pid) {
1269 snprintf(process_key, sizeof(process_key), "pid=%d|uid=%u|ns=%llu",
1270 n->pid,
1271 (unsigned)n->uid,
1272 (unsigned long long)n->net_ns_inode);
1273 }
1274 else {
1275 char encoded_process[((TASK_COMM_LEN + 1) * 3) + 1];
1276 topology_encode_identifier_component(encoded_process, sizeof(encoded_process), process_name);
1277 snprintf(process_key, sizeof(process_key), "comm=%s", encoded_process);
1278 }
1279
1280 NV_PROCESS_ACTOR *pa = dictionary_get(ctx->process_actors, process_key);
1281 if(!pa) {
1282 NV_PROCESS_ACTOR tmp = { 0 };
1283 tmp.pid = n->pid;
1284 tmp.ppid = n->ppid;
1285 tmp.uid = n->uid;
1286 tmp.net_ns_inode = n->net_ns_inode;
1287 snprintf(tmp.process, sizeof(tmp.process), "%s", process_name);
1288 snprintf(tmp.username, sizeof(tmp.username), "%s", username);
1289 snprintf(tmp.namespace_type, sizeof(tmp.namespace_type), "%s", namespace_type);
1290 snprintf(tmp.local_ip, sizeof(tmp.local_ip), "%s", local_ip);
1291 snprintf(tmp.local_address_space, sizeof(tmp.local_address_space), "%s", local_address_space);
1292 if(cmdline && *cmdline)
1293 snprintf(tmp.cmdline, sizeof(tmp.cmdline), "%s", cmdline);
1294 pa = dictionary_set(ctx->process_actors, process_key, &tmp, sizeof(tmp));
1295 }
1296 pa->sockets++;
1297 if(!pa->ppid && n->ppid)
1298 pa->ppid = n->ppid;
1299 if(topology_process_name_is_unknown(pa->process) && !topology_process_name_is_unknown(process_name))
1300 snprintf(pa->process, sizeof(pa->process), "%s", process_name);
1301 if(!pa->cmdline[0] && cmdline && *cmdline)
1302 snprintf(pa->cmdline, sizeof(pa->cmdline), "%s", cmdline);
1303 if((!pa->local_ip[0] || topology_ip_is_unspecified(pa->local_ip)) && local_ip[0] && !topology_ip_is_unspecified(local_ip))
1304 snprintf(pa->local_ip, sizeof(pa->local_ip), "%s", local_ip);
1305
1306 bool service_candidate = (n->direction == SOCKET_DIRECTION_LISTEN ||
1307 n->direction == SOCKET_DIRECTION_INBOUND ||
1308 n->direction == SOCKET_DIRECTION_LOCAL_INBOUND);
1309 topology_register_endpoint_owner(ctx, n->net_ns_inode, n->local.protocol, local_ip, n->local.port, pa, service_candidate);
1310
1311 if(n->direction == SOCKET_DIRECTION_LISTEN) {
1312 ctx->skipped_sockets++;
1313 return;
1314 }
1315
1316 if(!remote_ip[0] || topology_ip_is_unspecified(remote_ip)) {
1317 ctx->skipped_sockets++;
1318 return;
1319 }
1320
1321 bool process_is_client = (n->direction == SOCKET_DIRECTION_OUTBOUND ||
1322 n->direction == SOCKET_DIRECTION_LOCAL_OUTBOUND);
1323
1324 char client_ip[INET6_ADDRSTRLEN] = "";
1325 char server_ip[INET6_ADDRSTRLEN] = "";
1326 const char *client_address_space = NULL;
1327 const char *server_address_space = NULL;
1328 uint16_t client_port = 0;
1329 uint16_t server_port = 0;
1330
1331 if(process_is_client) {
1332 snprintf(client_ip, sizeof(client_ip), "%s", local_ip);
1333 snprintf(server_ip, sizeof(server_ip), "%s", remote_ip);
1334 client_address_space = local_address_space;
1335 server_address_space = remote_address_space;
1336 client_port = n->local.port;
1337 server_port = n->remote.port;
1338 }
1339 else {
1340 snprintf(client_ip, sizeof(client_ip), "%s", remote_ip);
1341 snprintf(server_ip, sizeof(server_ip), "%s", local_ip);
1342 client_address_space = remote_address_space;
1343 server_address_space = local_address_space;
1344 client_port = n->remote.port;
1345 server_port = n->local.port;
1346 }
1347
1348 const char *endpoint_ip = process_is_client ? server_ip : client_ip;
1349 const char *endpoint_address_space = process_is_client ? server_address_space : client_address_space;
1350 bool endpoint_is_self = topology_ip_belongs_to_self(ctx, endpoint_ip, endpoint_address_space);
1351 if(!endpoint_is_self) {
1352 char endpoint_actor_key[NV_TOPOLOGY_KEY_MAX];
1353 topology_actor_id_for_remote_endpoint(ctx, endpoint_ip, endpoint_address_space, endpoint_actor_key, sizeof(endpoint_actor_key));
1354 NV_REMOTE_ACTOR *ra = dictionary_get(ctx->remote_actors, endpoint_actor_key);
1355 if(!ra) {
1356 NV_REMOTE_ACTOR tmp = { 0 };
1357 snprintf(tmp.ip, sizeof(tmp.ip), "%s", endpoint_ip);
1358 snprintf(tmp.address_space, sizeof(tmp.address_space), "%s", endpoint_address_space);
1359 ra = dictionary_set(ctx->remote_actors, endpoint_actor_key, &tmp, sizeof(tmp));
1360 }
1361 ra->sockets++;
1362 }
1363
1364 char link_key[NV_TOPOLOGY_KEY_MAX];
1365 snprintf(link_key, sizeof(link_key), "pid=%d|uid=%u|ns=%llu|client=%s:%u|server=%s:%u|proto=%u|state=%u",
1366 n->pid,
1367 (unsigned)n->uid,
1368 (unsigned long long)n->net_ns_inode,
1369 client_ip,
1370 (unsigned)client_port,
1371 server_ip,
1372 (unsigned)server_port,
1373 (unsigned)n->local.protocol,
1374 (unsigned)n->state);
1375
1376 NV_TOPOLOGY_LINK *link = dictionary_get(ctx->links, link_key);
1377 if(!link) {
1378 NV_TOPOLOGY_LINK tmp = { 0 };
1379 tmp.pid = n->pid;
1380 tmp.ppid = n->ppid;
1381 tmp.uid = n->uid;
1382 tmp.net_ns_inode = n->net_ns_inode;
1383 tmp.client_port = client_port;
1384 tmp.server_port = server_port;
1385 tmp.process_port = n->local.port;
1386 tmp.protocol_id = n->local.protocol;
1387 tmp.process_is_client = process_is_client;
1388 snprintf(tmp.process, sizeof(tmp.process), "%s", process_name);
1389 snprintf(tmp.username, sizeof(tmp.username), "%s", username);
1390 snprintf(tmp.namespace_type, sizeof(tmp.namespace_type), "%s", namespace_type);
1391 snprintf(tmp.protocol, sizeof(tmp.protocol), "%s", socket_protocol_name(n->local.protocol));
1392 snprintf(tmp.protocol_family, sizeof(tmp.protocol_family), "%s", socket_protocol_family_name(n));
1393 snprintf(tmp.state, sizeof(tmp.state), "%s",
1394 n->local.protocol == IPPROTO_TCP ? TCP_STATE_2str(n->state) : "stateless");
1395 snprintf(tmp.client_ip, sizeof(tmp.client_ip), "%s", client_ip);
1396 snprintf(tmp.server_ip, sizeof(tmp.server_ip), "%s", server_ip);
1397 snprintf(tmp.client_address_space, sizeof(tmp.client_address_space), "%s", client_address_space);
1398 snprintf(tmp.server_address_space, sizeof(tmp.server_address_space), "%s", server_address_space);
1399 if(cmdline && *cmdline)
1400 snprintf(tmp.cmdline, sizeof(tmp.cmdline), "%s", cmdline);
1401 link = dictionary_set(ctx->links, link_key, &tmp, sizeof(tmp));
1402 }
1403
1404 link->sockets++;
1405 #if defined(OS_LINUX)
1406 link->retransmissions += n->info.tcp.tcpi_total_retrans;
1407 #endif
1408 if(link->max_rtt_usec < n->info.tcp.tcpi_rtt)
1409 link->max_rtt_usec = n->info.tcp.tcpi_rtt;
1410 #if defined(OS_LINUX)
1411 if(link->max_rcv_rtt_usec < n->info.tcp.tcpi_rcv_rtt)
1412 link->max_rcv_rtt_usec = n->info.tcp.tcpi_rcv_rtt;
1413 #endif
1414 }
1415
1416 static void topology_context_destroy(NV_TOPOLOGY_CONTEXT *ctx) {
1417 if(!ctx)
1418 return;
1419
1420 if(ctx->links)
1421 dictionary_destroy(ctx->links);
1422 if(ctx->endpoint_owners_service_any_ns)
1423 dictionary_destroy(ctx->endpoint_owners_service_any_ns);
1424 if(ctx->endpoint_owners_service)
1425 dictionary_destroy(ctx->endpoint_owners_service);
1426 if(ctx->endpoint_owners_exact_any_ns)
1427 dictionary_destroy(ctx->endpoint_owners_exact_any_ns);
1428 if(ctx->endpoint_owners_exact)
1429 dictionary_destroy(ctx->endpoint_owners_exact);
1430 if(ctx->local_ips)
1431 dictionary_destroy(ctx->local_ips);
1432 if(ctx->remote_actors)
1433 dictionary_destroy(ctx->remote_actors);
1434 if(ctx->process_actors)
1435 dictionary_destroy(ctx->process_actors);
1436 }
1437
1438 static bool topology_prepare_context(NV_TOPOLOGY_CONTEXT *ctx, usec_t now_ut, const NV_TOPOLOGY_OPTIONS *options) {
1439 if(!ctx)
1440 return false;
1441
1442 memset(ctx, 0, sizeof(*ctx));
1443 ctx->now_ut = now_ut;
1444 if(options)
1445 ctx->options = *options;
1446
1447 if(ctx->options.info_only)
1448 return true;
1449
1450 ctx->process_actors = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_PROCESS_ACTOR));
1451 ctx->remote_actors = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_REMOTE_ACTOR));
1452 ctx->local_ips = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_LOCAL_IP));
1453 ctx->endpoint_owners_exact = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_ENDPOINT_OWNER));
1454 ctx->endpoint_owners_exact_any_ns = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_ENDPOINT_OWNER));
1455 ctx->endpoint_owners_service = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_ENDPOINT_OWNER));
1456 ctx->endpoint_owners_service_any_ns = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_ENDPOINT_OWNER));
1457 ctx->links = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(NV_TOPOLOGY_LINK));
1458
1459 if(!(ctx->process_actors && ctx->remote_actors && ctx->local_ips &&
1460 ctx->endpoint_owners_exact && ctx->endpoint_owners_exact_any_ns &&
1461 ctx->endpoint_owners_service && ctx->endpoint_owners_service_any_ns &&
1462 ctx->links))
1463 return false;
1464
1465 if(!os_hostname(ctx->hostname, sizeof(ctx->hostname), netdata_configured_host_prefix))
1466 snprintf(ctx->hostname, sizeof(ctx->hostname), "%s", "localhost");
1467
1468 const char *machine_guid = network_viewer_machine_guid();
1469 if(machine_guid)
1470 snprintf(ctx->machine_guid, sizeof(ctx->machine_guid), "%s", machine_guid);
1471
1472 LS_STATE ls = {
1473 .config = {
1474 // Always collect listeners so inbound/outbound socket classification
1475 // remains stable across topology socket filters.
1476 .listening = true,
1477 .local = ctx->options.sockets_inbound || ctx->options.sockets_outbound,
1478 .inbound = ctx->options.sockets_inbound,
1479 .outbound = ctx->options.sockets_outbound,
1480 .tcp4 = ctx->options.protocols_ipv4_tcp,
1481 .tcp6 = ctx->options.protocols_ipv6_tcp,
1482 .udp4 = ctx->options.protocols_ipv4_udp,
1483 .udp6 = ctx->options.protocols_ipv6_udp,
1484 .pid = true,
1485 .uid = true,
1486 .cmdline = true,
1487 .comm = true,
1488 .namespaces = true,
1489 .tcp_info = true,
1490 .max_errors = 10,
1491 .max_concurrent_namespaces = 5,
1492 .cb = local_sockets_cb_to_topology,
1493 .data = ctx,
1494 },
1495 #if defined(LOCAL_SOCKETS_USE_SETNS)
1496 .spawn_server = spawn_srv,
1497 #endif
1498 .stats = { 0 },
1499 .sockets_hashtable = { 0 },
1500 .local_ips_hashtable = { 0 },
1501 .listening_ports_hashtable = { 0 },
1502 };
1503
1504 local_sockets_process(&ls);
1505 return true;
1506 }
1507
1508 static void topology_render_state_init(NV_TOPOLOGY_RENDER_STATE *state, const NV_TOPOLOGY_CONTEXT *ctx) {
1509 if(!state)
1510 return;
1511
1512 memset(state, 0, sizeof(*state));
1513 if(!ctx)
1514 return;
1515
1516 state->process_actor_count = ctx->process_actors ? dictionary_entries(ctx->process_actors) : 0;
1517 state->socket_link_count = ctx->links ? dictionary_entries(ctx->links) : 0;
1518 state->local_ip_count = ctx->local_ips ? dictionary_entries(ctx->local_ips) : 0;
1519 topology_actor_id_for_host(ctx, state->host_actor_id, sizeof(state->host_actor_id));
1520 }
1521
1522 static void topology_finalize_response(const char *transaction, BUFFER *wb, time_t now_s) {
1523 buffer_json_member_add_time_t(wb, "expires", now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
1524 buffer_json_finalize(wb);
1525
1526 netdata_mutex_lock(&stdout_mutex);
1527 wb->response_code = HTTP_RESP_OK;
1528 wb->content_type = CT_APPLICATION_JSON;
1529 wb->expires = now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY;
1530 pluginsd_function_result_to_stdout(transaction, wb);
1531 netdata_mutex_unlock(&stdout_mutex);
1532 }
1533
1534 static void topology_write_response_metadata(BUFFER *wb) {
1535 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1536 buffer_json_member_add_string(wb, "type", "topology");
1537 buffer_json_member_add_time_t(wb, "update_every", NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
1538 buffer_json_member_add_boolean(wb, "has_history", false);
1539 buffer_json_member_add_string(wb, "help", NETWORK_TOPOLOGY_VIEWER_HELP);
1540 buffer_json_member_add_array(wb, "accepted_params");
1541 {
1542 buffer_json_add_array_item_string(wb, "info");
1543 buffer_json_add_array_item_string(wb, "processes");
1544 buffer_json_add_array_item_string(wb, "__topology_mode");
1545 buffer_json_add_array_item_string(wb, "mode");
1546 buffer_json_add_array_item_string(wb, "sockets");
1547 buffer_json_add_array_item_string(wb, "protocols");
1548 buffer_json_add_array_item_string(wb, "endpoints");
1549 }
1550 buffer_json_array_close(wb);
1551 buffer_json_member_add_array(wb, "required_params");
1552 {
1553 buffer_json_add_array_item_object(wb);
1554 {
1555 buffer_json_member_add_string(wb, "id", "processes");
1556 buffer_json_member_add_string(wb, "name", "Processes");
1557 buffer_json_member_add_string(wb, "help", "Group process actors by process name or by PID.");
1558 buffer_json_member_add_boolean(wb, "unique_view", true);
1559 buffer_json_member_add_string(wb, "type", "select");
1560 buffer_json_member_add_array(wb, "options");
1561 {
1562 buffer_json_add_array_item_object(wb);
1563 {
1564 buffer_json_member_add_string(wb, "id", "by_name");
1565 buffer_json_member_add_string(wb, "name", "Processes by Name");
1566 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1567 }
1568 buffer_json_object_close(wb);
1569 buffer_json_add_array_item_object(wb);
1570 {
1571 buffer_json_member_add_string(wb, "id", "by_pid");
1572 buffer_json_member_add_string(wb, "name", "Processes by PID");
1573 }
1574 buffer_json_object_close(wb);
1575 }
1576 buffer_json_array_close(wb);
1577 }
1578 buffer_json_object_close(wb);
1579
1580 buffer_json_add_array_item_object(wb);
1581 {
1582 buffer_json_member_add_string(wb, "id", "__topology_mode");
1583 buffer_json_member_add_string(wb, "name", "Mode");
1584 buffer_json_member_add_string(wb, "help", "Return an aggregated graph by default, or include lossless socket evidence for detailed correlation.");
1585 buffer_json_member_add_boolean(wb, "unique_view", true);
1586 buffer_json_member_add_string(wb, "type", "select");
1587 buffer_json_member_add_array(wb, "options");
1588 {
1589 buffer_json_add_array_item_object(wb);
1590 {
1591 buffer_json_member_add_string(wb, "id", "aggregated");
1592 buffer_json_member_add_string(wb, "name", "Aggregated");
1593 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1594 }
1595 buffer_json_object_close(wb);
1596 buffer_json_add_array_item_object(wb);
1597 {
1598 buffer_json_member_add_string(wb, "id", "detailed");
1599 buffer_json_member_add_string(wb, "name", "Detailed");
1600 }
1601 buffer_json_object_close(wb);
1602 }
1603 buffer_json_array_close(wb);
1604 }
1605 buffer_json_object_close(wb);
1606
1607 buffer_json_add_array_item_object(wb);
1608 {
1609 buffer_json_member_add_string(wb, "id", "sockets");
1610 buffer_json_member_add_string(wb, "name", "Sockets");
1611 buffer_json_member_add_string(wb, "help", "Select one or more socket directions.");
1612 buffer_json_member_add_string(wb, "type", "multiselect");
1613 buffer_json_member_add_array(wb, "options");
1614 {
1615 buffer_json_add_array_item_object(wb);
1616 {
1617 buffer_json_member_add_string(wb, "id", "listening");
1618 buffer_json_member_add_string(wb, "name", "Listening");
1619 }
1620 buffer_json_object_close(wb);
1621 buffer_json_add_array_item_object(wb);
1622 {
1623 buffer_json_member_add_string(wb, "id", "inbound");
1624 buffer_json_member_add_string(wb, "name", "Inbound");
1625 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1626 }
1627 buffer_json_object_close(wb);
1628 buffer_json_add_array_item_object(wb);
1629 {
1630 buffer_json_member_add_string(wb, "id", "outbound");
1631 buffer_json_member_add_string(wb, "name", "Outbound");
1632 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1633 }
1634 buffer_json_object_close(wb);
1635 }
1636 buffer_json_array_close(wb);
1637 }
1638 buffer_json_object_close(wb);
1639
1640 buffer_json_add_array_item_object(wb);
1641 {
1642 buffer_json_member_add_string(wb, "id", "protocols");
1643 buffer_json_member_add_string(wb, "name", "Protocols");
1644 buffer_json_member_add_string(wb, "help", "Select one or more socket protocol families.");
1645 buffer_json_member_add_string(wb, "type", "multiselect");
1646 buffer_json_member_add_array(wb, "options");
1647 {
1648 buffer_json_add_array_item_object(wb);
1649 {
1650 buffer_json_member_add_string(wb, "id", "ipv4_tcp");
1651 buffer_json_member_add_string(wb, "name", "IPv4 TCP");
1652 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1653 }
1654 buffer_json_object_close(wb);
1655 buffer_json_add_array_item_object(wb);
1656 {
1657 buffer_json_member_add_string(wb, "id", "ipv6_tcp");
1658 buffer_json_member_add_string(wb, "name", "IPv6 TCP");
1659 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1660 }
1661 buffer_json_object_close(wb);
1662 buffer_json_add_array_item_object(wb);
1663 {
1664 buffer_json_member_add_string(wb, "id", "ipv4_udp");
1665 buffer_json_member_add_string(wb, "name", "IPv4 UDP");
1666 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1667 }
1668 buffer_json_object_close(wb);
1669 buffer_json_add_array_item_object(wb);
1670 {
1671 buffer_json_member_add_string(wb, "id", "ipv6_udp");
1672 buffer_json_member_add_string(wb, "name", "IPv6 UDP");
1673 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1674 }
1675 buffer_json_object_close(wb);
1676 }
1677 buffer_json_array_close(wb);
1678 }
1679 buffer_json_object_close(wb);
1680
1681 buffer_json_add_array_item_object(wb);
1682 {
1683 buffer_json_member_add_string(wb, "id", "endpoints");
1684 buffer_json_member_add_string(wb, "name", "Endpoints");
1685 buffer_json_member_add_string(wb, "help", "Keep non-private endpoints by IP until AS grouping is available.");
1686 buffer_json_member_add_boolean(wb, "unique_view", true);
1687 buffer_json_member_add_string(wb, "type", "select");
1688 buffer_json_member_add_array(wb, "options");
1689 {
1690 buffer_json_add_array_item_object(wb);
1691 {
1692 buffer_json_member_add_string(wb, "id", "by_ip");
1693 buffer_json_member_add_string(wb, "name", "Non-Private Endpoints by IP");
1694 buffer_json_member_add_boolean(wb, "defaultSelected", true);
1695 }
1696 buffer_json_object_close(wb);
1697 }
1698 buffer_json_array_close(wb);
1699 }
1700 buffer_json_object_close(wb);
1701 }
1702 buffer_json_array_close(wb);
1703 }
1704
1705 typedef struct {
1706 char id[NV_TOPOLOGY_KEY_MAX];
1707 char type[16];
1708 char machine_guid[128];
1709 char hostname[256];
1710 char process[TASK_COMM_LEN + 1];
1711 char username[NV_TOPOLOGY_USERNAME_MAX];
1712 char namespace_type[16];
1713 char local_ip[INET6_ADDRSTRLEN];
1714 char local_address_space[16];
1715 char ip[INET6_ADDRSTRLEN];
1716 char address_space[16];
1717 char display_name[NV_TOPOLOGY_KEY_MAX];
1718 char cmdline[NV_TOPOLOGY_CMDLINE_MAX];
1719 uint64_t pid;
1720 uint64_t ppid;
1721 uint64_t uid;
1722 uint64_t net_ns_inode;
1723 uint64_t sockets;
1724 uint64_t local_ip_count;
1725 bool has_pid;
1726 bool has_ppid;
1727 bool has_uid;
1728 bool has_net_ns_inode;
1729 bool has_local_ip_count;
1730 } NV_TOPOLOGY_V1_ACTOR;
1731
1732 typedef struct {
1733 uint64_t src_actor;
1734 uint64_t dst_actor;
1735 char type[32];
1736 char protocol[16];
1737 char state[32];
1738 uint64_t evidence_count;
1739 uint64_t socket_count;
1740 uint64_t retransmissions;
1741 uint32_t max_rtt_usec;
1742 uint32_t max_rcv_rtt_usec;
1743 } NV_TOPOLOGY_V1_GRAPH_LINK;
1744
1745 typedef struct {
1746 uint64_t link;
1747 uint64_t src_actor;
1748 uint64_t dst_actor;
1749 const NV_TOPOLOGY_LINK *source;
1750 } NV_TOPOLOGY_V1_SOCKET_EVIDENCE;
1751
1752 typedef struct {
1753 uint64_t src_actor;
1754 uint64_t dst_actor;
1755 uint64_t socket_count;
1756 uint64_t retransmissions;
1757 uint32_t max_rtt_usec;
1758 uint32_t max_rcv_rtt_usec;
1759 char client_ip[INET6_ADDRSTRLEN];
1760 char server_ip[INET6_ADDRSTRLEN];
1761 char protocol[16];
1762 char state[32];
1763 } NV_TOPOLOGY_V1_CONNECTION_ROW;
1764
1765 typedef struct {
1766 uint64_t actor;
1767 uint64_t port;
1768 uint64_t socket_count;
1769 char protocol[16];
1770 } NV_TOPOLOGY_V1_PORT_ROW;
1771
1772 typedef struct {
1773 uint64_t actor;
1774 uint64_t port;
1775 char protocol[16];
1776 char address_space[16];
1777 char ip[INET6_ADDRSTRLEN];
1778 } NV_TOPOLOGY_V1_CORRELATION_ROW;
1779
1780 typedef struct {
1781 uint64_t actor;
1782 uint64_t value_index;
1783 bool has_value_index;
1784 char key[NV_TOPOLOGY_LABEL_KEY_MAX];
1785 char value[NV_TOPOLOGY_LABEL_VALUE_MAX];
1786 char source[32];
1787 char kind[32];
1788 } NV_TOPOLOGY_V1_ACTOR_LABEL;
1789
1790 typedef struct {
1791 NV_TOPOLOGY_V1_ACTOR *actors;
1792 size_t actors_used;
1793 size_t actors_size;
1794
1795 NV_TOPOLOGY_V1_GRAPH_LINK *links;
1796 size_t links_used;
1797 size_t links_size;
1798
1799 NV_TOPOLOGY_V1_SOCKET_EVIDENCE *evidence;
1800 size_t evidence_used;
1801 size_t evidence_size;
1802
1803 NV_TOPOLOGY_V1_CONNECTION_ROW *connections;
1804 size_t connections_used;
1805 size_t connections_size;
1806
1807 NV_TOPOLOGY_V1_PORT_ROW *ports;
1808 size_t ports_used;
1809 size_t ports_size;
1810
1811 NV_TOPOLOGY_V1_CORRELATION_ROW *correlation_points;
1812 size_t correlation_points_used;
1813 size_t correlation_points_size;
1814
1815 NV_TOPOLOGY_V1_CORRELATION_ROW *correlation_claims;
1816 size_t correlation_claims_used;
1817 size_t correlation_claims_size;
1818
1819 NV_TOPOLOGY_V1_ACTOR_LABEL *labels;
1820 size_t labels_used;
1821 size_t labels_size;
1822
1823 DICTIONARY *actor_index;
1824 DICTIONARY *graph_link_index;
1825 DICTIONARY *connection_index;
1826 DICTIONARY *port_index;
1827 DICTIONARY *correlation_point_index;
1828 DICTIONARY *correlation_claim_index;
1829 } NV_TOPOLOGY_V1_PAYLOAD;
1830
1831 typedef struct {
1832 const char **values;
1833 uint64_t *indexes;
1834 size_t rows;
1835 size_t rows_used;
1836 size_t values_used;
1837 size_t values_size;
1838 size_t values_json_size;
1839 size_t unique_json_size;
1840 size_t indexes_json_size;
1841 DICTIONARY *index;
1842 } NV_TOPOLOGY_V1_STRING_COLUMN;
1843
1844 static void topology_v1_strncpy(char *dst, size_t dst_size, const char *src) {
1845 if(!dst || !dst_size)
1846 return;
1847
1848 strncpyz(dst, src ? src : "", dst_size - 1);
1849 }
1850
1851 static void topology_v1_actor_index_set(NV_TOPOLOGY_V1_PAYLOAD *payload, const char *actor_id, uint64_t index) {
1852 dictionary_set(payload->actor_index, actor_id, &index, sizeof(index));
1853 }
1854
1855 static bool topology_v1_actor_index_get(NV_TOPOLOGY_V1_PAYLOAD *payload, const char *actor_id, uint64_t *index) {
1856 uint64_t *stored = dictionary_get(payload->actor_index, actor_id);
1857 if(!stored)
1858 return false;
1859
1860 if(index)
1861 *index = *stored;
1862
1863 return true;
1864 }
1865
1866 static NV_TOPOLOGY_V1_ACTOR *topology_v1_add_actor(NV_TOPOLOGY_V1_PAYLOAD *payload, const char *actor_id) {
1867 if(payload->actors_used == payload->actors_size) {
1868 size_t new_size = payload->actors_size ? payload->actors_size * 2 : 32;
1869 payload->actors = reallocz(payload->actors, new_size * sizeof(*payload->actors));
1870 payload->actors_size = new_size;
1871 }
1872
1873 NV_TOPOLOGY_V1_ACTOR *actor = &payload->actors[payload->actors_used];
1874 *actor = (NV_TOPOLOGY_V1_ACTOR){ 0 };
1875 topology_v1_strncpy(actor->id, sizeof(actor->id), actor_id);
1876 topology_v1_actor_index_set(payload, actor_id, payload->actors_used);
1877 payload->actors_used++;
1878 return actor;
1879 }
1880
1881 static NV_TOPOLOGY_V1_GRAPH_LINK *topology_v1_add_graph_link(NV_TOPOLOGY_V1_PAYLOAD *payload) {
1882 if(payload->links_used == payload->links_size) {
1883 size_t new_size = payload->links_size ? payload->links_size * 2 : 64;
1884 payload->links = reallocz(payload->links, new_size * sizeof(*payload->links));
1885 payload->links_size = new_size;
1886 }
1887
1888 NV_TOPOLOGY_V1_GRAPH_LINK *link = &payload->links[payload->links_used++];
1889 *link = (NV_TOPOLOGY_V1_GRAPH_LINK){ 0 };
1890 return link;
1891 }
1892
1893 static NV_TOPOLOGY_V1_SOCKET_EVIDENCE *topology_v1_add_socket_evidence(NV_TOPOLOGY_V1_PAYLOAD *payload) {
1894 if(payload->evidence_used == payload->evidence_size) {
1895 size_t new_size = payload->evidence_size ? payload->evidence_size * 2 : 256;
1896 payload->evidence = reallocz(payload->evidence, new_size * sizeof(*payload->evidence));
1897 payload->evidence_size = new_size;
1898 }
1899
1900 NV_TOPOLOGY_V1_SOCKET_EVIDENCE *row = &payload->evidence[payload->evidence_used++];
1901 *row = (NV_TOPOLOGY_V1_SOCKET_EVIDENCE){ 0 };
1902 return row;
1903 }
1904
1905 static void topology_v1_add_connection_row(
1906 NV_TOPOLOGY_V1_PAYLOAD *payload,
1907 uint64_t src_actor,
1908 uint64_t dst_actor,
1909 const NV_TOPOLOGY_LINK *source) {
1910 if(!payload || !payload->connection_index || !source)
1911 return;
1912
1913 char key[NV_TOPOLOGY_KEY_MAX];
1914 snprintfz(key, sizeof(key), "%"PRIu64"|%"PRIu64"|%s|%s",
1915 src_actor,
1916 dst_actor,
1917 source->protocol,
1918 source->state);
1919
1920 uint64_t *stored = dictionary_get(payload->connection_index, key);
1921 if(stored) {
1922 NV_TOPOLOGY_V1_CONNECTION_ROW *row = &payload->connections[*stored];
1923 row->socket_count += source->sockets;
1924 row->retransmissions += source->retransmissions;
1925 if(row->max_rtt_usec < source->max_rtt_usec)
1926 row->max_rtt_usec = source->max_rtt_usec;
1927 if(row->max_rcv_rtt_usec < source->max_rcv_rtt_usec)
1928 row->max_rcv_rtt_usec = source->max_rcv_rtt_usec;
1929 return;
1930 }
1931
1932 if(payload->connections_used == payload->connections_size) {
1933 size_t new_size = payload->connections_size ? payload->connections_size * 2 : 256;
1934 payload->connections = reallocz(payload->connections, new_size * sizeof(*payload->connections));
1935 payload->connections_size = new_size;
1936 }
1937
1938 uint64_t index = payload->connections_used;
1939 NV_TOPOLOGY_V1_CONNECTION_ROW *row = &payload->connections[payload->connections_used++];
1940 *row = (NV_TOPOLOGY_V1_CONNECTION_ROW){ 0 };
1941 row->src_actor = src_actor;
1942 row->dst_actor = dst_actor;
1943 row->socket_count = source->sockets;
1944 row->retransmissions = source->retransmissions;
1945 row->max_rtt_usec = source->max_rtt_usec;
1946 row->max_rcv_rtt_usec = source->max_rcv_rtt_usec;
1947 topology_v1_strncpy(row->client_ip, sizeof(row->client_ip), source->client_ip);
1948 topology_v1_strncpy(row->server_ip, sizeof(row->server_ip), source->server_ip);
1949 topology_v1_strncpy(row->protocol, sizeof(row->protocol), source->protocol);
1950 topology_v1_strncpy(row->state, sizeof(row->state), source->state);
1951 dictionary_set(payload->connection_index, key, &index, sizeof(index));
1952 }
1953
1954 static void topology_v1_add_port_row(
1955 NV_TOPOLOGY_V1_PAYLOAD *payload,
1956 uint64_t actor,
1957 const char *protocol,
1958 uint64_t port,
1959 uint64_t socket_count) {
1960 if(!payload || !payload->port_index || !port || !socket_count)
1961 return;
1962
1963 char key[NV_TOPOLOGY_KEY_MAX];
1964 snprintfz(key, sizeof(key), "%"PRIu64"|%s|%"PRIu64,
1965 actor,
1966 protocol ? protocol : "",
1967 port);
1968
1969 uint64_t *stored = dictionary_get(payload->port_index, key);
1970 if(stored) {
1971 payload->ports[*stored].socket_count += socket_count;
1972 return;
1973 }
1974
1975 if(payload->ports_used == payload->ports_size) {
1976 size_t new_size = payload->ports_size ? payload->ports_size * 2 : 256;
1977 payload->ports = reallocz(payload->ports, new_size * sizeof(*payload->ports));
1978 payload->ports_size = new_size;
1979 }
1980
1981 uint64_t index = payload->ports_used;
1982 NV_TOPOLOGY_V1_PORT_ROW *row = &payload->ports[payload->ports_used++];
1983 *row = (NV_TOPOLOGY_V1_PORT_ROW){ 0 };
1984 row->actor = actor;
1985 row->port = port;
1986 row->socket_count = socket_count;
1987 topology_v1_strncpy(row->protocol, sizeof(row->protocol), protocol);
1988
1989 dictionary_set(payload->port_index, key, &index, sizeof(index));
1990 }
1991
1992 static void topology_v1_add_actor_label_ex(
1993 NV_TOPOLOGY_V1_PAYLOAD *payload,
1994 uint64_t actor,
1995 const char *key,
1996 const char *value,
1997 const char *source,
1998 const char *kind,
1999 bool has_value_index,
2000 uint64_t value_index) {
2001 if(!payload || !key || !*key || !value || !*value)
2002 return;
2003
2004 if(payload->labels_used == payload->labels_size) {
2005 size_t new_size = payload->labels_size ? payload->labels_size * 2 : 128;
2006 payload->labels = reallocz(payload->labels, new_size * sizeof(*payload->labels));
2007 payload->labels_size = new_size;
2008 }
2009
2010 NV_TOPOLOGY_V1_ACTOR_LABEL *row = &payload->labels[payload->labels_used++];
2011 *row = (NV_TOPOLOGY_V1_ACTOR_LABEL){ 0 };
2012 row->actor = actor;
2013 row->has_value_index = has_value_index;
2014 row->value_index = value_index;
2015 topology_v1_strncpy(row->key, sizeof(row->key), key);
2016 topology_v1_strncpy(row->value, sizeof(row->value), value);
2017 topology_v1_strncpy(row->source, sizeof(row->source), source ? source : NETWORK_TOPOLOGY_SOURCE);
2018 topology_v1_strncpy(row->kind, sizeof(row->kind), kind ? kind : "attribute");
2019 }
2020
2021 static void topology_v1_add_actor_label(
2022 NV_TOPOLOGY_V1_PAYLOAD *payload,
2023 uint64_t actor,
2024 const char *key,
2025 const char *value,
2026 const char *kind) {
2027 topology_v1_add_actor_label_ex(payload, actor, key, value, NETWORK_TOPOLOGY_SOURCE, kind, false, 0);
2028 }
2029
2030 static void topology_v1_add_actor_label_uint(
2031 NV_TOPOLOGY_V1_PAYLOAD *payload,
2032 uint64_t actor,
2033 const char *key,
2034 uint64_t value,
2035 const char *kind) {
2036 char buffer[32];
2037 snprintfz(buffer, sizeof(buffer), "%"PRIu64, value);
2038 topology_v1_add_actor_label(payload, actor, key, buffer, kind);
2039 }
2040
2041 static void topology_v1_add_correlation_row(
2042 NV_TOPOLOGY_V1_CORRELATION_ROW **rows,
2043 size_t *rows_used,
2044 size_t *rows_size,
2045 DICTIONARY *index,
2046 uint64_t actor,
2047 const char *protocol,
2048 const char *address_space,
2049 const char *ip,
2050 uint64_t port) {
2051 if(!rows || !rows_used || !rows_size || !index || !port || topology_ip_is_unspecified(ip))
2052 return;
2053
2054 char key[NV_TOPOLOGY_KEY_MAX];
2055 snprintfz(key, sizeof(key), "%"PRIu64"|%s|%s|%s|%"PRIu64,
2056 actor,
2057 protocol ? protocol : "",
2058 address_space ? address_space : "",
2059 ip ? ip : "",
2060 port);
2061
2062 if(dictionary_get(index, key))
2063 return;
2064
2065 if(*rows_used == *rows_size) {
2066 size_t new_size = *rows_size ? *rows_size * 2 : 256;
2067 *rows = reallocz(*rows, new_size * sizeof(**rows));
2068 *rows_size = new_size;
2069 }
2070
2071 NV_TOPOLOGY_V1_CORRELATION_ROW *row = &(*rows)[(*rows_used)++];
2072 *row = (NV_TOPOLOGY_V1_CORRELATION_ROW){ 0 };
2073 row->actor = actor;
2074 row->port = port;
2075 topology_v1_strncpy(row->protocol, sizeof(row->protocol), protocol);
2076 topology_v1_strncpy(row->address_space, sizeof(row->address_space), address_space);
2077 topology_v1_strncpy(row->ip, sizeof(row->ip), ip);
2078
2079 bool stored = true;
2080 dictionary_set(index, key, &stored, sizeof(stored));
2081 }
2082
2083 static void topology_v1_add_correlation_claim_endpoint(
2084 NV_TOPOLOGY_V1_PAYLOAD *payload,
2085 uint64_t actor,
2086 const char *protocol,
2087 const char *address_space,
2088 const char *ip,
2089 uint64_t port) {
2090 if(!payload)
2091 return;
2092
2093 topology_v1_add_correlation_row(
2094 &payload->correlation_claims,
2095 &payload->correlation_claims_used,
2096 &payload->correlation_claims_size,
2097 payload->correlation_claim_index,
2098 actor,
2099 protocol,
2100 address_space,
2101 ip,
2102 port);
2103 }
2104
2105 static void topology_v1_add_correlation_point_endpoint(
2106 NV_TOPOLOGY_V1_PAYLOAD *payload,
2107 uint64_t actor,
2108 const char *protocol,
2109 const char *address_space,
2110 const char *ip,
2111 uint64_t port) {
2112 if(!payload)
2113 return;
2114
2115 topology_v1_add_correlation_row(
2116 &payload->correlation_points,
2117 &payload->correlation_points_used,
2118 &payload->correlation_points_size,
2119 payload->correlation_point_index,
2120 actor,
2121 protocol,
2122 address_space,
2123 ip,
2124 port);
2125 }
2126
2127 static void topology_v1_free(NV_TOPOLOGY_V1_PAYLOAD *payload) {
2128 if(!payload)
2129 return;
2130
2131 freez(payload->actors);
2132 freez(payload->links);
2133 freez(payload->evidence);
2134 freez(payload->connections);
2135 freez(payload->ports);
2136 freez(payload->correlation_points);
2137 freez(payload->correlation_claims);
2138 freez(payload->labels);
2139 if(payload->actor_index)
2140 dictionary_destroy(payload->actor_index);
2141 if(payload->graph_link_index)
2142 dictionary_destroy(payload->graph_link_index);
2143 if(payload->connection_index)
2144 dictionary_destroy(payload->connection_index);
2145 if(payload->port_index)
2146 dictionary_destroy(payload->port_index);
2147 if(payload->correlation_point_index)
2148 dictionary_destroy(payload->correlation_point_index);
2149 if(payload->correlation_claim_index)
2150 dictionary_destroy(payload->correlation_claim_index);
2151 *payload = (NV_TOPOLOGY_V1_PAYLOAD){ 0 };
2152 }
2153
2154 static bool topology_v1_should_emit_endpoint_actor(
2155 const NV_TOPOLOGY_CONTEXT *ctx,
2156 const NV_REMOTE_ACTOR *actor) {
2157 if(!actor || !actor->ip[0])
2158 return false;
2159
2160 return !topology_ip_belongs_to_self(ctx, actor->ip, actor->address_space);
2161 }
2162
2163 static void topology_v1_collect_actors(
2164 const NV_TOPOLOGY_CONTEXT *ctx,
2165 NV_TOPOLOGY_RENDER_STATE *state,
2166 NV_TOPOLOGY_V1_PAYLOAD *payload) {
2167 NV_TOPOLOGY_V1_ACTOR *self = topology_v1_add_actor(payload, state->host_actor_id);
2168 topology_v1_strncpy(self->type, sizeof(self->type), "self");
2169 topology_v1_strncpy(self->machine_guid, sizeof(self->machine_guid), ctx->machine_guid);
2170 topology_v1_strncpy(self->hostname, sizeof(self->hostname), ctx->hostname);
2171 topology_v1_strncpy(self->display_name, sizeof(self->display_name), ctx->hostname);
2172 self->sockets = ctx->sockets_total;
2173 self->local_ip_count = state->local_ip_count;
2174 self->has_local_ip_count = true;
2175 uint64_t self_index = payload->actors_used - 1;
2176 topology_v1_add_actor_label(payload, self_index, "display_name", self->display_name, "attribute");
2177 topology_v1_add_actor_label(payload, self_index, "type", self->type, "metadata");
2178 topology_v1_add_actor_label(payload, self_index, "hostname", self->hostname, "identity");
2179 topology_v1_add_actor_label(payload, self_index, "machine_guid", self->machine_guid, "identity");
2180 topology_v1_add_actor_label_uint(payload, self_index, "socket_count", self->sockets, "metric");
2181 topology_v1_add_actor_label_uint(payload, self_index, "local_ip_count", self->local_ip_count, "metric");
2182
2183 NV_PROCESS_ACTOR *pa;
2184 dfe_start_read(ctx->process_actors, pa) {
2185 char actor_id[NV_TOPOLOGY_KEY_MAX];
2186 char display_name[NV_TOPOLOGY_KEY_MAX];
2187 topology_actor_id_for_process(ctx, pa->pid, pa->uid, pa->net_ns_inode, pa->process, actor_id, sizeof(actor_id));
2188 topology_process_display_name(ctx, pa->process, pa->pid, display_name, sizeof(display_name));
2189
2190 NV_TOPOLOGY_V1_ACTOR *actor = topology_v1_add_actor(payload, actor_id);
2191 topology_v1_strncpy(actor->type, sizeof(actor->type), "process");
2192 topology_v1_strncpy(actor->machine_guid, sizeof(actor->machine_guid), ctx->machine_guid);
2193 topology_v1_strncpy(actor->hostname, sizeof(actor->hostname), ctx->hostname);
2194 topology_v1_strncpy(actor->process, sizeof(actor->process), pa->process);
2195 topology_v1_strncpy(actor->username, sizeof(actor->username), pa->username);
2196 topology_v1_strncpy(actor->namespace_type, sizeof(actor->namespace_type), pa->namespace_type);
2197 topology_v1_strncpy(actor->local_ip, sizeof(actor->local_ip), pa->local_ip);
2198 topology_v1_strncpy(actor->local_address_space, sizeof(actor->local_address_space), pa->local_address_space);
2199 topology_v1_strncpy(actor->display_name, sizeof(actor->display_name), display_name);
2200 topology_v1_strncpy(actor->cmdline, sizeof(actor->cmdline), pa->cmdline);
2201 actor->pid = (uint64_t)pa->pid;
2202 actor->ppid = (uint64_t)pa->ppid;
2203 actor->uid = (uint64_t)pa->uid;
2204 actor->net_ns_inode = pa->net_ns_inode;
2205 actor->sockets = pa->sockets;
2206 actor->has_pid = ctx->options.processes_by_pid;
2207 actor->has_ppid = ctx->options.processes_by_pid;
2208 actor->has_uid = ctx->options.processes_by_pid;
2209 actor->has_net_ns_inode = ctx->options.processes_by_pid;
2210
2211 uint64_t actor_index = payload->actors_used - 1;
2212 topology_v1_add_actor_label(payload, actor_index, "display_name", actor->display_name, "attribute");
2213 topology_v1_add_actor_label(payload, actor_index, "type", actor->type, "metadata");
2214 topology_v1_add_actor_label(payload, actor_index, "process", actor->process, "identity");
2215 topology_v1_add_actor_label(payload, actor_index, "username", actor->username, "attribute");
2216 topology_v1_add_actor_label(payload, actor_index, "cmdline", actor->cmdline, "attribute");
2217 topology_v1_add_actor_label(payload, actor_index, "namespace_type", actor->namespace_type, "attribute");
2218 topology_v1_add_actor_label(payload, actor_index, "local_ip", actor->local_ip, "attribute");
2219 topology_v1_add_actor_label(payload, actor_index, "local_address_space", actor->local_address_space, "attribute");
2220 topology_v1_add_actor_label_uint(payload, actor_index, "socket_count", actor->sockets, "metric");
2221 if(actor->has_pid)
2222 topology_v1_add_actor_label_uint(payload, actor_index, "pid", actor->pid, "identity");
2223 if(actor->has_uid)
2224 topology_v1_add_actor_label_uint(payload, actor_index, "uid", actor->uid, "attribute");
2225 if(actor->has_net_ns_inode)
2226 topology_v1_add_actor_label_uint(payload, actor_index, "net_ns_inode", actor->net_ns_inode, "identity");
2227 }
2228 dfe_done(pa);
2229
2230 NV_REMOTE_ACTOR *ra;
2231 dfe_start_read(ctx->remote_actors, ra) {
2232 // Remote actors are discovered while sockets are scanned, before all
2233 // local IPs may be known. Recheck with the final local-IP set so actor
2234 // emission and link destination resolution use the same self test.
2235 if(!topology_v1_should_emit_endpoint_actor(ctx, ra))
2236 continue;
2237
2238 char actor_id[NV_TOPOLOGY_KEY_MAX];
2239 topology_actor_id_for_remote_endpoint(ctx, ra->ip, ra->address_space, actor_id, sizeof(actor_id));
2240
2241 NV_TOPOLOGY_V1_ACTOR *actor = topology_v1_add_actor(payload, actor_id);
2242 topology_v1_strncpy(actor->type, sizeof(actor->type), "endpoint");
2243 topology_v1_strncpy(actor->ip, sizeof(actor->ip), ra->ip);
2244 topology_v1_strncpy(actor->address_space, sizeof(actor->address_space), ra->address_space);
2245 topology_v1_strncpy(actor->display_name, sizeof(actor->display_name), ra->ip);
2246 actor->sockets = ra->sockets;
2247 uint64_t actor_index = payload->actors_used - 1;
2248 topology_v1_add_actor_label(payload, actor_index, "display_name", actor->display_name, "attribute");
2249 topology_v1_add_actor_label(payload, actor_index, "type", actor->type, "metadata");
2250 topology_v1_add_actor_label(payload, actor_index, "ip", actor->ip, "identity");
2251 topology_v1_add_actor_label(payload, actor_index, "address_space", actor->address_space, "attribute");
2252 topology_v1_add_actor_label_uint(payload, actor_index, "socket_count", actor->sockets, "metric");
2253 state->endpoint_actor_count++;
2254 }
2255 dfe_done(ra);
2256 }
2257
2258 static bool topology_v1_process_actor_index(
2259 const NV_TOPOLOGY_CONTEXT *ctx,
2260 NV_TOPOLOGY_V1_PAYLOAD *payload,
2261 uint64_t pid,
2262 uint64_t uid,
2263 uint64_t net_ns_inode,
2264 const char *process,
2265 uint64_t *index) {
2266 char actor_id[NV_TOPOLOGY_KEY_MAX];
2267 topology_actor_id_for_process(ctx, pid, uid, net_ns_inode, process, actor_id, sizeof(actor_id));
2268 return topology_v1_actor_index_get(payload, actor_id, index);
2269 }
2270
2271 static bool topology_v1_endpoint_actor_index(
2272 const NV_TOPOLOGY_CONTEXT *ctx,
2273 NV_TOPOLOGY_V1_PAYLOAD *payload,
2274 const char *ip,
2275 const char *address_space,
2276 uint64_t *index) {
2277 char actor_id[NV_TOPOLOGY_KEY_MAX];
2278 topology_actor_id_for_remote_endpoint(ctx, ip, address_space, actor_id, sizeof(actor_id));
2279 return topology_v1_actor_index_get(payload, actor_id, index);
2280 }
2281
2282 static uint64_t topology_v1_graph_link_get_or_add(
2283 NV_TOPOLOGY_V1_PAYLOAD *payload,
2284 uint64_t src_actor,
2285 uint64_t dst_actor,
2286 const char *type,
2287 const char *protocol,
2288 const char *state) {
2289 char key[NV_TOPOLOGY_KEY_MAX];
2290 snprintfz(key, sizeof(key), "%"PRIu64"|%"PRIu64"|%s|%s|%s",
2291 src_actor, dst_actor,
2292 type ? type : "",
2293 protocol ? protocol : "",
2294 state ? state : "");
2295
2296 uint64_t *stored = dictionary_get(payload->graph_link_index, key);
2297 if(stored)
2298 return *stored;
2299
2300 uint64_t index = payload->links_used;
2301 NV_TOPOLOGY_V1_GRAPH_LINK *link = topology_v1_add_graph_link(payload);
2302 link->src_actor = src_actor;
2303 link->dst_actor = dst_actor;
2304 topology_v1_strncpy(link->type, sizeof(link->type), type);
2305 topology_v1_strncpy(link->protocol, sizeof(link->protocol), protocol);
2306 topology_v1_strncpy(link->state, sizeof(link->state), state);
2307 dictionary_set(payload->graph_link_index, key, &index, sizeof(index));
2308 return index;
2309 }
2310
2311 static bool topology_v1_socket_peer_actor_index(
2312 const NV_TOPOLOGY_CONTEXT *ctx,
2313 NV_TOPOLOGY_V1_PAYLOAD *payload,
2314 const NV_TOPOLOGY_LINK *link,
2315 NV_TOPOLOGY_ENDPOINT_ROLE role,
2316 uint64_t *actor,
2317 bool *is_correlation_point) {
2318 if(is_correlation_point)
2319 *is_correlation_point = false;
2320
2321 const char *ip = NULL;
2322 const char *address_space = NULL;
2323 uint16_t port = 0;
2324
2325 if(role == NV_TOPOLOGY_ENDPOINT_ROLE_CLIENT) {
2326 ip = link->client_ip;
2327 address_space = link->client_address_space;
2328 port = link->client_port;
2329 }
2330 else if(role == NV_TOPOLOGY_ENDPOINT_ROLE_SERVER) {
2331 ip = link->server_ip;
2332 address_space = link->server_address_space;
2333 port = link->server_port;
2334 }
2335 else
2336 return false;
2337
2338 bool endpoint_is_self = topology_ip_belongs_to_self(ctx, ip, address_space);
2339 if(endpoint_is_self) {
2340 NV_ENDPOINT_OWNER *owner = topology_lookup_endpoint_owner(ctx, link->net_ns_inode, link->protocol_id, ip, port, true);
2341 if(owner) {
2342 return topology_v1_process_actor_index(ctx, payload,
2343 owner->pid, owner->uid, owner->net_ns_inode,
2344 owner->process, actor);
2345 }
2346
2347 return false;
2348 }
2349
2350 if(is_correlation_point)
2351 *is_correlation_point = true;
2352 return topology_v1_endpoint_actor_index(ctx, payload, ip, address_space, actor);
2353 }
2354
2355 static void topology_v1_collect_links(
2356 const NV_TOPOLOGY_CONTEXT *ctx,
2357 NV_TOPOLOGY_RENDER_STATE *state,
2358 NV_TOPOLOGY_V1_PAYLOAD *payload) {
2359 uint64_t host_actor = 0;
2360 topology_v1_actor_index_get(payload, state->host_actor_id, &host_actor);
2361
2362 NV_PROCESS_ACTOR *pa;
2363 dfe_start_read(ctx->process_actors, pa) {
2364 uint64_t process_actor;
2365 if(!topology_v1_process_actor_index(ctx, payload, pa->pid, pa->uid, pa->net_ns_inode, pa->process, &process_actor))
2366 continue;
2367
2368 uint64_t link_index = topology_v1_graph_link_get_or_add(
2369 payload, host_actor, process_actor, "ownership", "ownership", "active");
2370 NV_TOPOLOGY_V1_GRAPH_LINK *link = &payload->links[link_index];
2371 link->socket_count += pa->sockets;
2372 state->ownership_link_count++;
2373 }
2374 dfe_done(pa);
2375
2376 NV_TOPOLOGY_LINK *source;
2377 dfe_start_read(ctx->links, source) {
2378 uint64_t src_actor;
2379 if(!topology_v1_process_actor_index(ctx, payload,
2380 source->pid, source->uid, source->net_ns_inode,
2381 source->process, &src_actor))
2382 continue;
2383
2384 uint64_t client_actor = 0;
2385 uint64_t server_actor = 0;
2386 bool client_is_correlation_point = false;
2387 bool server_is_correlation_point = false;
2388
2389 if(source->process_is_client) {
2390 client_actor = src_actor;
2391 if(!topology_v1_socket_peer_actor_index(
2392 ctx, payload, source, NV_TOPOLOGY_ENDPOINT_ROLE_SERVER, &server_actor, &server_is_correlation_point))
2393 continue;
2394 }
2395 else {
2396 server_actor = src_actor;
2397 if(!topology_v1_socket_peer_actor_index(
2398 ctx, payload, source, NV_TOPOLOGY_ENDPOINT_ROLE_CLIENT, &client_actor, &client_is_correlation_point))
2399 continue;
2400 }
2401
2402 if(!client_actor || !server_actor)
2403 continue;
2404
2405 if(source->process_is_client) {
2406 topology_v1_add_correlation_claim_endpoint(
2407 payload, client_actor, source->protocol, source->client_address_space, source->client_ip, source->client_port);
2408 if(server_is_correlation_point)
2409 topology_v1_add_correlation_point_endpoint(
2410 payload, server_actor, source->protocol, source->server_address_space, source->server_ip, source->server_port);
2411 }
2412 else {
2413 topology_v1_add_correlation_claim_endpoint(
2414 payload, server_actor, source->protocol, source->server_address_space, source->server_ip, source->server_port);
2415 if(client_is_correlation_point)
2416 topology_v1_add_correlation_point_endpoint(
2417 payload, client_actor, source->protocol, source->client_address_space, source->client_ip, source->client_port);
2418 }
2419
2420 topology_v1_add_port_row(
2421 payload, src_actor, source->protocol, source->process_port, source->sockets);
2422 if(!source->process_is_client && !client_is_correlation_point)
2423 topology_v1_add_port_row(
2424 payload, client_actor, source->protocol, source->client_port, source->sockets);
2425 else if(source->process_is_client && !server_is_correlation_point)
2426 topology_v1_add_port_row(
2427 payload, server_actor, source->protocol, source->server_port, source->sockets);
2428
2429 uint64_t link_index = topology_v1_graph_link_get_or_add(
2430 payload, client_actor, server_actor,
2431 (client_is_correlation_point || server_is_correlation_point) ? "endpoint_socket" : "socket",
2432 source->protocol, source->state);
2433 NV_TOPOLOGY_V1_GRAPH_LINK *link = &payload->links[link_index];
2434 link->evidence_count++;
2435 link->socket_count += source->sockets;
2436 link->retransmissions += source->retransmissions;
2437 if(link->max_rtt_usec < source->max_rtt_usec)
2438 link->max_rtt_usec = source->max_rtt_usec;
2439 if(link->max_rcv_rtt_usec < source->max_rcv_rtt_usec)
2440 link->max_rcv_rtt_usec = source->max_rcv_rtt_usec;
2441
2442 if(ctx->options.detailed) {
2443 NV_TOPOLOGY_V1_SOCKET_EVIDENCE *row = topology_v1_add_socket_evidence(payload);
2444 row->link = link_index;
2445 row->src_actor = client_actor;
2446 row->dst_actor = server_actor;
2447 row->source = source;
2448 }
2449 else
2450 topology_v1_add_connection_row(payload, client_actor, server_actor, source);
2451 }
2452 dfe_done(source);
2453 }
2454
2455 static void topology_v1_emit_column(
2456 BUFFER *wb,
2457 const char *id,
2458 const char *type,
2459 const char *role,
2460 bool nullable,
2461 const char *aggregation) {
2462 buffer_json_add_array_item_object(wb);
2463 buffer_json_member_add_string(wb, "id", id);
2464 buffer_json_member_add_string(wb, "type", type);
2465 if(nullable)
2466 buffer_json_member_add_boolean(wb, "nullable", true);
2467 if(role)
2468 buffer_json_member_add_string(wb, "role", role);
2469 if(aggregation)
2470 buffer_json_member_add_string(wb, "aggregation", aggregation);
2471 buffer_json_object_close(wb);
2472 }
2473
2474 static void topology_v1_values_start(BUFFER *wb) {
2475 buffer_json_add_array_item_object(wb);
2476 buffer_json_member_add_string(wb, "codec", "values");
2477 buffer_json_member_add_array(wb, "values");
2478 }
2479
2480 static void topology_v1_const_string(BUFFER *wb, const char *value) {
2481 buffer_json_add_array_item_object(wb);
2482 buffer_json_member_add_string(wb, "codec", "const");
2483 buffer_json_member_add_string(wb, "value", value);
2484 buffer_json_object_close(wb);
2485 }
2486
2487 static void topology_v1_values_end(BUFFER *wb) {
2488 buffer_json_array_close(wb);
2489 buffer_json_object_close(wb);
2490 }
2491
2492 static void topology_v1_add_nullable_uint(BUFFER *wb, bool has_value, uint64_t value) {
2493 if(has_value)
2494 buffer_json_add_array_item_uint64(wb, value);
2495 else
2496 buffer_json_add_array_item_string(wb, NULL);
2497 }
2498
2499 static size_t topology_v1_uint_json_size(uint64_t value) {
2500 size_t size = 1;
2501 while(value >= 10) {
2502 value /= 10;
2503 size++;
2504 }
2505
2506 return size;
2507 }
2508
2509 static size_t topology_v1_string_json_size(const char *value) {
2510 if(!value || !*value)
2511 return 4; // null
2512
2513 size_t size = 2; // quotes
2514 for(const unsigned char *s = (const unsigned char *)value; *s; s++) {
2515 if(*s == '"' || *s == '\\')
2516 size += 2;
2517 else if(*s <= 0x1f)
2518 size += 6;
2519 else
2520 size++;
2521 }
2522
2523 return size;
2524 }
2525
2526 static void topology_v1_string_column_init(NV_TOPOLOGY_V1_STRING_COLUMN *column, size_t rows) {
2527 *column = (NV_TOPOLOGY_V1_STRING_COLUMN){
2528 .rows = rows,
2529 .indexes = rows ? callocz(rows, sizeof(*column->indexes)) : NULL,
2530 .index = dictionary_create_advanced(
2531 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2532 NULL,
2533 sizeof(uint64_t)),
2534 };
2535 }
2536
2537 static void topology_v1_string_column_free(NV_TOPOLOGY_V1_STRING_COLUMN *column) {
2538 if(!column)
2539 return;
2540
2541 freez(column->values);
2542 freez(column->indexes);
2543 if(column->index)
2544 dictionary_destroy(column->index);
2545 *column = (NV_TOPOLOGY_V1_STRING_COLUMN){ 0 };
2546 }
2547
2548 static void topology_v1_string_column_add(NV_TOPOLOGY_V1_STRING_COLUMN *column, const char *value) {
2549 if(!column || column->rows_used >= column->rows)
2550 return;
2551
2552 const char *stored_value = (value && *value) ? value : NULL;
2553 const char *key = stored_value ? stored_value : "\001";
2554 uint64_t index = 0;
2555 uint64_t *stored = column->index ? dictionary_get(column->index, key) : NULL;
2556
2557 if(stored) {
2558 index = *stored;
2559 }
2560 else {
2561 index = column->values_used;
2562 if(column->values_used == column->values_size) {
2563 size_t new_size = column->values_size ? column->values_size * 2 : 16;
2564 column->values = reallocz(column->values, new_size * sizeof(*column->values));
2565 column->values_size = new_size;
2566 }
2567
2568 column->values[column->values_used++] = stored_value;
2569 if(column->index)
2570 dictionary_set(column->index, key, &index, sizeof(index));
2571
2572 column->unique_json_size += topology_v1_string_json_size(stored_value);
2573 if(column->values_used > 1)
2574 column->unique_json_size++;
2575 }
2576
2577 column->indexes[column->rows_used] = index;
2578 column->indexes_json_size += topology_v1_uint_json_size(index);
2579 column->values_json_size += topology_v1_string_json_size(stored_value);
2580 if(column->rows_used)
2581 column->indexes_json_size++, column->values_json_size++;
2582 column->rows_used++;
2583 }
2584
2585 static void topology_v1_emit_auto_string_column(BUFFER *wb, const NV_TOPOLOGY_V1_STRING_COLUMN *column) {
2586 size_t values_encoding_size = sizeof("{\"codec\":\"values\",\"values\":[]}") - 1 + column->values_json_size;
2587 size_t dict_encoding_size =
2588 sizeof("{\"codec\":\"dict\",\"values\":[],\"indexes\":[]}") - 1 +
2589 column->unique_json_size + column->indexes_json_size;
2590 bool use_dict = column->rows > 0 && column->values_used < column->rows && dict_encoding_size < values_encoding_size;
2591
2592 buffer_json_add_array_item_object(wb);
2593 if(use_dict) {
2594 buffer_json_member_add_string(wb, "codec", "dict");
2595 buffer_json_member_add_array(wb, "values");
2596 for(size_t i = 0; i < column->values_used; i++)
2597 buffer_json_add_array_item_string(wb, column->values[i]);
2598 buffer_json_array_close(wb);
2599
2600 buffer_json_member_add_array(wb, "indexes");
2601 for(size_t i = 0; i < column->rows_used; i++)
2602 buffer_json_add_array_item_uint64(wb, column->indexes[i]);
2603 buffer_json_array_close(wb);
2604 }
2605 else {
2606 buffer_json_member_add_string(wb, "codec", "values");
2607 buffer_json_member_add_array(wb, "values");
2608 for(size_t i = 0; i < column->rows_used; i++)
2609 buffer_json_add_array_item_string(wb, column->values[column->indexes[i]]);
2610 buffer_json_array_close(wb);
2611 }
2612 buffer_json_object_close(wb);
2613 }
2614
2615 static void topology_v1_emit_actor_columns(BUFFER *wb) {
2616 topology_v1_emit_column(wb, "id", "string", "identity", false, NULL);
2617 topology_v1_emit_column(wb, "type", "string", "group_key", false, NULL);
2618 topology_v1_emit_column(wb, "layer", "string", "group_key", false, NULL);
2619 topology_v1_emit_column(wb, "machine_guid", "string", "merge_identity", true, NULL);
2620 topology_v1_emit_column(wb, "hostname", "string", "merge_identity", true, NULL);
2621 topology_v1_emit_column(wb, "process", "string", "group_key", true, NULL);
2622 topology_v1_emit_column(wb, "username", "string", "attribute", true, NULL);
2623 topology_v1_emit_column(wb, "cmdline", "string", "attribute", true, NULL);
2624 topology_v1_emit_column(wb, "pid", "uint", "identity", true, NULL);
2625 topology_v1_emit_column(wb, "ppid", "uint", "attribute", true, NULL);
2626 topology_v1_emit_column(wb, "uid", "uint", "attribute", true, NULL);
2627 topology_v1_emit_column(wb, "net_ns_inode", "uint", "identity", true, NULL);
2628 topology_v1_emit_column(wb, "namespace_type", "string", "group_key", true, NULL);
2629 topology_v1_emit_column(wb, "local_ip", "ip", "attribute", true, NULL);
2630 topology_v1_emit_column(wb, "local_address_space", "string", "attribute", true, NULL);
2631 topology_v1_emit_column(wb, "ip", "ip", "identity", true, NULL);
2632 topology_v1_emit_column(wb, "address_space", "string", "group_key", true, NULL);
2633 topology_v1_emit_column(wb, "display_name", "string", "attribute", true, NULL);
2634 topology_v1_emit_column(wb, "socket_count", "uint", "metric", false, "sum");
2635 topology_v1_emit_column(wb, "local_ip_count", "uint", "metric", true, "sum");
2636 }
2637
2638 static void topology_v1_emit_link_columns(BUFFER *wb) {
2639 topology_v1_emit_column(wb, "src_actor", "actor_ref", "reference", false, NULL);
2640 topology_v1_emit_column(wb, "dst_actor", "actor_ref", "reference", false, NULL);
2641 topology_v1_emit_column(wb, "type", "string", "group_key", false, NULL);
2642 topology_v1_emit_column(wb, "protocol", "string", "group_key", true, NULL);
2643 topology_v1_emit_column(wb, "state", "string", "group_key", true, NULL);
2644 topology_v1_emit_column(wb, "evidence_count", "uint", "metric", false, "sum");
2645 topology_v1_emit_column(wb, "socket_count", "uint", "metric", false, "sum");
2646 topology_v1_emit_column(wb, "retransmissions", "uint", "metric", false, "sum");
2647 topology_v1_emit_column(wb, "rtt_ms_max", "float", "metric", false, "max");
2648 topology_v1_emit_column(wb, "recv_rtt_ms_max", "float", "metric", false, "max");
2649 }
2650
2651 static void topology_v1_emit_socket_evidence_columns(BUFFER *wb) {
2652 topology_v1_emit_column(wb, "link", "link_ref", "reference", false, NULL);
2653 topology_v1_emit_column(wb, "src_actor", "actor_ref", "reference", false, NULL);
2654 topology_v1_emit_column(wb, "dst_actor", "actor_ref", "reference", false, NULL);
2655 topology_v1_emit_column(wb, "client_ip", "ip", "group_key", false, NULL);
2656 topology_v1_emit_column(wb, "client_port", "uint", "group_key", false, NULL);
2657 topology_v1_emit_column(wb, "server_ip", "ip", "group_key", false, NULL);
2658 topology_v1_emit_column(wb, "server_port", "uint", "group_key", false, NULL);
2659 topology_v1_emit_column(wb, "protocol", "string", "group_key", false, NULL);
2660 topology_v1_emit_column(wb, "protocol_family", "string", "group_key", false, NULL);
2661 topology_v1_emit_column(wb, "state", "string", "group_key", false, NULL);
2662 topology_v1_emit_column(wb, "namespace_type", "string", "group_key", true, NULL);
2663 topology_v1_emit_column(wb, "client_address_space", "string", "group_key", true, NULL);
2664 topology_v1_emit_column(wb, "server_address_space", "string", "group_key", true, NULL);
2665 topology_v1_emit_column(wb, "pid", "uint", "attribute", true, NULL);
2666 topology_v1_emit_column(wb, "uid", "uint", "attribute", true, NULL);
2667 topology_v1_emit_column(wb, "net_ns_inode", "uint", "attribute", true, NULL);
2668 topology_v1_emit_column(wb, "process", "string", "attribute", true, NULL);
2669 topology_v1_emit_column(wb, "socket_count", "uint", "metric", false, "sum");
2670 topology_v1_emit_column(wb, "retransmissions", "uint", "metric", false, "sum");
2671 topology_v1_emit_column(wb, "rtt_ms_max", "float", "metric", false, "max");
2672 topology_v1_emit_column(wb, "recv_rtt_ms_max", "float", "metric", false, "max");
2673 }
2674
2675 static void topology_v1_emit_connection_columns(BUFFER *wb) {
2676 topology_v1_emit_column(wb, "src_actor", "actor_ref", "reference", false, NULL);
2677 topology_v1_emit_column(wb, "dst_actor", "actor_ref", "reference", false, NULL);
2678 topology_v1_emit_column(wb, "client_ip", "ip", "group_key", false, NULL);
2679 topology_v1_emit_column(wb, "server_ip", "ip", "group_key", false, NULL);
2680 topology_v1_emit_column(wb, "protocol", "string", "group_key", false, NULL);
2681 topology_v1_emit_column(wb, "state", "string", "group_key", false, NULL);
2682 topology_v1_emit_column(wb, "socket_count", "uint", "metric", false, "sum");
2683 topology_v1_emit_column(wb, "retransmissions", "uint", "metric", false, "sum");
2684 topology_v1_emit_column(wb, "rtt_ms_max", "float", "metric", false, "max");
2685 topology_v1_emit_column(wb, "recv_rtt_ms_max", "float", "metric", false, "max");
2686 }
2687
2688 static void topology_v1_emit_socket_port_columns(BUFFER *wb) {
2689 topology_v1_emit_column(wb, "actor", "actor_ref", "reference", false, NULL);
2690 topology_v1_emit_column(wb, "port", "uint", "group_key", false, NULL);
2691 topology_v1_emit_column(wb, "protocol", "string", "group_key", false, NULL);
2692 topology_v1_emit_column(wb, "socket_count", "uint", "metric", false, "sum");
2693 }
2694
2695 static void topology_v1_emit_actor_label_columns(BUFFER *wb) {
2696 topology_v1_emit_column(wb, "actor", "actor_ref", "reference", false, NULL);
2697 topology_v1_emit_column(wb, "key", "string", "attribute", false, NULL);
2698 topology_v1_emit_column(wb, "value", "string", "attribute", false, NULL);
2699 topology_v1_emit_column(wb, "source", "string", "attribute", true, NULL);
2700 topology_v1_emit_column(wb, "kind", "string", "attribute", true, NULL);
2701 topology_v1_emit_column(wb, "value_index", "uint", "attribute", true, NULL);
2702 }
2703
2704 static void topology_v1_emit_modal_direct_column_with_visibility(
2705 BUFFER *wb,
2706 const char *id,
2707 const char *label,
2708 const char *column,
2709 const char *cell,
2710 const char *visibility);
2711
2712 static void topology_v1_emit_modal_direct_column(
2713 BUFFER *wb,
2714 const char *id,
2715 const char *label,
2716 const char *column,
2717 const char *cell) {
2718 topology_v1_emit_modal_direct_column_with_visibility(wb, id, label, column, cell, NULL);
2719 }
2720
2721 static void topology_v1_emit_modal_direct_column_with_visibility(
2722 BUFFER *wb,
2723 const char *id,
2724 const char *label,
2725 const char *column,
2726 const char *cell,
2727 const char *visibility) {
2728 buffer_json_add_array_item_object(wb);
2729 {
2730 buffer_json_member_add_string(wb, "id", id);
2731 buffer_json_member_add_string(wb, "label", label);
2732 buffer_json_member_add_object(wb, "projection");
2733 {
2734 buffer_json_member_add_string(wb, "kind", "direct");
2735 buffer_json_member_add_string(wb, "column", column);
2736 }
2737 buffer_json_object_close(wb);
2738 buffer_json_member_add_string(wb, "cell", cell);
2739 if(visibility)
2740 buffer_json_member_add_string(wb, "visibility", visibility);
2741 }
2742 buffer_json_object_close(wb);
2743 }
2744
2745 static void topology_v1_emit_modal_opposite_actor_column_labeled(BUFFER *wb, const char *id, const char *label) {
2746 buffer_json_add_array_item_object(wb);
2747 {
2748 buffer_json_member_add_string(wb, "id", id);
2749 buffer_json_member_add_string(wb, "label", label);
2750 buffer_json_member_add_object(wb, "projection");
2751 {
2752 buffer_json_member_add_string(wb, "kind", "opposite_actor");
2753 buffer_json_member_add_string(wb, "src_actor_column", "src_actor");
2754 buffer_json_member_add_string(wb, "dst_actor_column", "dst_actor");
2755 }
2756 buffer_json_object_close(wb);
2757 buffer_json_member_add_string(wb, "cell", "actor_link");
2758 }
2759 buffer_json_object_close(wb);
2760 }
2761
2762 static void topology_v1_emit_modal_formatted_endpoint_column(
2763 BUFFER *wb,
2764 const char *id,
2765 const char *label,
2766 const char *ip_column,
2767 const char *port_column) {
2768 buffer_json_add_array_item_object(wb);
2769 {
2770 buffer_json_member_add_string(wb, "id", id);
2771 buffer_json_member_add_string(wb, "label", label);
2772 buffer_json_member_add_object(wb, "projection");
2773 {
2774 buffer_json_member_add_string(wb, "kind", "formatted_endpoint");
2775 if(ip_column)
2776 buffer_json_member_add_string(wb, "ip_column", ip_column);
2777 if(port_column)
2778 buffer_json_member_add_string(wb, "port_column", port_column);
2779 buffer_json_member_add_string(wb, "protocol_column", "protocol");
2780 }
2781 buffer_json_object_close(wb);
2782 buffer_json_member_add_string(wb, "cell", "endpoint");
2783 }
2784 buffer_json_object_close(wb);
2785 }
2786
2787 static void topology_v1_emit_network_connection_modal_section(
2788 BUFFER *wb,
2789 const char *id,
2790 const char *label,
2791 const char *selected_actor_column,
2792 const char *peer_label,
2793 const char *endpoint_label,
2794 const char *endpoint_ip_column,
2795 const char *endpoint_port_column,
2796 bool detailed,
2797 uint64_t order) {
2798 buffer_json_add_array_item_object(wb);
2799 {
2800 buffer_json_member_add_string(wb, "id", id);
2801 buffer_json_member_add_string(wb, "label", label);
2802 buffer_json_member_add_uint64(wb, "order", order);
2803 buffer_json_member_add_object(wb, "source");
2804 {
2805 buffer_json_member_add_string(wb, "kind", detailed ? "evidence" : "relationship_table");
2806 if(detailed)
2807 buffer_json_member_add_string(wb, "evidence", "socket");
2808 else
2809 buffer_json_member_add_string(wb, "table", "connections");
2810 }
2811 buffer_json_object_close(wb);
2812 buffer_json_member_add_object(wb, "owner_filter");
2813 {
2814 buffer_json_member_add_string(wb, "mode", "actor_column");
2815 buffer_json_member_add_string(wb, "actor_column", selected_actor_column);
2816 }
2817 buffer_json_object_close(wb);
2818 buffer_json_member_add_array(wb, "columns");
2819 {
2820 topology_v1_emit_modal_opposite_actor_column_labeled(wb, "actor", peer_label);
2821 topology_v1_emit_modal_formatted_endpoint_column(
2822 wb, "endpoint", endpoint_label, endpoint_ip_column, detailed ? endpoint_port_column : NULL);
2823 topology_v1_emit_modal_direct_column(wb, "protocol", "Protocol", "protocol", "badge");
2824 topology_v1_emit_modal_direct_column(wb, "state", "State", "state", "badge");
2825 topology_v1_emit_modal_direct_column(wb, "sockets", "Sockets", "socket_count", "number");
2826 topology_v1_emit_modal_direct_column_with_visibility(
2827 wb, "retransmissions", "Retransmissions", "retransmissions", "number", "expanded");
2828 topology_v1_emit_modal_direct_column(wb, "rtt", "RTT max", "rtt_ms_max", "number");
2829 topology_v1_emit_modal_direct_column_with_visibility(
2830 wb, "recv_rtt", "Receiver RTT max", "recv_rtt_ms_max", "number", "expanded");
2831 }
2832 buffer_json_array_close(wb);
2833 }
2834 buffer_json_object_close(wb);
2835 }
2836
2837 static void topology_v1_emit_modal_identification_field(BUFFER *wb, const char *key, const char *label) {
2838 buffer_json_add_array_item_object(wb);
2839 {
2840 buffer_json_member_add_string(wb, "key", key);
2841 buffer_json_member_add_string(wb, "label", label);
2842 buffer_json_member_add_uint64(wb, "max_values", 1);
2843 }
2844 buffer_json_object_close(wb);
2845 }
2846
2847 static void topology_v1_emit_modal_label_identification(BUFFER *wb, const char *actor_type) {
2848 buffer_json_member_add_object(wb, "identification");
2849 {
2850 buffer_json_member_add_boolean(wb, "enabled", true);
2851 buffer_json_member_add_array(wb, "fields");
2852 {
2853 if(strcmp(actor_type, "self") == 0) {
2854 topology_v1_emit_modal_identification_field(wb, "hostname", "Hostname");
2855 topology_v1_emit_modal_identification_field(wb, "socket_count", "Sockets");
2856 topology_v1_emit_modal_identification_field(wb, "local_ip_count", "Local IPs");
2857 }
2858 else if(strcmp(actor_type, "process") == 0) {
2859 topology_v1_emit_modal_identification_field(wb, "process", "Process");
2860 topology_v1_emit_modal_identification_field(wb, "username", "User");
2861 topology_v1_emit_modal_identification_field(wb, "namespace_type", "Namespace");
2862 topology_v1_emit_modal_identification_field(wb, "local_ip", "Local IP");
2863 topology_v1_emit_modal_identification_field(wb, "cmdline", "Command");
2864 topology_v1_emit_modal_identification_field(wb, "socket_count", "Sockets");
2865 }
2866 else if(strcmp(actor_type, "endpoint") == 0) {
2867 topology_v1_emit_modal_identification_field(wb, "ip", "IP");
2868 topology_v1_emit_modal_identification_field(wb, "address_space", "Address Space");
2869 topology_v1_emit_modal_identification_field(wb, "socket_count", "Sockets");
2870 }
2871 }
2872 buffer_json_array_close(wb);
2873 }
2874 buffer_json_object_close(wb);
2875 }
2876
2877 static void topology_v1_emit_actor_type(
2878 BUFFER *wb,
2879 const char *id,
2880 const char *merge_a,
2881 const char *merge_b,
2882 const char *scope,
2883 const char *label,
2884 const char *color_slot,
2885 const char *icon,
2886 const char *role,
2887 bool border,
2888 const char *size_mode,
2889 const char *size_metric_column,
2890 const char *size_scale,
2891 const char *layout_repulsion,
2892 bool show_port_bullets,
2893 const char *port_table,
2894 bool detailed,
2895 const char *label_column_a,
2896 const char *label_column_b) {
2897 bool is_self = strcmp(id, "self") == 0;
2898
2899 buffer_json_member_add_object(wb, id);
2900 {
2901 buffer_json_member_add_string(wb, "layer", NETWORK_TOPOLOGY_LAYER);
2902 buffer_json_member_add_array(wb, "identity");
2903 buffer_json_add_array_item_string(wb, "id");
2904 buffer_json_array_close(wb);
2905 buffer_json_member_add_array(wb, "merge_identity");
2906 if(merge_a)
2907 buffer_json_add_array_item_string(wb, merge_a);
2908 if(merge_b)
2909 buffer_json_add_array_item_string(wb, merge_b);
2910 buffer_json_array_close(wb);
2911 buffer_json_member_add_array(wb, "aggregation_scopes");
2912 buffer_json_add_array_item_string(wb, scope);
2913 buffer_json_array_close(wb);
2914 buffer_json_member_add_object(wb, "search");
2915 {
2916 buffer_json_member_add_array(wb, "columns");
2917 if(strcmp(id, "self") == 0) {
2918 buffer_json_add_array_item_string(wb, "display_name");
2919 buffer_json_add_array_item_string(wb, "hostname");
2920 }
2921 else if(strcmp(id, "process") == 0) {
2922 buffer_json_add_array_item_string(wb, "display_name");
2923 buffer_json_add_array_item_string(wb, "process");
2924 buffer_json_add_array_item_string(wb, "username");
2925 buffer_json_add_array_item_string(wb, "cmdline");
2926 buffer_json_add_array_item_string(wb, "local_ip");
2927 }
2928 else if(strcmp(id, "endpoint") == 0) {
2929 buffer_json_add_array_item_string(wb, "display_name");
2930 buffer_json_add_array_item_string(wb, "ip");
2931 }
2932 buffer_json_array_close(wb);
2933 }
2934 buffer_json_object_close(wb);
2935 buffer_json_member_add_object(wb, "presentation");
2936 {
2937 buffer_json_member_add_string(wb, "label", label);
2938 buffer_json_member_add_string(wb, "role", role);
2939 buffer_json_member_add_string(wb, "icon", icon);
2940 buffer_json_member_add_string(wb, "color_slot", color_slot);
2941 buffer_json_member_add_object(wb, "border");
2942 {
2943 buffer_json_member_add_boolean(wb, "enabled", border);
2944 }
2945 buffer_json_object_close(wb);
2946 buffer_json_member_add_object(wb, "size");
2947 {
2948 buffer_json_member_add_string(wb, "mode", size_mode ? size_mode : "fixed");
2949 if(size_metric_column)
2950 buffer_json_member_add_string(wb, "metric_column", size_metric_column);
2951 if(size_scale)
2952 buffer_json_member_add_string(wb, "scale", size_scale);
2953 }
2954 buffer_json_object_close(wb);
2955 if(layout_repulsion) {
2956 buffer_json_member_add_object(wb, "layout");
2957 {
2958 buffer_json_member_add_string(wb, "repulsion", layout_repulsion);
2959 }
2960 buffer_json_object_close(wb);
2961 }
2962 buffer_json_member_add_object(wb, "label_policy");
2963 {
2964 buffer_json_member_add_array(wb, "columns");
2965 if(label_column_a)
2966 buffer_json_add_array_item_string(wb, label_column_a);
2967 if(label_column_b)
2968 buffer_json_add_array_item_string(wb, label_column_b);
2969 buffer_json_array_close(wb);
2970 buffer_json_member_add_string(wb, "fallback", "type_label");
2971 buffer_json_member_add_uint64(wb, "max_length", 80);
2972 buffer_json_member_add_string(wb, "array", "reject");
2973 }
2974 buffer_json_object_close(wb);
2975 buffer_json_member_add_object(wb, "ports");
2976 {
2977 buffer_json_member_add_boolean(wb, "show_bullets", show_port_bullets);
2978 if(show_port_bullets) {
2979 buffer_json_member_add_array(wb, "sources");
2980 {
2981 buffer_json_add_array_item_object(wb);
2982 buffer_json_member_add_string(wb, "source", "actor_table");
2983 buffer_json_member_add_string(wb, "table", port_table ? port_table : "socket_ports");
2984 buffer_json_member_add_string(wb, "actor_column", "actor");
2985 buffer_json_member_add_string(wb, "name_column", "port");
2986 buffer_json_member_add_string(wb, "value_column", "socket_count");
2987 buffer_json_member_add_string(wb, "default_type", "topology");
2988 buffer_json_object_close(wb);
2989 }
2990 buffer_json_array_close(wb);
2991 }
2992 }
2993 buffer_json_object_close(wb);
2994 buffer_json_member_add_object(wb, "modal");
2995 {
2996 buffer_json_member_add_object(wb, "labels");
2997 {
2998 buffer_json_member_add_string(wb, "table", "actor_labels");
2999 topology_v1_emit_modal_label_identification(wb, id);
3000 }
3001 buffer_json_object_close(wb);
3002 buffer_json_member_add_object(wb, "mini_topology");
3003 {
3004 buffer_json_member_add_uint64(wb, "depth", 1);
3005 buffer_json_member_add_array(wb, "exclude_link_types");
3006 if(!is_self)
3007 buffer_json_add_array_item_string(wb, "ownership");
3008 buffer_json_array_close(wb);
3009 }
3010 buffer_json_object_close(wb);
3011 buffer_json_member_add_array(wb, "sections");
3012 {
3013 if(is_self) {
3014 buffer_json_add_array_item_object(wb);
3015 {
3016 buffer_json_member_add_string(wb, "id", "processes");
3017 buffer_json_member_add_string(wb, "label", "Processes");
3018 buffer_json_member_add_uint64(wb, "order", 1);
3019 buffer_json_member_add_object(wb, "source");
3020 {
3021 buffer_json_member_add_string(wb, "kind", "links");
3022 }
3023 buffer_json_object_close(wb);
3024 buffer_json_member_add_object(wb, "owner_filter");
3025 {
3026 buffer_json_member_add_string(wb, "mode", "incident_link");
3027 buffer_json_member_add_string(wb, "src_actor_column", "src_actor");
3028 buffer_json_member_add_string(wb, "dst_actor_column", "dst_actor");
3029 }
3030 buffer_json_object_close(wb);
3031 buffer_json_member_add_array(wb, "row_filters");
3032 {
3033 buffer_json_add_array_item_object(wb);
3034 {
3035 buffer_json_member_add_string(wb, "column", "type");
3036 buffer_json_member_add_string(wb, "op", "eq");
3037 buffer_json_member_add_string(wb, "value", "ownership");
3038 }
3039 buffer_json_object_close(wb);
3040 }
3041 buffer_json_array_close(wb);
3042 buffer_json_member_add_array(wb, "columns");
3043 {
3044 topology_v1_emit_modal_opposite_actor_column_labeled(wb, "process", "Process");
3045 topology_v1_emit_modal_direct_column(wb, "sockets", "Sockets", "socket_count", "number");
3046 topology_v1_emit_modal_direct_column_with_visibility(
3047 wb, "evidence", "Evidence", "evidence_count", "number", "expanded");
3048 }
3049 buffer_json_array_close(wb);
3050 }
3051 buffer_json_object_close(wb);
3052 }
3053 else {
3054 topology_v1_emit_network_connection_modal_section(
3055 wb,
3056 detailed ? "dependency_sockets" : "dependencies",
3057 "Dependencies",
3058 "src_actor",
3059 "Service",
3060 "Server",
3061 "server_ip",
3062 "server_port",
3063 detailed,
3064 1);
3065 topology_v1_emit_network_connection_modal_section(
3066 wb,
3067 detailed ? "dependant_sockets" : "dependants",
3068 "Dependants",
3069 "dst_actor",
3070 "Client",
3071 "Client",
3072 "client_ip",
3073 "client_port",
3074 detailed,
3075 2);
3076 }
3077 }
3078 buffer_json_array_close(wb);
3079 }
3080 buffer_json_object_close(wb);
3081
3082 }
3083 buffer_json_object_close(wb);
3084
3085 }
3086 buffer_json_object_close(wb);
3087 }
3088
3089 static void topology_v1_emit_link_type(
3090 BUFFER *wb,
3091 const char *id,
3092 const char *orientation,
3093 const char *direction_role,
3094 const char *semantic_role,
3095 const char *evidence_type,
3096 const char *label,
3097 const char *color_slot,
3098 const char *line_style,
3099 const char *width,
3100 const char *arrow,
3101 const char *opacity,
3102 const char *scale_key,
3103 const char *value_column,
3104 const char *layout_strength,
3105 const char *layout_distance) {
3106 buffer_json_member_add_object(wb, id);
3107 {
3108 buffer_json_member_add_string(wb, "orientation", orientation);
3109 buffer_json_member_add_string(wb, "direction_role", direction_role);
3110 if(semantic_role)
3111 buffer_json_member_add_string(wb, "semantic_role", semantic_role);
3112 buffer_json_member_add_object(wb, "aggregation");
3113 {
3114 buffer_json_member_add_string(wb, "direction", "preserve");
3115 buffer_json_member_add_string(wb, "evidence", evidence_type ? "append" : "count");
3116 buffer_json_member_add_object(wb, "metrics");
3117 {
3118 buffer_json_member_add_string(wb, "evidence_count", "sum");
3119 buffer_json_member_add_string(wb, "socket_count", "sum");
3120 if(evidence_type) {
3121 buffer_json_member_add_string(wb, "retransmissions", "sum");
3122 buffer_json_member_add_string(wb, "rtt_ms_max", "max");
3123 buffer_json_member_add_string(wb, "recv_rtt_ms_max", "max");
3124 }
3125 }
3126 buffer_json_object_close(wb);
3127 }
3128 buffer_json_object_close(wb);
3129 if(evidence_type) {
3130 buffer_json_member_add_array(wb, "evidence_types");
3131 buffer_json_add_array_item_string(wb, evidence_type);
3132 buffer_json_array_close(wb);
3133 }
3134 buffer_json_member_add_object(wb, "presentation");
3135 {
3136 buffer_json_member_add_string(wb, "label", label);
3137 buffer_json_member_add_string(wb, "color_slot", color_slot);
3138 if(opacity)
3139 buffer_json_member_add_string(wb, "opacity", opacity);
3140 buffer_json_member_add_string(wb, "line_style", line_style);
3141 buffer_json_member_add_string(wb, "width", width);
3142 buffer_json_member_add_string(wb, "curve", "auto");
3143 buffer_json_member_add_string(wb, "arrow", arrow);
3144 if(layout_strength || layout_distance) {
3145 buffer_json_member_add_object(wb, "layout");
3146 {
3147 if(layout_strength)
3148 buffer_json_member_add_string(wb, "strength", layout_strength);
3149 if(layout_distance)
3150 buffer_json_member_add_string(wb, "distance", layout_distance);
3151 }
3152 buffer_json_object_close(wb);
3153 }
3154 if(scale_key && value_column) {
3155 buffer_json_member_add_object(wb, "variable");
3156 {
3157 buffer_json_member_add_string(wb, "channel", "width");
3158 buffer_json_member_add_string(wb, "scale_key", scale_key);
3159 buffer_json_member_add_string(wb, "value_column", value_column);
3160 buffer_json_member_add_string(wb, "min", width);
3161 buffer_json_member_add_string(wb, "max", "emphasis");
3162 }
3163 buffer_json_object_close(wb);
3164 }
3165 }
3166 buffer_json_object_close(wb);
3167
3168 }
3169 buffer_json_object_close(wb);
3170 }
3171
3172 static void topology_v1_emit_type_registry(BUFFER *wb, bool detailed __maybe_unused) {
3173 buffer_json_member_add_object(wb, "types");
3174 {
3175 buffer_json_member_add_object(wb, "actor_types");
3176 {
3177 topology_v1_emit_actor_type(
3178 wb, "self", "machine_guid", "hostname", "node",
3179 "This host", "self", "self", "actor", true, "fixed", NULL, "emphasized", "strongest", false, NULL,
3180 detailed,
3181 "display_name", "hostname");
3182 topology_v1_emit_actor_type(
3183 wb, "process", "machine_guid", "process", "process_name",
3184 "Process", "primary", "process", "actor", true, "metric", "socket_count", "normal", "normal", true, "socket_ports",
3185 detailed,
3186 "display_name", "process");
3187 topology_v1_emit_actor_type(
3188 wb, "endpoint", "ip", "address_space", "endpoint",
3189 "Correlation endpoint", "derived", "remote-endpoint", "endpoint", true, "fixed", NULL, "compact", "weaker", false, NULL,
3190 detailed,
3191 "display_name", "ip");
3192 }
3193 buffer_json_object_close(wb);
3194
3195 buffer_json_member_add_object(wb, "link_types");
3196 {
3197 topology_v1_emit_link_type(
3198 wb, "socket", "directed", "dependency", "traffic", "socket",
3199 "Local socket", "gray", "solid", "thin", "forward", NULL,
3200 "sockets", "socket_count", "normal", "normal");
3201 topology_v1_emit_link_type(
3202 wb, "endpoint_socket", "directed", "dependency", "traffic", "socket",
3203 "Endpoint connection", "primary", "solid", "thin", "forward", NULL,
3204 NULL, NULL, "normal", "normal");
3205 topology_v1_emit_link_type(
3206 wb, "correlated_socket", "directed", "dependency", "traffic", "socket",
3207 "Correlated socket", "primary", "solid", "thin", "forward", NULL,
3208 "sockets", "socket_count", "normal", "farthest");
3209 topology_v1_emit_link_type(
3210 wb, "ownership", "hierarchical", "ownership", "ownership", NULL,
3211 "Process ownership", "dim", "dotted", "thin", "none", "faded",
3212 NULL, NULL, "normal", "normal");
3213 }
3214 buffer_json_object_close(wb);
3215
3216 buffer_json_member_add_object(wb, "port_types");
3217 {
3218 buffer_json_member_add_object(wb, "topology");
3219 {
3220 buffer_json_member_add_object(wb, "presentation");
3221 {
3222 buffer_json_member_add_string(wb, "label", "Socket");
3223 buffer_json_member_add_string(wb, "color_slot", "primary");
3224 buffer_json_member_add_string(wb, "opacity", "normal");
3225 }
3226 buffer_json_object_close(wb);
3227 }
3228 buffer_json_object_close(wb);
3229 }
3230 buffer_json_object_close(wb);
3231
3232 buffer_json_member_add_object(wb, "evidence_types");
3233 {
3234 buffer_json_member_add_object(wb, "socket");
3235 {
3236 buffer_json_member_add_string(wb, "link_type", "socket");
3237 buffer_json_member_add_string(wb, "role", "relationship_evidence");
3238 buffer_json_member_add_array(wb, "columns");
3239 topology_v1_emit_socket_evidence_columns(wb);
3240 buffer_json_array_close(wb);
3241 buffer_json_member_add_array(wb, "match_columns");
3242 buffer_json_add_array_item_string(wb, "client_ip");
3243 buffer_json_add_array_item_string(wb, "client_port");
3244 buffer_json_add_array_item_string(wb, "server_ip");
3245 buffer_json_add_array_item_string(wb, "server_port");
3246 buffer_json_add_array_item_string(wb, "protocol");
3247 buffer_json_array_close(wb);
3248 }
3249 buffer_json_object_close(wb);
3250 }
3251 buffer_json_object_close(wb);
3252
3253 buffer_json_member_add_object(wb, "table_types");
3254 {
3255 buffer_json_member_add_object(wb, "socket_ports");
3256 {
3257 buffer_json_member_add_string(wb, "role", "actor_inventory");
3258 buffer_json_member_add_string(wb, "owner", "actor");
3259 buffer_json_member_add_string(wb, "aggregation", "sum");
3260 buffer_json_member_add_array(wb, "columns");
3261 topology_v1_emit_socket_port_columns(wb);
3262 buffer_json_array_close(wb);
3263 buffer_json_member_add_object(wb, "presentation");
3264 {
3265 buffer_json_member_add_string(wb, "label", "Ports");
3266 buffer_json_member_add_uint64(wb, "order", 1);
3267 buffer_json_member_add_array(wb, "columns");
3268 {
3269 topology_v1_emit_modal_direct_column(wb, "port", "Port", "port", "number");
3270 topology_v1_emit_modal_direct_column(wb, "protocol", "Protocol", "protocol", "badge");
3271 topology_v1_emit_modal_direct_column(wb, "sockets", "Sockets", "socket_count", "number");
3272 }
3273 buffer_json_array_close(wb);
3274 }
3275 buffer_json_object_close(wb);
3276 }
3277 buffer_json_object_close(wb);
3278
3279 buffer_json_member_add_object(wb, "connections");
3280 {
3281 buffer_json_member_add_string(wb, "role", "relationship_summary");
3282 buffer_json_member_add_string(wb, "owner", "link");
3283 buffer_json_member_add_string(wb, "aggregation", "merge_metrics");
3284 buffer_json_member_add_array(wb, "columns");
3285 topology_v1_emit_connection_columns(wb);
3286 buffer_json_array_close(wb);
3287 }
3288 buffer_json_object_close(wb);
3289
3290 buffer_json_member_add_object(wb, "actor_labels");
3291 {
3292 buffer_json_member_add_string(wb, "role", "actor_inventory");
3293 buffer_json_member_add_string(wb, "owner", "actor");
3294 buffer_json_member_add_string(wb, "aggregation", "set");
3295 buffer_json_member_add_array(wb, "columns");
3296 topology_v1_emit_actor_label_columns(wb);
3297 buffer_json_array_close(wb);
3298 buffer_json_member_add_object(wb, "presentation");
3299 {
3300 buffer_json_member_add_string(wb, "label", "Labels");
3301 buffer_json_member_add_uint64(wb, "order", 0);
3302 buffer_json_member_add_array(wb, "columns");
3303 {
3304 topology_v1_emit_modal_direct_column(wb, "key", "Label", "key", "text");
3305 topology_v1_emit_modal_direct_column(wb, "value", "Value", "value", "text");
3306 topology_v1_emit_modal_direct_column(wb, "source", "Source", "source", "badge");
3307 topology_v1_emit_modal_direct_column(wb, "kind", "Kind", "kind", "badge");
3308 }
3309 buffer_json_array_close(wb);
3310 }
3311 buffer_json_object_close(wb);
3312 }
3313 buffer_json_object_close(wb);
3314 }
3315 buffer_json_object_close(wb);
3316
3317 buffer_json_member_add_object(wb, "aggregation_scopes");
3318 {
3319 buffer_json_member_add_object(wb, "node");
3320 {
3321 buffer_json_member_add_array(wb, "columns");
3322 buffer_json_add_array_item_string(wb, "machine_guid");
3323 buffer_json_add_array_item_string(wb, "hostname");
3324 buffer_json_array_close(wb);
3325 buffer_json_member_add_string(wb, "evidence_policy", "preserve");
3326 }
3327 buffer_json_object_close(wb);
3328
3329 buffer_json_member_add_object(wb, "process_name");
3330 {
3331 buffer_json_member_add_array(wb, "columns");
3332 buffer_json_add_array_item_string(wb, "process");
3333 buffer_json_array_close(wb);
3334 buffer_json_member_add_string(wb, "evidence_policy", "preserve");
3335 }
3336 buffer_json_object_close(wb);
3337
3338 buffer_json_member_add_object(wb, "pid");
3339 {
3340 buffer_json_member_add_array(wb, "columns");
3341 buffer_json_add_array_item_string(wb, "pid");
3342 buffer_json_add_array_item_string(wb, "net_ns_inode");
3343 buffer_json_array_close(wb);
3344 buffer_json_member_add_string(wb, "evidence_policy", "preserve");
3345 }
3346 buffer_json_object_close(wb);
3347
3348 }
3349 buffer_json_object_close(wb);
3350 }
3351 buffer_json_object_close(wb);
3352 }
3353
3354 static void topology_v1_emit_presentation(BUFFER *wb) {
3355 buffer_json_member_add_object(wb, "presentation");
3356 {
3357 buffer_json_member_add_string(wb, "profile_version", "network-connections.v1");
3358 buffer_json_member_add_object(wb, "selection");
3359 {
3360 buffer_json_member_add_object(wb, "actor_click");
3361 {
3362 buffer_json_member_add_string(wb, "mode", "highlight_connections");
3363 }
3364 buffer_json_object_close(wb);
3365 }
3366 buffer_json_object_close(wb);
3367
3368 buffer_json_member_add_object(wb, "legend");
3369 {
3370 buffer_json_member_add_array(wb, "actors");
3371 {
3372 buffer_json_add_array_item_object(wb);
3373 buffer_json_member_add_string(wb, "type", "self");
3374 buffer_json_member_add_string(wb, "label", "This host");
3375 buffer_json_object_close(wb);
3376
3377 buffer_json_add_array_item_object(wb);
3378 buffer_json_member_add_string(wb, "type", "process");
3379 buffer_json_member_add_string(wb, "label", "Process");
3380 buffer_json_object_close(wb);
3381
3382 buffer_json_add_array_item_object(wb);
3383 buffer_json_member_add_string(wb, "type", "endpoint");
3384 buffer_json_member_add_string(wb, "label", "Correlation endpoint");
3385 buffer_json_object_close(wb);
3386 }
3387 buffer_json_array_close(wb);
3388
3389 buffer_json_member_add_array(wb, "links");
3390 {
3391 buffer_json_add_array_item_object(wb);
3392 buffer_json_member_add_string(wb, "type", "ownership");
3393 buffer_json_member_add_string(wb, "label", "Process ownership");
3394 buffer_json_object_close(wb);
3395
3396 buffer_json_add_array_item_object(wb);
3397 buffer_json_member_add_string(wb, "type", "socket");
3398 buffer_json_member_add_string(wb, "label", "Local socket");
3399 buffer_json_object_close(wb);
3400
3401 buffer_json_add_array_item_object(wb);
3402 buffer_json_member_add_string(wb, "type", "endpoint_socket");
3403 buffer_json_member_add_string(wb, "label", "Endpoint connection");
3404 buffer_json_object_close(wb);
3405
3406 buffer_json_add_array_item_object(wb);
3407 buffer_json_member_add_string(wb, "type", "correlated_socket");
3408 buffer_json_member_add_string(wb, "label", "Correlated socket");
3409 buffer_json_object_close(wb);
3410 }
3411 buffer_json_array_close(wb);
3412
3413 buffer_json_member_add_array(wb, "ports");
3414 {
3415 buffer_json_add_array_item_object(wb);
3416 buffer_json_member_add_string(wb, "type", "topology");
3417 buffer_json_member_add_string(wb, "label", "Socket");
3418 buffer_json_object_close(wb);
3419 }
3420 buffer_json_array_close(wb);
3421 }
3422 buffer_json_object_close(wb);
3423
3424 buffer_json_member_add_array(wb, "port_fields");
3425 {
3426 buffer_json_add_array_item_object(wb);
3427 buffer_json_member_add_string(wb, "key", "type");
3428 buffer_json_member_add_string(wb, "label", "Type");
3429 buffer_json_object_close(wb);
3430
3431 buffer_json_add_array_item_object(wb);
3432 buffer_json_member_add_string(wb, "key", "socket_count");
3433 buffer_json_member_add_string(wb, "label", "Sockets");
3434 buffer_json_object_close(wb);
3435 }
3436 buffer_json_array_close(wb);
3437
3438 buffer_json_member_add_object(wb, "scale_keys");
3439 {
3440 buffer_json_member_add_object(wb, "sockets");
3441 {
3442 buffer_json_member_add_string(wb, "label", "Sockets");
3443 buffer_json_member_add_string(wb, "unit", "count");
3444 }
3445 buffer_json_object_close(wb);
3446
3447 }
3448 buffer_json_object_close(wb);
3449 }
3450 buffer_json_object_close(wb);
3451 }
3452
3453 static void topology_v1_emit_actor_table(BUFFER *wb, NV_TOPOLOGY_V1_PAYLOAD *payload) {
3454 buffer_json_member_add_object(wb, "actors");
3455 {
3456 buffer_json_member_add_uint64(wb, "rows", payload->actors_used);
3457 buffer_json_member_add_array(wb, "columns");
3458 topology_v1_emit_actor_columns(wb);
3459 buffer_json_array_close(wb);
3460 buffer_json_member_add_array(wb, "values");
3461
3462 #define NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(member) do { \
3463 topology_v1_values_start(wb); \
3464 for(size_t i = 0; i < payload->actors_used; i++) \
3465 buffer_json_add_array_item_string(wb, payload->actors[i].member[0] ? payload->actors[i].member : NULL); \
3466 topology_v1_values_end(wb); \
3467 } while(0)
3468 #define NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(member, has_member) do { \
3469 topology_v1_values_start(wb); \
3470 for(size_t i = 0; i < payload->actors_used; i++) \
3471 topology_v1_add_nullable_uint(wb, payload->actors[i].has_member, payload->actors[i].member); \
3472 topology_v1_values_end(wb); \
3473 } while(0)
3474
3475 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(id);
3476 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(type);
3477 topology_v1_const_string(wb, NETWORK_TOPOLOGY_LAYER);
3478 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(machine_guid);
3479 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(hostname);
3480 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(process);
3481 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(username);
3482 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(cmdline);
3483 NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(pid, has_pid);
3484 NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(ppid, has_ppid);
3485 NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(uid, has_uid);
3486 NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(net_ns_inode, has_net_ns_inode);
3487 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(namespace_type);
3488 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(local_ip);
3489 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(local_address_space);
3490 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(ip);
3491 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(address_space);
3492 NV_TOPOLOGY_V1_ACTOR_STRING_VALUES(display_name);
3493
3494 topology_v1_values_start(wb);
3495 for(size_t i = 0; i < payload->actors_used; i++)
3496 buffer_json_add_array_item_uint64(wb, payload->actors[i].sockets);
3497 topology_v1_values_end(wb);
3498
3499 NV_TOPOLOGY_V1_ACTOR_UINT_VALUES(local_ip_count, has_local_ip_count);
3500
3501 #undef NV_TOPOLOGY_V1_ACTOR_STRING_VALUES
3502 #undef NV_TOPOLOGY_V1_ACTOR_UINT_VALUES
3503
3504 buffer_json_array_close(wb);
3505 }
3506 buffer_json_object_close(wb);
3507 }
3508
3509 static void topology_v1_emit_link_table(BUFFER *wb, NV_TOPOLOGY_V1_PAYLOAD *payload) {
3510 buffer_json_member_add_object(wb, "links");
3511 {
3512 buffer_json_member_add_uint64(wb, "rows", payload->links_used);
3513 buffer_json_member_add_array(wb, "columns");
3514 topology_v1_emit_link_columns(wb);
3515 buffer_json_array_close(wb);
3516 buffer_json_member_add_array(wb, "values");
3517
3518 #define NV_TOPOLOGY_V1_LINK_UINT_VALUES(member) do { \
3519 topology_v1_values_start(wb); \
3520 for(size_t i = 0; i < payload->links_used; i++) \
3521 buffer_json_add_array_item_uint64(wb, payload->links[i].member); \
3522 topology_v1_values_end(wb); \
3523 } while(0)
3524 #define NV_TOPOLOGY_V1_LINK_STRING_VALUES(member) do { \
3525 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3526 topology_v1_string_column_init(&column, payload->links_used); \
3527 for(size_t i = 0; i < payload->links_used; i++) \
3528 topology_v1_string_column_add(&column, payload->links[i].member); \
3529 topology_v1_emit_auto_string_column(wb, &column); \
3530 topology_v1_string_column_free(&column); \
3531 } while(0)
3532
3533 NV_TOPOLOGY_V1_LINK_UINT_VALUES(src_actor);
3534 NV_TOPOLOGY_V1_LINK_UINT_VALUES(dst_actor);
3535 NV_TOPOLOGY_V1_LINK_STRING_VALUES(type);
3536 NV_TOPOLOGY_V1_LINK_STRING_VALUES(protocol);
3537 NV_TOPOLOGY_V1_LINK_STRING_VALUES(state);
3538 NV_TOPOLOGY_V1_LINK_UINT_VALUES(evidence_count);
3539 NV_TOPOLOGY_V1_LINK_UINT_VALUES(socket_count);
3540 NV_TOPOLOGY_V1_LINK_UINT_VALUES(retransmissions);
3541
3542 topology_v1_values_start(wb);
3543 for(size_t i = 0; i < payload->links_used; i++)
3544 buffer_json_add_array_item_double(wb, (double)payload->links[i].max_rtt_usec / (double)USEC_PER_MS);
3545 topology_v1_values_end(wb);
3546
3547 topology_v1_values_start(wb);
3548 for(size_t i = 0; i < payload->links_used; i++)
3549 buffer_json_add_array_item_double(wb, (double)payload->links[i].max_rcv_rtt_usec / (double)USEC_PER_MS);
3550 topology_v1_values_end(wb);
3551
3552 #undef NV_TOPOLOGY_V1_LINK_UINT_VALUES
3553 #undef NV_TOPOLOGY_V1_LINK_STRING_VALUES
3554
3555 buffer_json_array_close(wb);
3556 }
3557 buffer_json_object_close(wb);
3558 }
3559
3560 static void topology_v1_emit_socket_port_table(BUFFER *wb, NV_TOPOLOGY_V1_PAYLOAD *payload) {
3561 buffer_json_member_add_object(wb, "tables");
3562 {
3563 buffer_json_member_add_object(wb, "actor");
3564 {
3565 buffer_json_member_add_object(wb, "socket_ports");
3566 {
3567 buffer_json_member_add_string(wb, "type", "socket_ports");
3568 buffer_json_member_add_object(wb, "table");
3569 {
3570 buffer_json_member_add_uint64(wb, "rows", payload->ports_used);
3571 buffer_json_member_add_array(wb, "columns");
3572 topology_v1_emit_socket_port_columns(wb);
3573 buffer_json_array_close(wb);
3574 buffer_json_member_add_array(wb, "values");
3575
3576 #define NV_TOPOLOGY_V1_PORT_UINT_VALUES(member) do { \
3577 topology_v1_values_start(wb); \
3578 for(size_t i = 0; i < payload->ports_used; i++) \
3579 buffer_json_add_array_item_uint64(wb, payload->ports[i].member); \
3580 topology_v1_values_end(wb); \
3581 } while(0)
3582 #define NV_TOPOLOGY_V1_PORT_STRING_VALUES(member) do { \
3583 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3584 topology_v1_string_column_init(&column, payload->ports_used); \
3585 for(size_t i = 0; i < payload->ports_used; i++) \
3586 topology_v1_string_column_add(&column, payload->ports[i].member); \
3587 topology_v1_emit_auto_string_column(wb, &column); \
3588 topology_v1_string_column_free(&column); \
3589 } while(0)
3590
3591 NV_TOPOLOGY_V1_PORT_UINT_VALUES(actor);
3592 NV_TOPOLOGY_V1_PORT_UINT_VALUES(port);
3593 NV_TOPOLOGY_V1_PORT_STRING_VALUES(protocol);
3594 NV_TOPOLOGY_V1_PORT_UINT_VALUES(socket_count);
3595
3596 #undef NV_TOPOLOGY_V1_PORT_UINT_VALUES
3597 #undef NV_TOPOLOGY_V1_PORT_STRING_VALUES
3598
3599 buffer_json_array_close(wb);
3600 }
3601 buffer_json_object_close(wb);
3602 }
3603 buffer_json_object_close(wb);
3604
3605 buffer_json_member_add_object(wb, "actor_labels");
3606 {
3607 buffer_json_member_add_string(wb, "type", "actor_labels");
3608 buffer_json_member_add_object(wb, "table");
3609 {
3610 buffer_json_member_add_uint64(wb, "rows", payload->labels_used);
3611 buffer_json_member_add_array(wb, "columns");
3612 topology_v1_emit_actor_label_columns(wb);
3613 buffer_json_array_close(wb);
3614 buffer_json_member_add_array(wb, "values");
3615
3616 #define NV_TOPOLOGY_V1_LABEL_UINT_VALUES(member) do { \
3617 topology_v1_values_start(wb); \
3618 for(size_t i = 0; i < payload->labels_used; i++) \
3619 buffer_json_add_array_item_uint64(wb, payload->labels[i].member); \
3620 topology_v1_values_end(wb); \
3621 } while(0)
3622 #define NV_TOPOLOGY_V1_LABEL_STRING_VALUES(member) do { \
3623 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3624 topology_v1_string_column_init(&column, payload->labels_used); \
3625 for(size_t i = 0; i < payload->labels_used; i++) \
3626 topology_v1_string_column_add(&column, payload->labels[i].member); \
3627 topology_v1_emit_auto_string_column(wb, &column); \
3628 topology_v1_string_column_free(&column); \
3629 } while(0)
3630
3631 NV_TOPOLOGY_V1_LABEL_UINT_VALUES(actor);
3632 NV_TOPOLOGY_V1_LABEL_STRING_VALUES(key);
3633 NV_TOPOLOGY_V1_LABEL_STRING_VALUES(value);
3634 NV_TOPOLOGY_V1_LABEL_STRING_VALUES(source);
3635 NV_TOPOLOGY_V1_LABEL_STRING_VALUES(kind);
3636
3637 topology_v1_values_start(wb);
3638 for(size_t i = 0; i < payload->labels_used; i++)
3639 topology_v1_add_nullable_uint(wb, payload->labels[i].has_value_index, payload->labels[i].value_index);
3640 topology_v1_values_end(wb);
3641
3642 #undef NV_TOPOLOGY_V1_LABEL_UINT_VALUES
3643 #undef NV_TOPOLOGY_V1_LABEL_STRING_VALUES
3644
3645 buffer_json_array_close(wb);
3646 }
3647 buffer_json_object_close(wb);
3648 }
3649 buffer_json_object_close(wb);
3650 }
3651 buffer_json_object_close(wb);
3652
3653 if(payload->connections_used) {
3654 buffer_json_member_add_object(wb, "relationship");
3655 {
3656 buffer_json_member_add_object(wb, "connections");
3657 {
3658 buffer_json_member_add_string(wb, "type", "connections");
3659 buffer_json_member_add_object(wb, "table");
3660 {
3661 buffer_json_member_add_uint64(wb, "rows", payload->connections_used);
3662 buffer_json_member_add_array(wb, "columns");
3663 topology_v1_emit_connection_columns(wb);
3664 buffer_json_array_close(wb);
3665 buffer_json_member_add_array(wb, "values");
3666
3667 #define NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES(member) do { \
3668 topology_v1_values_start(wb); \
3669 for(size_t i = 0; i < payload->connections_used; i++) \
3670 buffer_json_add_array_item_uint64(wb, payload->connections[i].member); \
3671 topology_v1_values_end(wb); \
3672 } while(0)
3673 #define NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES(member) do { \
3674 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3675 topology_v1_string_column_init(&column, payload->connections_used); \
3676 for(size_t i = 0; i < payload->connections_used; i++) \
3677 topology_v1_string_column_add(&column, payload->connections[i].member); \
3678 topology_v1_emit_auto_string_column(wb, &column); \
3679 topology_v1_string_column_free(&column); \
3680 } while(0)
3681
3682 NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES(src_actor);
3683 NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES(dst_actor);
3684 NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES(client_ip);
3685 NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES(server_ip);
3686 NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES(protocol);
3687 NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES(state);
3688 NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES(socket_count);
3689 NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES(retransmissions);
3690
3691 topology_v1_values_start(wb);
3692 for(size_t i = 0; i < payload->connections_used; i++)
3693 buffer_json_add_array_item_double(wb, (double)payload->connections[i].max_rtt_usec / (double)USEC_PER_MS);
3694 topology_v1_values_end(wb);
3695
3696 topology_v1_values_start(wb);
3697 for(size_t i = 0; i < payload->connections_used; i++)
3698 buffer_json_add_array_item_double(wb, (double)payload->connections[i].max_rcv_rtt_usec / (double)USEC_PER_MS);
3699 topology_v1_values_end(wb);
3700
3701 #undef NV_TOPOLOGY_V1_CONNECTION_UINT_VALUES
3702 #undef NV_TOPOLOGY_V1_CONNECTION_STRING_VALUES
3703
3704 buffer_json_array_close(wb);
3705 }
3706 buffer_json_object_close(wb);
3707 }
3708 buffer_json_object_close(wb);
3709 }
3710 buffer_json_object_close(wb);
3711 }
3712 }
3713 buffer_json_object_close(wb);
3714 }
3715
3716 static void topology_v1_emit_socket_evidence_table(BUFFER *wb, NV_TOPOLOGY_V1_PAYLOAD *payload) {
3717 buffer_json_member_add_object(wb, "evidence");
3718 {
3719 buffer_json_member_add_object(wb, "socket");
3720 {
3721 buffer_json_member_add_string(wb, "type", "socket");
3722 buffer_json_member_add_object(wb, "table");
3723 {
3724 buffer_json_member_add_uint64(wb, "rows", payload->evidence_used);
3725 buffer_json_member_add_array(wb, "columns");
3726 topology_v1_emit_socket_evidence_columns(wb);
3727 buffer_json_array_close(wb);
3728 buffer_json_member_add_array(wb, "values");
3729
3730 #define NV_TOPOLOGY_V1_EVIDENCE_UINT_VALUES(member) do { \
3731 topology_v1_values_start(wb); \
3732 for(size_t i = 0; i < payload->evidence_used; i++) \
3733 buffer_json_add_array_item_uint64(wb, payload->evidence[i].member); \
3734 topology_v1_values_end(wb); \
3735 } while(0)
3736 #define NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(member) do { \
3737 topology_v1_values_start(wb); \
3738 for(size_t i = 0; i < payload->evidence_used; i++) \
3739 buffer_json_add_array_item_uint64(wb, payload->evidence[i].source->member); \
3740 topology_v1_values_end(wb); \
3741 } while(0)
3742 #define NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(member) do { \
3743 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3744 topology_v1_string_column_init(&column, payload->evidence_used); \
3745 for(size_t i = 0; i < payload->evidence_used; i++) \
3746 topology_v1_string_column_add(&column, payload->evidence[i].source->member); \
3747 topology_v1_emit_auto_string_column(wb, &column); \
3748 topology_v1_string_column_free(&column); \
3749 } while(0)
3750
3751 NV_TOPOLOGY_V1_EVIDENCE_UINT_VALUES(link);
3752 NV_TOPOLOGY_V1_EVIDENCE_UINT_VALUES(src_actor);
3753 NV_TOPOLOGY_V1_EVIDENCE_UINT_VALUES(dst_actor);
3754 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(client_ip);
3755 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(client_port);
3756 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(server_ip);
3757 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(server_port);
3758 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(protocol);
3759 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(protocol_family);
3760 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(state);
3761 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(namespace_type);
3762 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(client_address_space);
3763 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(server_address_space);
3764 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(pid);
3765 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(uid);
3766 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(net_ns_inode);
3767 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES(process);
3768 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(sockets);
3769 NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES(retransmissions);
3770
3771 topology_v1_values_start(wb);
3772 for(size_t i = 0; i < payload->evidence_used; i++)
3773 buffer_json_add_array_item_double(wb, (double)payload->evidence[i].source->max_rtt_usec / (double)USEC_PER_MS);
3774 topology_v1_values_end(wb);
3775
3776 topology_v1_values_start(wb);
3777 for(size_t i = 0; i < payload->evidence_used; i++)
3778 buffer_json_add_array_item_double(wb, (double)payload->evidence[i].source->max_rcv_rtt_usec / (double)USEC_PER_MS);
3779 topology_v1_values_end(wb);
3780
3781 #undef NV_TOPOLOGY_V1_EVIDENCE_UINT_VALUES
3782 #undef NV_TOPOLOGY_V1_EVIDENCE_SOURCE_UINT_VALUES
3783 #undef NV_TOPOLOGY_V1_EVIDENCE_SOURCE_STRING_VALUES
3784
3785 buffer_json_array_close(wb);
3786 }
3787 buffer_json_object_close(wb);
3788 }
3789 buffer_json_object_close(wb);
3790 }
3791 buffer_json_object_close(wb);
3792 }
3793
3794 static void topology_v1_emit_correlation_table(
3795 BUFFER *wb,
3796 const NV_TOPOLOGY_V1_CORRELATION_ROW *rows,
3797 size_t rows_used) {
3798 buffer_json_member_add_uint64(wb, "rows", rows_used);
3799
3800 buffer_json_member_add_array(wb, "columns");
3801 topology_v1_emit_column(wb, "actor", "actor_ref", "reference", false, NULL);
3802 topology_v1_emit_column(wb, "rule", "string", "group_key", false, NULL);
3803 topology_v1_emit_column(wb, "protocol", "string", "group_key", false, NULL);
3804 topology_v1_emit_column(wb, "address_space", "string", "group_key", false, NULL);
3805 topology_v1_emit_column(wb, "ip", "ip", "group_key", false, NULL);
3806 topology_v1_emit_column(wb, "port", "uint", "group_key", false, NULL);
3807 buffer_json_array_close(wb);
3808
3809 buffer_json_member_add_array(wb, "values");
3810
3811 topology_v1_values_start(wb);
3812 for(size_t i = 0; i < rows_used; i++)
3813 buffer_json_add_array_item_uint64(wb, rows[i].actor);
3814 topology_v1_values_end(wb);
3815
3816 topology_v1_const_string(wb, "socket_exact");
3817
3818 #define NV_TOPOLOGY_V1_CORRELATION_STRING_VALUES(member) do { \
3819 NV_TOPOLOGY_V1_STRING_COLUMN column; \
3820 topology_v1_string_column_init(&column, rows_used); \
3821 for(size_t i = 0; i < rows_used; i++) \
3822 topology_v1_string_column_add(&column, rows[i].member); \
3823 topology_v1_emit_auto_string_column(wb, &column); \
3824 topology_v1_string_column_free(&column); \
3825 } while(0)
3826
3827 NV_TOPOLOGY_V1_CORRELATION_STRING_VALUES(protocol);
3828 NV_TOPOLOGY_V1_CORRELATION_STRING_VALUES(address_space);
3829 NV_TOPOLOGY_V1_CORRELATION_STRING_VALUES(ip);
3830
3831 #undef NV_TOPOLOGY_V1_CORRELATION_STRING_VALUES
3832
3833 topology_v1_values_start(wb);
3834 for(size_t i = 0; i < rows_used; i++)
3835 buffer_json_add_array_item_uint64(wb, rows[i].port);
3836 topology_v1_values_end(wb);
3837
3838 buffer_json_array_close(wb);
3839 }
3840
3841 static void topology_v1_emit_correlation(BUFFER *wb, NV_TOPOLOGY_V1_PAYLOAD *payload) {
3842 buffer_json_member_add_object(wb, "correlation");
3843 {
3844 buffer_json_member_add_object(wb, "rules");
3845 {
3846 buffer_json_member_add_object(wb, "socket_exact");
3847 {
3848 buffer_json_member_add_string(wb, "action", "absorb");
3849 buffer_json_member_add_string(wb, "class", "resolve_loose_side");
3850 buffer_json_member_add_uint64(wb, "priority", 1);
3851 buffer_json_member_add_string(wb, "key_space", "network_socket");
3852 buffer_json_member_add_array(wb, "key");
3853 {
3854 buffer_json_add_array_item_object(wb);
3855 buffer_json_member_add_string(wb, "column", "protocol");
3856 buffer_json_object_close(wb);
3857 buffer_json_add_array_item_object(wb);
3858 buffer_json_member_add_string(wb, "literal", ":");
3859 buffer_json_object_close(wb);
3860 buffer_json_add_array_item_object(wb);
3861 buffer_json_member_add_string(wb, "column", "address_space");
3862 buffer_json_object_close(wb);
3863 buffer_json_add_array_item_object(wb);
3864 buffer_json_member_add_string(wb, "literal", ":");
3865 buffer_json_object_close(wb);
3866 buffer_json_add_array_item_object(wb);
3867 buffer_json_member_add_string(wb, "column", "ip");
3868 buffer_json_object_close(wb);
3869 buffer_json_add_array_item_object(wb);
3870 buffer_json_member_add_string(wb, "literal", ":");
3871 buffer_json_object_close(wb);
3872 buffer_json_add_array_item_object(wb);
3873 buffer_json_member_add_string(wb, "column", "port");
3874 buffer_json_object_close(wb);
3875 }
3876 buffer_json_array_close(wb);
3877 buffer_json_member_add_array(wb, "point_actor_types");
3878 buffer_json_add_array_item_string(wb, "endpoint");
3879 buffer_json_array_close(wb);
3880 buffer_json_member_add_array(wb, "claim_actor_types");
3881 buffer_json_add_array_item_string(wb, "process");
3882 buffer_json_array_close(wb);
3883 buffer_json_member_add_array(wb, "correlation_link_types");
3884 buffer_json_add_array_item_string(wb, "endpoint_socket");
3885 buffer_json_array_close(wb);
3886 buffer_json_member_add_string(wb, "output_link_type", "correlated_socket");
3887 }
3888 buffer_json_object_close(wb);
3889 }
3890 buffer_json_object_close(wb);
3891
3892 buffer_json_member_add_object(wb, "points");
3893 topology_v1_emit_correlation_table(wb, payload->correlation_points, payload->correlation_points_used);
3894 buffer_json_object_close(wb);
3895
3896 buffer_json_member_add_object(wb, "claims");
3897 topology_v1_emit_correlation_table(wb, payload->correlation_claims, payload->correlation_claims_used);
3898 buffer_json_object_close(wb);
3899 }
3900 buffer_json_object_close(wb);
3901 }
3902
3903 static void topology_write_data(BUFFER *wb, const NV_TOPOLOGY_CONTEXT *ctx) {
3904 if(!ctx || ctx->options.info_only || !ctx->process_actors || !ctx->remote_actors || !ctx->local_ips || !ctx->links)
3905 return;
3906
3907 NV_TOPOLOGY_RENDER_STATE state;
3908 topology_render_state_init(&state, ctx);
3909
3910 NV_TOPOLOGY_V1_PAYLOAD topology = {
3911 .actor_index = dictionary_create_advanced(
3912 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3913 NULL, sizeof(uint64_t)),
3914 .graph_link_index = dictionary_create_advanced(
3915 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3916 NULL, sizeof(uint64_t)),
3917 .connection_index = dictionary_create_advanced(
3918 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3919 NULL, sizeof(uint64_t)),
3920 .port_index = dictionary_create_advanced(
3921 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3922 NULL, sizeof(uint64_t)),
3923 .correlation_point_index = dictionary_create_advanced(
3924 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3925 NULL, sizeof(bool)),
3926 .correlation_claim_index = dictionary_create_advanced(
3927 DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
3928 NULL, sizeof(bool)),
3929 };
3930
3931 if(!topology.actor_index || !topology.graph_link_index || !topology.connection_index || !topology.port_index ||
3932 !topology.correlation_point_index || !topology.correlation_claim_index) {
3933 topology_v1_free(&topology);
3934 return;
3935 }
3936
3937 topology_v1_collect_actors(ctx, &state, &topology);
3938 topology_v1_collect_links(ctx, &state, &topology);
3939
3940 buffer_json_member_add_object(wb, "data");
3941 {
3942 buffer_json_member_add_string(wb, "schema_version", NETWORK_TOPOLOGY_SCHEMA_VERSION);
3943 buffer_json_member_add_object(wb, "producer");
3944 {
3945 buffer_json_member_add_string(wb, "source", NETWORK_TOPOLOGY_SOURCE);
3946 buffer_json_member_add_string(wb, "instance", ctx->machine_guid[0] ? ctx->machine_guid : ctx->hostname);
3947 if(ctx->machine_guid[0])
3948 buffer_json_member_add_string(wb, "machine_guid", ctx->machine_guid);
3949 buffer_json_member_add_string(wb, "plugin", "network-viewer.plugin");
3950 buffer_json_member_add_array(wb, "capabilities");
3951 buffer_json_add_array_item_string(wb, "topology-v1");
3952 buffer_json_array_close(wb);
3953 }
3954 buffer_json_object_close(wb);
3955 buffer_json_member_add_datetime_rfc3339(wb, "collected_at", ctx->now_ut, true);
3956
3957 buffer_json_member_add_object(wb, "view");
3958 {
3959 buffer_json_member_add_string(wb, "id", "network-connections");
3960 buffer_json_member_add_string(wb, "scope", ctx->options.processes_by_pid ? "pid" : "process_name");
3961 buffer_json_member_add_string(wb, "mode", ctx->options.detailed ? "detailed" : "aggregated");
3962 buffer_json_member_add_array(wb, "supported_modes");
3963 buffer_json_add_array_item_string(wb, "aggregated");
3964 buffer_json_add_array_item_string(wb, "detailed");
3965 buffer_json_array_close(wb);
3966 buffer_json_member_add_array(wb, "group_by");
3967 buffer_json_add_array_item_string(wb, ctx->options.processes_by_pid ? "pid" : "process_name");
3968 buffer_json_array_close(wb);
3969 }
3970 buffer_json_object_close(wb);
3971
3972 buffer_json_member_add_object(wb, "dictionaries");
3973 {
3974 buffer_json_member_add_array(wb, "strings");
3975 buffer_json_array_close(wb);
3976 }
3977 buffer_json_object_close(wb);
3978
3979 topology_v1_emit_type_registry(wb, ctx->options.detailed);
3980 topology_v1_emit_presentation(wb);
3981 topology_v1_emit_correlation(wb, &topology);
3982 topology_v1_emit_actor_table(wb, &topology);
3983 topology_v1_emit_link_table(wb, &topology);
3984 topology_v1_emit_socket_port_table(wb, &topology);
3985 if(ctx->options.detailed)
3986 topology_v1_emit_socket_evidence_table(wb, &topology);
3987
3988 buffer_json_member_add_object(wb, "stats");
3989 {
3990 buffer_json_member_add_string(wb, "processes_mode", ctx->options.processes_by_pid ? "by_pid" : "by_name");
3991 buffer_json_member_add_string(wb, "mode", ctx->options.detailed ? "detailed" : "aggregated");
3992 buffer_json_member_add_boolean(wb, "sockets_listening", ctx->options.sockets_listening);
3993 buffer_json_member_add_boolean(wb, "sockets_inbound", ctx->options.sockets_inbound);
3994 buffer_json_member_add_boolean(wb, "sockets_outbound", ctx->options.sockets_outbound);
3995 buffer_json_member_add_string(wb, "endpoints_mode_selected", "by_ip");
3996 buffer_json_member_add_string(wb, "endpoints_mode_effective", "by_ip");
3997 buffer_json_member_add_boolean(wb, "protocol_ipv4_tcp", ctx->options.protocols_ipv4_tcp);
3998 buffer_json_member_add_boolean(wb, "protocol_ipv6_tcp", ctx->options.protocols_ipv6_tcp);
3999 buffer_json_member_add_boolean(wb, "protocol_ipv4_udp", ctx->options.protocols_ipv4_udp);
4000 buffer_json_member_add_boolean(wb, "protocol_ipv6_udp", ctx->options.protocols_ipv6_udp);
4001 buffer_json_member_add_uint64(wb, "sockets_total", ctx->sockets_total);
4002 buffer_json_member_add_uint64(wb, "sockets_without_remote_endpoint", ctx->skipped_sockets);
4003 buffer_json_member_add_uint64(wb, "actors", topology.actors_used);
4004 buffer_json_member_add_uint64(wb, "local_process_actors", state.process_actor_count);
4005 buffer_json_member_add_uint64(wb, "endpoint_actors", state.endpoint_actor_count);
4006 buffer_json_member_add_uint64(wb, "links", topology.links_used);
4007 buffer_json_member_add_uint64(wb, "socket_evidence_rows", topology.evidence_used);
4008 buffer_json_member_add_uint64(wb, "socket_port_rows", topology.ports_used);
4009 buffer_json_member_add_uint64(wb, "correlation_points", topology.correlation_points_used);
4010 buffer_json_member_add_uint64(wb, "correlation_claims", topology.correlation_claims_used);
4011 buffer_json_member_add_uint64(wb, "ownership_links", state.ownership_link_count);
4012 }
4013 buffer_json_object_close(wb);
4014 }
4015 buffer_json_object_close(wb);
4016
4017 topology_v1_free(&topology);
4018 }
4019
4020 static void network_viewer_topology_function(
4021 const char *transaction, char *function, usec_t *stop_monotonic_ut __maybe_unused,
4022 bool *cancelled __maybe_unused, BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused,
4023 const char *source __maybe_unused, void *data __maybe_unused) {
4024
4025 time_t now_s = now_realtime_sec();
4026 usec_t now_ut = now_realtime_usec();
4027 NV_TOPOLOGY_OPTIONS options = { 0 };
4028 topology_parse_options(function, &options);
4029
4030 CLEAN_BUFFER *wb = buffer_create(0, NULL);
4031 buffer_flush(wb);
4032 wb->content_type = CT_APPLICATION_JSON;
4033 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
4034
4035 topology_write_response_metadata(wb);
4036
4037 NV_TOPOLOGY_CONTEXT ctx;
4038 bool ctx_ready = topology_prepare_context(&ctx, now_ut, &options);
4039 if(ctx_ready)
4040 topology_write_data(wb, &ctx);
4041
4042 topology_context_destroy(&ctx);
4043 topology_finalize_response(transaction, wb, now_s);
4044 }
4045
4046 static int local_sockets_compar(const void *a, const void *b) {
4047 LOCAL_SOCKET *n1 = *(LOCAL_SOCKET **)a, *n2 = *(LOCAL_SOCKET **)b;
4048 return strcmp(n1->comm, n2->comm);
4049 }
4050
4051 void network_viewer_function(const char *transaction, char *function __maybe_unused, usec_t *stop_monotonic_ut __maybe_unused,
4052 bool *cancelled __maybe_unused, BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused,
4053 const char *source __maybe_unused, void *data __maybe_unused) {
4054
4055 time_t now_s = now_realtime_sec();
4056 bool aggregated = false;
4057
4058 CLEAN_BUFFER *wb = buffer_create(0, NULL);
4059 buffer_flush(wb);
4060 wb->content_type = CT_APPLICATION_JSON;
4061 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
4062
4063 struct sockets_stats st = {
4064 .wb = wb,
4065 };
4066
4067 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
4068 buffer_json_member_add_string(wb, "type", "table");
4069 buffer_json_member_add_time_t(wb, "update_every", NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
4070 buffer_json_member_add_boolean(wb, "has_history", false);
4071 buffer_json_member_add_string(wb, "help", NETWORK_CONNECTIONS_VIEWER_HELP);
4072
4073 #ifdef ENABLE_DETAILED_VIEW
4074 buffer_json_member_add_array(wb, "accepted_params");
4075 {
4076 buffer_json_add_array_item_string(wb, "sockets");
4077 }
4078 buffer_json_array_close(wb); // accepted_params
4079 buffer_json_member_add_array(wb, "required_params");
4080 {
4081 buffer_json_add_array_item_object(wb);
4082 {
4083 buffer_json_member_add_string(wb, "id", "sockets");
4084 buffer_json_member_add_string(wb, "name", "Sockets");
4085 buffer_json_member_add_string(wb, "help", "Select the source type to query");
4086 buffer_json_member_add_boolean(wb, "unique_view", true);
4087 buffer_json_member_add_string(wb, "type", "select");
4088 buffer_json_member_add_array(wb, "options");
4089 {
4090 buffer_json_add_array_item_object(wb);
4091 {
4092 buffer_json_member_add_string(wb, "id", "aggregated");
4093 buffer_json_member_add_string(wb, "name", "Aggregated view of sockets");
4094 }
4095 buffer_json_object_close(wb);
4096 buffer_json_add_array_item_object(wb);
4097 {
4098 buffer_json_member_add_string(wb, "id", "detailed");
4099 buffer_json_member_add_string(wb, "name", "Detailed view of all sockets");
4100 }
4101 buffer_json_object_close(wb);
4102 }
4103 buffer_json_array_close(wb); // options array
4104 }
4105 buffer_json_object_close(wb);
4106 }
4107 buffer_json_array_close(wb); // required_params
4108 #endif
4109
4110 char *function_copy = strdupz(function);
4111 char *words[1024];
4112 size_t num_words = quoted_strings_splitter_whitespace(function_copy, words, 1024);
4113 for(size_t i = 1; i < num_words ;i++) {
4114 char *param = get_word(words, num_words, i);
4115 if(!param || !*param) continue;
4116 if(strcmp(param, "sockets:aggregated") == 0) {
4117 aggregated = true;
4118 }
4119 else if(strcmp(param, "sockets:detailed") == 0) {
4120 aggregated = false;
4121 }
4122 else if(strcmp(param, "info") == 0) {
4123 freez(function_copy);
4124 goto close_and_send;
4125 }
4126 }
4127
4128 freez(function_copy);
4129
4130 if(aggregated) {
4131 buffer_json_member_add_object(wb, "aggregated_view");
4132 {
4133 buffer_json_member_add_string(wb, "column", "Count");
4134 buffer_json_member_add_string(wb, "results_label", "unique combinations");
4135 buffer_json_member_add_string(wb, "aggregated_label", "sockets");
4136 }
4137 buffer_json_object_close(wb);
4138 }
4139
4140 {
4141 buffer_json_member_add_array(wb, "data");
4142
4143 LS_STATE ls = {
4144 .config = {
4145 .listening = true,
4146 .inbound = true,
4147 .outbound = true,
4148 .local = true,
4149 .tcp4 = true,
4150 .tcp6 = true,
4151 .udp4 = true,
4152 .udp6 = true,
4153 .pid = true,
4154 .uid = true,
4155 .cmdline = true,
4156 .comm = true,
4157 .namespaces = true,
4158 .tcp_info = true,
4159
4160 .max_errors = 10,
4161 .max_concurrent_namespaces = 5,
4162 },
4163 #if defined(LOCAL_SOCKETS_USE_SETNS)
4164 .spawn_server = spawn_srv,
4165 #endif
4166 .stats = { 0 },
4167 .sockets_hashtable = { 0 },
4168 .local_ips_hashtable = { 0 },
4169 .listening_ports_hashtable = { 0 },
4170 };
4171
4172 SIMPLE_HASHTABLE_AGGREGATED_SOCKETS ht = { 0 };
4173 if(aggregated) {
4174 simple_hashtable_init_AGGREGATED_SOCKETS(&ht, 1024);
4175 ls.config.cb = local_sockets_cb_to_aggregation;
4176 ls.config.data = &ht;
4177 }
4178 else {
4179 ls.config.cb = local_sockets_cb_to_json;
4180 ls.config.data = &st;
4181 }
4182
4183 local_sockets_process(&ls);
4184
4185 if(aggregated) {
4186 size_t added = 0;
4187 uint64_t proc_self_net_ns_inode = ls.proc_self_net_ns_inode;
4188
4189 if(ht.used) {
4190 LOCAL_SOCKET **array = mallocz(ht.used * sizeof(LOCAL_SOCKET *));
4191 for(SIMPLE_HASHTABLE_SLOT_AGGREGATED_SOCKETS *sl = simple_hashtable_first_read_only_AGGREGATED_SOCKETS(&ht);
4192 sl;
4193 sl = simple_hashtable_next_read_only_AGGREGATED_SOCKETS(&ht, sl)) {
4194 LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl);
4195 if(!n || added >= ht.used) continue;
4196
4197 array[added++] = n;
4198 }
4199
4200 qsort(array, added, sizeof(LOCAL_SOCKET *), local_sockets_compar);
4201
4202 for(size_t i = 0; i < added ;i++) {
4203 local_socket_to_json_array(&st, array[i], proc_self_net_ns_inode, true);
4204 string_freez(array[i]->cmdline);
4205 freez(array[i]);
4206 }
4207
4208 freez(array);
4209 }
4210
4211 simple_hashtable_destroy_AGGREGATED_SOCKETS(&ht);
4212 }
4213
4214 buffer_json_array_close(wb);
4215 buffer_json_member_add_object(wb, "columns");
4216 {
4217 size_t field_id = 0;
4218
4219 // Direction
4220 buffer_rrdf_table_add_field(wb, field_id++, "Direction", "Socket Direction",
4221 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4222 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4223 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4224 RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_STICKY,
4225 NULL);
4226
4227 // Protocol
4228 buffer_rrdf_table_add_field(wb, field_id++, "Protocol", "Socket Protocol",
4229 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4230 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4231 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4232 RRDF_FIELD_OPTS_VISIBLE,
4233 NULL);
4234
4235 // Type
4236 buffer_rrdf_table_add_field(wb, field_id++, "Namespace", "Namespace",
4237 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4238 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4239 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4240 RRDF_FIELD_OPTS_VISIBLE,
4241 NULL);
4242
4243 // State
4244 buffer_rrdf_table_add_field(wb, field_id++, "State", "Socket State",
4245 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4246 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4247 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4248 RRDF_FIELD_OPTS_VISIBLE,
4249 NULL);
4250
4251 // Pid
4252 buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID",
4253 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4254 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4255 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4256 RRDF_FIELD_OPTS_VISIBLE,
4257 NULL);
4258
4259 // Comm
4260 buffer_rrdf_table_add_field(wb, field_id++, "Process", "Process Name",
4261 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4262 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4263 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4264 RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
4265 NULL);
4266
4267 // // Cmdline
4268 // buffer_rrdf_table_add_field(wb, field_id++, "CommandLine", "Command Line",
4269 // RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4270 // 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4271 // RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4272 // RRDF_FIELD_OPTS_NONE|RRDF_FIELD_OPTS_FULL_WIDTH,
4273 // NULL);
4274
4275 // // Uid
4276 // buffer_rrdf_table_add_field(wb, field_id++, "UID", "User ID",
4277 // RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4278 // 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4279 // RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4280 // RRDF_FIELD_OPTS_NONE,
4281 // NULL);
4282
4283 // Username
4284 buffer_rrdf_table_add_field(wb, field_id++, "User", "Username",
4285 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4286 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4287 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4288 RRDF_FIELD_OPTS_VISIBLE,
4289 NULL);
4290
4291 // Portname
4292 buffer_rrdf_table_add_field(wb, field_id++, "Portname", "Server Port Name",
4293 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4294 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4295 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4296 RRDF_FIELD_OPTS_VISIBLE,
4297 NULL);
4298
4299 if(!aggregated) {
4300 // Local Address
4301 buffer_rrdf_table_add_field(wb, field_id++, "LocalIP", "Local IP Address",
4302 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4303 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4304 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4305 RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
4306 NULL);
4307
4308 // Local Port
4309 buffer_rrdf_table_add_field(wb, field_id++, "LocalPort", "Local Port",
4310 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4311 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4312 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4313 RRDF_FIELD_OPTS_VISIBLE,
4314 NULL);
4315 }
4316
4317 // Local Address Space
4318 buffer_rrdf_table_add_field(wb, field_id++, "LocalAddressSpace", "Local IP Address Space",
4319 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4320 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4321 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4322 RRDF_FIELD_OPTS_NONE,
4323 NULL);
4324
4325 if(!aggregated) {
4326 // Remote Address
4327 buffer_rrdf_table_add_field(wb, field_id++, "RemoteIP", "Remote IP Address",
4328 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4329 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4330 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4331 RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
4332 NULL);
4333
4334 // Remote Port
4335 buffer_rrdf_table_add_field(wb, field_id++, "RemotePort", "Remote Port",
4336 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4337 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4338 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4339 RRDF_FIELD_OPTS_VISIBLE,
4340 NULL);
4341 }
4342
4343 // Remote Address Space
4344 buffer_rrdf_table_add_field(wb, field_id++, "RemoteAddressSpace", "Remote IP Address Space",
4345 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4346 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4347 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4348 RRDF_FIELD_OPTS_NONE,
4349 NULL);
4350
4351 if(aggregated) {
4352 // Server IP
4353 buffer_rrdf_table_add_field(wb, field_id++, "ServerIP", "Server IP Address",
4354 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4355 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4356 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4357 RRDF_FIELD_OPTS_FULL_WIDTH | (aggregated ? RRDF_FIELD_OPTS_VISIBLE : RRDF_FIELD_OPTS_NONE),
4358 NULL);
4359 }
4360
4361 // Server Port
4362 buffer_rrdf_table_add_field(wb, field_id++, "ServerPort", "Server Port",
4363 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4364 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4365 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4366 aggregated ? RRDF_FIELD_OPTS_VISIBLE : RRDF_FIELD_OPTS_NONE,
4367 NULL);
4368
4369 if(aggregated) {
4370 // Client Address Space
4371 buffer_rrdf_table_add_field(wb, field_id++, "ClientAddressSpace", "Client IP Address Space",
4372 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4373 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4374 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4375 RRDF_FIELD_OPTS_VISIBLE,
4376 NULL);
4377
4378 // Server Address Space
4379 buffer_rrdf_table_add_field(wb, field_id++, "ServerAddressSpace", "Server IP Address Space",
4380 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4381 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4382 RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
4383 RRDF_FIELD_OPTS_VISIBLE,
4384 NULL);
4385 }
4386
4387 // // inode
4388 // buffer_rrdf_table_add_field(wb, field_id++, "Inode", "Socket Inode",
4389 // RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4390 // 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4391 // RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4392 // RRDF_FIELD_OPTS_NONE,
4393 // NULL);
4394
4395 // // Namespace inode
4396 // buffer_rrdf_table_add_field(wb, field_id++, "Namespace Inode", "Namespace Inode",
4397 // RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4398 // 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
4399 // RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
4400 // RRDF_FIELD_OPTS_NONE,
4401 // NULL);
4402
4403
4404 // RTT
4405 buffer_rrdf_table_add_field(wb, field_id++, "RTT", aggregated ? "Max Smoothed Round Trip Time" : "Smoothed Round Trip Time",
4406 RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
4407 2, "ms", st.max.tcpi_rtt / USEC_PER_MS, RRDF_FIELD_SORT_DESCENDING, NULL,
4408 RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
4409 RRDF_FIELD_OPTS_VISIBLE,
4410 NULL);
4411
4412 // Asymmetry RTT
4413 buffer_rrdf_table_add_field(wb, field_id++, "RecvRTT", aggregated ? "Max Receiver ACKs RTT" : "Receiver ACKs RTT",
4414 RRDF_FIELD_TYPE_DURATION, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_DURATION_S,
4415 2, "ms", st.max.tcpi_rcv_rtt / USEC_PER_MS, RRDF_FIELD_SORT_DESCENDING, NULL,
4416 RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
4417 RRDF_FIELD_OPTS_VISIBLE,
4418 NULL);
4419
4420 // Rentrasmissions
4421 buffer_rrdf_table_add_field(wb, field_id++, "Retrans", "Total Retransmissions",
4422 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4423 0, "packets", st.max.tcpi_total_retrans, RRDF_FIELD_SORT_DESCENDING, NULL,
4424 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4425 RRDF_FIELD_OPTS_VISIBLE,
4426 NULL);
4427
4428 // Count
4429 buffer_rrdf_table_add_field(wb, field_id++, "Count", "Number of sockets like this",
4430 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4431 0, "sockets", NAN, RRDF_FIELD_SORT_DESCENDING, NULL,
4432 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
4433 aggregated ? (RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY) : RRDF_FIELD_OPTS_NONE,
4434 NULL);
4435 }
4436
4437 buffer_json_object_close(wb); // columns
4438 buffer_json_member_add_string(wb, "default_sort_column", aggregated ? "Count" : "Direction");
4439
4440 buffer_json_member_add_object(wb, "custom_charts");
4441 {
4442 buffer_json_member_add_object(wb, "Network Map");
4443 {
4444 buffer_json_member_add_string(wb, "type", "network-viewer");
4445 }
4446 buffer_json_object_close(wb);
4447 }
4448 buffer_json_object_close(wb); // custom_charts
4449
4450 buffer_json_member_add_object(wb, "charts");
4451 {
4452 buffer_json_member_add_object(wb, "Count by Direction");
4453 {
4454 buffer_json_member_add_string(wb, "type", "stacked-bar");
4455 buffer_json_member_add_array(wb, "columns");
4456 {
4457 buffer_json_add_array_item_string(wb, "Direction");
4458 }
4459 buffer_json_array_close(wb);
4460 }
4461 buffer_json_object_close(wb);
4462
4463 buffer_json_member_add_object(wb, "Count by Process");
4464 {
4465 buffer_json_member_add_string(wb, "type", "stacked-bar");
4466 buffer_json_member_add_array(wb, "columns");
4467 {
4468 buffer_json_add_array_item_string(wb, "Process");
4469 }
4470 buffer_json_array_close(wb);
4471 }
4472 buffer_json_object_close(wb);
4473
4474 buffer_json_member_add_object(wb, "Count by Protocol");
4475 {
4476 buffer_json_member_add_string(wb, "type", "stacked-bar");
4477 buffer_json_member_add_array(wb, "columns");
4478 {
4479 buffer_json_add_array_item_string(wb, "Protocol");
4480 }
4481 buffer_json_array_close(wb);
4482 }
4483 buffer_json_object_close(wb);
4484 }
4485 buffer_json_object_close(wb); // charts
4486
4487 buffer_json_member_add_array(wb, "default_charts");
4488 {
4489 buffer_json_add_array_item_array(wb);
4490 buffer_json_add_array_item_string(wb, "Count");
4491 buffer_json_add_array_item_string(wb, "Direction");
4492 buffer_json_array_close(wb);
4493
4494 buffer_json_add_array_item_array(wb);
4495 buffer_json_add_array_item_string(wb, "Count");
4496 buffer_json_add_array_item_string(wb, "Process");
4497 buffer_json_array_close(wb);
4498 }
4499 buffer_json_array_close(wb);
4500
4501 buffer_json_member_add_object(wb, "group_by");
4502 {
4503 buffer_json_member_add_object(wb, "Direction");
4504 {
4505 buffer_json_member_add_string(wb, "name", "Direction");
4506 buffer_json_member_add_array(wb, "columns");
4507 {
4508 buffer_json_add_array_item_string(wb, "Direction");
4509 }
4510 buffer_json_array_close(wb);
4511 }
4512 buffer_json_object_close(wb);
4513
4514 buffer_json_member_add_object(wb, "Protocol");
4515 {
4516 buffer_json_member_add_string(wb, "name", "Protocol");
4517 buffer_json_member_add_array(wb, "columns");
4518 {
4519 buffer_json_add_array_item_string(wb, "Protocol");
4520 }
4521 buffer_json_array_close(wb);
4522 }
4523 buffer_json_object_close(wb);
4524
4525 buffer_json_member_add_object(wb, "Namespace");
4526 {
4527 buffer_json_member_add_string(wb, "name", "Namespace");
4528 buffer_json_member_add_array(wb, "columns");
4529 {
4530 buffer_json_add_array_item_string(wb, "Namespace");
4531 }
4532 buffer_json_array_close(wb);
4533 }
4534 buffer_json_object_close(wb);
4535
4536 buffer_json_member_add_object(wb, "Process");
4537 {
4538 buffer_json_member_add_string(wb, "name", "Process");
4539 buffer_json_member_add_array(wb, "columns");
4540 {
4541 buffer_json_add_array_item_string(wb, "Process");
4542 }
4543 buffer_json_array_close(wb);
4544 }
4545 buffer_json_object_close(wb);
4546
4547 if(!aggregated) {
4548 buffer_json_member_add_object(wb, "LocalIP");
4549 {
4550 buffer_json_member_add_string(wb, "name", "Local IP");
4551 buffer_json_member_add_array(wb, "columns");
4552 {
4553 buffer_json_add_array_item_string(wb, "LocalIP");
4554 }
4555 buffer_json_array_close(wb);
4556 }
4557 buffer_json_object_close(wb);
4558
4559 buffer_json_member_add_object(wb, "LocalPort");
4560 {
4561 buffer_json_member_add_string(wb, "name", "Local Port");
4562 buffer_json_member_add_array(wb, "columns");
4563 {
4564 buffer_json_add_array_item_string(wb, "LocalPort");
4565 }
4566 buffer_json_array_close(wb);
4567 }
4568 buffer_json_object_close(wb);
4569
4570 buffer_json_member_add_object(wb, "RemoteIP");
4571 {
4572 buffer_json_member_add_string(wb, "name", "Remote IP");
4573 buffer_json_member_add_array(wb, "columns");
4574 {
4575 buffer_json_add_array_item_string(wb, "RemoteIP");
4576 }
4577 buffer_json_array_close(wb);
4578 }
4579 buffer_json_object_close(wb);
4580
4581 buffer_json_member_add_object(wb, "RemotePort");
4582 {
4583 buffer_json_member_add_string(wb, "name", "Remote Port");
4584 buffer_json_member_add_array(wb, "columns");
4585 {
4586 buffer_json_add_array_item_string(wb, "RemotePort");
4587 }
4588 buffer_json_array_close(wb);
4589 }
4590 buffer_json_object_close(wb);
4591 }
4592 }
4593 buffer_json_object_close(wb); // group_by
4594 }
4595
4596 close_and_send:
4597 buffer_json_member_add_time_t(wb, "expires", now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
4598 buffer_json_finalize(wb);
4599
4600 netdata_mutex_lock(&stdout_mutex);
4601 wb->response_code = HTTP_RESP_OK;
4602 wb->content_type = CT_APPLICATION_JSON;
4603 wb->expires = now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY;
4604 pluginsd_function_result_to_stdout(transaction, wb);
4605 netdata_mutex_unlock(&stdout_mutex);
4606 }
4607
4608 // ----------------------------------------------------------------------------------------------------------------
4609 // FreeBSD: network-protocols function
4610
4611 #if defined(OS_FREEBSD)
4612
4613 typedef struct {
4614 struct tcpstat tcp;
4615 struct udpstat udp;
4616 usec_t last_ut;
4617 bool initialized;
4618 } NV_PROTO_STATE;
4619
4620 static NV_PROTO_STATE nv_proto_prev = { .initialized = false };
4621
4622 static uint64_t nv_proto_delta(uint64_t cur, uint64_t prev, double elapsed_s) {
4623 if (elapsed_s <= 0.0 || cur < prev)
4624 return 0;
4625 return (uint64_t)((double)(cur - prev) / elapsed_s + 0.5);
4626 }
4627
4628 void function_network_protocols(
4629 const char *transaction, char *function __maybe_unused,
4630 usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
4631 BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused,
4632 const char *source __maybe_unused, void *data __maybe_unused)
4633 {
4634 // Sampling and delta computation must be atomic: acquiring the mutex first
4635 // prevents two concurrent requests from each sampling stale counters and
4636 // then computing deltas against the same nv_proto_prev with a near-zero
4637 // elapsed time, which would produce wildly inflated per-second rates.
4638 netdata_mutex_lock(&nv_proto_mutex);
4639
4640 struct tcpstat tcp_cur = { 0 };
4641 struct udpstat udp_cur = { 0 };
4642 uint64_t established = 0;
4643 size_t len;
4644
4645 len = sizeof(tcp_cur);
4646 if (sysctlbyname("net.inet.tcp.stats", &tcp_cur, &len, NULL, 0) < 0) {
4647 netdata_mutex_unlock(&nv_proto_mutex);
4648 netdata_mutex_lock(&stdout_mutex);
4649 pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_INTERNAL_SERVER_ERROR,
4650 "failed to read net.inet.tcp.stats");
4651 netdata_mutex_unlock(&stdout_mutex);
4652 return;
4653 }
4654
4655 uint64_t tcp_states[TCP_NSTATES] = { 0 };
4656 len = sizeof(tcp_states);
4657 if (sysctlbyname("net.inet.tcp.states", tcp_states, &len, NULL, 0) == 0)
4658 established = tcp_states[TCPS_ESTABLISHED];
4659
4660 len = sizeof(udp_cur);
4661 if (sysctlbyname("net.inet.udp.stats", &udp_cur, &len, NULL, 0) < 0) {
4662 netdata_mutex_unlock(&nv_proto_mutex);
4663 netdata_mutex_lock(&stdout_mutex);
4664 pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_INTERNAL_SERVER_ERROR,
4665 "failed to read net.inet.udp.stats");
4666 netdata_mutex_unlock(&stdout_mutex);
4667 return;
4668 }
4669
4670 usec_t now_ut = now_monotonic_usec();
4671
4672 bool first = !nv_proto_prev.initialized;
4673 double elapsed_s = first ? 0.0 : (double)(now_ut - nv_proto_prev.last_ut) / (double)USEC_PER_SEC;
4674
4675 #define TCP_DELTA(f) nv_proto_delta((uint64_t)tcp_cur.f, (uint64_t)nv_proto_prev.tcp.f, elapsed_s)
4676 #define UDP_DELTA(f) nv_proto_delta((uint64_t)udp_cur.f, (uint64_t)nv_proto_prev.udp.f, elapsed_s)
4677
4678 uint64_t tcp_received = first ? 0 : TCP_DELTA(tcps_rcvtotal);
4679 uint64_t tcp_sent = first ? 0 : TCP_DELTA(tcps_sndtotal);
4680 uint64_t tcp_errors = first ? 0 : TCP_DELTA(tcps_conndrops);
4681 uint64_t tcp_active = first ? 0 : TCP_DELTA(tcps_connattempt);
4682 uint64_t tcp_passive = first ? 0 : TCP_DELTA(tcps_accepts);
4683 uint64_t tcp_resets = first ? 0 : TCP_DELTA(tcps_drops);
4684 uint64_t tcp_segs_total = first ? 0 : tcp_received + tcp_sent;
4685 uint64_t tcp_retrans = first ? 0 : TCP_DELTA(tcps_sndrexmitpack);
4686
4687 uint64_t udp_received = first ? 0 : UDP_DELTA(udps_ipackets);
4688 uint64_t udp_sent = first ? 0 : UDP_DELTA(udps_opackets);
4689 uint64_t udp_errors = first ? 0 : (
4690 UDP_DELTA(udps_hdrops) +
4691 UDP_DELTA(udps_badlen) +
4692 UDP_DELTA(udps_badsum) +
4693 UDP_DELTA(udps_nosum));
4694 uint64_t udp_no_port = first ? 0 : UDP_DELTA(udps_noport);
4695
4696 #undef TCP_DELTA
4697 #undef UDP_DELTA
4698
4699 nv_proto_prev.tcp = tcp_cur;
4700 nv_proto_prev.udp = udp_cur;
4701 nv_proto_prev.last_ut = now_ut;
4702 nv_proto_prev.initialized = true;
4703
4704 netdata_mutex_unlock(&nv_proto_mutex);
4705
4706 time_t now_s = now_realtime_sec();
4707 CLEAN_BUFFER *wb = buffer_create(0, NULL);
4708 wb->content_type = CT_APPLICATION_JSON;
4709 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
4710
4711 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
4712 buffer_json_member_add_string(wb, "type", "table");
4713 buffer_json_member_add_time_t(wb, "update_every", NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
4714 buffer_json_member_add_boolean(wb, "has_history", false);
4715 buffer_json_member_add_string(wb, "help", NETWORK_PROTOCOLS_FUNCTION_HELP);
4716
4717 buffer_json_member_add_array(wb, "data");
4718 {
4719 // TCP row — FreeBSD stats cover both IPv4 and IPv6 in a single counter set
4720 buffer_json_add_array_item_array(wb);
4721 {
4722 buffer_json_add_array_item_string(wb, "TCP");
4723 buffer_json_add_array_item_string(wb, "IPv4+IPv6");
4724 buffer_json_add_array_item_uint64(wb, tcp_received);
4725 buffer_json_add_array_item_uint64(wb, tcp_sent);
4726 buffer_json_add_array_item_uint64(wb, tcp_errors);
4727 buffer_json_add_array_item_uint64(wb, tcp_active);
4728 buffer_json_add_array_item_uint64(wb, established);
4729 buffer_json_add_array_item_uint64(wb, tcp_passive);
4730 buffer_json_add_array_item_uint64(wb, tcp_resets);
4731 buffer_json_add_array_item_uint64(wb, tcp_segs_total);
4732 buffer_json_add_array_item_uint64(wb, tcp_retrans);
4733 buffer_json_add_array_item_uint64(wb, 0); // DatagramsNoPort — UDP only
4734 }
4735 buffer_json_array_close(wb);
4736
4737 // UDP row
4738 buffer_json_add_array_item_array(wb);
4739 {
4740 buffer_json_add_array_item_string(wb, "UDP");
4741 buffer_json_add_array_item_string(wb, "IPv4+IPv6");
4742 buffer_json_add_array_item_uint64(wb, udp_received);
4743 buffer_json_add_array_item_uint64(wb, udp_sent);
4744 buffer_json_add_array_item_uint64(wb, udp_errors);
4745 buffer_json_add_array_item_uint64(wb, 0); // ConnActive — TCP only
4746 buffer_json_add_array_item_uint64(wb, 0); // ConnEstablished — TCP only
4747 buffer_json_add_array_item_uint64(wb, 0); // ConnPassive — TCP only
4748 buffer_json_add_array_item_uint64(wb, 0); // ConnReset — TCP only
4749 buffer_json_add_array_item_uint64(wb, 0); // SegsTotal — TCP only
4750 buffer_json_add_array_item_uint64(wb, 0); // SegsRetransmitted — TCP only
4751 buffer_json_add_array_item_uint64(wb, udp_no_port);
4752 }
4753 buffer_json_array_close(wb);
4754 }
4755 buffer_json_array_close(wb); // data
4756
4757 size_t field_id = 0;
4758 buffer_json_member_add_object(wb, "columns");
4759 {
4760 buffer_rrdf_table_add_field(wb, field_id++, "Transport", "Transport Protocol",
4761 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4762 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4763 RRDF_FIELD_FILTER_MULTISELECT,
4764 RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4765
4766 buffer_rrdf_table_add_field(wb, field_id++, "Family", "IP Protocol Family",
4767 RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
4768 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4769 RRDF_FIELD_FILTER_MULTISELECT,
4770 RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4771
4772 #define NV_INT_FIELD(id, label, unit) \
4773 buffer_rrdf_table_add_field(wb, field_id++, id, label, \
4774 RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, \
4775 RRDF_FIELD_TRANSFORM_NUMBER, 0, unit, NAN, \
4776 RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, \
4777 RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_VISIBLE, NULL)
4778
4779 NV_INT_FIELD("Received", "Received (Segments/Datagrams)", "segments/datagrams/s");
4780 NV_INT_FIELD("Sent", "Sent (Segments/Datagrams)", "segments/datagrams/s");
4781 NV_INT_FIELD("Errors", "Errors (Failures/Rx Errors)", "errors");
4782 NV_INT_FIELD("ConnActive", "Active Connections Opened", "opens");
4783 NV_INT_FIELD("ConnEstablished", "Currently Established Connections", "connections");
4784 NV_INT_FIELD("ConnPassive", "Passive Connections Opened", "opens");
4785 NV_INT_FIELD("ConnReset", "Reset Connections", "resets");
4786 NV_INT_FIELD("SegsTotal", "Total Segments", "segments/s");
4787 NV_INT_FIELD("SegsRetransmitted", "Retransmitted Segments", "segments/s");
4788 NV_INT_FIELD("DatagramsNoPort", "Datagrams with No Port", "datagrams/s");
4789 #undef NV_INT_FIELD
4790 }
4791 buffer_json_object_close(wb); // columns
4792
4793 buffer_json_member_add_string(wb, "default_sort_column", "Received");
4794
4795 buffer_json_member_add_object(wb, "charts");
4796 {
4797 buffer_json_member_add_object(wb, "Traffic");
4798 {
4799 buffer_json_member_add_string(wb, "name", "Traffic");
4800 buffer_json_member_add_string(wb, "type", "stacked-bar");
4801 buffer_json_member_add_array(wb, "columns");
4802 {
4803 buffer_json_add_array_item_string(wb, "Received");
4804 buffer_json_add_array_item_string(wb, "Sent");
4805 }
4806 buffer_json_array_close(wb);
4807 }
4808 buffer_json_object_close(wb);
4809 }
4810 buffer_json_object_close(wb); // charts
4811
4812 buffer_json_member_add_array(wb, "default_charts");
4813 {
4814 buffer_json_add_array_item_array(wb);
4815 buffer_json_add_array_item_string(wb, "Traffic");
4816 buffer_json_add_array_item_string(wb, "Transport");
4817 buffer_json_array_close(wb);
4818 }
4819 buffer_json_array_close(wb); // default_charts
4820
4821 buffer_json_member_add_object(wb, "group_by");
4822 {
4823 buffer_json_member_add_object(wb, "Transport");
4824 {
4825 buffer_json_member_add_string(wb, "name", "Transport");
4826 buffer_json_member_add_array(wb, "columns");
4827 buffer_json_add_array_item_string(wb, "Transport");
4828 buffer_json_array_close(wb);
4829 }
4830 buffer_json_object_close(wb);
4831 }
4832 buffer_json_object_close(wb); // group_by
4833
4834 buffer_json_member_add_time_t(wb, "expires", now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY);
4835 buffer_json_finalize(wb);
4836
4837 netdata_mutex_lock(&stdout_mutex);
4838 wb->response_code = HTTP_RESP_OK;
4839 wb->expires = now_s + NETWORK_VIEWER_RESPONSE_UPDATE_EVERY;
4840 pluginsd_function_result_to_stdout(transaction, wb);
4841 netdata_mutex_unlock(&stdout_mutex);
4842 }
4843
4844 #endif // OS_FREEBSD
4845
4846 // ----------------------------------------------------------------------------------------------------------------
4847 // main
4848
4849 int main(int argc __maybe_unused, char **argv __maybe_unused) {
4850 nd_thread_tag_set("NETWORK-VIEWER");
4851 nd_log_initialize_for_external_plugins("network-viewer.plugin");
4852 netdata_threads_init_for_external_plugins(0);
4853
4854 netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
4855 if(verify_netdata_host_prefix(true) == -1) exit(1);
4856
4857 #if defined(LOCAL_SOCKETS_USE_SETNS)
4858 spawn_srv = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, "setns", local_sockets_spawn_server_callback, argc, (const char **)argv);
4859 if(spawn_srv == NULL) {
4860 fprintf(stderr, "Cannot create spawn server.\n");
4861 exit(1);
4862 }
4863 #endif
4864
4865 cached_usernames_init();
4866 update_cached_host_users();
4867 sc = system_servicenames_cache_init();
4868
4869 // ----------------------------------------------------------------------------------------------------------------
4870
4871 // Manual debug mode only; normal plugins.d execution never takes this path.
4872 if(argc == 2 && strcmp(argv[1], "debug") == 0) {
4873 // for(int i = 0; i < 100; i++) {
4874 bool cancelled = false;
4875 usec_t stop_monotonic_ut = now_monotonic_usec() + 600 * USEC_PER_SEC;
4876 char topo_buf[] = "topology:network-connections";
4877 network_viewer_topology_function("123", topo_buf, &stop_monotonic_ut, &cancelled,
4878 NULL, HTTP_ACCESS_ALL, NULL, NULL);
4879
4880 char buf[] = "network-connections sockets:aggregated";
4881 network_viewer_function("123", buf, &stop_monotonic_ut, &cancelled,
4882 NULL, HTTP_ACCESS_ALL, NULL, NULL);
4883
4884 char buf2[] = "network-connections sockets:detailed";
4885 network_viewer_function("123", buf2, &stop_monotonic_ut, &cancelled,
4886 NULL, HTTP_ACCESS_ALL, NULL, NULL);
4887 // }
4888
4889 #if defined(LOCAL_SOCKETS_USE_SETNS)
4890 spawn_server_destroy(spawn_srv);
4891 #endif
4892 exit(1);
4893 }
4894
4895 // ----------------------------------------------------------------------------------------------------------------
4896
4897 fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n",
4898 NETWORK_TOPOLOGY_VIEWER_FUNCTION, 60,
4899 NETWORK_TOPOLOGY_VIEWER_HELP,
4900 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
4901 RRDFUNCTIONS_PRIORITY_DEFAULT);
4902
4903 fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n",
4904 NETWORK_CONNECTIONS_VIEWER_FUNCTION, 60,
4905 NETWORK_CONNECTIONS_VIEWER_HELP,
4906 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
4907 RRDFUNCTIONS_PRIORITY_DEFAULT);
4908
4909 #if defined(OS_FREEBSD)
4910 fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n",
4911 NETWORK_PROTOCOLS_FUNCTION, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT,
4912 NETWORK_PROTOCOLS_FUNCTION_HELP,
4913 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE),
4914 RRDFUNCTIONS_PRIORITY_DEFAULT);
4915 #endif
4916
4917 // ----------------------------------------------------------------------------------------------------------------
4918
4919 struct functions_evloop_globals *wg =
4920 functions_evloop_init(5, "Network-Viewer", &stdout_mutex, &plugin_should_exit, NULL);
4921
4922 functions_evloop_add_function(wg, NETWORK_CONNECTIONS_VIEWER_FUNCTION,
4923 network_viewer_function,
4924 PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT,
4925 NULL);
4926
4927 functions_evloop_add_function(wg, NETWORK_TOPOLOGY_VIEWER_FUNCTION,
4928 network_viewer_topology_function,
4929 PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT,
4930 NULL);
4931
4932 #if defined(OS_FREEBSD)
4933 functions_evloop_add_function(wg, NETWORK_PROTOCOLS_FUNCTION,
4934 function_network_protocols,
4935 PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT,
4936 NULL);
4937 #endif
4938
4939 // ----------------------------------------------------------------------------------------------------------------
4940
4941 usec_t send_newline_ut = 0;
4942 bool tty = isatty(fileno(stdout)) == 1;
4943
4944 heartbeat_t hb;
4945 heartbeat_init(&hb, USEC_PER_SEC);
4946
4947 while(!__atomic_load_n(&plugin_should_exit, __ATOMIC_ACQUIRE)) {
4948 usec_t dt_ut = heartbeat_next(&hb);
4949 send_newline_ut += dt_ut;
4950
4951 if(!tty && send_newline_ut > USEC_PER_SEC) {
4952 send_newline_and_flush(&stdout_mutex);
4953 send_newline_ut = 0;
4954 }
4955 }
4956
4957 #if defined(LOCAL_SOCKETS_USE_SETNS)
4958 spawn_server_destroy(spawn_srv);
4959 spawn_srv = NULL;
4960 #endif
4961
4962 return 0;
4963 }