master
c 885 lines 30.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_functions.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 /*****************************************************************
8 * EBPF FUNCTION COMMON
9 *****************************************************************/
10
11 typedef struct ebpf_function_thread_start {
12 ebpf_module_t *em;
13 void (*start_routine)(void *);
14 bool ready;
15 bool run;
16 } ebpf_function_thread_start_t;
17
18 static void ebpf_function_thread_start(void *ptr)
19 {
20 ebpf_function_thread_start_t *ctx = ptr;
21
22 // Keep the new thread parked until its owner publishes the module state.
23 while (!__atomic_load_n(&ctx->ready, __ATOMIC_ACQUIRE))
24 tinysleep();
25
26 bool run = __atomic_load_n(&ctx->run, __ATOMIC_ACQUIRE);
27 ebpf_module_t *em = ctx->em;
28 void (*start_routine)(void *) = ctx->start_routine;
29 freez(ctx);
30
31 if (run)
32 start_routine(em);
33 }
34
35 /**
36 * Function Start thread
37 *
38 * Start a specific thread after user request.
39 *
40 * @param em The structure with thread information
41 * @param period
42 * @return
43 */
44 static int ebpf_function_start_thread(ebpf_module_t *em, int period)
45 {
46 struct netdata_static_thread *st = em->thread;
47 // another request for thread that already ran, cleanup and restart
48 if (period <= 0)
49 period = EBPF_DEFAULT_LIFETIME;
50
51 #ifdef NETDATA_INTERNAL_CHECKS
52 netdata_log_info("Starting thread %s with lifetime = %d", em->info.thread_name, period);
53 #endif
54
55 ebpf_function_thread_start_t *ctx = callocz(1, sizeof(*ctx));
56 ctx->em = em;
57 ctx->start_routine = st->start_routine;
58
59 ND_THREAD *thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_function_thread_start, ctx);
60 if (!thread) {
61 freez(ctx);
62 return 1;
63 }
64
65 bool run = true;
66 netdata_mutex_lock(&ebpf_exit_cleanup);
67 if (ebpf_plugin_stop())
68 run = false;
69 else {
70 st->thread = thread;
71 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_FUNCTION_RUNNING);
72 em->lifetime = period;
73 }
74 __atomic_store_n(&ctx->run, run, __ATOMIC_RELEASE);
75 __atomic_store_n(&ctx->ready, true, __ATOMIC_RELEASE);
76 netdata_mutex_unlock(&ebpf_exit_cleanup);
77
78 if (!run) {
79 nd_thread_signal_cancel(thread);
80 nd_thread_join(thread);
81 return 1;
82 }
83
84 return 0;
85 }
86
87 /*****************************************************************
88 * EBPF ERROR FUNCTIONS
89 *****************************************************************/
90
91 /**
92 * Function error
93 *
94 * Show error when a wrong function is given
95 *
96 * @param transaction the transaction id that Netdata sent for this function execution
97 * @param code the error code to show with the message.
98 * @param msg the error message
99 */
100 static inline void ebpf_function_error(const char *transaction, int code, const char *msg)
101 {
102 pluginsd_function_json_error_to_stdout(transaction, code, msg);
103 }
104
105 /**
106 * Thread Help
107 *
108 * Shows help with all options accepted by thread function.
109 *
110 * @param transaction the transaction id that Netdata sent for this function execution
111 */
112 static inline void ebpf_function_help(const char *transaction, const char *message)
113 {
114 pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600);
115 fprintf(stdout, "%s", message);
116 pluginsd_function_result_end_to_stdout();
117 fflush(stdout);
118 }
119
120 /*****************************************************************
121 * EBPF SOCKET FUNCTION
122 *****************************************************************/
123
124 /**
125 * Fill Fake socket
126 *
127 * Fill socket with an invalid request.
128 *
129 * @param fake_values is the structure where we are storing the value.
130 */
131 static inline void ebpf_socket_fill_fake_socket(netdata_socket_plus_t *fake_values)
132 {
133 snprintfz(fake_values->socket_string.src_ip, INET6_ADDRSTRLEN, "%s", "127.0.0.1");
134 snprintfz(fake_values->socket_string.dst_ip, INET6_ADDRSTRLEN, "%s", "127.0.0.1");
135 fake_values->pid = getpid();
136 //fake_values->socket_string.src_port = 0;
137 fake_values->socket_string.dst_port[0] = 0;
138 snprintfz(fake_values->socket_string.dst_ip, NI_MAXSERV, "%s", "none");
139 fake_values->data.family = AF_INET;
140 fake_values->data.protocol = AF_UNSPEC;
141 }
142
143 static NETDATA_DOUBLE bytes_to_mb(uint64_t bytes)
144 {
145 return (NETDATA_DOUBLE)bytes / (1024 * 1024);
146 }
147
148 /**
149 * Fill function buffer
150 *
151 * Fill buffer with data to be shown on cloud.
152 *
153 * @param wb buffer where we store data.
154 * @param values data read from hash table
155 * @param name the process name
156 */
157 static void ebpf_fill_function_buffer(BUFFER *wb, netdata_socket_plus_t *values, char *name)
158 {
159 buffer_json_add_array_item_array(wb);
160
161 // IMPORTANT!
162 // THE ORDER SHOULD BE THE SAME WITH THE FIELDS!
163
164 // PID
165 buffer_json_add_array_item_uint64(wb, (uint64_t)values->pid);
166
167 // NAME
168 if (!values->data.name[0])
169 buffer_json_add_array_item_string(wb, (name) ? name : "unknown");
170 else
171 buffer_json_add_array_item_string(wb, values->data.name);
172
173 // Origin
174 buffer_json_add_array_item_string(wb, (values->data.external_origin) ? "in" : "out");
175
176 // Source IP
177 buffer_json_add_array_item_string(wb, values->socket_string.src_ip);
178
179 // SRC Port
180 //buffer_json_add_array_item_uint64(wb, (uint64_t) values->socket_string.src_port);
181
182 // Destination IP
183 buffer_json_add_array_item_string(wb, values->socket_string.dst_ip);
184
185 // DST Port
186 buffer_json_add_array_item_string(wb, values->socket_string.dst_port);
187
188 uint64_t connections;
189 if (values->data.protocol == IPPROTO_TCP) {
190 buffer_json_add_array_item_string(wb, "TCP");
191 buffer_json_add_array_item_double(wb, bytes_to_mb(values->data.tcp.tcp_bytes_received));
192 buffer_json_add_array_item_double(wb, bytes_to_mb(values->data.tcp.tcp_bytes_sent));
193 connections = values->data.tcp.ipv4_connect + values->data.tcp.ipv6_connect;
194 } else if (values->data.protocol == IPPROTO_UDP) {
195 buffer_json_add_array_item_string(wb, "UDP");
196 buffer_json_add_array_item_double(wb, bytes_to_mb(values->data.udp.udp_bytes_received));
197 buffer_json_add_array_item_double(wb, bytes_to_mb(values->data.udp.udp_bytes_sent));
198 connections = values->data.udp.call_udp_sent + values->data.udp.call_udp_received;
199 } else {
200 buffer_json_add_array_item_string(wb, "UNSPEC");
201 buffer_json_add_array_item_double(wb, 0);
202 buffer_json_add_array_item_double(wb, 0);
203 connections = 1;
204 }
205
206 // Connections
207 if (values->flags & NETDATA_SOCKET_FLAGS_ALREADY_OPEN) {
208 connections++;
209 } else if (!connections) {
210 // If no connections, this means that we lost when connection was opened
211 values->flags |= NETDATA_SOCKET_FLAGS_ALREADY_OPEN;
212 connections++;
213 }
214 buffer_json_add_array_item_uint64(wb, connections);
215
216 buffer_json_array_close(wb);
217 }
218
219 /**
220 * Clean Judy array unsafe
221 *
222 * Clean all Judy Array allocated to show table when a function is called.
223 * Before to call this function it is necessary to lock `ebpf_judy_pid.index.rw_spinlock`.
224 **/
225 static void ebpf_socket_clean_judy_array_unsafe()
226 {
227 if (!ebpf_judy_pid.index.JudyLArray)
228 return;
229
230 Pvoid_t *pid_value, *socket_value;
231 Word_t local_pid = 0, local_socket = 0;
232 bool first_pid = true, first_socket = true;
233 while ((pid_value = JudyLFirstThenNext(ebpf_judy_pid.index.JudyLArray, &local_pid, &first_pid))) {
234 netdata_ebpf_judy_pid_stats_t *pid_ptr = (netdata_ebpf_judy_pid_stats_t *)*pid_value;
235 rw_spinlock_write_lock(&pid_ptr->socket_stats.rw_spinlock);
236 if (pid_ptr->socket_stats.JudyLArray) {
237 while (
238 (socket_value = JudyLFirstThenNext(pid_ptr->socket_stats.JudyLArray, &local_socket, &first_socket))) {
239 netdata_socket_plus_t *socket_clean = *socket_value;
240 aral_freez(aral_socket_table, socket_clean);
241 }
242 JudyLFreeArray(&pid_ptr->socket_stats.JudyLArray, PJE0);
243 pid_ptr->socket_stats.JudyLArray = NULL;
244 }
245 rw_spinlock_write_unlock(&pid_ptr->socket_stats.rw_spinlock);
246 }
247 }
248
249 /**
250 * Fill function buffer unsafe
251 *
252 * Fill the function buffer with socket information. Before to call this function it is necessary to lock
253 * ebpf_judy_pid.index.rw_spinlock
254 *
255 * @param buf buffer used to store data to be shown by function.
256 *
257 * @return it returns 0 on success and -1 otherwise.
258 */
259 static void ebpf_socket_fill_function_buffer_unsafe(BUFFER *buf)
260 {
261 int counter = 0;
262
263 Pvoid_t *pid_value, *socket_value;
264 Word_t local_pid = 0;
265 bool first_pid = true;
266 while ((pid_value = JudyLFirstThenNext(ebpf_judy_pid.index.JudyLArray, &local_pid, &first_pid))) {
267 netdata_ebpf_judy_pid_stats_t *pid_ptr = (netdata_ebpf_judy_pid_stats_t *)*pid_value;
268 bool first_socket = true;
269 Word_t local_timestamp = 0;
270 rw_spinlock_read_lock(&pid_ptr->socket_stats.rw_spinlock);
271 if (pid_ptr->socket_stats.JudyLArray) {
272 while ((
273 socket_value = JudyLFirstThenNext(pid_ptr->socket_stats.JudyLArray, &local_timestamp, &first_socket))) {
274 netdata_socket_plus_t *values = (netdata_socket_plus_t *)*socket_value;
275 ebpf_fill_function_buffer(buf, values, pid_ptr->cmdline);
276 }
277 counter++;
278 }
279 rw_spinlock_read_unlock(&pid_ptr->socket_stats.rw_spinlock);
280 }
281
282 if (!counter) {
283 netdata_socket_plus_t fake_values = {};
284 ebpf_socket_fill_fake_socket(&fake_values);
285 ebpf_fill_function_buffer(buf, &fake_values, NULL);
286 }
287 }
288
289 /**
290 * Socket read hash
291 *
292 * This is the thread callback.
293 * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
294 *
295 * @param buf the buffer to store data;
296 * @param em the module main structure.
297 *
298 * @return It always returns NULL.
299 */
300 void ebpf_socket_read_open_connections(BUFFER *buf, struct ebpf_module *em)
301 {
302 // thread was not initialized or Array was reset
303 rw_spinlock_read_lock(&ebpf_judy_pid.index.rw_spinlock);
304 if (!em->maps || (em->maps[NETDATA_SOCKET_OPEN_SOCKET].map_fd == ND_EBPF_MAP_FD_NOT_INITIALIZED) ||
305 !ebpf_judy_pid.index.JudyLArray) {
306 netdata_socket_plus_t fake_values = {};
307
308 ebpf_socket_fill_fake_socket(&fake_values);
309
310 ebpf_fill_function_buffer(buf, &fake_values, NULL);
311 rw_spinlock_read_unlock(&ebpf_judy_pid.index.rw_spinlock);
312 return;
313 }
314
315 rw_spinlock_read_lock(&network_viewer_opt.rw_spinlock);
316 ebpf_socket_fill_function_buffer_unsafe(buf);
317 rw_spinlock_read_unlock(&network_viewer_opt.rw_spinlock);
318 rw_spinlock_read_unlock(&ebpf_judy_pid.index.rw_spinlock);
319 }
320
321 /**
322 * Function: Socket
323 *
324 * Show information for sockets stored in hash tables.
325 *
326 * @param transaction the transaction id that Netdata sent for this function execution
327 * @param function function name and arguments given to thread.
328 * @param timeout The function timeout
329 * @param cancelled Variable used to store function status.
330 */
331 static void ebpf_function_socket_manipulation(
332 const char *transaction,
333 char *function __maybe_unused,
334 usec_t *stop_monotonic_ut __maybe_unused,
335 bool *cancelled __maybe_unused,
336 BUFFER *payload __maybe_unused,
337 HTTP_ACCESS access __maybe_unused,
338 const char *source __maybe_unused,
339 void *data __maybe_unused)
340 {
341 ebpf_module_t *em = &ebpf_modules[EBPF_MODULE_SOCKET_IDX];
342
343 char *words[PLUGINSD_MAX_WORDS] = {NULL};
344 size_t num_words = quoted_strings_splitter_whitespace(function, words, PLUGINSD_MAX_WORDS);
345 const char *name;
346 int period = -1;
347 rw_spinlock_write_lock(&ebpf_judy_pid.index.rw_spinlock);
348 network_viewer_opt.enabled = CONFIG_BOOLEAN_YES;
349 uint32_t previous;
350 bool info = false;
351 time_t now_s = now_realtime_sec();
352
353 static const char *socket_help = {
354 "ebpf.plugin / socket\n"
355 "\n"
356 "Function `socket` display information for all open sockets during ebpf.plugin runtime.\n"
357 "During thread runtime the plugin is always collecting data, but when an option is modified, the plugin\n"
358 "resets completely the previous table and can show a clean data for the first request before to bring the\n"
359 "modified request.\n"
360 "\n"
361 "The following filters are supported:\n"
362 "\n"
363 " family:FAMILY\n"
364 " Shows information for the FAMILY specified. Option accepts IPV4, IPV6 and all, that is the default.\n"
365 "\n"
366 " period:PERIOD\n"
367 " Enable socket to run a specific PERIOD in seconds. When PERIOD is not\n"
368 " specified plugin will use the default 300 seconds\n"
369 "\n"
370 " resolve:BOOL\n"
371 " Resolve service name, default value is YES.\n"
372 "\n"
373 " range:CIDR\n"
374 " Show sockets that have only a specific destination. Default all addresses.\n"
375 "\n"
376 " port:range\n"
377 " Show sockets that have only a specific destination.\n"
378 "\n"
379 " reset\n"
380 " Send a reset to collector. When a collector receives this command, it uses everything defined in configuration file.\n"
381 "\n"
382 " interfaces\n"
383 " When the collector receives this command, it read all available interfaces on host.\n"
384 "\n"
385 "Filters can be combined. Each filter can be given only one time. Default all ports\n"};
386
387 for (int i = 1; i < PLUGINSD_MAX_WORDS; i++) {
388 const char *keyword = get_word(words, num_words, i);
389 if (!keyword)
390 break;
391
392 if (strncmp(keyword, EBPF_FUNCTION_SOCKET_FAMILY, sizeof(EBPF_FUNCTION_SOCKET_FAMILY) - 1) == 0) {
393 name = &keyword[sizeof(EBPF_FUNCTION_SOCKET_FAMILY) - 1];
394 previous = network_viewer_opt.family;
395 uint32_t family = AF_UNSPEC;
396 if (!strcmp(name, "IPV4"))
397 family = AF_INET;
398 else if (!strcmp(name, "IPV6"))
399 family = AF_INET6;
400
401 if (family != previous) {
402 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
403 network_viewer_opt.family = family;
404 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
405 ebpf_socket_clean_judy_array_unsafe();
406 }
407 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_PERIOD, sizeof(EBPF_FUNCTION_SOCKET_PERIOD) - 1) == 0) {
408 name = &keyword[sizeof(EBPF_FUNCTION_SOCKET_PERIOD) - 1];
409 netdata_mutex_lock(&ebpf_exit_cleanup);
410 period = str2i(name);
411 if (period > 0) {
412 em->lifetime = period;
413 } else
414 em->lifetime = EBPF_NON_FUNCTION_LIFE_TIME;
415
416 #ifdef NETDATA_DEV_MODE
417 collector_info("Lifetime modified for %u", em->lifetime);
418 #endif
419 netdata_mutex_unlock(&ebpf_exit_cleanup);
420 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_RESOLVE, sizeof(EBPF_FUNCTION_SOCKET_RESOLVE) - 1) == 0) {
421 previous = network_viewer_opt.service_resolution_enabled;
422 uint32_t resolution;
423 name = &keyword[sizeof(EBPF_FUNCTION_SOCKET_RESOLVE) - 1];
424 resolution = (!strcasecmp(name, "YES")) ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
425
426 if (previous != resolution) {
427 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
428 network_viewer_opt.service_resolution_enabled = resolution;
429 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
430
431 ebpf_socket_clean_judy_array_unsafe();
432 }
433 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_RANGE, sizeof(EBPF_FUNCTION_SOCKET_RANGE) - 1) == 0) {
434 name = &keyword[sizeof(EBPF_FUNCTION_SOCKET_RANGE) - 1];
435 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
436 ebpf_clean_ip_structure(&network_viewer_opt.included_ips);
437 ebpf_clean_ip_structure(&network_viewer_opt.excluded_ips);
438 ebpf_parse_ips_unsafe((char *)name);
439 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
440
441 ebpf_socket_clean_judy_array_unsafe();
442 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_PORT, sizeof(EBPF_FUNCTION_SOCKET_PORT) - 1) == 0) {
443 name = &keyword[sizeof(EBPF_FUNCTION_SOCKET_PORT) - 1];
444 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
445 ebpf_clean_port_structure(&network_viewer_opt.included_port);
446 ebpf_clean_port_structure(&network_viewer_opt.excluded_port);
447 ebpf_parse_ports((char *)name);
448 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
449
450 ebpf_socket_clean_judy_array_unsafe();
451 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_RESET, sizeof(EBPF_FUNCTION_SOCKET_RESET) - 1) == 0) {
452 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
453 ebpf_clean_port_structure(&network_viewer_opt.included_port);
454 ebpf_clean_port_structure(&network_viewer_opt.excluded_port);
455
456 ebpf_clean_ip_structure(&network_viewer_opt.included_ips);
457 ebpf_clean_ip_structure(&network_viewer_opt.excluded_ips);
458 ebpf_clean_ip_structure(&network_viewer_opt.ipv4_local_ip);
459 ebpf_clean_ip_structure(&network_viewer_opt.ipv6_local_ip);
460
461 parse_network_viewer_section(&socket_config);
462 ebpf_read_local_addresses_unsafe();
463 network_viewer_opt.enabled = CONFIG_BOOLEAN_YES;
464 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
465 } else if (strncmp(keyword, EBPF_FUNCTION_SOCKET_INTERFACES, sizeof(EBPF_FUNCTION_SOCKET_INTERFACES) - 1) == 0) {
466 rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
467 ebpf_read_local_addresses_unsafe();
468 rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
469 } else if (strncmp(keyword, "help", 4) == 0) {
470 ebpf_function_help(transaction, socket_help);
471 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
472 return;
473 } else if (strncmp(keyword, "info", 4) == 0)
474 info = true;
475 }
476 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
477
478 if (ebpf_module_enabled_get(em) > NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
479 // Cleanup when we already had a thread running
480 rw_spinlock_write_lock(&ebpf_judy_pid.index.rw_spinlock);
481 ebpf_socket_clean_judy_array_unsafe();
482 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
483
484 collect_pids |= 1 << EBPF_MODULE_SOCKET_IDX;
485 if (ebpf_function_start_thread(em, period)) {
486 ebpf_function_error(transaction, HTTP_RESP_INTERNAL_SERVER_ERROR, "Cannot start thread.");
487 return;
488 }
489 } else {
490 netdata_mutex_lock(&ebpf_exit_cleanup);
491 if (period < 0)
492 em->lifetime = (ebpf_module_enabled_get(em) != NETDATA_THREAD_EBPF_FUNCTION_RUNNING) ?
493 EBPF_NON_FUNCTION_LIFE_TIME :
494 EBPF_DEFAULT_LIFETIME;
495 netdata_mutex_unlock(&ebpf_exit_cleanup);
496 }
497
498 BUFFER *wb = buffer_create(4096, NULL);
499 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
500 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
501 buffer_json_member_add_string(wb, "type", "table");
502 buffer_json_member_add_time_t(wb, "update_every", em->update_every);
503 buffer_json_member_add_boolean(wb, "has_history", false);
504 buffer_json_member_add_string(wb, "help", EBPF_PLUGIN_SOCKET_FUNCTION_DESCRIPTION);
505
506 if (info)
507 goto close_and_send;
508
509 // Collect data
510 buffer_json_member_add_array(wb, "data");
511 ebpf_socket_read_open_connections(wb, em);
512 buffer_json_array_close(wb); // data
513
514 buffer_json_member_add_object(wb, "columns");
515 {
516 int fields_id = 0;
517
518 // IMPORTANT!
519 // THE ORDER SHOULD BE THE SAME WITH THE VALUES!
520 buffer_rrdf_table_add_field(
521 wb,
522 fields_id++,
523 "PID",
524 "Process ID",
525 RRDF_FIELD_TYPE_INTEGER,
526 RRDF_FIELD_VISUAL_VALUE,
527 RRDF_FIELD_TRANSFORM_NUMBER,
528 0,
529 NULL,
530 NAN,
531 RRDF_FIELD_SORT_ASCENDING,
532 NULL,
533 RRDF_FIELD_SUMMARY_COUNT,
534 RRDF_FIELD_FILTER_MULTISELECT,
535 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY,
536 NULL);
537
538 buffer_rrdf_table_add_field(
539 wb,
540 fields_id++,
541 "Name",
542 "Process Name",
543 RRDF_FIELD_TYPE_STRING,
544 RRDF_FIELD_VISUAL_VALUE,
545 RRDF_FIELD_TRANSFORM_NONE,
546 0,
547 NULL,
548 NAN,
549 RRDF_FIELD_SORT_ASCENDING,
550 NULL,
551 RRDF_FIELD_SUMMARY_COUNT,
552 RRDF_FIELD_FILTER_MULTISELECT,
553 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY | RRDF_FIELD_OPTS_FULL_WIDTH,
554 NULL);
555
556 buffer_rrdf_table_add_field(
557 wb,
558 fields_id++,
559 "Origin",
560 "Connection Origin",
561 RRDF_FIELD_TYPE_STRING,
562 RRDF_FIELD_VISUAL_VALUE,
563 RRDF_FIELD_TRANSFORM_NONE,
564 0,
565 NULL,
566 NAN,
567 RRDF_FIELD_SORT_ASCENDING,
568 NULL,
569 RRDF_FIELD_SUMMARY_COUNT,
570 RRDF_FIELD_FILTER_MULTISELECT,
571 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
572 NULL);
573
574 buffer_rrdf_table_add_field(
575 wb,
576 fields_id++,
577 "Src",
578 "Source IP Address",
579 RRDF_FIELD_TYPE_STRING,
580 RRDF_FIELD_VISUAL_VALUE,
581 RRDF_FIELD_TRANSFORM_NONE,
582 0,
583 NULL,
584 NAN,
585 RRDF_FIELD_SORT_ASCENDING,
586 NULL,
587 RRDF_FIELD_SUMMARY_COUNT,
588 RRDF_FIELD_FILTER_MULTISELECT,
589 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
590 NULL);
591
592 /*
593 buffer_rrdf_table_add_field(wb, fields_id++, "SrcPort", "Source Port", RRDF_FIELD_TYPE_INTEGER,
594 RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
595 RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
596 RRDF_FIELD_FILTER_MULTISELECT,
597 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY,
598 NULL);
599 */
600
601 buffer_rrdf_table_add_field(
602 wb,
603 fields_id++,
604 "Dst",
605 "Destination IP Address",
606 RRDF_FIELD_TYPE_STRING,
607 RRDF_FIELD_VISUAL_VALUE,
608 RRDF_FIELD_TRANSFORM_NONE,
609 0,
610 NULL,
611 NAN,
612 RRDF_FIELD_SORT_ASCENDING,
613 NULL,
614 RRDF_FIELD_SUMMARY_COUNT,
615 RRDF_FIELD_FILTER_MULTISELECT,
616 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
617 NULL);
618
619 buffer_rrdf_table_add_field(
620 wb,
621 fields_id++,
622 "DstPort",
623 "Destination Port",
624 RRDF_FIELD_TYPE_STRING,
625 RRDF_FIELD_VISUAL_VALUE,
626 RRDF_FIELD_TRANSFORM_NONE,
627 0,
628 NULL,
629 NAN,
630 RRDF_FIELD_SORT_ASCENDING,
631 NULL,
632 RRDF_FIELD_SUMMARY_COUNT,
633 RRDF_FIELD_FILTER_MULTISELECT,
634 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
635 NULL);
636
637 buffer_rrdf_table_add_field(
638 wb,
639 fields_id++,
640 "Protocol",
641 "Transport Layer Protocol",
642 RRDF_FIELD_TYPE_STRING,
643 RRDF_FIELD_VISUAL_VALUE,
644 RRDF_FIELD_TRANSFORM_NONE,
645 0,
646 NULL,
647 NAN,
648 RRDF_FIELD_SORT_ASCENDING,
649 NULL,
650 RRDF_FIELD_SUMMARY_COUNT,
651 RRDF_FIELD_FILTER_MULTISELECT,
652 RRDF_FIELD_OPTS_NONE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY,
653 NULL);
654
655 buffer_rrdf_table_add_field(
656 wb,
657 fields_id++,
658 "Rcvd",
659 "Traffic Received",
660 RRDF_FIELD_TYPE_INTEGER,
661 RRDF_FIELD_VISUAL_VALUE,
662 RRDF_FIELD_TRANSFORM_NUMBER,
663 3,
664 "MB",
665 NAN,
666 RRDF_FIELD_SORT_DESCENDING,
667 NULL,
668 RRDF_FIELD_SUMMARY_SUM,
669 RRDF_FIELD_FILTER_NONE,
670 RRDF_FIELD_OPTS_VISIBLE,
671 NULL);
672
673 buffer_rrdf_table_add_field(
674 wb,
675 fields_id++,
676 "Sent",
677 "Traffic Sent",
678 RRDF_FIELD_TYPE_INTEGER,
679 RRDF_FIELD_VISUAL_VALUE,
680 RRDF_FIELD_TRANSFORM_NUMBER,
681 3,
682 "MB",
683 NAN,
684 RRDF_FIELD_SORT_DESCENDING,
685 NULL,
686 RRDF_FIELD_SUMMARY_SUM,
687 RRDF_FIELD_FILTER_NONE,
688 RRDF_FIELD_OPTS_VISIBLE,
689 NULL);
690
691 buffer_rrdf_table_add_field(
692 wb,
693 fields_id,
694 "Conns",
695 "Connections",
696 RRDF_FIELD_TYPE_INTEGER,
697 RRDF_FIELD_VISUAL_VALUE,
698 RRDF_FIELD_TRANSFORM_NUMBER,
699 0,
700 "connections",
701 NAN,
702 RRDF_FIELD_SORT_DESCENDING,
703 NULL,
704 RRDF_FIELD_SUMMARY_SUM,
705 RRDF_FIELD_FILTER_NONE,
706 RRDF_FIELD_OPTS_VISIBLE,
707 NULL);
708 }
709 buffer_json_object_close(wb); // columns
710
711 buffer_json_member_add_string(wb, "default_sort_column", "Rcvd");
712
713 buffer_json_member_add_object(wb, "charts");
714 {
715 buffer_json_member_add_object(wb, "Traffic");
716 {
717 buffer_json_member_add_string(wb, "name", "Traffic");
718 buffer_json_member_add_string(wb, "type", "stacked-bar");
719 buffer_json_member_add_array(wb, "columns");
720 {
721 buffer_json_add_array_item_string(wb, "Rcvd");
722 buffer_json_add_array_item_string(wb, "Sent");
723 }
724 buffer_json_array_close(wb);
725 }
726 buffer_json_object_close(wb);
727
728 buffer_json_member_add_object(wb, "Connections");
729 {
730 buffer_json_member_add_string(wb, "name", "Connections");
731 buffer_json_member_add_string(wb, "type", "stacked-bar");
732 buffer_json_member_add_array(wb, "columns");
733 {
734 buffer_json_add_array_item_string(wb, "Conns");
735 }
736 buffer_json_array_close(wb);
737 }
738 buffer_json_object_close(wb);
739 }
740 buffer_json_object_close(wb); // charts
741
742 buffer_json_member_add_array(wb, "default_charts");
743 {
744 buffer_json_add_array_item_array(wb);
745 buffer_json_add_array_item_string(wb, "Traffic");
746 buffer_json_add_array_item_string(wb, "Name");
747 buffer_json_array_close(wb);
748
749 buffer_json_add_array_item_array(wb);
750 buffer_json_add_array_item_string(wb, "Connections");
751 buffer_json_add_array_item_string(wb, "Name");
752 buffer_json_array_close(wb);
753 }
754 buffer_json_array_close(wb);
755
756 buffer_json_member_add_object(wb, "group_by");
757 {
758 buffer_json_member_add_object(wb, "Name");
759 {
760 buffer_json_member_add_string(wb, "name", "Process Name");
761 buffer_json_member_add_array(wb, "columns");
762 {
763 buffer_json_add_array_item_string(wb, "Name");
764 }
765 buffer_json_array_close(wb);
766 }
767 buffer_json_object_close(wb);
768
769 buffer_json_member_add_object(wb, "Origin");
770 {
771 buffer_json_member_add_string(wb, "name", "Origin");
772 buffer_json_member_add_array(wb, "columns");
773 {
774 buffer_json_add_array_item_string(wb, "Origin");
775 }
776 buffer_json_array_close(wb);
777 }
778 buffer_json_object_close(wb);
779
780 buffer_json_member_add_object(wb, "Src");
781 {
782 buffer_json_member_add_string(wb, "name", "Source IP");
783 buffer_json_member_add_array(wb, "columns");
784 {
785 buffer_json_add_array_item_string(wb, "Src");
786 }
787 buffer_json_array_close(wb);
788 }
789 buffer_json_object_close(wb);
790
791 buffer_json_member_add_object(wb, "Dst");
792 {
793 buffer_json_member_add_string(wb, "name", "Destination IP");
794 buffer_json_member_add_array(wb, "columns");
795 {
796 buffer_json_add_array_item_string(wb, "Dst");
797 }
798 buffer_json_array_close(wb);
799 }
800 buffer_json_object_close(wb);
801
802 buffer_json_member_add_object(wb, "DstPort");
803 {
804 buffer_json_member_add_string(wb, "name", "Destination Port");
805 buffer_json_member_add_array(wb, "columns");
806 {
807 buffer_json_add_array_item_string(wb, "DstPort");
808 }
809 buffer_json_array_close(wb);
810 }
811 buffer_json_object_close(wb);
812
813 buffer_json_member_add_object(wb, "Protocol");
814 {
815 buffer_json_member_add_string(wb, "name", "Protocol");
816 buffer_json_member_add_array(wb, "columns");
817 {
818 buffer_json_add_array_item_string(wb, "Protocol");
819 }
820 buffer_json_array_close(wb);
821 }
822 buffer_json_object_close(wb);
823 }
824 buffer_json_object_close(wb); // group_by
825
826 close_and_send:
827 buffer_json_member_add_time_t(wb, "expires", now_s + em->update_every);
828 buffer_json_finalize(wb);
829
830 // Lock necessary to avoid race condition
831 pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "application/json", now_s + em->update_every);
832
833 fwrite(buffer_tostring(wb), buffer_strlen(wb), 1, stdout);
834
835 pluginsd_function_result_end_to_stdout();
836 fflush(stdout);
837
838 buffer_free(wb);
839 }
840
841 /*****************************************************************
842 * EBPF FUNCTION THREAD
843 *****************************************************************/
844
845 /**
846 * FUNCTION thread.
847 *
848 * @param ptr a `ebpf_module_t *`.
849 *
850 * @return always NULL.
851 */
852 void ebpf_function_thread(void *ptr)
853 {
854 (void)ptr;
855
856 struct functions_evloop_globals *wg = functions_evloop_init(1, "EBPF", &lock, &ebpf_plugin_exit, NULL);
857
858 functions_evloop_add_function(
859 wg, EBPF_FUNCTION_SOCKET, ebpf_function_socket_manipulation, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, NULL);
860
861 netdata_mutex_lock(&lock);
862 int i;
863 for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
864 ebpf_module_t *em = &ebpf_modules[i];
865 if (!em->functions.fnct_routine)
866 continue;
867
868 EBPF_PLUGIN_FUNCTIONS(em->functions.fcnt_name, em->functions.fcnt_desc, em->update_every);
869 }
870 netdata_mutex_unlock(&lock);
871
872 heartbeat_t hb;
873 heartbeat_init(&hb, USEC_PER_SEC);
874 while (!ebpf_plugin_stop()) {
875 if (ebpf_plugin_stop()) {
876 break;
877 }
878
879 heartbeat_next(&hb);
880
881 if (ebpf_plugin_stop()) {
882 break;
883 }
884 }
885 }