@cryptotaxi247 / netdata-1 / commits / a4ebf1d4a

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>.'