summaryrefslogtreecommitdiffstats
path: root/collectors
diff options
context:
space:
mode:
authorthiagoftsm <thiagoftsm@gmail.com>2020-08-14 15:16:13 +0000
committerGitHub <noreply@github.com>2020-08-14 15:16:13 +0000
commitabd8e61b649136698307b5509d3d735735b291dd (patch)
treed0d0f527faebe392021f34d1122b6db0f39f62ca /collectors
parent87fbd817994eefb40c10084fa3cee757ac1cbb8b (diff)
Network viewer charts (#9591)
Bring next steps to build the Network Viewer charts.
Diffstat (limited to 'collectors')
-rw-r--r--collectors/ebpf.plugin/ebpf.c107
-rw-r--r--collectors/ebpf.plugin/ebpf.conf6
-rw-r--r--collectors/ebpf.plugin/ebpf.h6
-rw-r--r--collectors/ebpf.plugin/ebpf_socket.c577
-rw-r--r--collectors/ebpf.plugin/ebpf_socket.h62
5 files changed, 661 insertions, 97 deletions
diff --git a/collectors/ebpf.plugin/ebpf.c b/collectors/ebpf.plugin/ebpf.c
index 719aeed5d2..4457723811 100644
--- a/collectors/ebpf.plugin/ebpf.c
+++ b/collectors/ebpf.plugin/ebpf.c
@@ -104,11 +104,14 @@ netdata_ebpf_events_t socket_probes[] = {
ebpf_module_t ebpf_modules[] = {
{ .thread_name = "process", .config_name = "process", .enabled = 0, .start_routine = ebpf_process_thread,
- .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = process_probes },
+ .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = process_probes,
+ .optional = 0 },
{ .thread_name = "socket", .config_name = "network viewer", .enabled = 0, .start_routine = ebpf_socket_thread,
- .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = socket_probes },
+ .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = socket_probes,
+ .optional = 0 },
{ .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1,
- .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY, .probes = NULL },
+ .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY, .probes = NULL,
+ .optional = 0 },
};
// Link with apps.plugin
@@ -116,9 +119,10 @@ pid_t *pid_index;
ebpf_process_stat_t *global_process_stat = NULL;
//Network viewer
-ebpf_network_viewer_options_t network_viewer_opt = { .max_dim = 500, .name_resolution_enabled = 0,
- .excluded_port = NULL, .included_port = NULL,
- .names = NULL, .ipv4_local_ip = NULL, .ipv6_local_ip = NULL };
+ebpf_network_viewer_options_t network_viewer_opt = { .max_dim = NETDATA_NV_CAP_VALUE, .hostname_resolution_enabled = 0,
+ .service_resolution_enabled = 0, .excluded_port = NULL,
+ .included_port = NULL, .names = NULL, .ipv4_local_ip = NULL,
+ .ipv6_local_ip = NULL };
/*****************************************************************
*
@@ -699,6 +703,43 @@ static inline void fill_ip_list(ebpf_network_viewer_ip_list_t **out, ebpf_networ
#endif
}
+/**
+ * Read Local Ports
+ *
+ * Parse /proc/net/{tcp,udp} and get the ports Linux is listening.
+ *
+ * @param filename the proc file to parse.
+ * @param proto is the magic number associated to the protocol file we are reading.
+ */
+static void read_local_ports(char *filename, uint8_t proto)
+{
+ procfile *ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ if (!ff)
+ return;
+
+ ff = procfile_readall(ff);
+ if (!ff)
+ return;
+
+ size_t lines = procfile_lines(ff), l;
+ for(l = 0; l < lines ;l++) {
+ size_t words = procfile_linewords(ff, l);
+ // This is header or end of file
+ if (unlikely(words < 14))
+ continue;
+
+ // https://elixir.bootlin.com/linux/v5.7.8/source/include/net/tcp_states.h
+ // 0A = TCP_LISTEN
+ if (strcmp("0A", procfile_lineword(ff, l, 5)))
+ continue;
+
+ // Read local port
+ uint16_t port = (uint16_t)strtol(procfile_lineword(ff, l, 2), NULL, 16);
+ update_listen_table(htons(port), proto);
+ }
+
+ procfile_close(ff);
+}
/**
* Read Local addresseses
@@ -1480,25 +1521,54 @@ static void link_hostnames(char *parse)
}
/**
+ * Read max dimension.
+ *
+ * Netdata plot two dimensions per connection, so it is necessary to adjust the values.
+ */
+static void read_max_dimension()
+{
+ int maxdim ;
+ maxdim = (int) appconfig_get_number(&collector_config,
+ EBPF_NETWORK_VIEWER_SECTION,
+ "maximum dimensions",
+ NETDATA_NV_CAP_VALUE);
+ if (maxdim < 0) {
+ error("'maximum dimensions = %d' must be a positive number, Netdata will change for default value %ld.",
+ maxdim, NETDATA_NV_CAP_VALUE);
+ maxdim = NETDATA_NV_CAP_VALUE;
+ }
+
+ maxdim /= 2;
+ if (!maxdim) {
+ info("The number of dimensions is too small (%u), we are setting it to minimum 2", network_viewer_opt.max_dim);
+ network_viewer_opt.max_dim = 1;
+ }
+
+ network_viewer_opt.max_dim = (uint32_t)maxdim;
+}
+
+/**
* Parse network viewer section
*/
static void parse_network_viewer_section()
{
- network_viewer_opt.max_dim = appconfig_get_number(&collector_config,
- EBPF_NETWORK_VIEWER_SECTION,
- "maximum dimensions",
- 50);
+ read_max_dimension();
- network_viewer_opt.name_resolution_enabled = appconfig_get_boolean(&collector_config,
+ network_viewer_opt.hostname_resolution_enabled = appconfig_get_boolean(&collector_config,
EBPF_NETWORK_VIEWER_SECTION,
- "resolve hostname ips",
+ "resolve hostnames",
0);
+ network_viewer_opt.service_resolution_enabled = appconfig_get_boolean(&collector_config,
+ EBPF_NETWORK_VIEWER_SECTION,
+ "resolve service names",
+ 0);
+
char *value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION,
"ports", NULL);
parse_ports(value);
- if (network_viewer_opt.name_resolution_enabled) {
+ if (network_viewer_opt.hostname_resolution_enabled) {
value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION, "hostnames", NULL);
link_hostnames(value);
} else {
@@ -1609,7 +1679,8 @@ static void read_collector_values(int *disable_apps)
*disable_apps = parse_disable_apps(value);
// Read ebpf programs section
- uint32_t enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, ebpf_modules[0].config_name, 1);
+ uint32_t enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, ebpf_modules[0].config_name,
+ 1);
int started = 0;
if (enabled) {
ebpf_enable_chart(EBPF_MODULE_PROCESS_IDX, *disable_apps);
@@ -1625,6 +1696,10 @@ static void read_collector_values(int *disable_apps)
started++;
}
+ enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "network connection monitoring",
+ 0);
+ ebpf_modules[1].optional = enabled;
+
if (!started){
ebpf_enable_all_charts(*disable_apps);
// Read network viewer section
@@ -1882,6 +1957,10 @@ int main(int argc, char **argv)
ebpf_allocate_common_vectors();
read_local_addresses();
+ read_local_ports("/proc/net/tcp", IPPROTO_TCP);
+ read_local_ports("/proc/net/tcp6", IPPROTO_TCP);
+ read_local_ports("/proc/net/udp", IPPROTO_UDP);
+ read_local_ports("/proc/net/udp6", IPPROTO_UDP);
struct netdata_static_thread ebpf_threads[] = {
{"EBPF PROCESS", NULL, NULL, 1, NULL, NULL, ebpf_modules[0].start_routine},
diff --git a/collectors/ebpf.plugin/ebpf.conf b/collectors/ebpf.plugin/ebpf.conf
index 0092a12a57..4e935700fa 100644
--- a/collectors/ebpf.plugin/ebpf.conf
+++ b/collectors/ebpf.plugin/ebpf.conf
@@ -5,10 +5,12 @@
[ebpf programs]
process = yes
network viewer = yes
+ network connection monitoring = no
[network viewer]
- maximum dimensions = 500
- resolve hostname ips = no
+ maximum dimensions = 50
+ resolve hostnames = no
+ resolve service names = no
ports = *
ips = !127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
hostnames = *
diff --git a/collectors/ebpf.plugin/ebpf.h b/collectors/ebpf.plugin/ebpf.h
index aa7490f24a..fc7d31cab9 100644
--- a/collectors/ebpf.plugin/ebpf.h
+++ b/collectors/ebpf.plugin/ebpf.h
@@ -85,6 +85,7 @@ typedef struct ebpf_module {
netdata_run_mode_t mode;
netdata_ebpf_events_t *probes;
uint32_t thread_id;
+ int optional;
} ebpf_module_t;
extern ebpf_module_t ebpf_modules[];
@@ -189,9 +190,10 @@ extern void write_end_chart();
#define EBPF_NETWORK_VIEWER_SECTION "network viewer"
#define EBPF_SERVICE_NAME_SECTION "service name"
-#define EBPF_COMMON_DIMENSION_CALL "Calls"
+#define EBPF_COMMON_DIMENSION_CALL "calls"
#define EBPF_COMMON_DIMENSION_BYTESS "bytes/s"
-#define EBPF_COMMON_DIMENSION_DIFFERENCE "Difference"
+#define EBPF_COMMON_DIMENSION_DIFFERENCE "difference"
+#define EBPF_COMMON_DIMENSION_PACKETS "packets"
// Common variables
extern char *ebpf_user_config_dir;
diff --git a/collectors/ebpf.plugin/ebpf_socket.c b/collectors/ebpf.plugin/ebpf_socket.c
index 2d5a1822f6..c651dc4f53 100644
--- a/collectors/ebpf.plugin/ebpf_socket.c
+++ b/collectors/ebpf.plugin/ebpf_socket.c
@@ -27,6 +27,8 @@ static ebpf_socket_publish_apps_t **socket_bandwidth_prev = NULL;
static ebpf_bandwidth_t *bandwidth_vector = NULL;
static int socket_apps_created = 0;
+pthread_mutex_t nv_mutex;
+int wait_to_plot = 0;
netdata_vector_plot_t inbound_vectors = { .plot = NULL, .next = 0, .last = 0 };
netdata_vector_plot_t outbound_vectors = { .plot = NULL, .next = 0, .last = 0 };
@@ -89,6 +91,171 @@ static void ebpf_update_global_publish(
}
/**
+ * Update Network Viewer plot data
+ *
+ * @param plot the structure where the data will be stored
+ * @param sock the last update from the socket
+ */
+static inline void update_nv_plot_data(netdata_plot_values_t *plot, netdata_socket_t *sock)
+{
+ if (sock->ct > plot->last_time) {
+ plot->last_time = sock->ct;
+ plot->plot_recv_packets = sock->recv_packets;
+ plot->plot_sent_packets = sock->sent_packets;
+ plot->plot_recv_bytes = sock->recv_bytes;
+ plot->plot_sent_bytes = sock->sent_bytes;
+ plot->plot_retransmit = sock->retransmit;
+ }
+
+ sock->recv_packets = 0;
+ sock->sent_packets = 0;
+ sock->recv_bytes = 0;
+ sock->sent_bytes = 0;
+ sock->retransmit = 0;
+}
+
+/**
+ * Calculate Network Viewer Plot
+ *
+ * Do math with collected values before to plot data.
+ */
+static inline void calculate_nv_plot()
+{
+ uint32_t i;
+ uint32_t end = inbound_vectors.next;
+ for (i = 0; i < end; i++) {
+ update_nv_plot_data(&inbound_vectors.plot[i].plot, &inbound_vectors.plot[i].sock);
+ }
+ inbound_vectors.max_plot = end;
+
+ // The 'Other' dimension is always calculated for the chart to have at least one dimension
+ update_nv_plot_data(&inbound_vectors.plot[inbound_vectors.last].plot,
+ &inbound_vectors.plot[inbound_vectors.last].sock);
+
+ end = outbound_vectors.next;
+ for (i = 0; i < end; i++) {
+ update_nv_plot_data(&outbound_vectors.plot[i].plot, &outbound_vectors.plot[i].sock);
+ }
+ outbound_vectors.max_plot = end;
+
+ // The 'Other' dimension is always calculated for the chart to have at least one dimension
+ update_nv_plot_data(&outbound_vectors.plot[outbound_vectors.last].plot,
+ &outbound_vectors.plot[outbound_vectors.last].sock);
+}
+
+/**
+ * Network viewer send bytes
+ *
+ * @param ptr the structure with values to plot
+ * @param chart the chart name.
+ */
+static inline void ebpf_socket_nv_send_bytes(netdata_vector_plot_t *ptr, char *chart)
+{
+ uint32_t i;
+ uint32_t end = ptr->last_plot;
+ netdata_socket_plot_t *w = ptr->plot;
+ collected_number value;
+
+ write_begin_chart(NETDATA_EBPF_FAMILY, chart);
+ for (i = 0; i < end; i++) {
+ value = ((collected_number) w[i].plot.plot_sent_bytes);
+ write_chart_dimension(w[i].dimension_sent, value);
+ value = (collected_number) w[i].plot.plot_recv_bytes;
+ write_chart_dimension(w[i].dimension_recv, value);
+ }
+
+ i = ptr->last;
+ value = ((collected_number) w[i].plot.plot_sent_bytes);
+ write_chart_dimension(w[i].dimension_sent, value);
+ value = (collected_number) w[i].plot.plot_recv_bytes;
+ write_chart_dimension(w[i].dimension_recv, value);
+ write_end_chart();
+}
+
+/**
+ * Network Viewer Send packets
+ *
+ * @param ptr the structure with values to plot
+ * @param chart the chart name.
+ */
+static inline void ebpf_socket_nv_send_packets(netdata_vector_plot_t *ptr, char *chart)
+{
+ uint32_t i;
+ uint32_t end = ptr->last_plot;
+ netdata_socket_plot_t *w = ptr->plot;
+ collected_number value;
+
+ write_begin_chart(NETDATA_EBPF_FAMILY, chart);
+ for (i = 0; i < end; i++) {
+ value = ((collected_number)w[i].plot.plot_sent_packets);
+ write_chart_dimension(w[i].dimension_sent, value);
+ value = (collected_number) w[i].plot.plot_recv_packets;
+ write_chart_dimension(w[i].dimension_recv, value);
+ }
+
+ i = ptr->last;
+ value = ((collected_number)w[i].plot.plot_sent_packets);
+ write_chart_dimension(w[i].dimension_sent, value);
+ value = (collected_number)w[i].plot.plot_recv_packets;
+ write_chart_dimension(w[i].dimension_recv, value);
+ write_end_chart();
+}
+
+/**
+ * Network Viewer Send Retransmit
+ *
+ * @param ptr the structure with values to plot
+ * @param chart the chart name.
+ */
+static inline void ebpf_socket_nv_send_retransmit(netdata_vector_plot_t *ptr, char *chart)
+{
+ uint32_t i;
+ uint32_t end = ptr->last_plot;
+ netdata_socket_plot_t *w = ptr->plot;
+ collected_number value;
+
+ write_begin_chart(NETDATA_EBPF_FAMILY, chart);
+ for (i = 0; i < end; i++) {
+ value = (collected_number) w[i].plot.plot_retransmit;
+ write_chart_dimension(w[i].dimension_retransmit, value);
+ }
+
+ i = ptr->last;
+ value = (collected_number)w[i].plot.plot_retransmit;
+ write_chart_dimension(w[i].dimension_retransmit, value);
+ write_end_chart();
+}
+
+/**
+ * Send network viewer data
+ *
+ * @param ptr the pointer to plot data
+ */
+static void ebpf_socket_send_nv_data(netdata_vector_plot_t *ptr)
+{
+ if (!ptr->flags)
+ return;
+
+ if (ptr == (netdata_vector_plot_t *)&outbound_vectors) {
+ ebpf_socket_nv_send_bytes(ptr, NETDATA_NV_OUTBOUND_BYTES);
+ fflush(stdout);
+
+ ebpf_socket_nv_send_packets(ptr, NETDATA_NV_OUTBOUND_PACKETS);
+ fflush(stdout);
+
+ ebpf_socket_nv_send_retransmit(ptr, NETDATA_NV_OUTBOUND_RETRANSMIT);
+ fflush(stdout);
+ } else {
+ ebpf_socket_nv_send_bytes(ptr, NETDATA_NV_INBOUND_BYTES);
+ fflush(stdout);
+
+ ebpf_socket_nv_send_packets(ptr, NETDATA_NV_INBOUND_PACKETS);
+ fflush(stdout);
+ }
+}
+
+
+/**
* Update the publish strctures to create the dimenssions
*
* @param curr Last values read from memory.
@@ -308,6 +475,131 @@ void ebpf_socket_create_apps_charts(ebpf_module_t *em, struct target *root)
socket_apps_created = 1;
}
+/**
+ * Create network viewer chart
+ *
+ * Create common charts.
+ *
+ * @param id the chart id
+ * @param title the chart title
+ * @param units the units label
+ * @param family the group name used to attach the chart on dashaboard
+ * @param order the chart order
+ * @param ptr the plot structure with values.
+ */
+static void ebpf_socket_create_nv_chart(char *id, char *title, char *units,
+ char *family, int order, netdata_vector_plot_t *ptr)
+{
+ ebpf_write_chart_cmd(NETDATA_EBPF_FAMILY,
+ id,
+ title,
+ units,
+ family,
+ "stacked",
+ order);
+
+ uint32_t i;
+ uint32_t end = ptr->last_plot;
+ netdata_socket_plot_t *w = ptr->plot;
+ for (i = 0; i < end; i++) {
+ fprintf(stdout, "DIMENSION %s '' incremental -1 1\n", w[i].dimension_sent);
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w[i].dimension_recv);
+ }
+
+ end = ptr->last;
+ fprintf(stdout, "DIMENSION %s '' incremental -1 1\n", w[end].dimension_sent);
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w[end].dimension_recv);
+}
+
+/**
+ * Create network viewer retransmit
+ *
+ * Create a specific chart.
+ *
+ * @param id the chart id
+ * @param title the chart title
+ * @param units the units label
+ * @param family the group name used to attach the chart on dashaboard
+ * @param order the chart order
+ * @param ptr the plot structure with values.
+ */
+static void ebpf_socket_create_nv_retransmit(char *id, char *title, char *units,
+ char *family, int order, netdata_vector_plot_t *ptr)
+{
+ ebpf_write_chart_cmd(NETDATA_EBPF_FAMILY,
+ id,
+ title,
+ units,
+ family,
+ "stacked",
+ order);
+
+ uint32_t i;
+ uint32_t end = ptr->last_plot;
+ netdata_socket_plot_t *w = ptr->plot;
+ for (i = 0; i < end; i++) {
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w[i].dimension_retransmit);
+ }
+
+ end = ptr->last;
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w[end].dimension_retransmit);
+}
+
+/**
+ * Create Network Viewer charts
+ *
+ * Recreate the charts when new sockets are created.
+ *
+ * @param ptr a pointer for inbound or outbound vectors.
+ */
+static void ebpf_socket_create_nv_charts(netdata_vector_plot_t *ptr)
+{
+ // We do not have new sockets, so we do not need move forward
+ if (ptr->max_plot == ptr->last_plot)
+ return;
+
+ ptr->last_plot = ptr->max_plot;
+
+ if (ptr == (netdata_vector_plot_t *)&outbound_vectors) {
+ ebpf_socket_create_nv_chart(NETDATA_NV_OUTBOUND_BYTES,
+ "Outbound connections (bytes).",
+ EBPF_COMMON_DIMENSION_BYTESS,
+ NETDATA_NETWORK_CONNECTIONS_GROUP,
+ 21080,
+ ptr);
+
+ ebpf_socket_create_nv_chart(NETDATA_NV_OUTBOUND_PACKETS,
+ "Outbound connections (packets)",
+ EBPF_COMMON_DIMENSION_PACKETS,
+ NETDATA_NETWORK_CONNECTIONS_GROUP,
+ 21082,
+ ptr);
+
+ ebpf_socket_create_nv_retransmit(NETDATA_NV_OUTBOUND_RETRANSMIT,
+ "Retransmitted packets",
+ EBPF_COMMON_DIMENSION_CALL,
+ NETDATA_NETWORK_CONNECTIONS_GROUP,
+ 21083,
+ ptr);
+ } else {
+ ebpf_socket_create_nv_chart(NETDATA_NV_INBOUND_BYTES,
+ "Inbound connections (bytes)",
+ EBPF_COMMON_DIMENSION_BYTESS,
+ NETDATA_NETWORK_CONNECTIONS_GROUP,
+ 21084,
+ ptr);
+
+ ebpf_socket_create_nv_chart(NETDATA_NV_INBOUND_PACKETS,
+ "Inbound connections (packets)",
+ EBPF_COMMON_DIMENSION_PACKETS,
+ NETDATA_NETWORK_CONNECTIONS_GROUP,
+ 21085,
+ ptr);
+ }
+
+ ptr->flags |= NETWORK_VIEWER_CHARTS_CREATED;
+}
+
/*****************************************************************
*
* READ INFORMATION FROM KERNEL RING
@@ -471,14 +763,30 @@ static int compare_sockets(void *a, void *b)
// We do not need to compare val2 family, because data inside hash table is always from the same family
if (val1->family == AF_INET) { //IPV4
- cmp = memcmp(&val1->index.daddr.addr32[0], &val2->index.daddr.addr32[0], sizeof(uint32_t));
- if (!cmp) {
+ if (val1->flags & NETDATA_INBOUND_DIRECTION) {
+ if (val1->index.sport == val2->index.sport)
+ cmp = 0;
+ else {
+ cmp = (val1->index.sport > val2->index.sport)?1:-1;
+ }
+ } else {
cmp = memcmp(&val1->index.dport, &val2->index.dport, sizeof(uint16_t));
+ if (!cmp) {
+ cmp = memcmp(&val1->index.daddr.addr32[0], &val2->index.daddr.addr32[0], sizeof(uint32_t));
+ }
}
} else {
- cmp = memcmp(&val1->index.daddr.addr32, &val2->index.daddr.addr32, 4*sizeof(uint32_t));
- if (!cmp) {
+ if (val1->flags & NETDATA_INBOUND_DIRECTION) {
+ if (val1->index.sport == val2->index.sport)
+ cmp = 0;
+ else {
+ cmp = (val1->index.sport > val2->index.sport)?1:-1;
+ }
+ } else {
cmp = memcmp(&val1->index.dport, &val2->index.dport, sizeof(uint16_t));
+ if (!cmp) {
+ cmp = memcmp(&val1->index.daddr.addr32, &val2->index.daddr.addr32, 4*sizeof(uint32_t));
+ }
}
}
@@ -549,7 +857,15 @@ static inline void fill_resolved_name(netdata_socket_plot_t *ptr, char *hostname
char dimname[CONFIG_MAX_NAME];
int size;
- char *protocol = (ptr->sock.protocol == IPPROTO_UDP) ? "UDP" : "TCP";
+ char *protocol;
+ if (ptr->sock.protocol == IPPROTO_UDP) {
+ protocol = "UDP";
+ } else if (ptr->sock.protocol == IPPROTO_TCP) {
+ protocol = "TCP";
+ } else {
+ protocol = "ALL";
+ }
+
if (is_outbound)
size = build_outbound_dimension_name(dimname, hostname, service_name, protocol, ptr->family);
else
@@ -562,6 +878,9 @@ static inline void fill_resolved_name(netdata_socket_plot_t *ptr, char *hostname
strcpy(&dimname[size], "recv");
ptr->dimension_recv = strdupz(dimname);
+
+ dimname[size - 1] = '\0';
+ ptr->dimension_retransmit = strdupz(dimname);
}
}
@@ -572,11 +891,10 @@ static inline void fill_resolved_name(netdata_socket_plot_t *ptr, char *hostname
*
* @param ptr a pointer to the structure where the values are stored.
* @param is_outbound is a outbound ptr value?
- * @param is_last is this the last value possible?
*
* @return It returns 1 if the name is valid and 0 otherwise.
*/
-int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
+int fill_names(netdata_socket_plot_t *ptr, int is_outbound)
{
char hostname[NI_MAXHOST], service_name[NI_MAXSERV];
if (ptr->resolved)
@@ -584,21 +902,12 @@ int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
int ret;
static int resolve_name = -1;
+ static int resolve_service = -1;
if (resolve_name == -1)
- resolve_name = network_viewer_opt.name_resolution_enabled;
-
- if (is_last) {
- char *other = { "Other" };
- // We are also copying the NULL bytes to avoid warnings in new compilers
- strncpy(hostname, other, 6);
- strncpy(service_name, other, 6);
-
- ptr->family = AF_INET;
+ resolve_name = network_viewer_opt.hostname_resolution_enabled;
- fill_resolved_name(ptr, hostname, 10 + NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH, service_name, is_outbound);
- ret = 1;
- goto laststep;
- }
+ if (resolve_service == -1)
+ resolve_service = network_viewer_opt.service_resolution_enabled;
netdata_socket_idx_t *idx = &ptr->index;
@@ -619,6 +928,11 @@ int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
ret = (!resolve_name)?-1:getnameinfo((struct sockaddr *)&myaddr, sizeof(myaddr), hostname,
sizeof(hostname), service_name, sizeof(service_name), NI_NAMEREQD);
+
+ if (!ret && !resolve_service) {
+ snprintf(service_name, sizeof(service_name), "%u", ntohs(myaddr.sin_port));
+ }
+
if (ret) {
// I cannot resolve the name, I will use the IP
if (!inet_ntop(AF_INET, &myaddr.sin_addr.s_addr, hostname, NI_MAXHOST)) {
@@ -643,12 +957,19 @@ int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
ret = (!resolve_name)?-1:getnameinfo((struct sockaddr *)&myaddr6, sizeof(myaddr6), hostname,
sizeof(hostname), service_name, sizeof(service_name), NI_NAMEREQD);
+
+ if (!ret && !resolve_service) {
+ snprintf(service_name, sizeof(service_name), "%u", ntohs(myaddr6.sin6_port));
+ }
+
if (ret) {
// I cannot resolve the name, I will use the IP
if (!inet_ntop(AF_INET6, myaddr6.sin6_addr.s6_addr, hostname, NI_MAXHOST)) {
strncpy(hostname, errname, 13);
}
+
snprintf(service_name, sizeof(service_name), "%u", ntohs(myaddr6.sin6_port));
+
ret = 1;
}
}
@@ -657,8 +978,6 @@ int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
strlen(hostname) + strlen(service_name)+ NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH,
service_name, is_outbound);
-laststep:
-
if (resolve_name && !ret)
ret = hostname_matches_pattern(hostname);
@@ -668,6 +987,35 @@ laststep:
}
/**
+ * Fill last Network Viewer Dimension
+ *
+ * Fill the unique dimension that is always plotted.
+ *
+ * @param ptr the pointer for the last dimension
+ * @param is_outbound is this an inbound structure?
+ */
+static void fill_last_nv_dimension(netdata_socket_plot_t *ptr, int is_outbound)
+{
+ char hostname[NI_MAXHOST], service_name[NI_MAXSERV];
+ char *other = { "other" };
+ // We are also copying the NULL bytes to avoid warnings in new compilers
+ strncpy(hostname, other, 6);
+ strncpy(service_name, other, 6);
+
+ ptr->family = AF_INET;
+ ptr->sock.protocol = 255;
+ ptr->flags = (!is_outbound)?NETDATA_INBOUND_DIRECTION:NETDATA_OUTBOUND_DIRECTION;
+
+ fill_resolved_name(ptr, hostname, 10 + NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH, service_name, is_outbound);
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("Last %s dimension added: ID = %u, IP = OTHER, NAME = %s, DIM1 = %s, DIM2 = %s, DIM3 = %s",
+ (is_outbound)?"outbound":"inbound", network_viewer_opt.max_dim - 1, ptr->resolved_name,
+ ptr->dimension_recv, ptr->dimension_sent, ptr->dimension_retransmit);
+#endif
+}
+
+/**
* Update Socket Data
*
* Update the socket information with last collected data
@@ -677,10 +1025,14 @@ laststep:
*/
static inline void update_socket_data(netdata_socket_t *sock, netdata_socket_t *lvalues)
{
- sock->recv_packets = lvalues->recv_packets;
- sock->sent_packets = lvalues->sent_packets;
- sock->recv_bytes = lvalues->recv_bytes;
- sock->sent_bytes = lvalues->sent_bytes;
+ sock->recv_packets += lvalues->recv_packets;
+ sock->sent_packets += lvalues->sent_packets;
+ sock->recv_bytes += lvalues->recv_bytes;
+ sock->sent_bytes += lvalues->sent_bytes;
+ sock->retransmit += lvalues->retransmit;
+
+ if (lvalues->ct > sock->ct)
+ sock->ct = lvalues->ct;
}
/**
@@ -692,17 +1044,21 @@ static inline void update_socket_data(netdata_socket_t *sock, netdata_socket_t *
* @param lvalues Values read from socket ring.
* @param lindex the index information, the real socket.
* @param family the family associated to the socket
+ * @param flags the connection flags
*/
static void store_socket_inside_avl(netdata_vector_plot_t *out, netdata_socket_t *lvalues,
- netdata_socket_idx_t *lindex, int family)
+ netdata_socket_idx_t *lindex, int family, uint32_t flags)
{
netdata_socket_plot_t test, *ret ;
- memcpy(&test.index, lindex, sizeof(*lindex));
+ memcpy(&test.index, lindex, sizeof(netdata_socket_idx_t));
+ test.flags = flags;
ret = (netdata_socket_plot_t *) avl_search_lock(&out->tree, (avl *)&test);
if (ret) {
- update_socket_data(&ret->sock, lvalues);
+ if (lvalues->ct > ret->plot.last_time) {
+ update_socket_data(&ret->sock, lvalues);
+ }
} else {
uint32_t curr = out->next;
uint32_t last = out->last;
@@ -711,36 +1067,30 @@ static void store_socket_inside_avl(netdata_vector_plot_t *out, netdata_socket_t
int resolved;
if (curr == last) {
- if (!w->resolved) {
- resolved = fill_names(w, out != (netdata_vector_plot_t *)&inbound_vectors, 1);
- UNUSED(resolved);
-#ifdef NETDATA_INTERNAL_CHECKS
- info("Last %s dimension added: ID = %u, IP = OTHER, NAME = %s, DIM1 = %s, DIM2 = %s",
- (out == &inbound_vectors)?"inbound":"outbound", curr, w->resolved_name,
- w->dimension_recv, w->dimension_sent);
-#endif
+ if (lvalues->ct > w->plot.last_time) {
+ update_socket_data(&w->sock, lvalues);
}
-
- update_socket_data(&w->sock, lvalues);
return;
} else {
- memcpy(&w->sock, lvalues, sizeof(*lvalues));
- memcpy(&w->index, lindex, sizeof(*lindex));
+ memcpy(&w->sock, lvalues, sizeof(netdata_socket_t));
+ memcpy(&w->index, lindex, sizeof(netdata_socket_idx_t));
w->family = family;
- resolved = fill_names(w, out != (netdata_vector_plot_t *)&inbound_vectors, 0);
+ resolved = fill_names(w, out != (netdata_vector_plot_t *)&inbound_vectors);
}
if (!resolved) {
freez(w->resolved_name);
freez(w->dimension_sent);
freez(w->dimension_recv);
+ freez(w->dimension_retransmit);
memset(w, 0, sizeof(netdata_socket_plot_t));
return;
}
+ w->flags = flags;
netdata_socket_plot_t *check ;
check = (netdata_socket_plot_t *) avl_insert_lock(&out->tree, (avl *)w);
if (check != w)
@@ -749,10 +1099,9 @@ static void store_socket_inside_avl(netdata_vector_plot_t *out, netdata_socket_t
#ifdef NETDATA_INTERNAL_CHECKS
char iptext[INET6_ADDRSTRLEN];
if (inet_ntop(family, &w->index.daddr.addr8, iptext, sizeof(iptext)))
- info("New %s dimension added: ID = %u, IP = %s, NAME = %s, DIM1 = %s, DIM2 = %s, SENT = %lu(%lu), RECV = %lu(%lu)",
+ info("New %s dimension added: ID = %u, IP = %s, NAME = %s, DIM1 = %s, DIM2 = %s, DIM3 = %s",
(out == &inbound_vectors)?"inbound":"outbound", curr, iptext, w->resolved_name,
- w->dimension_recv, w->dimension_sent, w->sock.sent_bytes, w->sock.sent_packets,
- w->sock.recv_bytes, w->sock.recv_packets);
+ w->dimension_recv, w->dimension_sent, w->dimension_retransmit);
#endif
curr++;
if (curr > last)
@@ -766,32 +1115,50 @@ static void store_socket_inside_avl(netdata_vector_plot_t *out, netdata_socket_t
*
* Compare input values with local address to select table to store.
*
- * @param cmp index read from hash table.
+ * @param direction store inbound and outbound direction.
+ * @param cmp index read from hash table.
+ * @param proto the protocol read.
*
* @return It returns the structure with address to compare.
*/
-netdata_vector_plot_t * select_vector_to_store(netdata_socket_idx_t *cmp)
+netdata_vector_plot_t * select_vector_to_store(uint32_t *direction, netdata_socket_idx_t *cmp, uint8_t proto)
{
- if (!listen_ports)
+ if (!listen_ports) {
+ *direction = NETDATA_OUTBOUND_DIRECTION;
return &outbound_vectors;
+ }
ebpf_network_viewer_port_list_t *move_ports = listen_ports;
while (move_ports) {
- if (move_ports->first == cmp->sport) {
+ if (move_ports->protocol == proto && move_ports->first == cmp->sport) {
+ *direction = NETDATA_INBOUND_DIRECTION;
return &inbound_vectors;
}
move_ports = move_ports->next;
}
+ *direction = NETDATA_OUTBOUND_DIRECTION;
return &outbound_vectors;
}
+/**
+ * Hash accumulator
+ *
+ * @param values the values used to calculate the data.
+ * @param key the key to store data.
+ * @param removesock check if this socket must be removed .
+ * @param family the connection family
+ * @param end the values size.
+ */
static void hash_accumulator(netdata_socket_t *values, netdata_socket_idx_t *key, int *removesock, int family, int end)
{
uint64_t bsent = 0, brecv = 0, psent = 0, precv = 0;
+ uint16_t retransmit = 0;
int i;
uint8_t protocol = values[0].protocol;
+ uint64_t ct = values[0].ct;
+ error("KILLME_FIRST %u (%u)", protocol, ntohs(key->sport));
for (i = 1; i < end; i++) {
netdata_socket_t