Read socket information from kernel ring (#9549)
Read socket information from kernel ring.
thiagoftsm committed
Jul 29, 2020 at 12:03 UTC
a4ebf1d4af885c9997e888de5042125df7571425
9 files changed
+1070
-151
collectors/ebpf.plugin/README.md
+5
@@ -198,6 +198,7 @@ You can configure the information shown on `outbound` and `inbound` charts with
198
```conf
199
[network viewer]
200
maximum dimensions = 500
201
+ resolve hostname ips = no
202
ports = 1-1024 !145 !domain
203
hostnames = !example.com
204
ips = !127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
@@ -223,6 +224,10 @@ By default, Netdata displays up to 500 dimensions on network viewer charts. If t
224
will be bundled into the `other` dimension. You can increase the number of shown dimensions by changing the `maximum
225
dimensions` setting.
226
227
+The dimensions for the traffic charts are created using the destination IPs of the sockets by default. This can be
228
+changed setting `resolve hostname ips = yes` and restarting Netdata, after this Netdata will create dimensions using
229
+the `hostnames` every time that is possible to resolve IPs to their hostnames.
230
+
231
### `[service name]`
232
233
Netdata uses the list of services in `/etc/services` to plot network viewer charts. If this file does not contain the
collectors/ebpf.plugin/ebpf.c
+199
-115
@@ -2,6 +2,7 @@
2
3
#include <sys/time.h>
4
#include <sys/resource.h>
5
+#include <ifaddrs.h>
6
7
#include "ebpf.h"
8
#include "ebpf_socket.h"
@@ -27,7 +28,6 @@ void send_statistics(const char *action, const char *action_result, const char *
28
UNUSED(action);
29
UNUSED(action_result);
30
UNUSED(action_data);
30
- return;
31
}
32
33
// callbacks required by popen()
@@ -116,8 +116,9 @@ pid_t *pid_index;
116
ebpf_process_stat_t *global_process_stat = NULL;
117
118
//Network viewer
119
-ebpf_network_viewer_options_t network_viewer_opt = { .max_dim = 500, .excluded_port = NULL, .included_port = NULL,
120
- .names = NULL };
119
+ebpf_network_viewer_options_t network_viewer_opt = { .max_dim = 500, .name_resolution_enabled = 0,
120
+ .excluded_port = NULL, .included_port = NULL,
121
+ .names = NULL, .ipv4_local_ip = NULL, .ipv6_local_ip = NULL };
122
123
/*****************************************************************
124
*
@@ -132,7 +133,7 @@ ebpf_network_viewer_options_t network_viewer_opt = { .max_dim = 500, .excluded_p
133
*
134
* @param clean the list that will be cleaned
135
*/
135
-static void clean_port_structure(ebpf_network_viewer_port_list_t **clean)
136
+void clean_port_structure(ebpf_network_viewer_port_list_t **clean)
137
{
138
ebpf_network_viewer_port_list_t *move = *clean;
139
while (move) {
@@ -224,7 +225,7 @@ static void ebpf_exit(int sig)
225
int sid = setsid();
226
if (sid >= 0) {
227
debug(D_EXIT, "Wait for father %d die", getpid());
227
- sleep_usec(200000); //Sleep 200 miliseconds to father dies.
228
+ sleep_usec(200000); // Sleep 200 miliseconds to father dies.
229
clean_loaded_events();
230
} else {
231
error("Cannot become session id leader, so I won't try to clean kprobe_events.\n");
@@ -617,6 +618,150 @@ void ebpf_print_help()
618
*
619
*****************************************************************/
620
621
+/**
622
+ * Is ip inside the range
623
+ *
624
+ * Check if the ip is inside a IP range
625
+ *
626
+ * @param rfirst the first ip address of the range
627
+ * @param rlast the last ip address of the range
628
+ * @param cmpfirst the first ip to compare
629
+ * @param cmplast the last ip to compare
630
+ * @param family the IP family
631
+ *
632
+ * @return It returns 1 if the IP is inside the range and 0 otherwise
633
+ */
634
+static int is_ip_inside_range(union netdata_ip_t *rfirst, union netdata_ip_t *rlast,
635
+ union netdata_ip_t *cmpfirst, union netdata_ip_t *cmplast, int family)
636
+{
637
+ if (family == AF_INET) {
638
+ if (ntohl(rfirst->addr32[0]) <= ntohl(cmpfirst->addr32[0]) &&
639
+ ntohl(rlast->addr32[0]) >= ntohl(cmplast->addr32[0]))
640
+ return 1;
641
+ } else {
642
+ if (memcmp(rfirst->addr8, cmpfirst->addr8, sizeof(union netdata_ip_t)) <= 0 &&
643
+ memcmp(rlast->addr8, cmplast->addr8, sizeof(union netdata_ip_t)) >= 0) {
644
+ return 1;
645
+ }
646
+
647
+ }
648
+ return 0;
649
+}
650
+
651
+
652
+/**
653
+ * Fill IP list
654
+ *
655
+ * @param out a pointer to the link list.
656
+ * @param in the structure that will be linked.
657
+ */
658
+static inline void fill_ip_list(ebpf_network_viewer_ip_list_t **out, ebpf_network_viewer_ip_list_t *in, char *table)
659
+{
660
+#ifndef NETDATA_INTERNAL_CHECKS
661
+ UNUSED(table);
662
+#endif
663
+ if (likely(*out)) {
664
+ ebpf_network_viewer_ip_list_t *move = *out, *store = *out;
665
+ while (move) {
666
+ if (in->ver == move->ver && is_ip_inside_range(&move->first, &move->last, &in->first, &in->last, in->ver)) {
667
+ info("The range/value (%s) is inside the range/value (%s) already inserted, it will be ignored.",
668
+ in->value, move->value);
669
+ freez(in->value);
670
+ freez(in);
671
+ return;
672
+ }
673
+ store = move;
674
+ move = move->next;
675
+ }
676
+
677
+ store->next = in;
678
+ } else {
679
+ *out = in;
680
+ }
681
+
682
+#ifdef NETDATA_INTERNAL_CHECKS
683
+ char first[512], last[512];
684
+ if (in->ver == AF_INET) {
685
+ if (inet_ntop(AF_INET, in->first.addr8, first, INET_ADDRSTRLEN) &&
686
+ inet_ntop(AF_INET, in->last.addr8, last, INET_ADDRSTRLEN))
687
+ info("Adding values %s - %s to %s IP list \"%s\" used on network viewer",
688
+ first, last,
689
+ (*out == network_viewer_opt.included_ips)?"included":"excluded",
690
+ table);
691
+ } else {
692
+ if (inet_ntop(AF_INET6, in->first.addr8, first, INET6_ADDRSTRLEN) &&
693
+ inet_ntop(AF_INET6, in->last.addr8, last, INET6_ADDRSTRLEN))
694
+ info("Adding values %s - %s to %s IP list \"%s\" used on network viewer",
695
+ first, last,
696
+ (*out == network_viewer_opt.included_ips)?"included":"excluded",
697
+ table);
698
+ }
699
+#endif
700
+}
701
+
702
+
703
+/**
704
+ * Read Local addresseses
705
+ *
706
+ * Read the local address from the interfaces.
707
+ */
708
+static void read_local_addresses()
709
+{
710
+ struct ifaddrs *ifaddr, *ifa;
711
+ if (getifaddrs(&ifaddr) == -1) {
712
+ error("Cannot get the local IP addresses, it is no possible to do separation between inbound and outbound connections");
713
+ return;
714
+ }
715
+
716
+ char *notext = { "No text representation" };
717
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
718
+ if (ifa->ifa_addr == NULL)
719
+ continue;
720
+
721
+ if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6))
722
+ continue;
723
+
724
+ ebpf_network_viewer_ip_list_t *w = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
725
+
726
+ int family = ifa->ifa_addr->sa_family;
727
+ w->ver = (uint8_t) family;
728
+ char text[INET6_ADDRSTRLEN];
729
+ if (family == AF_INET) {
730
+ struct sockaddr_in *in = (struct sockaddr_in*) ifa->ifa_addr;
731
+
732
+ w->first.addr32[0] = in->sin_addr.s_addr;
733
+ w->last.addr32[0] = in->sin_addr.s_addr;
734
+
735
+ if (inet_ntop(AF_INET, w->first.addr8, text, INET_ADDRSTRLEN)) {
736
+ w->value = strdupz(text);
737
+ w->hash = simple_hash(text);
738
+ } else {
739
+ w->value = strdupz(notext);
740
+ w->hash = simple_hash(notext);
741
+ }
742
+ } else {
743
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa->ifa_addr;
744
+
745
+ memcpy(w->first.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
746
+ memcpy(w->last.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
747
+
748
+ if (inet_ntop(AF_INET6, w->first.addr8, text, INET_ADDRSTRLEN)) {
749
+ w->value = strdupz(text);
750
+ w->hash = simple_hash(text);
751
+ } else {
752
+ w->value = strdupz(notext);
753
+ w->hash = simple_hash(notext);
754
+ }
755
+ }
756
+
757
+ fill_ip_list((family == AF_INET)?&network_viewer_opt.ipv4_local_ip:&network_viewer_opt.ipv6_local_ip,
758
+ w,
759
+ "selector");
760
+ }
761
+
762
+ freeifaddrs(ifaddr);
763
+}
764
+
765
/**
766
* Start Ptherad Variable
767
*
@@ -689,7 +834,7 @@ static inline int parse_disable_apps(char *ptr)
834
if (!strcasecmp(ptr, "yes")) {
835
ebpf_disable_apps();
836
return 1;
692
- } else if (strcasecmp(ptr, "no")) {
837
+ } else if (strcasecmp(ptr, "no") != 0) {
838
error("The option %s for \"disable apps\" is not a valid option.", ptr);
839
}
840
@@ -814,6 +959,8 @@ fillenvpl:
959
w->hash = simple_hash(copied);
960
w->first = (uint16_t)htons((uint16_t)first);
961
w->last = (uint16_t)htons((uint16_t)last);
962
+ w->cmp_first = (uint16_t)first;
963
+ w->cmp_last = (uint16_t)last;
964
965
fill_port_list(list, w);
966
}
@@ -913,81 +1060,6 @@ static inline int ip2nl(uint8_t *dst, char *ip, int domain, char *source)
1060
return 0;
1061
}
1062
916
-/**
917
- * Is ip inside the range
918
- *
919
- * Check if the ip is inside a IP range
920
- *
921
- * @param rfirst the first ip address of the range
922
- * @param rlast the last ip address of the range
923
- * @param cmpfirst the first ip to compare
924
- * @param cmplast the last ip to compare
925
- * @param family the IP family
926
- *
927
- * @return It returns 1 if the IP is inside the range and 0 otherwise
928
- */
929
-static int is_ip_inside_range(union netdata_ip_t *rfirst, union netdata_ip_t *rlast,
930
- union netdata_ip_t *cmpfirst, union netdata_ip_t *cmplast, int family)
931
-{
932
- if (family == AF_INET) {
933
- if (ntohl(rfirst->addr32[0]) <= ntohl(cmpfirst->addr32[0]) &&
934
- ntohl(rlast->addr32[0]) >= ntohl(cmplast->addr32[0]))
935
- return 1;
936
- } else {
937
- if (memcmp(rfirst->addr8, cmpfirst->addr8, sizeof(union netdata_ip_t)) <= 0 &&
938
- memcmp(rlast->addr8, cmplast->addr8, sizeof(union netdata_ip_t)) >= 0) {
939
- return 1;
940
- }
941
-
942
- }
943
- return 0;
944
-}
945
-
946
-/**
947
- * Fill IP list
948
- *
949
- * @param out a pointer to the link list.
950
- * @param in the structure that will be linked.
951
- */
952
-static inline void fill_ip_list(ebpf_network_viewer_ip_list_t **out, ebpf_network_viewer_ip_list_t *in)
953
-{
954
- if (likely(*out)) {
955
- ebpf_network_viewer_ip_list_t *move = *out, *store = *out;
956
- while (move) {
957
- if (in->ver == move->ver && is_ip_inside_range(&move->first, &move->last, &in->first, &in->last, in->ver)) {
958
- info("The range/value (%s) is inside the range/value (%s) already inserted, it will be ignored.",
959
- in->value, move->value);
960
- freez(in->value);
961
- freez(in);
962
- return;
963
- }
964
- store = move;
965
- move = move->next;
966
- }
967
-
968
- store->next = in;
969
- } else {
970
- *out = in;
971
- }
972
-
973
-#ifdef NETDATA_INTERNAL_CHECKS
974
- char first[512], last[512];
975
- if (in->ver == AF_INET) {
976
- if (inet_ntop(AF_INET, in->first.addr8, first, INET_ADDRSTRLEN) &&
977
- inet_ntop(AF_INET, in->last.addr8, last, INET_ADDRSTRLEN))
978
- info("Adding values %s - %s to %s IP list used on network viewer",
979
- first, last,
980
- (*out == network_viewer_opt.included_ips)?"included":"excluded");
981
- } else {
982
- if (inet_ntop(AF_INET6, in->first.addr8, first, INET6_ADDRSTRLEN) &&
983
- inet_ntop(AF_INET6, in->last.addr8, last, INET6_ADDRSTRLEN))
984
- info("Adding values %s - %s to %s IP list used on network viewer",
985
- first, last,
986
- (*out == network_viewer_opt.included_ips)?"included":"excluded");
987
- }
988
-#endif
989
-}
990
-
1063
/**
1064
* Get IPV6 Last Address
1065
*
@@ -1094,15 +1166,15 @@ static void parse_ip_list(void **out, char *ip)
1166
}
1167
1168
char *end = ip;
1097
- //Move while I cannot find a separator
1169
+ // Move while I cannot find a separator
1170
while (*end && *end != '/' && *end != '-') end++;
1171
1100
- //We will use only the classic IPV6 for while, but we could consider the base 85 in a near future
1101
- //https://tools.ietf.org/html/rfc1924
1172
+ // We will use only the classic IPV6 for while, but we could consider the base 85 in a near future
1173
+ // https://tools.ietf.org/html/rfc1924
1174
is_ipv6 = strchr(ip, ':');
1175
1176
int select;
1105
- if (*end && !is_ipv6) { //IPV4 range
1177
+ if (*end && !is_ipv6) { // IPV4 range
1178
select = (*end == '/') ? 0 : 1;
1179
*end++ = '\0';
1180
if (*end == '!') {
@@ -1110,7 +1182,7 @@ static void parse_ip_list(void **out, char *ip)
1182
goto cleanipdup;
1183
}
1184
1113
- if (!select) { //CIDR
1185
+ if (!select) { // CIDR
1186
select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1187
if (select)
1188
goto cleanipdup;
@@ -1122,7 +1194,8 @@ static void parse_ip_list(void **out, char *ip)
1194
}
1195
1196
last.addr32[0] = htonl(broadcast(ntohl(first.addr32[0]), select));
1125
- //This was added to remove https://app.codacy.com/manual/netdata/netdata/pullRequest?prid=5810941&bid=19021977
1197
+ // This was added to remove
1198
+ // https://app.codacy.com/manual/netdata/netdata/pullRequest?prid=5810941&bid=19021977
1199
UNUSED(last.addr32[0]);
1200
1201
uint32_t ipv4_test = htonl(ipv4_network(ntohl(first.addr32[0]), select));
@@ -1134,7 +1207,7 @@ static void parse_ip_list(void **out, char *ip)
1207
if(inet_ntop(AF_INET, &ipv4_convert, ipv4_msg, INET_ADDRSTRLEN))
1208
info("The network value of CIDR %s was updated for %s .", ipdup, ipv4_msg);
1209
}
1137
- } else { //Range
1210
+ } else { // Range
1211
select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1212
if (select)
1213
goto cleanipdup;
@@ -1149,8 +1222,8 @@ static void parse_ip_list(void **out, char *ip)
1222
ipdup);
1223
goto cleanipdup;
1224
}
1152
- } else if (is_ipv6) { //IPV6
1153
- if (!*end) { // unique
1225
+ } else if (is_ipv6) { // IPV6
1226
+ if (!*end) { // Unique
1227
select = ip2nl(first.addr8, ip, AF_INET6, ipdup);
1228
if (select)
1229
goto cleanipdup;
@@ -1170,7 +1243,7 @@ static void parse_ip_list(void **out, char *ip)
1243
select = ip2nl(last.addr8, end, AF_INET6, ipdup);
1244
if (select)
1245
goto cleanipdup;
1173
- } else { //CIDR
1246
+ } else { // CIDR
1247
*end++ = 0x00;
1248
if (*end == '!') {
1249
info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
@@ -1212,7 +1285,7 @@ static void parse_ip_list(void **out, char *ip)
1285
ipdup);
1286
goto cleanipdup;
1287
}
1215
- } else { //Unique ip
1288
+ } else { // Unique ip
1289
select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1290
if (select)
1291
goto cleanipdup;
@@ -1230,7 +1303,7 @@ storethisip:
1303
memcpy(store->first.addr8, first.addr8, sizeof(first.addr8));
1304
memcpy(store->last.addr8, last.addr8, sizeof(last.addr8));
1305
1233
- fill_ip_list(list, store);
1306
+ fill_ip_list(list, store, "socket");
1307
return;
1308
1309
cleanipdup:
@@ -1246,19 +1319,19 @@ cleanipdup:
1319
*/
1320
static void parse_ips(char *ptr)
1321
{
1249
- //No value
1322
+ // No value
1323
if (unlikely(!ptr))
1324
return;
1325
1326
while (likely(ptr)) {
1254
- //Move forward until next valid character
1327
+ // Move forward until next valid character
1328
while (isspace(*ptr)) ptr++;
1329
1257
- //No valid value found
1330
+ // No valid value found
1331
if (unlikely(!*ptr))
1332
return;
1333
1261
- //Find space that ends the list
1334
+ // Find space that ends the list
1335
char *end = strchr(ptr, ' ');
1336
if (end) {
1337
*end++ = '\0';
@@ -1270,7 +1343,7 @@ static void parse_ips(char *ptr)
1343
ptr++;
1344
}
1345
1273
- if (isascii(*ptr)) { //Parse port
1346
+ if (isascii(*ptr)) { // Parse port
1347
parse_ip_list((!neg)?(void **)&network_viewer_opt.included_ips:(void **)&network_viewer_opt.excluded_ips,
1348
ptr);
1349
}
@@ -1289,19 +1362,19 @@ static void parse_ips(char *ptr)
1362
*/
1363
static void parse_ports(char *ptr)
1364
{
1292
- //No value
1365
+ // No value
1366
if (unlikely(!ptr))
1367
return;
1368
1369
while (likely(ptr)) {
1297
- //Move forward until next valid character
1370
+ // Move forward until next valid character
1371
while (isspace(*ptr)) ptr++;
1372
1300
- //No valid value found
1373
+ // No valid value found
1374
if (unlikely(!*ptr))
1375
return;
1376
1304
- //Find space that ends the list
1377
+ // Find space that ends the list
1378
char *end = strchr(ptr, ' ');
1379
if (end) {
1380
*end++ = '\0';
@@ -1313,13 +1386,13 @@ static void parse_ports(char *ptr)
1386
ptr++;
1387
}
1388
1316
- if (isdigit(*ptr)) { //Parse port
1389
+ if (isdigit(*ptr)) { // Parse port
1390
parse_port_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1391
ptr);
1319
- } else if (isalpha(*ptr)) { //Parse service
1392
+ } else if (isalpha(*ptr)) { // Parse service
1393
parse_service_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1394
ptr);
1322
- } else if (*ptr == '*') { //All
1395
+ } else if (*ptr == '*') { // All
1396
parse_port_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1397
ptr);
1398
}
@@ -1370,19 +1443,19 @@ static void link_hostname(ebpf_network_viewer_hostname_list_t **out, ebpf_networ
1443
*/
1444
static void link_hostnames(char *parse)
1445
{
1373
- //No value
1446
+ // No value
1447
if (unlikely(!parse))
1448
return;
1449
1450
while (likely(parse)) {
1378
- //Find the first valid value
1451
+ // Find the first valid value
1452
while (isspace(*parse)) parse++;
1453
1381
- //No valid value found
1454
+ // No valid value found
1455
if (unlikely(!*parse))
1456
return;
1457
1385
- //Find space that ends the list
1458
+ // Find space that ends the list
1459
char *end = strchr(parse, ' ');
1460
if (end) {
1461
*end++ = '\0';
@@ -1414,14 +1487,23 @@ static void parse_network_viewer_section()
1487
network_viewer_opt.max_dim = appconfig_get_number(&collector_config,
1488
EBPF_NETWORK_VIEWER_SECTION,
1489
"maximum dimensions",
1417
- 500);
1490
+ 50);
1491
+
1492
+ network_viewer_opt.name_resolution_enabled = appconfig_get_boolean(&collector_config,
1493
+ EBPF_NETWORK_VIEWER_SECTION,
1494
+ "resolve hostname ips",
1495
+ 0);
1496
1497
char *value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION,
1498
"ports", NULL);
1499
parse_ports(value);
1500
1423
- value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION, "hostnames", NULL);
1424
- link_hostnames(value);
1501
+ if (network_viewer_opt.name_resolution_enabled) {
1502
+ value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION, "hostnames", NULL);
1503
+ link_hostnames(value);
1504
+ } else {
1505
+ info("Name resolution is disabled, collector will not parser \"hostnames\" list.");
1506
+ }
1507
1508
value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION,
1509
"ips", "!127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7");
@@ -1490,7 +1572,7 @@ static void parse_service_name_section()
1572
}
1573
}
1574
1493
- //Always associated the default port to Netdata
1575
+ // Always associated the default port to Netdata
1576
ebpf_network_viewer_dim_name_t *names = network_viewer_opt.names;
1577
if (names) {
1578
uint16_t default_port = htons(19999);
@@ -1537,7 +1619,7 @@ static void read_collector_values(int *disable_apps)
1619
enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, ebpf_modules[1].config_name, 1);
1620
if (enabled) {
1621
ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_apps);
1540
- //Read network viewer section if network viewer is enabled
1622
+ // Read network viewer section if network viewer is enabled
1623
parse_network_viewer_section();
1624
parse_service_name_section();
1625
started++;
@@ -1545,7 +1627,7 @@ static void read_collector_values(int *disable_apps)
1627
1628
if (!started){
1629
ebpf_enable_all_charts(*disable_apps);
1548
- //Read network viewer section
1630
+ // Read network viewer section
1631
parse_network_viewer_section();
1632
parse_service_name_section();
1633
}
@@ -1799,6 +1881,8 @@ int main(int argc, char **argv)
1881
1882
ebpf_allocate_common_vectors();
1883
1884
+ read_local_addresses();
1885
+
1886
struct netdata_static_thread ebpf_threads[] = {
1887
{"EBPF PROCESS", NULL, NULL, 1, NULL, NULL, ebpf_modules[0].start_routine},
1888
{"EBPF SOCKET" , NULL, NULL, 1, NULL, NULL, ebpf_modules[1].start_routine},
collectors/ebpf.plugin/ebpf.conf
+2
-1
@@ -8,9 +8,10 @@
8
9
[network viewer]
10
maximum dimensions = 500
11
+ resolve hostname ips = no
12
ports = *
13
ips = !127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
13
- hostnames = !example.com
14
+ hostnames = *
15
16
[service name]
17
19999 = Netdata
collectors/ebpf.plugin/ebpf.h
+5
-5
@@ -184,10 +184,10 @@ extern void ebpf_create_charts_on_apps(char *name,
184
185
extern void write_end_chart();
186
187
-# define EBPF_GLOBAL_SECTION "global"
188
-# define EBPF_PROGRAMS_SECTION "ebpf programs"
189
-# define EBPF_NETWORK_VIEWER_SECTION "network viewer"
190
-# define EBPF_SERVICE_NAME_SECTION "service name"
187
+#define EBPF_GLOBAL_SECTION "global"
188
+#define EBPF_PROGRAMS_SECTION "ebpf programs"
189
+#define EBPF_NETWORK_VIEWER_SECTION "network viewer"
190
+#define EBPF_SERVICE_NAME_SECTION "service name"
191
192
#define EBPF_COMMON_DIMENSION_CALL "Calls"
193
#define EBPF_COMMON_DIMENSION_BYTESS "bytes/s"
@@ -210,7 +210,7 @@ extern int update_every;
210
211
#define EBPF_MAX_SYNCHRONIZATION_TIME 300
212
213
-//External functions
213
+// External functions
214
extern void change_socket_event();
215
extern void change_process_event();
216
collectors/ebpf.plugin/ebpf_socket.c
+745
-8
@@ -11,9 +11,10 @@
11
*
12
*****************************************************************/
13
14
-static char *socket_dimension_names[NETDATA_MAX_SOCKET_VECTOR] = { "sent", "received", "close", "sent", "received" };
14
+static char *socket_dimension_names[NETDATA_MAX_SOCKET_VECTOR] = { "sent", "received", "close", "sent",
15
+ "received", "retransmitted" };
16
static char *socket_id_names[NETDATA_MAX_SOCKET_VECTOR] = { "tcp_sendmsg", "tcp_cleanup_rbuf", "tcp_close",
16
- "udp_sendmsg", "udp_recvmsg" };
17
+ "udp_sendmsg", "udp_recvmsg", "tcp_retransmit_skb" };
18
19
static netdata_idx_t *socket_hash_values = NULL;
20
static netdata_syscall_stat_t *socket_aggregated_data = NULL;
@@ -27,6 +28,12 @@ static ebpf_bandwidth_t *bandwidth_vector = NULL;
28
29
static int socket_apps_created = 0;
30
31
+netdata_vector_plot_t inbound_vectors = { .plot = NULL, .next = 0, .last = 0 };
32
+netdata_vector_plot_t outbound_vectors = { .plot = NULL, .next = 0, .last = 0 };
33
+netdata_socket_t *socket_values;
34
+
35
+ebpf_network_viewer_port_list_t *listen_ports = NULL;
36
+
37
static int *map_fd = NULL;
38
39
/*****************************************************************
@@ -50,7 +57,7 @@ static void ebpf_update_global_publish(
57
netdata_publish_syscall_t *move = publish;
58
while (move) {
59
if (input->call != move->pcall) {
53
- //This condition happens to avoid initial values with dimensions higher than normal values.
60
+ // This condition happens to avoid initial values with dimensions higher than normal values.
61
if (move->pcall) {
62
move->ncall = (input->call > move->pcall) ? input->call - move->pcall : move->pcall - input->call;
63
move->nbyte = (input->bytes > move->pbyte) ? input->bytes - move->pbyte : move->pbyte - input->bytes;
@@ -112,6 +119,8 @@ static void ebpf_socket_send_data(ebpf_module_t *em)
119
write_err_chart(
120
NETDATA_TCP_FUNCTION_ERROR, NETDATA_EBPF_FAMILY, socket_publish_aggregated, 2);
121
}
122
+ write_count_chart(
123
+ NETDATA_TCP_RETRANSMIT, NETDATA_EBPF_FAMILY, &socket_publish_aggregated[NETDATA_RETRANSMIT_START], 1);
124
125
write_count_chart(
126
NETDATA_UDP_FUNCTION_COUNT, NETDATA_EBPF_FAMILY, &socket_publish_aggregated[NETDATA_UDP_START], 2);
@@ -229,12 +238,22 @@ static void ebpf_create_global_charts(ebpf_module_t *em)
238
2);
239
}
240
241
+ ebpf_create_chart(NETDATA_EBPF_FAMILY,
242
+ NETDATA_TCP_RETRANSMIT,
243
+ "Packages retransmitted",
244
+ EBPF_COMMON_DIMENSION_CALL,
245
+ NETDATA_SOCKET_GROUP,
246
+ 21073,
247
+ ebpf_create_global_dimension,
248
+ &socket_publish_aggregated[NETDATA_RETRANSMIT_START],
249
+ 1);
250
+
251
ebpf_create_chart(NETDATA_EBPF_FAMILY,
252
NETDATA_UDP_FUNCTION_COUNT,
253
"UDP calls",
254
EBPF_COMMON_DIMENSION_CALL,
255
NETDATA_SOCKET_GROUP,
237
- 21073,
256
+ 21074,
257
ebpf_create_global_dimension,
258
&socket_publish_aggregated[NETDATA_UDP_START],
259
2);
@@ -244,7 +263,7 @@ static void ebpf_create_global_charts(ebpf_module_t *em)
263
"UDP bandwidth",
264
EBPF_COMMON_DIMENSION_BYTESS,
265
NETDATA_SOCKET_GROUP,
247
- 21074,
266
+ 21075,
267
ebpf_create_global_dimension,
268
&socket_publish_aggregated[NETDATA_UDP_START],
269
2);
@@ -255,7 +274,7 @@ static void ebpf_create_global_charts(ebpf_module_t *em)
274
"UDP errors",
275
EBPF_COMMON_DIMENSION_CALL,
276
NETDATA_SOCKET_GROUP,
258
- 21075,
277
+ 21076,
278
ebpf_create_global_dimension,
279
&socket_publish_aggregated[NETDATA_UDP_START],
280
2);
@@ -295,6 +314,659 @@ void ebpf_socket_create_apps_charts(ebpf_module_t *em, struct target *root)
314
*
315
*****************************************************************/
316
317
+/**
318
+ * Is specific ip inside the range
319
+ *
320
+ * Check if the ip is inside a IP range previously defined
321
+ *
322
+ * @param cmp the IP to compare
323
+ * @param family the IP family
324
+ *
325
+ * @return It returns 1 if the IP is inside the range and 0 otherwise
326
+ */
327
+static int is_specific_ip_inside_range(union netdata_ip_t *cmp, int family)
328
+{
329
+ if (!network_viewer_opt.excluded_ips && !network_viewer_opt.included_ips)
330
+ return 1;
331
+
332
+ uint32_t ipv4_test = ntohl(cmp->addr32[0]);
333
+ ebpf_network_viewer_ip_list_t *move = network_viewer_opt.excluded_ips;
334
+ while (move) {
335
+ if (family == AF_INET) {
336
+ if (ntohl(move->first.addr32[0]) <= ipv4_test &&
337
+ ipv4_test <= ntohl(move->last.addr32[0]) )
338
+ return 0;
339
+ } else {
340
+ if (memcmp(move->first.addr8, cmp->addr8, sizeof(union netdata_ip_t)) <= 0 &&
341
+ memcmp(move->last.addr8, cmp->addr8, sizeof(union netdata_ip_t)) >= 0) {
342
+ return 0;
343
+ }
344
+ }
345
+ move = move->next;
346
+ }
347
+
348
+ move = network_viewer_opt.included_ips;
349
+ while (move) {
350
+ if (family == AF_INET) {
351
+ if (ntohl(move->first.addr32[0]) <= ipv4_test &&
352
+ ntohl(move->last.addr32[0]) >= ipv4_test)
353
+ return 1;
354
+ } else {
355
+ if (memcmp(move->first.addr8, cmp->addr8, sizeof(union netdata_ip_t)) <= 0 &&
356
+ memcmp(move->last.addr8, cmp->addr8, sizeof(union netdata_ip_t)) >= 0) {
357
+ return 1;
358
+ }
359
+ }
360
+ move = move->next;
361
+ }
362
+
363
+ return 0;
364
+}
365
+
366
+/**
367
+ * Is port inside range
368
+ *
369
+ * Verify if the cmp port is inside the range [first, last].
370
+ * This function expects only the last parameter as big endian.
371
+ *
372
+ * @param cmp the value to compare
373
+ *
374
+ * @return It returns 1 when cmp is inside and 0 otherwise.
375
+ */
376
+static int is_port_inside_range(uint16_t cmp)
377
+{
378
+ // We do not have restrictions for ports.
379
+ if (!network_viewer_opt.excluded_port && !network_viewer_opt.included_port)
380
+ return 1;
381
+
382
+ // Test if port is excluded
383
+ ebpf_network_viewer_port_list_t *move = network_viewer_opt.excluded_port;
384
+ cmp = htons(cmp);
385
+ while (move) {
386
+ if (move->cmp_first <= cmp && cmp <= move->cmp_last)
387
+ return 0;
388
+
389
+ move = move->next;
390
+ }
391
+
392
+ // Test if the port is inside allowed range
393
+ move = network_viewer_opt.included_port;
394
+ while (move) {
395
+ if (move->cmp_first <= cmp && cmp <= move->cmp_last)
396
+ return 1;
397
+
398
+ move = move->next;
399
+ }
400
+
401
+ return 0;
402
+}
403
+
404
+/**
405
+ * Hostname matches pattern
406
+ *
407
+ * @param cmp the value to compare
408
+ *
409
+ * @return It returns 1 when the value matches and zero otherwise.
410
+ */
411
+int hostname_matches_pattern(char *cmp)
412
+{
413
+ if (!network_viewer_opt.included_hostnames && !network_viewer_opt.excluded_hostnames)
414
+ return 1;
415
+
416
+ ebpf_network_viewer_hostname_list_t *move = network_viewer_opt.excluded_hostnames;
417
+ while (move) {
418
+ if (simple_pattern_matches(move->value_pattern, cmp))
419
+ return 0;
420
+
421
+ move = move->next;
422
+ }
423
+
424
+ move = network_viewer_opt.included_hostnames;
425
+ while (move) {
426
+ if (simple_pattern_matches(move->value_pattern, cmp))
427
+ return 1;
428
+
429
+ move = move->next;
430
+ }
431
+
432
+
433
+ return 0;
434
+}
435
+
436
+/**
437
+ * Is socket allowed?
438
+ *
439
+ * Compare destination addresses and destination ports to define next steps
440
+ *
441
+ * @param key the socket read from kernel ring
442
+ * @param family the family used to compare IPs (AF_INET and AF_INET6)
443
+ *
444
+ * @return It returns 1 if this socket is inside the ranges and 0 otherwise.
445
+ */
446
+int is_socket_allowed(netdata_socket_idx_t *key, int family)
447
+{
448
+ if (!is_port_inside_range(key->dport))
449
+ return 0;
450
+
451
+ return is_specific_ip_inside_range(&key->daddr, family);
452
+}
453
+
454
+/**
455
+ * Compare sockets
456
+ *
457
+ * Compare destination address and destination port.
458
+ * We do not compare source port, because it is random.
459
+ * We also do not compare source address, because inbound and outbound connections are stored in separated AVL trees.
460
+ *
461
+ * @param a pointer to netdata_socket_plot
462
+ * @param b pointer to netdata_socket_plot
463
+ *
464
+ * @return It returns 0 case the values are equal, 1 case a is bigger than b and -1 case a is smaller than b.
465
+ */
466
+static int compare_sockets(void *a, void *b)
467
+{
468
+ struct netdata_socket_plot *val1 = a;
469
+ struct netdata_socket_plot *val2 = b;
470
+ int cmp;
471
+
472
+ // We do not need to compare val2 family, because data inside hash table is always from the same family
473
+ if (val1->family == AF_INET) { //IPV4
474
+ cmp = memcmp(&val1->index.daddr.addr32[0], &val2->index.daddr.addr32[0], sizeof(uint32_t));
475
+ if (!cmp) {
476
+ cmp = memcmp(&val1->index.dport, &val2->index.dport, sizeof(uint16_t));
477
+ }
478
+ } else {
479
+ cmp = memcmp(&val1->index.daddr.addr32, &val2->index.daddr.addr32, 4*sizeof(uint32_t));
480
+ if (!cmp) {
481
+ cmp = memcmp(&val1->index.dport, &val2->index.dport, sizeof(uint16_t));
482
+ }
483
+ }
484
+
485
+ return cmp;
486
+}
487
+
488
+/**
489
+ * Build dimension name
490
+ *
491
+ * Fill dimension name vector with values given
492
+ *
493
+ * @param dimname the output vector
494
+ * @param hostname the hostname for the socket.
495
+ * @param service_name the service used to connect.
496
+ * @param proto the protocol used in this connection
497
+ * @param family is this IPV4(AF_INET) or IPV6(AF_INET6)
498
+ *
499
+ * @return it returns the size of the data copied on success and -1 otherwise.
500
+ */
501
+static inline int build_outbound_dimension_name(char *dimname, char *hostname, char *service_name,
502
+ char *proto, int family)
503
+{
504
+ return snprintf(dimname, CONFIG_MAX_NAME - 7, (family == AF_INET)?"%s:%s:%s_":"%s:%s:[%s]_",
505
+ service_name, proto,
506
+ hostname);
507
+}
508
+
509
+/**
510
+ * Fill inbound dimension name
511
+ *
512
+ * Mount the dimension name with the input given
513
+ *
514
+ * @param dimname the output vector
515
+ * @param service_name the service used to connect.
516
+ * @param proto the protocol used in this connection
517
+ *
518
+ * @return it returns the size of the data copied on success and -1 otherwise.
519
+ */
520
+static inline int build_inbound_dimension_name(char *dimname, char *service_name, char *proto)
521
+{
522
+ return snprintf(dimname, CONFIG_MAX_NAME - 7, "%s:%s_", service_name,
523
+ proto);
524
+}
525
+
526
+/**
527
+ * Fill Resolved Name
528
+ *
529
+ * Fill the resolved name structure with the value given.
530
+ * The hostname is the largest value possible, if it is necessary to cut some value, it must be cut.
531
+ *
532
+ * @param ptr the output vector
533
+ * @param hostname the hostname resolved or IP.
534
+ * @param length the length for the hostname.
535
+ * @param service_name the service name associated to the connection
536
+ * @param is_outbound the is this an outbound connection
537
+ */
538
+static inline void fill_resolved_name(netdata_socket_plot_t *ptr, char *hostname, size_t length,
539
+ char *service_name, int is_outbound)
540
+{
541
+ if (length < NETDATA_MAX_NETWORK_COMBINED_LENGTH)
542
+ ptr->resolved_name = strdupz(hostname);
543
+ else {
544
+ length = NETDATA_MAX_NETWORK_COMBINED_LENGTH;
545
+ ptr->resolved_name = mallocz(NETDATA_DIM_LENGTH_WITHOUT_SERVICE_PROTOCOL + 1);
546
+ memcpy(ptr->resolved_name, hostname, length);
547
+ ptr->resolved_name[length] = '\0';
548
+ }
549
+
550
+ char dimname[CONFIG_MAX_NAME];
551
+ int size;
552
+ char *protocol = (ptr->sock.protocol == IPPROTO_UDP) ? "UDP" : "TCP";
553
+ if (is_outbound)
554
+ size = build_outbound_dimension_name(dimname, hostname, service_name, protocol, ptr->family);
555
+ else
556
+ size = build_inbound_dimension_name(dimname,service_name, protocol);
557
+
558
+ if (size > 0) {
559
+ strcpy(&dimname[size], "sent");
560
+ dimname[size + 4] = '\0';
561
+ ptr->dimension_sent = strdupz(dimname);
562
+
563
+ strcpy(&dimname[size], "recv");
564
+ ptr->dimension_recv = strdupz(dimname);
565
+ }
566
+}
567
+
568
+/**
569
+ * Mount dimension names
570
+ *
571
+ * Fill the vector names after to resolve the addresses
572
+ *
573
+ * @param ptr a pointer to the structure where the values are stored.
574
+ * @param is_outbound is a outbound ptr value?
575
+ * @param is_last is this the last value possible?
576
+ *
577
+ * @return It returns 1 if the name is valid and 0 otherwise.
578
+ */
579
+int fill_names(netdata_socket_plot_t *ptr, int is_outbound, uint32_t is_last)
580
+{
581
+ char hostname[NI_MAXHOST], service_name[NI_MAXSERV];
582
+ if (ptr->resolved)
583
+ return 1;
584
+
585
+ int ret;
586
+ static int resolve_name = -1;
587
+ if (resolve_name == -1)
588
+ resolve_name = network_viewer_opt.name_resolution_enabled;
589
+
590
+ if (is_last) {
591
+ char *other = { "Other" };
592
+ // We are also copying the NULL bytes to avoid warnings in new compilers
593
+ strncpy(hostname, other, 6);
594
+ strncpy(service_name, other, 6);
595
+
596
+ ptr->family = AF_INET;
597
+
598
+ fill_resolved_name(ptr, hostname, 10 + NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH, service_name, is_outbound);
599
+ ret = 1;
600
+ goto laststep;
601
+ }
602
+
603
+ netdata_socket_idx_t *idx = &ptr->index;
604
+
605
+ char *errname = { "Not resolved" };
606
+ // Resolve Name
607
+ if (ptr->family == AF_INET) { //IPV4
608
+ struct sockaddr_in myaddr;
609
+ memset(&myaddr, 0 , sizeof(myaddr));
610
+
611
+ myaddr.sin_family = ptr->family;
612
+ if (is_outbound) {
613
+ myaddr.sin_port = idx->dport;
614
+ myaddr.sin_addr.s_addr = idx->daddr.addr32[0];
615
+ } else {
616
+ myaddr.sin_port = idx->sport;
617
+ myaddr.sin_addr.s_addr = idx->saddr.addr32[0];
618
+ }
619
+
620
+ ret = (!resolve_name)?-1:getnameinfo((struct sockaddr *)&myaddr, sizeof(myaddr), hostname,
621
+ sizeof(hostname), service_name, sizeof(service_name), NI_NAMEREQD);
622
+ if (ret) {
623
+ // I cannot resolve the name, I will use the IP
624
+ if (!inet_ntop(AF_INET, &myaddr.sin_addr.s_addr, hostname, NI_MAXHOST)) {
625
+ strncpy(hostname, errname, 13);
626
+ }
627
+
628
+ snprintf(service_name, sizeof(service_name), "%u", ntohs(myaddr.sin_port));
629
+ ret = 1;
630
+ }
631
+ } else { // IPV6
632
+ struct sockaddr_in6 myaddr6;
633
+ memset(&myaddr6, 0 , sizeof(myaddr6));
634
+
635
+ myaddr6.sin6_family = AF_INET6;
636
+ if (is_outbound) {
637
+ myaddr6.sin6_port = idx->dport;
638
+ memcpy(myaddr6.sin6_addr.s6_addr, idx->daddr.addr8, sizeof(union netdata_ip_t));
639
+ } else {
640
+ myaddr6.sin6_port = idx->sport;
641
+ memcpy(myaddr6.sin6_addr.s6_addr, idx->saddr.addr8, sizeof(union netdata_ip_t));
642
+ }
643
+
644
+ ret = (!resolve_name)?-1:getnameinfo((struct sockaddr *)&myaddr6, sizeof(myaddr6), hostname,
645
+ sizeof(hostname), service_name, sizeof(service_name), NI_NAMEREQD);
646
+ if (ret) {
647
+ // I cannot resolve the name, I will use the IP
648
+ if (!inet_ntop(AF_INET6, myaddr6.sin6_addr.s6_addr, hostname, NI_MAXHOST)) {
649
+ strncpy(hostname, errname, 13);
650
+ }
651
+ snprintf(service_name, sizeof(service_name), "%u", ntohs(myaddr6.sin6_port));
652
+ ret = 1;
653
+ }
654
+ }
655
+
656
+ fill_resolved_name(ptr, hostname,
657
+ strlen(hostname) + strlen(service_name)+ NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH,
658
+ service_name, is_outbound);
659
+
660
+laststep:
661
+
662
+ if (resolve_name && !ret)
663
+ ret = hostname_matches_pattern(hostname);
664
+
665
+ ptr->resolved++;
666
+
667
+ return ret;
668
+}
669
+
670
+/**
671
+ * Update Socket Data
672
+ *
673
+ * Update the socket information with last collected data
674
+ *
675
+ * @param sock
676
+ * @param lvalues
677
+ */
678
+static inline void update_socket_data(netdata_socket_t *sock, netdata_socket_t *lvalues)
679
+{
680
+ sock->recv_packets = lvalues->recv_packets;
681
+ sock->sent_packets = lvalues->sent_packets;
682
+ sock->recv_bytes = lvalues->recv_bytes;
683
+ sock->sent_bytes = lvalues->sent_bytes;
684
+}
685
+
686
+/**
687
+ * Store socket inside avl
688
+ *
689
+ * Store the socket values inside the avl tree.
690
+ *
691
+ * @param out the structure with information used to plot charts.
692
+ * @param lvalues Values read from socket ring.
693
+ * @param lindex the index information, the real socket.
694
+ * @param family the family associated to the socket
695
+ */
696
+static void store_socket_inside_avl(netdata_vector_plot_t *out, netdata_socket_t *lvalues,
697
+ netdata_socket_idx_t *lindex, int family)
698
+{
699
+ netdata_socket_plot_t test, *ret ;
700
+
701
+ memcpy(&test.index, lindex, sizeof(*lindex));
702
+
703
+ ret = (netdata_socket_plot_t *) avl_search_lock(&out->tree, (avl *)&test);
704
+ if (ret) {
705
+ update_socket_data(&ret->sock, lvalues);
706
+ } else {
707
+ uint32_t curr = out->next;
708
+ uint32_t last = out->last;
709
+
710
+ netdata_socket_plot_t *w = &out->plot[curr];
711
+
712
+ int resolved;
713
+ if (curr == last) {
714
+ if (!w->resolved) {
715
+ resolved = fill_names(w, out != (netdata_vector_plot_t *)&inbound_vectors, 1);
716
+ UNUSED(resolved);
717
+#ifdef NETDATA_INTERNAL_CHECKS
718
+ info("Last %s dimension added: ID = %u, IP = OTHER, NAME = %s, DIM1 = %s, DIM2 = %s",
719
+ (out == &inbound_vectors)?"inbound":"outbound", curr, w->resolved_name,
720
+ w->dimension_recv, w->dimension_sent);
721
+#endif
722
+ }
723
+
724
+ update_socket_data(&w->sock, lvalues);
725
+ return;
726
+ } else {
727
+ memcpy(&w->sock, lvalues, sizeof(*lvalues));
728
+ memcpy(&w->index, lindex, sizeof(*lindex));
729
+ w->family = family;
730
+
731
+ resolved = fill_names(w, out != (netdata_vector_plot_t *)&inbound_vectors, 0);
732
+ }
733
+
734
+ if (!resolved) {
735
+ freez(w->resolved_name);
736
+ freez(w->dimension_sent);
737
+ freez(w->dimension_recv);
738
+
739
+ memset(w, 0, sizeof(netdata_socket_plot_t));
740
+
741
+ return;
742
+ }
743
+
744
+ netdata_socket_plot_t *check ;
745
+ check = (netdata_socket_plot_t *) avl_insert_lock(&out->tree, (avl *)w);
746
+ if (check != w)
747
+ error("Internal error, cannot insert the AVL tree.");
748
+
749
+#ifdef NETDATA_INTERNAL_CHECKS
750
+ char iptext[INET6_ADDRSTRLEN];
751
+ if (inet_ntop(family, &w->index.daddr.addr8, iptext, sizeof(iptext)))
752
+ info("New %s dimension added: ID = %u, IP = %s, NAME = %s, DIM1 = %s, DIM2 = %s, SENT = %lu(%lu), RECV = %lu(%lu)",
753
+ (out == &inbound_vectors)?"inbound":"outbound", curr, iptext, w->resolved_name,
754
+ w->dimension_recv, w->dimension_sent, w->sock.sent_bytes, w->sock.sent_packets,
755
+ w->sock.recv_bytes, w->sock.recv_packets);
756
+#endif
757
+ curr++;
758
+ if (curr > last)
759
+ curr = last;
760
+ out->next = curr;
761
+ }
762
+}
763
+
764
+/**
765
+ * Compare Vector to store
766
+ *
767
+ * Compare input values with local address to select table to store.
768
+ *
769
+ * @param cmp index read from hash table.
770
+ *
771
+ * @return It returns the structure with address to compare.
772
+ */
773
+netdata_vector_plot_t * select_vector_to_store(netdata_socket_idx_t *cmp)
774
+{
775
+ if (!listen_ports)
776
+ return &outbound_vectors;
777
+
778
+ ebpf_network_viewer_port_list_t *move_ports = listen_ports;
779
+ while (move_ports) {
780
+ if (move_ports->first == cmp->sport) {
781
+ return &inbound_vectors;
782
+ }
783
+
784
+ move_ports = move_ports->next;
785
+ }
786
+
787
+ return &outbound_vectors;
788
+}
789
+
790
+static void hash_accumulator(netdata_socket_t *values, netdata_socket_idx_t *key, int *removesock, int family, int end)
791
+{
792
+ uint64_t bsent = 0, brecv = 0, psent = 0, precv = 0;
793
+ int i;
794
+ uint8_t protocol = values[0].protocol;
795
+ for (i = 1; i < end; i++) {
796
+ netdata_socket_t *w = &values[i];
797
+
798
+ precv += w->recv_packets;
799
+ psent += w->sent_packets;
800
+ brecv += w->recv_bytes;
801
+ bsent += w->sent_bytes;
802
+
803
+ if (!protocol)
804
+ protocol = w->protocol;
805
+
806
+ *removesock += (int)w->removeme;
807
+ }
808
+
809
+ values[0].recv_packets += precv;
810
+ values[0].sent_packets += psent;
811
+ values[0].recv_bytes += brecv;
812
+ values[0].sent_bytes += bsent;
813
+ values[0].removeme += *removesock;
814
+ values[0].protocol = protocol;
815
+
816
+ if (is_socket_allowed(key, family)) {
817
+ netdata_vector_plot_t *table = select_vector_to_store(key);
818
+ store_socket_inside_avl(table, values, key, family);
819
+ }
820
+}
821
+
822
+/**
823
+ * Read socket hash table
824
+ *
825
+ * Read data from hash tables created on kernel ring.
826
+ *
827
+ * @param fd the hash table with data.
828
+ * @param family the family associated to the hash table
829
+ *
830
+ * @return it returns 0 on success and -1 otherwise.
831
+ */
832
+static void read_socket_hash_table(int fd, int family)
833
+{
834
+ netdata_socket_idx_t key = {};
835
+ netdata_socket_idx_t next_key;
836
+ netdata_socket_idx_t removeme;
837
+ int removesock = 0;
838
+
839
+ netdata_socket_t *values = socket_values;
840
+ int test, end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
841
+
842
+ while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
843
+ test = bpf_map_lookup_elem(fd, &key, values);
844
+ if (test < 0) {
845
+ key = next_key;
846
+ continue;
847
+ }
848
+
849
+ if (removesock)
850
+ bpf_map_delete_elem(fd, &removeme);
851
+
852
+ removesock = 0;
853
+ hash_accumulator(values, &key, &removesock, family, end);
854
+
855
+ if (removesock)
856
+ removeme = key;
857
+
858
+ key = next_key;
859
+ }
860
+
861
+ if (removesock)
862
+ bpf_map_delete_elem(fd, &removeme);
863
+
864
+ test = bpf_map_lookup_elem(fd, &next_key, values);
865
+ if (test < 0) {
866
+ return;
867
+ }
868
+
869
+ removesock = 0;
870
+ hash_accumulator(values, &next_key, &removesock, family, end);
871
+
872
+ if (removesock)
873
+ bpf_map_delete_elem(fd, &next_key);
874
+}
875
+
876
+/**
877
+ * Update listen table
878
+ *
879
+ * Update link list when it is necessary.
880
+ *
881
+ * @param value the ports we are listen to.
882
+ */
883
+void update_listen_table(uint16_t value)
884
+{
885
+ ebpf_network_viewer_port_list_t *w;
886
+ if (likely(listen_ports)) {
887
+ ebpf_network_viewer_port_list_t *move = listen_ports, *store = listen_ports;
888
+ while (move) {
889
+ if (move->first == value)
890
+ return;
891
+
892
+ store = move;
893
+ move = move->next;
894
+ }
895
+
896
+ w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
897
+ w->first = value;
898
+ store->next = w;
899
+ } else {
900
+ w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
901
+ w->first = value;
902
+
903
+ listen_ports = w;
904
+ }
905
+
906
+#ifdef NETDATA_INTERNAL_CHECKS
907
+ info("The network viewer is monitoring inbound connections for port %u", ntohs(value));
908
+#endif
909
+}
910
+
911
+/**
912
+ * Read listen table
913
+ *
914
+ * Read the table with all ports that we are listen on host.
915
+ */
916
+static void read_listen_table()
917
+{
918
+ uint16_t key = 0;
919
+ uint16_t next_key;
920
+
921
+ int fd = map_fd[NETDATA_SOCKET_LISTEN_TABLE];
922
+ uint8_t value;
923
+ while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
924
+ int test = bpf_map_lookup_elem(fd, &key, &value);
925
+ if (test < 0) {
926
+ key = next_key;
927
+ continue;
928
+ }
929
+
930
+ update_listen_table(htons(key));
931
+
932
+ key = next_key;
933
+ }
934
+
935
+ if (next_key)
936
+ update_listen_table(htons(next_key));
937
+}
938
+
939
+/**
940
+ * Socket read hash
941
+ *
942
+ * This is the thread callback.
943
+ * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
944
+ *
945
+ * @param ptr It is a NULL value for this thread.
946
+ *
947
+ * @return It always returns NULL.
948
+ */
949
+void *ebpf_socket_read_hash(void *ptr)
950
+{
951
+ UNUSED(ptr);
952
+
953
+ heartbeat_t hb;
954
+ heartbeat_init(&hb);
955
+ usec_t step = NETDATA_SOCKET_READ_SLEEP_MS;
956
+ int fd_ipv4 = map_fd[NETDATA_SOCKET_IPV4_HASH_TABLE];
957
+ int fd_ipv6 = map_fd[NETDATA_SOCKET_IPV6_HASH_TABLE];
958
+ while (!close_ebpf_plugin) {
959
+ usec_t dt = heartbeat_next(&hb, step);
960
+ (void)dt;
961
+
962
+ read_listen_table();
963
+ read_socket_hash_table(fd_ipv4, AF_INET);
964
+ read_socket_hash_table(fd_ipv6, AF_INET6);
965
+ }
966
+
967
+ return NULL;
968
+}
969
+
970
/**
971
* Read the hash table and store data to allocated vectors.
972
*/
@@ -304,7 +976,7 @@ static void read_hash_global_tables()
976
netdata_idx_t res[NETDATA_SOCKET_COUNTER];
977
978
netdata_idx_t *val = socket_hash_values;
307
- int fd = map_fd[4];
979
+ int fd = map_fd[NETDATA_SOCKET_GLOBAL_HASH_TABLE];
980
for (idx = 0; idx < NETDATA_SOCKET_COUNTER; idx++) {
981
if (!bpf_map_lookup_elem(fd, &idx, val)) {
982
uint64_t total = 0;
@@ -324,6 +996,7 @@ static void read_hash_global_tables()
996
socket_aggregated_data[2].call = res[NETDATA_KEY_CALLS_TCP_CLOSE];
997
socket_aggregated_data[3].call = res[NETDATA_KEY_CALLS_UDP_RECVMSG];
998
socket_aggregated_data[4].call = res[NETDATA_KEY_CALLS_UDP_SENDMSG];
999
+ socket_aggregated_data[5].call = res[NETDATA_KEY_TCP_RETRANSMIT];
1000
1001
socket_aggregated_data[0].ecall = res[NETDATA_KEY_ERROR_TCP_SENDMSG];
1002
socket_aggregated_data[1].ecall = res[NETDATA_KEY_ERROR_TCP_CLEANUP_RBUF];
@@ -383,7 +1056,7 @@ void ebpf_socket_bandwidth_accumulator(ebpf_bandwidth_t *out)
1056
*/
1057
static void ebpf_socket_update_apps_data()
1058
{
386
- int fd = map_fd[0];
1059
+ int fd = map_fd[NETDATA_SOCKET_APPS_HASH_TABLE];
1060
ebpf_bandwidth_t *eb = bandwidth_vector;
1061
uint32_t key;
1062
struct pid_stat *pids = root_of_pids;
@@ -425,6 +1098,14 @@ static void socket_collector(usec_t step, ebpf_module_t *em)
1098
heartbeat_t hb;
1099
heartbeat_init(&hb);
1100
1101
+ struct netdata_static_thread socket_threads = {"EBPF SOCKET READ",
1102
+ NULL, NULL, 1, NULL,
1103
+ NULL, ebpf_socket_read_hash };
1104
+ socket_threads.thread = mallocz(sizeof(netdata_thread_t));;
1105
+
1106
+ netdata_thread_create(socket_threads.thread, socket_threads.name,
1107
+ NETDATA_THREAD_OPTION_JOINABLE, ebpf_socket_read_hash, em);
1108
+
1109
int socket_apps_enabled = ebpf_modules[EBPF_MODULE_SOCKET_IDX].apps_charts;
1110
int socket_global_enabled = ebpf_modules[EBPF_MODULE_SOCKET_IDX].global_charts;
1111
while (!close_ebpf_plugin) {
@@ -458,6 +1139,45 @@ static void socket_collector(usec_t step, ebpf_module_t *em)
1139
*
1140
*****************************************************************/
1141
1142
+
1143
+/**
1144
+ * Clean internal socket plot
1145
+ *
1146
+ * Clean all structures allocated with strdupz.
1147
+ *
1148
+ * @param ptr the pointer with addresses to clean.
1149
+ */
1150
+static inline void clean_internal_socket_plot(netdata_socket_plot_t *ptr)
1151
+{
1152
+ freez(ptr->dimension_recv);
1153
+ freez(ptr->dimension_sent);
1154
+ freez(ptr->resolved_name);
1155
+}
1156
+
1157
+/**
1158
+ * Clean socket plot
1159
+ *
1160
+ * Clean the allocated data for inbound and outbound vectors.
1161
+ */
1162
+static void clean_allocated_socket_plot()
1163
+{
1164
+ uint32_t i;
1165
+ uint32_t end = inbound_vectors.last;
1166
+ netdata_socket_plot_t *plot = inbound_vectors.plot;
1167
+ for (i = 0; i < end; i++) {
1168
+ clean_internal_socket_plot(&plot[i]);
1169
+ }
1170
+
1171
+ clean_internal_socket_plot(&plot[inbound_vectors.last]);
1172
+
1173
+ end = outbound_vectors.last;
1174
+ plot = outbound_vectors.plot;
1175
+ for (i = 0; i < end; i++) {
1176
+ clean_internal_socket_plot(&plot[i]);
1177
+ }
1178
+ clean_internal_socket_plot(&plot[outbound_vectors.last]);
1179
+}
1180
+
1181
/**
1182
* Clean netowrk ports allocated during initializaion.
1183
*
@@ -532,6 +1252,13 @@ static void ebpf_socket_cleanup(void *ptr)
1252
freez(socket_bandwidth_prev);
1253
freez(bandwidth_vector);
1254
1255
+ freez(socket_values);
1256
+ clean_allocated_socket_plot();
1257
+ freez(inbound_vectors.plot);
1258
+ freez(outbound_vectors.plot);
1259
+
1260
+ clean_port_structure(&listen_ports);
1261
+
1262
ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled = 0;
1263
1264
clean_network_ports(network_viewer_opt.included_port);
@@ -563,6 +1290,10 @@ static void ebpf_socket_allocate_global_vectors(size_t length)
1290
socket_bandwidth_curr = callocz((size_t)pid_max, sizeof(ebpf_socket_publish_apps_t *));
1291
socket_bandwidth_prev = callocz((size_t)pid_max, sizeof(ebpf_socket_publish_apps_t *));
1292
bandwidth_vector = callocz((size_t)ebpf_nprocs, sizeof(ebpf_bandwidth_t));
1293
+
1294
+ socket_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_socket_t));
1295
+ inbound_vectors.plot = callocz(network_viewer_opt.max_dim, sizeof(netdata_socket_plot_t));
1296
+ outbound_vectors.plot = callocz(network_viewer_opt.max_dim, sizeof(netdata_socket_plot_t));
1297
}
1298
1299
void change_socket_event()
@@ -604,6 +1335,12 @@ void *ebpf_socket_thread(void *ptr)
1335
{
1336
netdata_thread_cleanup_push(ebpf_socket_cleanup, ptr);
1337
1338
+ avl_init_lock(&inbound_vectors.tree, compare_sockets);
1339
+ avl_init_lock(&outbound_vectors.tree, compare_sockets);
1340
+
1341
+ inbound_vectors.last = network_viewer_opt.max_dim - 1;
1342
+ outbound_vectors.last = inbound_vectors.last;
1343
+
1344
ebpf_module_t *em = (ebpf_module_t *)ptr;
1345
fill_ebpf_data(&socket_data);
1346
collectors/ebpf.plugin/ebpf_socket.h
+105
-18
@@ -1,13 +1,21 @@
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
-
2
#ifndef NETDATA_EBPF_SOCKET_H
3
#define NETDATA_EBPF_SOCKET_H 1
4
+#include <stdint.h>
5
+#include "libnetdata/avl/avl.h"
6
6
-#define NETDATA_SOCKET_COUNTER 13
7
+// Vector indexes
8
+#define NETDATA_MAX_SOCKET_VECTOR 6
9
+#define NETDATA_UDP_START 3
10
+#define NETDATA_RETRANSMIT_START 5
11
8
-#define NETDATA_MAX_SOCKET_VECTOR 5
12
+#define NETDATA_SOCKET_APPS_HASH_TABLE 0
13
+#define NETDATA_SOCKET_IPV4_HASH_TABLE 1
14
+#define NETDATA_SOCKET_IPV6_HASH_TABLE 2
15
+#define NETDATA_SOCKET_GLOBAL_HASH_TABLE 4
16
+#define NETDATA_SOCKET_LISTEN_TABLE 5
17
10
-#define NETDATA_UDP_START 3
18
+#define NETDATA_SOCKET_READ_SLEEP_MS 400000
19
20
typedef enum ebpf_socket_idx {
21
NETDATA_KEY_CALLS_TCP_SENDMSG,
@@ -26,7 +34,11 @@ typedef enum ebpf_socket_idx {
34
35
NETDATA_KEY_CALLS_UDP_SENDMSG,
36
NETDATA_KEY_ERROR_UDP_SENDMSG,
29
- NETDATA_KEY_BYTES_UDP_SENDMSG
37
+ NETDATA_KEY_BYTES_UDP_SENDMSG,
38
+
39
+ NETDATA_KEY_TCP_RETRANSMIT,
40
+
41
+ NETDATA_SOCKET_COUNTER
42
} ebpf_socket_index_t;
43
44
#define NETDATA_SOCKET_GROUP "Socket"
@@ -35,6 +47,7 @@ typedef enum ebpf_socket_idx {
47
#define NETDATA_TCP_FUNCTION_COUNT "tcp_functions"
48
#define NETDATA_TCP_FUNCTION_BYTES "tcp_bandwidth"
49
#define NETDATA_TCP_FUNCTION_ERROR "tcp_error"
50
+#define NETDATA_TCP_RETRANSMIT "tcp_retransmit"
51
#define NETDATA_UDP_FUNCTION_COUNT "udp_functions"
52
#define NETDATA_UDP_FUNCTION_BYTES "udp_bandwidth"
53
#define NETDATA_UDP_FUNCTION_ERROR "udp_error"
@@ -43,12 +56,12 @@ typedef enum ebpf_socket_idx {
56
#define NETDATA_NET_APPS_BANDWIDTH_SENT "bandwidth_sent"
57
#define NETDATA_NET_APPS_BANDWIDTH_RECV "bandwidth_recv"
58
46
-//Port range
47
-# define NETDATA_MINIMUM_PORT_VALUE 1
48
-# define NETDATA_MAXIMUM_PORT_VALUE 65535
59
+// Port range
60
+#define NETDATA_MINIMUM_PORT_VALUE 1
61
+#define NETDATA_MAXIMUM_PORT_VALUE 65535
62
50
-# define NETDATA_MINIMUM_IPV4_CIDR 0
51
-# define NETDATA_MAXIMUM_IPV4_CIDR 32
63
+#define NETDATA_MINIMUM_IPV4_CIDR 0
64
+#define NETDATA_MAXIMUM_IPV4_CIDR 32
65
66
typedef struct ebpf_socket_publish_apps {
67
// Data read
@@ -75,30 +88,37 @@ typedef struct ebpf_network_viewer_port_list {
88
89
uint16_t first;
90
uint16_t last;
91
+
92
+ uint16_t cmp_first;
93
+ uint16_t cmp_last;
94
struct ebpf_network_viewer_port_list *next;
95
} ebpf_network_viewer_port_list_t;
96
97
+/**
98
+ * Union used to store ip addresses
99
+ */
100
union netdata_ip_t {
101
uint8_t addr8[16];
102
uint16_t addr16[8];
103
uint32_t addr32[4];
104
+ uint64_t addr64[2];
105
};
106
107
typedef struct ebpf_network_viewer_ip_list {
88
- char *value; //IP value
89
- uint32_t hash; //IP hash
108
+ char *value; // IP value
109
+ uint32_t hash; // IP hash
110
91
- uint8_t ver; //IP version
111
+ uint8_t ver; // IP version
112
93
- union netdata_ip_t first; //The IP address informed
94
- union netdata_ip_t last; //The IP address informed
113
+ union netdata_ip_t first; // The IP address informed
114
+ union netdata_ip_t last; // The IP address informed
115
116
struct ebpf_network_viewer_ip_list *next;
117
} ebpf_network_viewer_ip_list_t;
118
119
typedef struct ebpf_network_viewer_hostname_list {
100
- char *value; //IP value
101
- uint32_t hash; //IP hash
120
+ char *value; // IP value
121
+ uint32_t hash; // IP hash
122
123
SIMPLE_PATTERN *value_pattern;
124
@@ -106,7 +126,9 @@ typedef struct ebpf_network_viewer_hostname_list {
126
} ebpf_network_viewer_hostname_list_t;
127
128
typedef struct ebpf_network_viewer_options {
109
- uint32_t max_dim; //Store value read from 'maximum dimensions'
129
+ uint32_t max_dim; // Store value read from 'maximum dimensions'
130
+
131
+ uint32_t name_resolution_enabled;
132
133
ebpf_network_viewer_port_list_t *excluded_port;
134
ebpf_network_viewer_port_list_t *included_port;
@@ -118,8 +140,73 @@ typedef struct ebpf_network_viewer_options {
140
141
ebpf_network_viewer_hostname_list_t *excluded_hostnames;
142
ebpf_network_viewer_hostname_list_t *included_hostnames;
143
+
144
+ ebpf_network_viewer_ip_list_t *ipv4_local_ip;
145
+ ebpf_network_viewer_ip_list_t *ipv6_local_ip;
146
} ebpf_network_viewer_options_t;
147
148
extern ebpf_network_viewer_options_t network_viewer_opt;
149
150
+/**
151
+ * Structure to store socket information
152
+ */
153
+typedef struct netdata_socket {
154
+ uint64_t recv_packets;
155
+ uint64_t sent_packets;
156
+ uint64_t recv_bytes;
157
+ uint64_t sent_bytes;
158
+ uint64_t first; // First timestamp
159
+ uint64_t ct; // Current timestamp
160
+ uint16_t retransmit; // It is never used with UDP
161
+ uint8_t protocol; // Should this to be in the index?
162
+ uint8_t removeme;
163
+ uint32_t reserved;
164
+} netdata_socket_t __attribute__((__aligned__(8)));
165
+
166
+/**
167
+ * Index used together previous structure
168
+ */
169
+typedef struct netdata_socket_idx {
170
+ union netdata_ip_t saddr;
171
+ uint16_t sport;
172
+ union netdata_ip_t daddr;
173
+ uint16_t dport;
174
+} netdata_socket_idx_t __attribute__((__aligned__(8)));
175
+
176
+// Next values were defined according getnameinfo(3)
177
+#define NETDATA_MAX_NETWORK_COMBINED_LENGTH 1018
178
+#define NETDATA_DOTS_PROTOCOL_COMBINED_LENGTH 5 // :TCP:
179
+#define NETDATA_DIM_LENGTH_WITHOUT_SERVICE_PROTOCOL 979
180
+
181
+/**
182
+ * Allocate the maximum number of structures in the beginning, this can force the collector to use more memory
183
+ * in the long term, on the other had it is faster.
184
+ */
185
+typedef struct netdata_socket_plot {
186
+ // Search
187
+ avl avl;
188
+ netdata_socket_idx_t index;
189
+
190
+ // Updated data
191
+ netdata_socket_t sock;
192
+
193
+ int family; // AF_INET or AF_INET6
194
+ char *resolved_name; // Resolve only in the first call
195
+ unsigned char resolved;
196
+
197
+ char *dimension_sent;
198
+ char *dimension_recv;
199
+} netdata_socket_plot_t;
200
+
201
+typedef struct netdata_vector_plot {
202
+ netdata_socket_plot_t *plot;
203
+
204
+ avl_tree_lock tree;
205
+ uint32_t last;
206
+ uint32_t next;
207
+
208
+} netdata_vector_plot_t;
209
+
210
+extern void clean_port_structure(ebpf_network_viewer_port_list_t **clean);
211
+
212
#endif
packaging/ebpf.checksums
+3
-3
@@ -1,3 +1,3 @@
1
-d36bfbc727f639b0db8d1525b4e1a0bf5caab61a6a78b40a85581a1f4c1523c8 netdata-kernel-collector-glibc-v0.4.5.tar.xz
2
-9903cebfbf3846810287aa755186000a7badfca5ea49703c836ee788b775466b netdata-kernel-collector-musl-v0.4.5.tar.xz
3
-558ccce60b28cabe8759ec43b3ee519a0fdd5b1aaa9e44d75ac511e5de874261 netdata-kernel-collector-static-v0.4.5.tar.xz
1
+feedc98c84cb9452f062275e369da99bc43274bfd6383daa7c05923b282c0e1b netdata-kernel-collector-glibc-v0.4.8.tar.xz
2
+2a4c9e08a164c85d980a2b51054e0c7bfbf9eb0618922dda4ab0a0b0a8c79dc4 netdata-kernel-collector-musl-v0.4.8.tar.xz
3
+80b501360e9fb8c05d9812442dca08a6500b6f56389cd602acbf1d0769fd1613 netdata-kernel-collector-static-v0.4.8.tar.xz
packaging/ebpf.version
+1
-1
@@ -1 +1 @@
1
-v0.4.5
1
+v0.4.8
web/gui/dashboard_info.js
+5
@@ -3123,6 +3123,11 @@ netdataDashboard.context = {
3123
info: 'Bytes sent and received for functions <code>tcp_sendmsg</code> and <code>tcp_cleanup_rbuf</code>.'
3124
},
3125
3126
+ 'ebpf.tcp_retransmit': {
3127
+ title : 'TCP retransmit',
3128
+ info: 'Number of packets retransmitted for function <code>tcp_retranstmit_skb</code>.'
3129
+ },
3130
+
3131
'ebpf.tcp_error': {
3132
title : 'TCP errors',
3133
info: 'Failed calls that to functions <code>tcp_sendmsg</code>, <code>tcp_cleanup_rbuf</code> and <code>tcp_close</code>.'