diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:08:18 +0000 |
commit | 5da14042f70711ea5cf66e034699730335462f66 (patch) | |
tree | 0f6354ccac934ed87a2d555f45be4c831cf92f4a /src/collectors/network-viewer.plugin/network-viewer.c | |
parent | Releasing debian version 1.44.3-2. (diff) | |
download | netdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz netdata-5da14042f70711ea5cf66e034699730335462f66.zip |
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/collectors/network-viewer.plugin/network-viewer.c | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/src/collectors/network-viewer.plugin/network-viewer.c b/src/collectors/network-viewer.plugin/network-viewer.c new file mode 100644 index 000000000..074535a0b --- /dev/null +++ b/src/collectors/network-viewer.plugin/network-viewer.c @@ -0,0 +1,803 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "collectors/all.h" +#include "libnetdata/libnetdata.h" +#include "libnetdata/required_dummies.h" + +#define ENABLE_DETAILED_VIEW + +#define LOCAL_SOCKETS_EXTENDED_MEMBERS struct { \ + size_t count; \ + const char *local_address_space; \ + const char *remote_address_space; \ + } network_viewer; + +#include "libnetdata/maps/local-sockets.h" +#include "libnetdata/maps/system-users.h" + +#define NETWORK_CONNECTIONS_VIEWER_FUNCTION "network-connections" +#define NETWORK_CONNECTIONS_VIEWER_HELP "Network connections explorer" + +#define SIMPLE_HASHTABLE_VALUE_TYPE LOCAL_SOCKET +#define SIMPLE_HASHTABLE_NAME _AGGREGATED_SOCKETS +#include "libnetdata/simple_hashtable.h" + +netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER; +static bool plugin_should_exit = false; +static USERNAMES_CACHE *uc; + +ENUM_STR_MAP_DEFINE(SOCKET_DIRECTION) = { + { .id = SOCKET_DIRECTION_LISTEN, .name = "listen" }, + { .id = SOCKET_DIRECTION_LOCAL_INBOUND, .name = "local" }, + { .id = SOCKET_DIRECTION_LOCAL_OUTBOUND, .name = "local" }, + { .id = SOCKET_DIRECTION_INBOUND, .name = "inbound" }, + { .id = SOCKET_DIRECTION_OUTBOUND, .name = "outbound" }, + + // terminator + { . id = 0, .name = NULL } +}; +ENUM_STR_DEFINE_FUNCTIONS(SOCKET_DIRECTION, SOCKET_DIRECTION_LISTEN, "unknown"); + +typedef int TCP_STATE; +ENUM_STR_MAP_DEFINE(TCP_STATE) = { + { .id = TCP_ESTABLISHED, .name = "established" }, + { .id = TCP_SYN_SENT, .name = "syn-sent" }, + { .id = TCP_SYN_RECV, .name = "syn-received" }, + { .id = TCP_FIN_WAIT1, .name = "fin1-wait1" }, + { .id = TCP_FIN_WAIT2, .name = "fin1-wait2" }, + { .id = TCP_TIME_WAIT, .name = "time-wait" }, + { .id = TCP_CLOSE, .name = "close" }, + { .id = TCP_CLOSE_WAIT, .name = "close-wait" }, + { .id = TCP_LAST_ACK, .name = "last-ack" }, + { .id = TCP_LISTEN, .name = "listen" }, + { .id = TCP_CLOSING, .name = "closing" }, + + // terminator + { . id = 0, .name = NULL } +}; +ENUM_STR_DEFINE_FUNCTIONS(TCP_STATE, 0, "unknown"); + +static void local_socket_to_json_array(BUFFER *wb, LOCAL_SOCKET *n, uint64_t proc_self_net_ns_inode, bool aggregated) { + char local_address[INET6_ADDRSTRLEN]; + char remote_address[INET6_ADDRSTRLEN]; + char *protocol; + + if(n->local.family == AF_INET) { + ipv4_address_to_txt(n->local.ip.ipv4, local_address); + ipv4_address_to_txt(n->remote.ip.ipv4, remote_address); + protocol = n->local.protocol == IPPROTO_TCP ? "tcp4" : "udp4"; + } + else if(n->local.family == AF_INET6) { + ipv6_address_to_txt(&n->local.ip.ipv6, local_address); + ipv6_address_to_txt(&n->remote.ip.ipv6, remote_address); + protocol = n->local.protocol == IPPROTO_TCP ? "tcp6" : "udp6"; + } + else + return; + + const char *type; + if(n->net_ns_inode == proc_self_net_ns_inode) + type = "system"; + else if(n->net_ns_inode == 0) + type = "[unknown]"; + else + type = "container"; + + buffer_json_add_array_item_array(wb); + { + buffer_json_add_array_item_string(wb, SOCKET_DIRECTION_2str(n->direction)); + buffer_json_add_array_item_string(wb, protocol); + buffer_json_add_array_item_string(wb, type); // system or container + if(n->local.protocol == IPPROTO_TCP) + buffer_json_add_array_item_string(wb, TCP_STATE_2str(n->state)); + else + buffer_json_add_array_item_string(wb, "stateless"); + + buffer_json_add_array_item_uint64(wb, n->pid); + + if(!n->comm[0]) + buffer_json_add_array_item_string(wb, "[unknown]"); + else + buffer_json_add_array_item_string(wb, n->comm); + + // buffer_json_add_array_item_string(wb, string2str(n->cmdline)); + + if(n->uid == UID_UNSET) { + // buffer_json_add_array_item_uint64(wb, n->uid); + buffer_json_add_array_item_string(wb, "[unknown]"); + } + else { + // buffer_json_add_array_item_uint64(wb, n->uid); + STRING *u = system_usernames_cache_lookup_uid(uc, n->uid); + buffer_json_add_array_item_string(wb, string2str(u)); + string_freez(u); + } + + if(!aggregated) { + buffer_json_add_array_item_string(wb, local_address); + buffer_json_add_array_item_uint64(wb, n->local.port); + } + buffer_json_add_array_item_string(wb, n->network_viewer.local_address_space); + + if(!aggregated) { + buffer_json_add_array_item_string(wb, remote_address); + buffer_json_add_array_item_uint64(wb, n->remote.port); + } + buffer_json_add_array_item_string(wb, n->network_viewer.remote_address_space); + + uint16_t server_port; + const char *server_address; + const char *client_address_space; + const char *server_address_space; + switch (n->direction) { + case SOCKET_DIRECTION_LISTEN: + case SOCKET_DIRECTION_INBOUND: + case SOCKET_DIRECTION_LOCAL_INBOUND: + server_port = n->local.port; + server_address = local_address; + server_address_space = n->network_viewer.local_address_space; + client_address_space = n->network_viewer.remote_address_space; + break; + + case SOCKET_DIRECTION_OUTBOUND: + case SOCKET_DIRECTION_LOCAL_OUTBOUND: + server_port = n->remote.port; + server_address = remote_address; + server_address_space = n->network_viewer.remote_address_space; + client_address_space = n->network_viewer.local_address_space; + break; + + case SOCKET_DIRECTION_NONE: + break; + } + if(aggregated) + buffer_json_add_array_item_string(wb, server_address); + + buffer_json_add_array_item_uint64(wb, server_port); + + if(aggregated) { + buffer_json_add_array_item_string(wb, client_address_space); + buffer_json_add_array_item_string(wb, server_address_space); + } + + // buffer_json_add_array_item_uint64(wb, n->inode); + // buffer_json_add_array_item_uint64(wb, n->net_ns_inode); + buffer_json_add_array_item_uint64(wb, n->network_viewer.count); + } + buffer_json_array_close(wb); +} + +static void local_sockets_cb_to_json(LS_STATE *ls, LOCAL_SOCKET *n, void *data) { + n->network_viewer.count = 1; + n->network_viewer.local_address_space = local_sockets_address_space(&n->local); + n->network_viewer.remote_address_space = local_sockets_address_space(&n->remote); + local_socket_to_json_array(data, n, ls->proc_self_net_ns_inode, false); +} + +static void local_sockets_cb_to_aggregation(LS_STATE *ls __maybe_unused, LOCAL_SOCKET *n, void *data) { + SIMPLE_HASHTABLE_AGGREGATED_SOCKETS *ht = data; + n->network_viewer.count = 1; + n->network_viewer.local_address_space = local_sockets_address_space(&n->local); + n->network_viewer.remote_address_space = local_sockets_address_space(&n->remote); + + switch(n->direction) { + case SOCKET_DIRECTION_INBOUND: + case SOCKET_DIRECTION_LOCAL_INBOUND: + case SOCKET_DIRECTION_LISTEN: + memset(&n->remote.ip, 0, sizeof(n->remote.ip)); + n->remote.port = 0; + break; + + case SOCKET_DIRECTION_OUTBOUND: + case SOCKET_DIRECTION_LOCAL_OUTBOUND: + memset(&n->local.ip, 0, sizeof(n->local.ip)); + n->local.port = 0; + break; + + case SOCKET_DIRECTION_NONE: + return; + } + + n->inode = 0; + n->local_ip_hash = 0; + n->remote_ip_hash = 0; + n->local_port_hash = 0; + n->timer = 0; + n->retransmits = 0; + n->expires = 0; + n->rqueue = 0; + n->wqueue = 0; + memset(&n->local_port_key, 0, sizeof(n->local_port_key)); + + XXH64_hash_t hash = XXH3_64bits(n, sizeof(*n)); + SIMPLE_HASHTABLE_SLOT_AGGREGATED_SOCKETS *sl = simple_hashtable_get_slot_AGGREGATED_SOCKETS(ht, hash, n, true); + LOCAL_SOCKET *t = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(t) { + t->network_viewer.count++; + } + else { + t = mallocz(sizeof(*t)); + memcpy(t, n, sizeof(*t)); + t->cmdline = string_dup(t->cmdline); + simple_hashtable_set_slot_AGGREGATED_SOCKETS(ht, sl, hash, t); + } +} + +static int local_sockets_compar(const void *a, const void *b) { + LOCAL_SOCKET *n1 = *(LOCAL_SOCKET **)a, *n2 = *(LOCAL_SOCKET **)b; + return strcmp(n1->comm, n2->comm); +} + +void network_viewer_function(const char *transaction, char *function __maybe_unused, usec_t *stop_monotonic_ut __maybe_unused, + bool *cancelled __maybe_unused, BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused, + const char *source __maybe_unused, void *data __maybe_unused) { + + time_t now_s = now_realtime_sec(); + bool aggregated = false; + + CLEAN_BUFFER *wb = buffer_create(0, NULL); + buffer_flush(wb); + wb->content_type = CT_APPLICATION_JSON; + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY); + + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK); + buffer_json_member_add_string(wb, "type", "table"); + buffer_json_member_add_time_t(wb, "update_every", 5); + buffer_json_member_add_boolean(wb, "has_history", false); + buffer_json_member_add_string(wb, "help", NETWORK_CONNECTIONS_VIEWER_HELP); + +#ifdef ENABLE_DETAILED_VIEW + buffer_json_member_add_array(wb, "accepted_params"); + { + buffer_json_add_array_item_string(wb, "sockets"); + } + buffer_json_array_close(wb); // accepted_params + buffer_json_member_add_array(wb, "required_params"); + { + buffer_json_add_array_item_object(wb); + { + buffer_json_member_add_string(wb, "id", "sockets"); + buffer_json_member_add_string(wb, "name", "Sockets"); + buffer_json_member_add_string(wb, "help", "Select the source type to query"); + buffer_json_member_add_boolean(wb, "unique_view", true); + buffer_json_member_add_string(wb, "type", "select"); + buffer_json_member_add_array(wb, "options"); + { + buffer_json_add_array_item_object(wb); + { + buffer_json_member_add_string(wb, "id", "aggregated"); + buffer_json_member_add_string(wb, "name", "Aggregated view of sockets"); + } + buffer_json_object_close(wb); + buffer_json_add_array_item_object(wb); + { + buffer_json_member_add_string(wb, "id", "detailed"); + buffer_json_member_add_string(wb, "name", "Detailed view of all sockets"); + } + buffer_json_object_close(wb); + } + buffer_json_array_close(wb); // options array + } + buffer_json_object_close(wb); + } + buffer_json_array_close(wb); // required_params +#endif + + char function_copy[strlen(function) + 1]; + memcpy(function_copy, function, sizeof(function_copy)); + char *words[1024]; + size_t num_words = quoted_strings_splitter_pluginsd(function_copy, words, 1024); + for(size_t i = 1; i < num_words ;i++) { + char *param = get_word(words, num_words, i); + if(strcmp(param, "sockets:aggregated") == 0) { + aggregated = true; + } + else if(strcmp(param, "sockets:detailed") == 0) { + aggregated = false; + } + else if(strcmp(param, "info") == 0) { + goto close_and_send; + } + } + + if(aggregated) { + buffer_json_member_add_object(wb, "aggregated_view"); + { + buffer_json_member_add_string(wb, "column", "Count"); + buffer_json_member_add_string(wb, "results_label", "unique combinations"); + buffer_json_member_add_string(wb, "aggregated_label", "sockets"); + } + buffer_json_object_close(wb); + } + + { + buffer_json_member_add_array(wb, "data"); + + LS_STATE ls = { + .config = { + .listening = true, + .inbound = true, + .outbound = true, + .local = true, + .tcp4 = true, + .tcp6 = true, + .udp4 = true, + .udp6 = true, + .pid = true, + .uid = true, + .cmdline = true, + .comm = true, + .namespaces = true, + + .max_errors = 10, + }, + .stats = { 0 }, + .sockets_hashtable = { 0 }, + .local_ips_hashtable = { 0 }, + .listening_ports_hashtable = { 0 }, + }; + + SIMPLE_HASHTABLE_AGGREGATED_SOCKETS ht = { 0 }; + if(aggregated) { + simple_hashtable_init_AGGREGATED_SOCKETS(&ht, 1024); + ls.config.cb = local_sockets_cb_to_aggregation; + ls.config.data = &ht; + } + else { + ls.config.cb = local_sockets_cb_to_json; + ls.config.data = wb; + } + + local_sockets_process(&ls); + + if(aggregated) { + LOCAL_SOCKET *array[ht.used]; + size_t added = 0; + uint64_t proc_self_net_ns_inode = ls.proc_self_net_ns_inode; + for(SIMPLE_HASHTABLE_SLOT_AGGREGATED_SOCKETS *sl = simple_hashtable_first_read_only_AGGREGATED_SOCKETS(&ht); + sl; + sl = simple_hashtable_next_read_only_AGGREGATED_SOCKETS(&ht, sl)) { + LOCAL_SOCKET *n = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(!n || added >= ht.used) continue; + + array[added++] = n; + } + + qsort(array, added, sizeof(LOCAL_SOCKET *), local_sockets_compar); + + for(size_t i = 0; i < added ;i++) { + local_socket_to_json_array(wb, array[i], proc_self_net_ns_inode, true); + string_freez(array[i]->cmdline); + freez(array[i]); + } + + simple_hashtable_destroy_AGGREGATED_SOCKETS(&ht); + } + + buffer_json_array_close(wb); + buffer_json_member_add_object(wb, "columns"); + { + size_t field_id = 0; + + // Direction + buffer_rrdf_table_add_field(wb, field_id++, "Direction", "Socket Direction", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_STICKY, + NULL); + + // Protocol + buffer_rrdf_table_add_field(wb, field_id++, "Protocol", "Socket Protocol", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + // Type + buffer_rrdf_table_add_field(wb, field_id++, "Namespace", "Namespace", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + // State + buffer_rrdf_table_add_field(wb, field_id++, "State", "Socket State", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + // Pid + buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + // Comm + buffer_rrdf_table_add_field(wb, field_id++, "Process", "Process Name", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH, + NULL); + +// // Cmdline +// buffer_rrdf_table_add_field(wb, field_id++, "CommandLine", "Command Line", +// RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, +// 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, +// RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, +// RRDF_FIELD_OPTS_NONE|RRDF_FIELD_OPTS_FULL_WIDTH, +// NULL); + +// // Uid +// buffer_rrdf_table_add_field(wb, field_id++, "UID", "User ID", +// RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, +// 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, +// RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, +// RRDF_FIELD_OPTS_NONE, +// NULL); + + // Username + buffer_rrdf_table_add_field(wb, field_id++, "User", "Username", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + if(!aggregated) { + // Local Address + buffer_rrdf_table_add_field(wb, field_id++, "LocalIP", "Local IP Address", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH, + NULL); + + // Local Port + buffer_rrdf_table_add_field(wb, field_id++, "LocalPort", "Local Port", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + } + + // Local Address Space + buffer_rrdf_table_add_field(wb, field_id++, "LocalAddressSpace", "Local IP Address Space", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_NONE, + NULL); + + if(!aggregated) { + // Remote Address + buffer_rrdf_table_add_field(wb, field_id++, "RemoteIP", "Remote IP Address", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH, + NULL); + + // Remote Port + buffer_rrdf_table_add_field(wb, field_id++, "RemotePort", "Remote Port", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + } + + // Remote Address Space + buffer_rrdf_table_add_field(wb, field_id++, "RemoteAddressSpace", "Remote IP Address Space", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_NONE, + NULL); + + if(aggregated) { + // Server IP + buffer_rrdf_table_add_field(wb, field_id++, "ServerIP", "Server IP Address", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_FULL_WIDTH | (aggregated ? RRDF_FIELD_OPTS_VISIBLE : RRDF_FIELD_OPTS_NONE), + NULL); + } + + // Server Port + buffer_rrdf_table_add_field(wb, field_id++, "ServerPort", "Server Port", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + aggregated ? RRDF_FIELD_OPTS_VISIBLE : RRDF_FIELD_OPTS_NONE, + NULL); + + if(aggregated) { + // Client Address Space + buffer_rrdf_table_add_field(wb, field_id++, "ClientAddressSpace", "Client IP Address Space", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + + // Server Address Space + buffer_rrdf_table_add_field(wb, field_id++, "ServerAddressSpace", "Server IP Address Space", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + } + +// // inode +// buffer_rrdf_table_add_field(wb, field_id++, "Inode", "Socket Inode", +// RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, +// 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, +// RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, +// RRDF_FIELD_OPTS_NONE, +// NULL); + +// // Namespace inode +// buffer_rrdf_table_add_field(wb, field_id++, "Namespace Inode", "Namespace Inode", +// RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, +// 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, +// RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, +// RRDF_FIELD_OPTS_NONE, +// NULL); + + // Count + buffer_rrdf_table_add_field(wb, field_id++, "Count", "Number of sockets like this", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_DESCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE, + aggregated ? (RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY) : RRDF_FIELD_OPTS_NONE, + NULL); + } + buffer_json_object_close(wb); // columns + buffer_json_member_add_string(wb, "default_sort_column", aggregated ? "Count" : "Direction"); + + buffer_json_member_add_object(wb, "custom_charts"); + { + buffer_json_member_add_object(wb, "Network Map"); + { + buffer_json_member_add_string(wb, "type", "network-viewer"); + } + buffer_json_object_close(wb); + } + buffer_json_object_close(wb); // custom_charts + + buffer_json_member_add_object(wb, "charts"); + { + // Data Collection Age chart + buffer_json_member_add_object(wb, "Count"); + { + buffer_json_member_add_string(wb, "type", "stacked-bar"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Direction"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + // Streaming Age chart + buffer_json_member_add_object(wb, "Count"); + { + buffer_json_member_add_string(wb, "type", "stacked-bar"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Process"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + // DB Duration + buffer_json_member_add_object(wb, "Count"); + { + buffer_json_member_add_string(wb, "type", "stacked-bar"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Protocol"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + } + buffer_json_object_close(wb); // charts + + buffer_json_member_add_array(wb, "default_charts"); + { + buffer_json_add_array_item_array(wb); + buffer_json_add_array_item_string(wb, "Count"); + buffer_json_add_array_item_string(wb, "Direction"); + buffer_json_array_close(wb); + + buffer_json_add_array_item_array(wb); + buffer_json_add_array_item_string(wb, "Count"); + buffer_json_add_array_item_string(wb, "Process"); + buffer_json_array_close(wb); + } + buffer_json_array_close(wb); + + buffer_json_member_add_object(wb, "group_by"); + { + buffer_json_member_add_object(wb, "Direction"); + { + buffer_json_member_add_string(wb, "name", "Direction"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Direction"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "Protocol"); + { + buffer_json_member_add_string(wb, "name", "Protocol"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Protocol"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "Namespace"); + { + buffer_json_member_add_string(wb, "name", "Namespace"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Namespace"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "Process"); + { + buffer_json_member_add_string(wb, "name", "Process"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Process"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + if(!aggregated) { + buffer_json_member_add_object(wb, "LocalIP"); + { + buffer_json_member_add_string(wb, "name", "Local IP"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "LocalIP"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "LocalPort"); + { + buffer_json_member_add_string(wb, "name", "Local Port"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "LocalPort"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "RemoteIP"); + { + buffer_json_member_add_string(wb, "name", "Remote IP"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "RemoteIP"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + + buffer_json_member_add_object(wb, "RemotePort"); + { + buffer_json_member_add_string(wb, "name", "Remote Port"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "RemotePort"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + } + } + buffer_json_object_close(wb); // group_by + } + +close_and_send: + buffer_json_member_add_time_t(wb, "expires", now_s + 1); + buffer_json_finalize(wb); + + netdata_mutex_lock(&stdout_mutex); + pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", now_s + 1, wb); + netdata_mutex_unlock(&stdout_mutex); +} + +// ---------------------------------------------------------------------------------------------------------------- +// main + +int main(int argc __maybe_unused, char **argv __maybe_unused) { + clocks_init(); + netdata_thread_set_tag("NETWORK-VIEWER"); + nd_log_initialize_for_external_plugins("network-viewer.plugin"); + + netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX"); + if(verify_netdata_host_prefix(true) == -1) exit(1); + + uc = system_usernames_cache_init(); + + // ---------------------------------------------------------------------------------------------------------------- + + if(argc == 2 && strcmp(argv[1], "debug") == 0) { + bool cancelled = false; + usec_t stop_monotonic_ut = now_monotonic_usec() + 600 * USEC_PER_SEC; + char buf[] = "network-connections sockets:aggregated"; + network_viewer_function("123", buf, &stop_monotonic_ut, &cancelled, + NULL, HTTP_ACCESS_ALL, NULL, NULL); + + char buf2[] = "network-connections sockets:detailed"; + network_viewer_function("123", buf2, &stop_monotonic_ut, &cancelled, + NULL, HTTP_ACCESS_ALL, NULL, NULL); + exit(1); + } + + // ---------------------------------------------------------------------------------------------------------------- + + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", + NETWORK_CONNECTIONS_VIEWER_FUNCTION, 60, + NETWORK_CONNECTIONS_VIEWER_HELP, + (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA), + RRDFUNCTIONS_PRIORITY_DEFAULT); + + // ---------------------------------------------------------------------------------------------------------------- + + struct functions_evloop_globals *wg = + functions_evloop_init(5, "Network-Viewer", &stdout_mutex, &plugin_should_exit); + + functions_evloop_add_function(wg, NETWORK_CONNECTIONS_VIEWER_FUNCTION, + network_viewer_function, + PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, + NULL); + + // ---------------------------------------------------------------------------------------------------------------- + + usec_t step_ut = 100 * USEC_PER_MS; + usec_t send_newline_ut = 0; + bool tty = isatty(fileno(stdout)) == 1; + + heartbeat_t hb; + heartbeat_init(&hb); + while(!plugin_should_exit) { + + usec_t dt_ut = heartbeat_next(&hb, step_ut); + send_newline_ut += dt_ut; + + if(!tty && send_newline_ut > USEC_PER_SEC) { + send_newline_and_flush(&stdout_mutex); + send_newline_ut = 0; + } + } + + return 0; +} |