Network Viewer options (#9495)
Bring new options to ebpf.plugin.
thiagoftsm committed
Jul 21, 2020 at 16:10 UTC
3cf1b2cde579ccfb301786b73992cc833e36e236
7 files changed
+1064
-4
collectors/ebpf.plugin/README.md
+45
@@ -191,6 +191,51 @@ The eBPF collector enables and runs the following eBPF programs by default:
191
- `network viewer`: This eBPF program creates charts with information about `TCP` and `UDP` functions, including the
192
bandwidth consumed by each.
193
194
+### `[network viewer]`
195
+
196
+You can configure the information shown on `outbound` and `inbound` charts with the settings in this section.
197
+
198
+```conf
199
+[network viewer]
200
+ maximum dimensions = 500
201
+ ports = 1-1024 !145 !domain
202
+ hostnames = !example.com
203
+ ips = !127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
204
+```
205
+
206
+When you define a `ports` setting, Netdata will collect network metrics for that specific port. For example, if you
207
+write `ports = 19999`, Netdata will collect only connections for itself. The `hostnames` setting accepts
208
+[simple patterns](/libnetdata/simple_pattern/README.md). The `ports`, and `ips` settings accept negation (`!`) to
209
+ deny specific values or asterisk alone to define all values.
210
+
211
+In the above example, Netdata will collect metrics for all ports between 1 and 443, with the exception of 53 (domain)
212
+and 145.
213
+
214
+The following options are available:
215
+
216
+- `ports`: Define the destination ports for Netdata to monitor.
217
+- `hostnames`: The list of hostnames that can be resolved to an IP address.
218
+- `ips`: The IP or range of IPs that you want to monitor. You can use IPv4 or IPv6 addresses, use dashes to define a
219
+ range of IPs, or use CIDR values. The default behavior is to only collect data for private IP addresess, but this
220
+ can be changed with the `ips` setting.
221
+
222
+By default, Netdata displays up to 500 dimensions on network viewer charts. If there are more possible dimensions, they
223
+will be bundled into the `other` dimension. You can increase the number of shown dimensions by changing the `maximum
224
+dimensions` setting.
225
+
226
+### `[service name]`
227
+
228
+Netdata uses the list of services in `/etc/services` to plot network viewer charts. If this file does not contain the
229
+name for a particular service you use in your infrastructure, you will need to add it to the `[service name]` section.
230
+
231
+For example, Netdata's default port (`19999`) is not listed in `/etc/services`. To associate that port with the Netdata
232
+service in network viewer charts, and thus see the name of the service instead of its port, define it:
233
+
234
+```conf
235
+[service name]
236
+ 19999 = Netdata
237
+```
238
+
239
## Troubleshooting
240
241
If the eBPF collector does not work, you can troubleshoot it by running the `ebpf.plugin` command and investigating its
collectors/ebpf.plugin/ebpf.c
+862
-1
@@ -4,6 +4,7 @@
4
#include <sys/resource.h>
5
6
#include "ebpf.h"
7
+#include "ebpf_socket.h"
8
9
/*****************************************************************
10
*
@@ -114,12 +115,54 @@ ebpf_module_t ebpf_modules[] = {
115
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 };
121
+
122
/*****************************************************************
123
*
124
* FUNCTIONS USED TO CLEAN MEMORY AND OPERATE SYSTEM FILES
125
*
126
*****************************************************************/
127
128
+/**
129
+ * Clean port Structure
130
+ *
131
+ * Clean the allocated list.
132
+ *
133
+ * @param clean the list that will be cleaned
134
+ */
135
+static void clean_port_structure(ebpf_network_viewer_port_list_t **clean)
136
+{
137
+ ebpf_network_viewer_port_list_t *move = *clean;
138
+ while (move) {
139
+ ebpf_network_viewer_port_list_t *next = move->next;
140
+ freez(move);
141
+
142
+ move = next;
143
+ }
144
+ *clean = NULL;
145
+}
146
+
147
+/**
148
+ * Clean IP structure
149
+ *
150
+ * Clean the allocated list.
151
+ *
152
+ * @param clean the list that will be cleaned
153
+ */
154
+static void clean_ip_structure(ebpf_network_viewer_ip_list_t **clean)
155
+{
156
+ ebpf_network_viewer_ip_list_t *move = *clean;
157
+ while (move) {
158
+ ebpf_network_viewer_ip_list_t *next = move->next;
159
+ freez(move);
160
+
161
+ move = next;
162
+ }
163
+ *clean = NULL;
164
+}
165
+
166
static void change_events()
167
{
168
if (ebpf_modules[0].mode == MODE_ENTRY)
@@ -653,6 +696,817 @@ static inline int parse_disable_apps(char *ptr)
696
return 0;
697
}
698
699
+/**
700
+ * Fill Port list
701
+ *
702
+ * @param out a pointer to the link list.
703
+ * @param in the structure that will be linked.
704
+ */
705
+static inline void fill_port_list(ebpf_network_viewer_port_list_t **out, ebpf_network_viewer_port_list_t *in)
706
+{
707
+ if (likely(*out)) {
708
+ ebpf_network_viewer_port_list_t *move = *out, *store = *out;
709
+ uint16_t first = ntohs(in->first);
710
+ uint16_t last = ntohs(in->last);
711
+ while (move) {
712
+ uint16_t cmp_first = ntohs(move->first);
713
+ uint16_t cmp_last = ntohs(move->last);
714
+ if (cmp_first <= first && first <= cmp_last &&
715
+ cmp_first <= last && last <= cmp_last ) {
716
+ info("The range/value (%u, %u) is inside the range/value (%u, %u) already inserted, it will be ignored.",
717
+ first, last, cmp_first, cmp_last);
718
+ freez(in->value);
719
+ freez(in);
720
+ return;
721
+ } else if (first <= cmp_first && cmp_first <= last &&
722
+ first <= cmp_last && cmp_last <= last) {
723
+ info("The range (%u, %u) is bigger than previous range (%u, %u) already inserted, the previous will be ignored.",
724
+ first, last, cmp_first, cmp_last);
725
+ freez(move->value);
726
+ move->value = in->value;
727
+ move->first = in->first;
728
+ move->last = in->last;
729
+ freez(in);
730
+ return;
731
+ }
732
+
733
+ store = move;
734
+ move = move->next;
735
+ }
736
+
737
+ store->next = in;
738
+ } else {
739
+ *out = in;
740
+ }
741
+
742
+#ifdef NETDATA_INTERNAL_CHECKS
743
+ info("Adding values %s( %u, %u) to %s port list used on network viewer",
744
+ in->value, ntohs(in->first), ntohs(in->last),
745
+ (*out == network_viewer_opt.included_port)?"included":"excluded");
746
+#endif
747
+}
748
+
749
+/**
750
+ * Fill port list
751
+ *
752
+ * Fill an allocated port list with the range given
753
+ *
754
+ * @param out a pointer to store the link list
755
+ * @param range the informed range for the user.
756
+ */
757
+static void parse_port_list(void **out, char *range)
758
+{
759
+ int first, last;
760
+ ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
761
+
762
+ char *copied = strdupz(range);
763
+ if (*range == '*' && *(range+1) == '\0') {
764
+ first = 1;
765
+ last = 65535;
766
+
767
+ clean_port_structure(list);
768
+ goto fillenvpl;
769
+ }
770
+
771
+ char *end = range;
772
+ //Move while I cannot find a separator
773
+ while (*end && *end != ':' && *end != '-') end++;
774
+
775
+ //It has a range
776
+ if (likely(*end)) {
777
+ *end++ = '\0';
778
+ if (*end == '!') {
779
+ info("The exclusion cannot be in the second part of the range, the range %s will be ignored.", copied);
780
+ freez(copied);
781
+ return;
782
+ }
783
+ last = str2i((const char *)end);
784
+ } else {
785
+ last = 0;
786
+ }
787
+
788
+ first = str2i((const char *)range);
789
+ if (first < NETDATA_MINIMUM_PORT_VALUE || first > NETDATA_MAXIMUM_PORT_VALUE) {
790
+ info("The first port %d of the range \"%s\" is invalid and it will be ignored!", first, copied);
791
+ freez(copied);
792
+ return;
793
+ }
794
+
795
+ if (!last)
796
+ last = first;
797
+
798
+ if (last < NETDATA_MINIMUM_PORT_VALUE || last > NETDATA_MAXIMUM_PORT_VALUE) {
799
+ info("The second port %d of the range \"%s\" is invalid and the whole range will be ignored!", last, copied);
800
+ freez(copied);
801
+ return;
802
+ }
803
+
804
+ if (first > last) {
805
+ info("The specified order %s is wrong, the smallest value is always the first, it will be ignored!", copied);
806
+ freez(copied);
807
+ return;
808
+ }
809
+
810
+ ebpf_network_viewer_port_list_t *w;
811
+fillenvpl:
812
+ w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
813
+ w->value = copied;
814
+ w->hash = simple_hash(copied);
815
+ w->first = (uint16_t)htons((uint16_t)first);
816
+ w->last = (uint16_t)htons((uint16_t)last);
817
+
818
+ fill_port_list(list, w);
819
+}
820
+
821
+/**
822
+ * Parse Service List
823
+ *
824
+ * @param out a pointer to store the link list
825
+ * @param service the service used to create the structure that will be linked.
826
+ */
827
+static void parse_service_list(void **out, char *service)
828
+{
829
+ ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
830
+ struct servent *serv = getservbyname((const char *)service, "tcp");
831
+ if (!serv)
832
+ serv = getservbyname((const char *)service, "udp");
833
+
834
+ if (!serv) {
835
+ info("Cannot resolv the service '%s' with protocols TCP and UDP, it will be ignored", service);
836
+ return;
837
+ }
838
+
839
+ ebpf_network_viewer_port_list_t *w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
840
+ w->value = strdupz(service);
841
+ w->hash = simple_hash(service);
842
+
843
+ w->first = w->last = (uint16_t)serv->s_port;
844
+
845
+ fill_port_list(list, w);
846
+}
847
+
848
+/**
849
+ * Netmask
850
+ *
851
+ * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
852
+ *
853
+ * @param prefix create the netmask based in the CIDR value.
854
+ *
855
+ * @return
856
+ */
857
+static inline in_addr_t netmask(int prefix) {
858
+
859
+ if (prefix == 0)
860
+ return (~((in_addr_t) - 1));
861
+ else
862
+ return (in_addr_t)(~((1 << (32 - prefix)) - 1));
863
+
864
+}
865
+
866
+/**
867
+ * Broadcast
868
+ *
869
+ * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
870
+ *
871
+ * @param addr is the ip address
872
+ * @param prefix is the CIDR value.
873
+ *
874
+ * @return It returns the last address of the range
875
+ */
876
+static inline in_addr_t broadcast(in_addr_t addr, int prefix)
877
+{
878
+ return (addr | ~netmask(prefix));
879
+}
880
+
881
+/**
882
+ * Network
883
+ *
884
+ * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
885
+ *
886
+ * @param addr is the ip address
887
+ * @param prefix is the CIDR value.
888
+ *
889
+ * @return It returns the first address of the range.
890
+ */
891
+static inline in_addr_t ipv4_network(in_addr_t addr, int prefix)
892
+{
893
+ return (addr & netmask(prefix));
894
+}
895
+
896
+/**
897
+ * IP to network long
898
+ *
899
+ * @param dst the vector to store the result
900
+ * @param ip the source ip given by our users.
901
+ * @param domain the ip domain (IPV4 or IPV6)
902
+ * @param source the original string
903
+ *
904
+ * @return it returns 0 on success and -1 otherwise.
905
+ */
906
+static inline int ip2nl(uint8_t *dst, char *ip, int domain, char *source)
907
+{
908
+ if (inet_pton(domain, ip, dst) <= 0) {
909
+ error("The address specified (%s) is invalid ", source);
910
+ return -1;
911
+ }
912
+
913
+ return 0;
914
+}
915
+
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
+
991
+/**
992
+ * Get IPV6 Last Address
993
+ *
994
+ * @param out the address to store the last address.
995
+ * @param in the address used to do the math.
996
+ * @param prefix number of bits used to calculate the address
997
+ */
998
+static void get_ipv6_last_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
999
+{
1000
+ uint64_t mask,tmp;
1001
+ uint64_t ret[2];
1002
+ memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
1003
+
1004
+ if (prefix == 128) {
1005
+ memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
1006
+ return;
1007
+ } else if (!prefix) {
1008
+ ret[0] = ret[1] = 0xFFFFFFFFFFFFFFFF;
1009
+ memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1010
+ return;
1011
+ } else if (prefix <= 64) {
1012
+ ret[1] = 0xFFFFFFFFFFFFFFFFULL;
1013
+
1014
+ tmp = be64toh(ret[0]);
1015
+ if (prefix > 0) {
1016
+ mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
1017
+ tmp |= ~mask;
1018
+ }
1019
+ ret[0] = htobe64(tmp);
1020
+ } else {
1021
+ mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
1022
+ tmp = be64toh(ret[1]);
1023
+ tmp |= ~mask;
1024
+ ret[1] = htobe64(tmp);
1025
+ }
1026
+
1027
+ memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1028
+}
1029
+
1030
+/**
1031
+ * Calculate ipv6 first address
1032
+ *
1033
+ * @param out the address to store the first address.
1034
+ * @param in the address used to do the math.
1035
+ * @param prefix number of bits used to calculate the address
1036
+ */
1037
+static void get_ipv6_first_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
1038
+{
1039
+ uint64_t mask,tmp;
1040
+ uint64_t ret[2];
1041
+
1042
+ memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
1043
+
1044
+ if (prefix == 128) {
1045
+ memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
1046
+ return;
1047
+ } else if (!prefix) {
1048
+ ret[0] = ret[1] = 0;
1049
+ memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1050
+ return;
1051
+ } else if (prefix <= 64) {
1052
+ ret[1] = 0ULL;
1053
+
1054
+ tmp = be64toh(ret[0]);
1055
+ if (prefix > 0) {
1056
+ mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
1057
+ tmp &= mask;
1058
+ }
1059
+ ret[0] = htobe64(tmp);
1060
+ } else {
1061
+ mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
1062
+ tmp = be64toh(ret[1]);
1063
+ tmp &= mask;
1064
+ ret[1] = htobe64(tmp);
1065
+ }
1066
+
1067
+ memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1068
+}
1069
+
1070
+/**
1071
+ * Parse IP List
1072
+ *
1073
+ * Parse IP list and link it.
1074
+ *
1075
+ * @param out a pointer to store the link list
1076
+ * @param ip the value given as parameter
1077
+ */
1078
+static void parse_ip_list(void **out, char *ip)
1079
+{
1080
+ ebpf_network_viewer_ip_list_t **list = (ebpf_network_viewer_ip_list_t **)out;
1081
+
1082
+ char *ipdup = strdupz(ip);
1083
+ union netdata_ip_t first = { };
1084
+ union netdata_ip_t last = { };
1085
+ char *is_ipv6;
1086
+ if (*ip == '*' && *(ip+1) == '\0') {
1087
+ memset(first.addr8, 0, sizeof(first.addr8));
1088
+ memset(last.addr8, 0xFF, sizeof(last.addr8));
1089
+
1090
+ is_ipv6 = ip;
1091
+
1092
+ clean_ip_structure(list);
1093
+ goto storethisip;
1094
+ }
1095
+
1096
+ char *end = ip;
1097
+ //Move while I cannot find a separator
1098
+ while (*end && *end != '/' && *end != '-') end++;
1099
+
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
1102
+ is_ipv6 = strchr(ip, ':');
1103
+
1104
+ int select;
1105
+ if (*end && !is_ipv6) { //IPV4 range
1106
+ select = (*end == '/') ? 0 : 1;
1107
+ *end++ = '\0';
1108
+ if (*end == '!') {
1109
+ info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1110
+ goto cleanipdup;
1111
+ }
1112
+
1113
+ if (!select) { //CIDR
1114
+ select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1115
+ if (select)
1116
+ goto cleanipdup;
1117
+
1118
+ select = (int) str2i(end);
1119
+ if (select < NETDATA_MINIMUM_IPV4_CIDR || select > NETDATA_MAXIMUM_IPV4_CIDR) {
1120
+ info("The specified CIDR %s is not valid, the IP %s will be ignored.", end, ip);
1121
+ goto cleanipdup;
1122
+ }
1123
+
1124
+ 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
1126
+ UNUSED(last.addr32[0]);
1127
+
1128
+ uint32_t ipv4_test = htonl(ipv4_network(ntohl(first.addr32[0]), select));
1129
+ if (first.addr32[0] != ipv4_test) {
1130
+ first.addr32[0] = ipv4_test;
1131
+ struct in_addr ipv4_convert;
1132
+ ipv4_convert.s_addr = ipv4_test;
1133
+ char ipv4_msg[INET_ADDRSTRLEN];
1134
+ if(inet_ntop(AF_INET, &ipv4_convert, ipv4_msg, INET_ADDRSTRLEN))
1135
+ info("The network value of CIDR %s was updated for %s .", ipdup, ipv4_msg);
1136
+ }
1137
+ } else { //Range
1138
+ select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1139
+ if (select)
1140
+ goto cleanipdup;
1141
+
1142
+ select = ip2nl(last.addr8, end, AF_INET, ipdup);
1143
+ if (select)
1144
+ goto cleanipdup;
1145
+ }
1146
+
1147
+ if (htonl(first.addr32[0]) > htonl(last.addr32[0])) {
1148
+ info("The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
1149
+ ipdup);
1150
+ goto cleanipdup;
1151
+ }
1152
+ } else if (is_ipv6) { //IPV6
1153
+ if (!*end) { // unique
1154
+ select = ip2nl(first.addr8, ip, AF_INET6, ipdup);
1155
+ if (select)
1156
+ goto cleanipdup;
1157
+
1158
+ memcpy(last.addr8, first.addr8, sizeof(first.addr8));
1159
+ } else if (*end == '-') {
1160
+ *end++ = 0x00;
1161
+ if (*end == '!') {
1162
+ info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1163
+ goto cleanipdup;
1164
+ }
1165
+
1166
+ select = ip2nl(first.addr8, ip, AF_INET6, ipdup);
1167
+ if (select)
1168
+ goto cleanipdup;
1169
+
1170
+ select = ip2nl(last.addr8, end, AF_INET6, ipdup);
1171
+ if (select)
1172
+ goto cleanipdup;
1173
+ } else { //CIDR
1174
+ *end++ = 0x00;
1175
+ if (*end == '!') {
1176
+ info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1177
+ goto cleanipdup;
1178
+ }
1179
+
1180
+ select = str2i(end);
1181
+ if (select < 0 || select > 128) {
1182
+ info("The CIDR %s is not valid, the address %s will be ignored.", end, ip);
1183
+ goto cleanipdup;
1184
+ }
1185
+
1186
+ uint64_t prefix = (uint64_t)select;
1187
+ select = ip2nl(first.addr8, ip, AF_INET6, ipdup);
1188
+ if (select)
1189
+ goto cleanipdup;
1190
+
1191
+ get_ipv6_last_addr(&last, &first, prefix);
1192
+
1193
+ union netdata_ip_t ipv6_test;
1194
+ get_ipv6_first_addr(&ipv6_test, &first, prefix);
1195
+
1196
+ if (memcmp(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t)) != 0) {
1197
+ memcpy(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t));
1198
+
1199
+ struct in6_addr ipv6_convert;
1200
+ memcpy(ipv6_convert.s6_addr, ipv6_test.addr8, sizeof(union netdata_ip_t));
1201
+
1202
+ char ipv6_msg[INET6_ADDRSTRLEN];
1203
+ if(inet_ntop(AF_INET6, &ipv6_convert, ipv6_msg, INET6_ADDRSTRLEN))
1204
+ info("The network value of CIDR %s was updated for %s .", ipdup, ipv6_msg);
1205
+ }
1206
+ }
1207
+
1208
+ if ((be64toh(*(uint64_t *)&first.addr32[2]) > be64toh(*(uint64_t *)&last.addr32[2]) &&
1209
+ !memcmp(first.addr32, last.addr32, 2*sizeof(uint32_t))) ||
1210
+ (be64toh(*(uint64_t *)&first.addr32) > be64toh(*(uint64_t *)&last.addr32)) ) {
1211
+ info("The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
1212
+ ipdup);
1213
+ goto cleanipdup;
1214
+ }
1215
+ } else { //Unique ip
1216
+ select = ip2nl(first.addr8, ip, AF_INET, ipdup);
1217
+ if (select)
1218
+ goto cleanipdup;
1219
+
1220
+ memcpy(last.addr8, first.addr8, sizeof(first.addr8));
1221
+ }
1222
+
1223
+ ebpf_network_viewer_ip_list_t *store;
1224
+
1225
+storethisip:
1226
+ store = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
1227
+ store->value = ipdup;
1228
+ store->hash = simple_hash(ipdup);
1229
+ store->ver = (uint8_t)(!is_ipv6)?AF_INET:AF_INET6;
1230
+ memcpy(store->first.addr8, first.addr8, sizeof(first.addr8));
1231
+ memcpy(store->last.addr8, last.addr8, sizeof(last.addr8));
1232
+
1233
+ fill_ip_list(list, store);
1234
+ return;
1235
+
1236
+cleanipdup:
1237
+ freez(ipdup);
1238
+}
1239
+
1240
+/**
1241
+ * Parse IP Range
1242
+ *
1243
+ * Parse the IP ranges given and create Network Viewer IP Structure
1244
+ *
1245
+ * @param ptr is a pointer with the text to parse.
1246
+ */
1247
+static void parse_ips(char *ptr)
1248
+{
1249
+ //No value
1250
+ if (unlikely(!ptr))
1251
+ return;
1252
+
1253
+ while (likely(ptr)) {
1254
+ //Move forward until next valid character
1255
+ while (isspace(*ptr)) ptr++;
1256
+
1257
+ //No valid value found
1258
+ if (unlikely(!*ptr))
1259
+ return;
1260
+
1261
+ //Find space that ends the list
1262
+ char *end = strchr(ptr, ' ');
1263
+ if (end) {
1264
+ *end++ = '\0';
1265
+ }
1266
+
1267
+ int neg = 0;
1268
+ if (*ptr == '!') {
1269
+ neg++;
1270
+ ptr++;
1271
+ }
1272
+
1273
+ if (isascii(*ptr)) { //Parse port
1274
+ parse_ip_list((!neg)?(void **)&network_viewer_opt.included_ips:(void **)&network_viewer_opt.excluded_ips,
1275
+ ptr);
1276
+ }
1277
+
1278
+ ptr = end;
1279
+ }
1280
+}
1281
+
1282
+
1283
+/**
1284
+ * Parse Port Range
1285
+ *
1286
+ * Parse the port ranges given and create Network Viewer Port Structure
1287
+ *
1288
+ * @param ptr is a pointer with the text to parse.
1289
+ */
1290
+static void parse_ports(char *ptr)
1291
+{
1292
+ //No value
1293
+ if (unlikely(!ptr))
1294
+ return;
1295
+
1296
+ while (likely(ptr)) {
1297
+ //Move forward until next valid character
1298
+ while (isspace(*ptr)) ptr++;
1299
+
1300
+ //No valid value found
1301
+ if (unlikely(!*ptr))
1302
+ return;
1303
+
1304
+ //Find space that ends the list
1305
+ char *end = strchr(ptr, ' ');
1306
+ if (end) {
1307
+ *end++ = '\0';
1308
+ }
1309
+
1310
+ int neg = 0;
1311
+ if (*ptr == '!') {
1312
+ neg++;
1313
+ ptr++;
1314
+ }
1315
+
1316
+ if (isdigit(*ptr)) { //Parse port
1317
+ parse_port_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1318
+ ptr);
1319
+ } else if (isalpha(*ptr)) { //Parse service
1320
+ parse_service_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1321
+ ptr);
1322
+ } else if (*ptr == '*') { //All
1323
+ parse_port_list((!neg)?(void **)&network_viewer_opt.included_port:(void **)&network_viewer_opt.excluded_port,
1324
+ ptr);
1325
+ }
1326
+
1327
+ ptr = end;
1328
+ }
1329
+}
1330
+
1331
+/**
1332
+ * Link hostname
1333
+ *
1334
+ * @param out is the output link list
1335
+ * @param in the hostname to add to list.
1336
+ */
1337
+static void link_hostname(ebpf_network_viewer_hostname_list_t **out, ebpf_network_viewer_hostname_list_t *in)
1338
+{
1339
+ if (likely(*out)) {
1340
+ ebpf_network_viewer_hostname_list_t *move = *out;
1341
+ for (; move->next ; move = move->next ) {
1342
+ if (move->hash == in->hash && !strcmp(move->value, in->value)) {
1343
+ info("The hostname %s was already inserted, it will be ignored.", in->value);
1344
+ freez(in->value);
1345
+ simple_pattern_free(in->value_pattern);
1346
+ freez(in);
1347
+ return;
1348
+ }
1349
+ }
1350
+
1351
+ move->next = in;
1352
+ } else {
1353
+ *out = in;
1354
+ }
1355
+#ifdef NETDATA_INTERNAL_CHECKS
1356
+ info("Adding value %s to %s hostname list used on network viewer",
1357
+ in->value,
1358
+ (*out == network_viewer_opt.included_hostnames)?"included":"excluded");
1359
+#endif
1360
+}
1361
+
1362
+/**
1363
+ * Link Hostnames
1364
+ *
1365
+ * Parse the list of hostnames to create the link list.
1366
+ * This is not associated with the IP, because simple patterns like *example* cannot be resolved to IP.
1367
+ *
1368
+ * @param out is the output link list
1369
+ * @param parse is a pointer with the text to parser.
1370
+ */
1371
+static void link_hostnames(char *parse)
1372
+{
1373
+ //No value
1374
+ if (unlikely(!parse))
1375
+ return;
1376
+
1377
+ while (likely(parse)) {
1378
+ //Find the first valid value
1379
+ while (isspace(*parse)) parse++;
1380
+
1381
+ //No valid value found
1382
+ if (unlikely(!*parse))
1383
+ return;
1384
+
1385
+ //Find space that ends the list
1386
+ char *end = strchr(parse, ' ');
1387
+ if (end) {
1388
+ *end++ = '\0';
1389
+ }
1390
+
1391
+ int neg = 0;
1392
+ if (*parse == '!') {
1393
+ neg++;
1394
+ parse++;
1395
+ }
1396
+
1397
+ ebpf_network_viewer_hostname_list_t *hostname = callocz(1 , sizeof(ebpf_network_viewer_hostname_list_t));
1398
+ hostname->value = strdupz(parse);
1399
+ hostname->hash = simple_hash(parse);
1400
+ hostname->value_pattern = simple_pattern_create(parse, NULL, SIMPLE_PATTERN_EXACT);
1401
+
1402
+ link_hostname((!neg)?&network_viewer_opt.included_hostnames:&network_viewer_opt.excluded_hostnames,
1403
+ hostname);
1404
+
1405
+ parse = end;
1406
+ }
1407
+}
1408
+
1409
+/**
1410
+ * Parse network viewer section
1411
+ */
1412
+static void parse_network_viewer_section()
1413
+{
1414
+ network_viewer_opt.max_dim = appconfig_get_number(&collector_config,
1415
+ EBPF_NETWORK_VIEWER_SECTION,
1416
+ "maximum dimensions",
1417
+ 500);
1418
+
1419
+ char *value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION,
1420
+ "ports", NULL);
1421
+ parse_ports(value);
1422
+
1423
+ value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION, "hostnames", NULL);
1424
+ link_hostnames(value);
1425
+
1426
+ value = appconfig_get(&collector_config, EBPF_NETWORK_VIEWER_SECTION,
1427
+ "ips", "!127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7");
1428
+ parse_ips(value);
1429
+}
1430
+
1431
+/**
1432
+ * Link dimension name
1433
+ *
1434
+ * Link user specified names inside a link list.
1435
+ *
1436
+ * @param port the port number associated to the dimension name.
1437
+ * @param hash the calculated hash for the dimension name.
1438
+ * @param name the dimension name.
1439
+ */
1440
+static void link_dimension_name(char *port, uint32_t hash, char *value)
1441
+{
1442
+ int test = str2i(port);
1443
+ if (test < NETDATA_MINIMUM_PORT_VALUE || test > NETDATA_MAXIMUM_PORT_VALUE){
1444
+ error("The dimension given (%s = %s) has an invalid value and it will be ignored.", port, value);
1445
+ return;
1446
+ }
1447
+
1448
+ ebpf_network_viewer_dim_name_t *w;
1449
+ w = callocz(1, sizeof(ebpf_network_viewer_dim_name_t));
1450
+
1451
+ w->name = strdupz(value);
1452
+ w->hash = hash;
1453
+
1454
+ w->port = (uint16_t) htons(test);
1455
+
1456
+ ebpf_network_viewer_dim_name_t *names = network_viewer_opt.names;
1457
+ if (unlikely(!names)) {
1458
+ network_viewer_opt.names = w;
1459
+ } else {
1460
+ for (; names->next; names = names->next) {
1461
+ if (names->port == w->port) {
1462
+ info("Dupplicated definition for a service, the name %s will be ignored. ", names->name);
1463
+ freez(names->name);
1464
+ names->name = w->name;
1465
+ names->hash = w->hash;
1466
+ freez(w);
1467
+ return;
1468
+ }
1469
+ }
1470
+ names->next = w;
1471
+ }
1472
+
1473
+#ifdef NETDATA_INTERNAL_CHECKS
1474
+ info("Adding values %s( %u) to dimension name list used on network viewer", w->name, htons(w->port));
1475
+#endif
1476
+}
1477
+
1478
+/**
1479
+ * Parse service Name section.
1480
+ *
1481
+ * This function gets the values that will be used to overwrite dimensions.
1482
+ */
1483
+static void parse_service_name_section()
1484
+{
1485
+ struct section *co = appconfig_get_section(&collector_config, EBPF_SERVICE_NAME_SECTION);
1486
+ if (co) {
1487
+ struct config_option *cv;
1488
+ for (cv = co->values; cv ; cv = cv->next) {
1489
+ link_dimension_name(cv->name, cv->hash, cv->value);
1490
+ }
1491
+ }
1492
+
1493
+ //Always associated the default port to Netdata
1494
+ ebpf_network_viewer_dim_name_t *names = network_viewer_opt.names;
1495
+ if (names) {
1496
+ uint16_t default_port = htons(19999);
1497
+ while (names) {
1498
+ if (names->port == default_port)
1499
+ return;
1500
+
1501
+ names = names->next;
1502
+ }
1503
+ }
1504
+
1505
+ char *port_string = getenv("NETDATA_LISTEN_PORT");
1506
+ if (port_string)
1507
+ link_dimension_name(port_string, simple_hash(port_string), "Netdata");
1508
+}
1509
+
1510
/**
1511
* Read collector values
1512
*
@@ -683,11 +1537,18 @@ static void read_collector_values(int *disable_apps)
1537
enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, ebpf_modules[1].config_name, 1);
1538
if (enabled) {
1539
ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_apps);
1540
+ //Read network viewer section if network viewer is enabled
1541
+ parse_network_viewer_section();
1542
+ parse_service_name_section();
1543
started++;
1544
}
1545
689
- if (!started)
1546
+ if (!started){
1547
ebpf_enable_all_charts(*disable_apps);
1548
+ //Read network viewer section
1549
+ parse_network_viewer_section();
1550
+ parse_service_name_section();
1551
+ }
1552
}
1553
1554
/**
collectors/ebpf.plugin/ebpf.conf
+9
@@ -5,3 +5,12 @@
5
[ebpf programs]
6
process = yes
7
network viewer = yes
8
+
9
+[network viewer]
10
+ maximum dimensions = 500
11
+ ports = *
12
+ 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
+
15
+[service name]
16
+ 19999 = Netdata
collectors/ebpf.plugin/ebpf.h
+4
-2
@@ -184,8 +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"
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"
collectors/ebpf.plugin/ebpf_socket.c
+63
@@ -458,6 +458,63 @@ static void socket_collector(usec_t step, ebpf_module_t *em)
458
*
459
*****************************************************************/
460
461
+/**
462
+ * Clean netowrk ports allocated during initializaion.
463
+ *
464
+ * @param ptr a pointer to the link list.
465
+ */
466
+static void clean_network_ports(ebpf_network_viewer_port_list_t *ptr)
467
+{
468
+ if (unlikely(!ptr))
469
+ return;
470
+
471
+ while (ptr) {
472
+ ebpf_network_viewer_port_list_t *next = ptr->next;
473
+ freez(ptr->value);
474
+ freez(ptr);
475
+ ptr = next;
476
+ }
477
+}
478
+
479
+/**
480
+ * Clean service names
481
+ *
482
+ * Clean the allocated link list that stores names.
483
+ *
484
+ * @param names the link list.
485
+ */
486
+static void clean_service_names(ebpf_network_viewer_dim_name_t *names)
487
+{
488
+ if (unlikely(!names))
489
+ return;
490
+
491
+ while (names) {
492
+ ebpf_network_viewer_dim_name_t *next = names->next;
493
+ freez(names->name);
494
+ freez(names);
495
+ names = next;
496
+ }
497
+}
498
+
499
+/**
500
+ * Clean hostnames
501
+ *
502
+ * @param hostnames the hostnames to clean
503
+ */
504
+static void clean_hostnames(ebpf_network_viewer_hostname_list_t *hostnames)
505
+{
506
+ if (unlikely(!hostnames))
507
+ return;
508
+
509
+ while (hostnames) {
510
+ ebpf_network_viewer_hostname_list_t *next = hostnames->next;
511
+ freez(hostnames->value);
512
+ simple_pattern_free(hostnames->value_pattern);
513
+ freez(hostnames);
514
+ hostnames = next;
515
+ }
516
+}
517
+
518
/**
519
* Clean up the main thread.
520
*
@@ -476,6 +533,12 @@ static void ebpf_socket_cleanup(void *ptr)
533
freez(bandwidth_vector);
534
535
ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled = 0;
536
+
537
+ clean_network_ports(network_viewer_opt.included_port);
538
+ clean_network_ports(network_viewer_opt.excluded_port);
539
+ clean_service_names(network_viewer_opt.names);
540
+ clean_hostnames(network_viewer_opt.included_hostnames);
541
+ clean_hostnames(network_viewer_opt.excluded_hostnames);
542
}
543
544
/*****************************************************************
collectors/ebpf.plugin/ebpf_socket.h
+70
-1
@@ -43,6 +43,13 @@ typedef enum ebpf_socket_idx {
43
#define NETDATA_NET_APPS_BANDWIDTH_SENT "bandwidth_sent"
44
#define NETDATA_NET_APPS_BANDWIDTH_RECV "bandwidth_recv"
45
46
+//Port range
47
+# define NETDATA_MINIMUM_PORT_VALUE 1
48
+# define NETDATA_MAXIMUM_PORT_VALUE 65535
49
+
50
+# define NETDATA_MINIMUM_IPV4_CIDR 0
51
+# define NETDATA_MAXIMUM_IPV4_CIDR 32
52
+
53
typedef struct ebpf_socket_publish_apps {
54
// Data read
55
uint64_t sent;
@@ -53,4 +60,66 @@ typedef struct ebpf_socket_publish_apps {
60
uint64_t publish_recv;
61
} ebpf_socket_publish_apps_t;
62
56
-#endif /* NETDATA_EBPF_SOCKET_H */
63
+typedef struct ebpf_network_viewer_dimension_names {
64
+ char *name;
65
+ uint32_t hash;
66
+
67
+ uint16_t port;
68
+
69
+ struct ebpf_network_viewer_dimension_names *next;
70
+} ebpf_network_viewer_dim_name_t ;
71
+
72
+typedef struct ebpf_network_viewer_port_list {
73
+ char *value;
74
+ uint32_t hash;
75
+
76
+ uint16_t first;
77
+ uint16_t last;
78
+ struct ebpf_network_viewer_port_list *next;
79
+} ebpf_network_viewer_port_list_t;
80
+
81
+union netdata_ip_t {
82
+ uint8_t addr8[16];
83
+ uint16_t addr16[8];
84
+ uint32_t addr32[4];
85
+};
86
+
87
+typedef struct ebpf_network_viewer_ip_list {
88
+ char *value; //IP value
89
+ uint32_t hash; //IP hash
90
+
91
+ uint8_t ver; //IP version
92
+
93
+ union netdata_ip_t first; //The IP address informed
94
+ union netdata_ip_t last; //The IP address informed
95
+
96
+ struct ebpf_network_viewer_ip_list *next;
97
+} ebpf_network_viewer_ip_list_t;
98
+
99
+typedef struct ebpf_network_viewer_hostname_list {
100
+ char *value; //IP value
101
+ uint32_t hash; //IP hash
102
+
103
+ SIMPLE_PATTERN *value_pattern;
104
+
105
+ struct ebpf_network_viewer_hostname_list *next;
106
+} ebpf_network_viewer_hostname_list_t;
107
+
108
+typedef struct ebpf_network_viewer_options {
109
+ uint32_t max_dim; //Store value read from 'maximum dimensions'
110
+
111
+ ebpf_network_viewer_port_list_t *excluded_port;
112
+ ebpf_network_viewer_port_list_t *included_port;
113
+
114
+ ebpf_network_viewer_dim_name_t *names;
115
+
116
+ ebpf_network_viewer_ip_list_t *excluded_ips;
117
+ ebpf_network_viewer_ip_list_t *included_ips;
118
+
119
+ ebpf_network_viewer_hostname_list_t *excluded_hostnames;
120
+ ebpf_network_viewer_hostname_list_t *included_hostnames;
121
+} ebpf_network_viewer_options_t;
122
+
123
+extern ebpf_network_viewer_options_t network_viewer_opt;
124
+
125
+#endif
daemon/main.c
+11
@@ -696,6 +696,17 @@ void set_global_environment() {
696
setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1);
697
setenv("NETDATA_HOST_PREFIX" , netdata_configured_host_prefix, 1);
698
699
+ char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
700
+ int clean = 0;
701
+ if (!default_port) {
702
+ default_port = strdupz("19999");
703
+ clean = 1;
704
+ }
705
+
706
+ setenv("NETDATA_LISTEN_PORT" , default_port, 1);
707
+ if(clean)
708
+ freez(default_port);
709
+
710
get_system_timezone();
711
712
// set the path we need