diff options
-rw-r--r-- | CMakeLists.txt | 62 | ||||
-rw-r--r-- | Makefile.old | 32 | ||||
-rw-r--r-- | nDPId-test.c | 283 | ||||
-rw-r--r-- | nDPId.c | 358 | ||||
-rw-r--r-- | nDPIsrvd.c | 116 | ||||
-rw-r--r-- | schema/flow_event_schema.json | 4 | ||||
-rwxr-xr-x | test/run_tests.sh | 51 |
7 files changed, 669 insertions, 237 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index efd10b58e..c07e2b742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,11 +12,14 @@ set(STATIC_LIBNDPI_INSTALLDIR "" CACHE STRING "Path to a installation directory add_executable(nDPId nDPId.c utils.c) add_executable(nDPIsrvd nDPIsrvd.c utils.c) +add_executable(nDPId-test nDPId-test.c utils.c) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -DJSMN_STATIC=1 -DJSMN_STRICT=1") if(ENABLE_MEMORY_PROFILING) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_MEMORY_PROFILING=1 -Duthash_malloc=nDPIsrvd_uthash_malloc -Duthash_free=nDPIsrvd_uthash_free") + set(MEMORY_PROFILING_CFLAGS "-DENABLE_MEMORY_PROFILING=1" "-Duthash_malloc=nDPIsrvd_uthash_malloc" "-Duthash_free=nDPIsrvd_uthash_free") +else() + set(MEMORY_PROFILING_CFLAGS "") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") @@ -45,56 +48,83 @@ if(NOT STATIC_LIBNDPI_INSTALLDIR STREQUAL "") endif() if(NDPI_WITH_PCRE) - pkg_check_modules(PCRE REQUIRED libpcre) + pkg_check_modules(PCRE REQUIRED libpcre>=8.39) endif() if(NDPI_WITH_MAXMINDDB) - pkg_check_modules(MAXMINDDB REQUIRED libmaxminddb) + pkg_check_modules(MAXMINDDB REQUIRED libmaxminddb>=1.3.2) endif() - find_package(PCAP "1.8.1" REQUIRED) - target_compile_options(nDPId PRIVATE "-pthread") - target_include_directories(nDPId PRIVATE "${STATIC_LIBNDPI_INSTALLDIR}/include/ndpi") - target_link_libraries(nDPId "${STATIC_LIBNDPI_INSTALLDIR}/lib/libndpi.a" - "${GCRYPT_LIBRARY}" "${PCAP_LIBRARY}" - "${pkgcfg_lib_MAXMINDDB_maxminddb}" "-pthread") - target_include_directories(nDPIsrvd PRIVATE - "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/dependencies" - "${CMAKE_SOURCE_DIR}/dependencies/jsmn" - "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") + set(STATIC_LIBNDPI_INC "${STATIC_LIBNDPI_INSTALLDIR}/include/ndpi") + set(STATIC_LIBNDPI_LIB "${STATIC_LIBNDPI_INSTALLDIR}/lib/libndpi.a") else() - pkg_check_modules(NDPI "3.6.0" REQUIRED) + pkg_check_modules(NDPI REQUIRED libndpi>=3.6.0) + set(STATIC_LIBNDPI_INC "") + set(STATIC_LIBNDPI_LIB "") endif() +find_package(PCAP "1.8.1" REQUIRED) +target_compile_options(nDPId PRIVATE ${MEMORY_PROFILING_CFLAGS} "-pthread") +target_include_directories(nDPId PRIVATE "${STATIC_LIBNDPI_INC}") +target_link_libraries(nDPId "${STATIC_LIBNDPI_LIB}" + "${GCRYPT_LIBRARY}" "${PCAP_LIBRARY}" + "${pkgcfg_lib_MAXMINDDB_maxminddb}" "-pthread") + +target_compile_options(nDPId PRIVATE ${MEMORY_PROFILING_CFLAGS}) +target_include_directories(nDPIsrvd PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_SOURCE_DIR}/dependencies" + "${CMAKE_SOURCE_DIR}/dependencies/jsmn" + "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") + +target_include_directories(nDPId-test PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_SOURCE_DIR}/dependencies" + "${CMAKE_SOURCE_DIR}/dependencies/jsmn" + "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") +target_compile_options(nDPId-test PRIVATE "-Wno-unused-function" "-pthread") +target_include_directories(nDPId-test PRIVATE "${STATIC_LIBNDPI_INC}") +target_compile_definitions(nDPId-test PRIVATE "-D_GNU_SOURCE=1" "-DNO_MAIN=1" "-Dsyslog=mock_syslog_stderr") +target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" + "${GCRYPT_LIBRARY}" "${PCAP_LIBRARY}" + "${pkgcfg_lib_MAXMINDDB_maxminddb}" "-pthread") + if(BUILD_EXAMPLES) add_executable(nDPIsrvd-collectd examples/c-collectd/c-collectd.c) + target_compile_options(nDPIsrvd-collectd PRIVATE ${MEMORY_PROFILING_CFLAGS}) target_include_directories(nDPIsrvd-collectd PRIVATE "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/dependencies" "${CMAKE_SOURCE_DIR}/dependencies/jsmn" "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") + add_executable(nDPIsrvd-captured examples/c-captured/c-captured.c utils.c) + target_compile_options(nDPIsrvd-captured PRIVATE ${MEMORY_PROFILING_CFLAGS}) target_include_directories(nDPIsrvd-captured PRIVATE "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/dependencies" "${CMAKE_SOURCE_DIR}/dependencies/jsmn" "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") target_link_libraries(nDPIsrvd-captured "${PCAP_LIBRARY}") + add_executable(nDPIsrvd-json-dump examples/c-json-stdout/c-json-stdout.c) target_include_directories(nDPIsrvd-json-dump PRIVATE "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/dependencies" "${CMAKE_SOURCE_DIR}/dependencies/jsmn") endif() message(STATUS "--------------------------") message(STATUS "CMAKE_BUILD_TYPE.........: ${CMAKE_BUILD_TYPE}") message(STATUS "CMAKE_C_FLAGS............: ${CMAKE_C_FLAGS}") +if(ENABLE_MEMORY_PROFILING) +message(STATUS "MEMORY_PROFILING_CFLAGS..: ${MEMORY_PROFILING_CFLAGS}") +endif() message(STATUS "ENABLE_SANITIZER.........: ${ENABLE_SANITIZER}") message(STATUS "ENABLE_SANITIZER_THREAD..: ${ENABLE_SANITIZER_THREAD}") message(STATUS "ENABLE_MEMORY_PROFILING..: ${ENABLE_MEMORY_PROFILING}") message(STATUS "STATIC_LIBNDPI_INSTALLDIR: ${STATIC_LIBNDPI_INSTALLDIR}") if(NOT STATIC_LIBNDPI_INSTALLDIR STREQUAL "") +message(STATUS "`- STATIC_LIBNDPI_INC....: ${STATIC_LIBNDPI_INC}") +message(STATUS "`- STATIC_LIBNDPI_LIB....: ${STATIC_LIBNDPI_LIB}") message(STATUS "`- NDPI_WITH_GCRYPT......: ${NDPI_WITH_GCRYPT}") message(STATUS "`- NDPI_WITH_PCRE........: ${NDPI_WITH_PCRE}") message(STATUS "`- NDPI_WITH_MAXMINDDB...: ${NDPI_WITH_MAXMINDDB}") diff --git a/Makefile.old b/Makefile.old index 8e2cc2bd6..2a51fe731 100644 --- a/Makefile.old +++ b/Makefile.old @@ -1,7 +1,7 @@ CC = gcc PROJECT_CFLAGS += -Wall -Wextra $(EXTRA_CFLAGS) -I. DEPS_CFLAGS := -DJSMN_STATIC=1 -DJSMN_STRICT=1 -Idependencies -Idependencies/jsmn -Idependencies/uthash/src -LIBS += -pthread -lpcap -lm -lmaxminddb +LIBS += -pthread -lpcap -lm GOCC = GOFLAGS = -ldflags='-s -w' @@ -25,6 +25,9 @@ endif ifeq ($(NDPI_WITH_PCRE),yes) LIBS += -lpcre endif +ifeq ($(NDPI_WITH_MAXMINDDB),yes) +LIBS += -lmaxminddb +endif ifneq ($(CUSTOM_LIBNDPI),) STATIC_NDPI_LIB += '$(CUSTOM_LIBNDPI)' @@ -39,8 +42,7 @@ endif endif # PKG_CONFIG_BIN ifeq ($(ENABLE_MEMORY_PROFILING),yes) -PROJECT_CFLAGS += -DENABLE_MEMORY_PROFILING=1 -DEPS_CFLAGS += -Duthash_malloc=nDPIsrvd_uthash_malloc -Duthash_free=nDPIsrvd_uthash_free +MEMORY_PROFILING_CFLAGS += -DENABLE_MEMORY_PROFILING=1 -Duthash_malloc=nDPIsrvd_uthash_malloc -Duthash_free=nDPIsrvd_uthash_free endif ifeq ($(ENABLE_DEBUG),yes) @@ -66,24 +68,27 @@ ifeq ($(ENABLE_DEBUG),yes) INSTALL_ARGS = -s endif -all: help nDPId nDPIsrvd examples +all: help nDPId nDPIsrvd nDPId-test examples examples: examples/c-collectd/c-collectd examples/c-captured/c-captured examples/c-json-stdout/c-json-stdout examples/go-dashboard/go-dashboard nDPId: nDPId.c utils.c - $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(PC_CFLAGS) $^ -o $@ $(LDFLAGS) $(PC_LDFLAGS) $(STATIC_NDPI_LIB) $(LIBS) + $(CC) $(PROJECT_CFLAGS) $(MEMORY_PROFILING_CFLAGS) $(CFLAGS) $(PC_CFLAGS) $^ -o $@ $(LDFLAGS) $(PC_LDFLAGS) $(STATIC_NDPI_LIB) $(LIBS) nDPIsrvd: nDPIsrvd.c utils.c - $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $^ -o $@ $(LDFLAGS) $(STATIC_NDPI_LIB) $(LIBS) + $(CC) $(PROJECT_CFLAGS) $(MEMORY_PROFILING_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $^ -o $@ $(LDFLAGS) + +nDPId-test: nDPId-test.c utils.c + $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) -D_GNU_SOURCE=1 -DNO_MAIN=1 -Dsyslog=mock_syslog_stderr -Wno-unused-function $^ -o $@ $(LDFLAGS) $(STATIC_NDPI_LIB) $(LIBS) examples/c-collectd/c-collectd: examples/c-collectd/c-collectd.c - $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $@.c -o $@ $(LDFLAGS) $(LIBS) + $(CC) $(PROJECT_CFLAGS) $(MEMORY_PROFILING_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $@.c -o $@ $(LDFLAGS) examples/c-captured/c-captured: examples/c-captured/c-captured.c utils.c - $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS) + $(CC) $(PROJECT_CFLAGS) $(MEMORY_PROFILING_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $^ -o $@ $(LDFLAGS) -lpcap examples/c-json-stdout/c-json-stdout: examples/c-json-stdout/c-json-stdout.c - $(CC) $(PROJECT_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $@.c -o $@ $(LDFLAGS) $(LIBS) + $(CC) $(PROJECT_CFLAGS) $(MEMORY_PROFILING_CFLAGS) $(CFLAGS) $(DEPS_CFLAGS) $@.c -o $@ $(LDFLAGS) examples/go-dashboard/go-dashboard: $(GO_DASHBOARD_SRCS) ifneq ($(GOCC),) @@ -95,6 +100,7 @@ endif install: all $(INSTALL) -d '$(DESTDIR)$(PREFIX)/bin' '$(DESTDIR)$(PREFIX)/sbin' + $(INSTALL) $(INSTALL_ARGS) ./nDPId-test '$(DESTDIR)$(PREFIX)/bin' $(INSTALL) $(INSTALL_ARGS) ./nDPIsrvd '$(DESTDIR)$(PREFIX)/bin' $(INSTALL) $(INSTALL_ARGS) ./nDPId '$(DESTDIR)$(PREFIX)/sbin' $(INSTALL) $(INSTALL_ARGS) ./examples/c-captured/c-captured '$(DESTDIR)$(PREFIX)/bin/nDPIsrvd-captured' @@ -106,7 +112,7 @@ ifneq ($(GOCC),) endif clean: - $(RM) -f nDPId nDPIsrvd examples/c-collectd/c-collectd examples/c-captured/c-captured examples/c-json-stdout/c-json-stdout examples/go-dashboard/go-dashboard + $(RM) -f nDPId nDPIsrvd nDPId-test examples/c-collectd/c-collectd examples/c-captured/c-captured examples/c-json-stdout/c-json-stdout examples/go-dashboard/go-dashboard help: @echo '------------------------------------' @@ -134,6 +140,11 @@ ifeq ($(NDPI_WITH_PCRE),yes) else @echo 'NDPI_WITH_PCRE = no' endif +ifeq ($(NDPI_WITH_MAXMINDDB),yes) + @echo 'NDPI_WITH_MAXMINDDB = yes' +else + @echo 'NDPI_WITH_MAXMINDDB = no' +endif endif # PKG_CONFIG_BIN ifeq ($(ENABLE_MEMORY_PROFILING),yes) @echo 'ENABLE_MEMORY_PROFILING = yes' @@ -160,6 +171,7 @@ endif @echo ' make ENABLE_DEBUG=yes ENABLE_SANITIZER=yes GOCC=go-1.14.1 \\' @echo ' CUSTOM_LIBNDPI="~/git/libnDPI/_install/lib/libndpi.a)" \\' @echo ' NDPI_WITH_GCRYPT=yes ENABLE_MEMORY_PROFILING=no all' + @echo run-mocksrvd: nc -k -l -U $(UNIX_SOCK_COLLECTOR) diff --git a/nDPId-test.c b/nDPId-test.c new file mode 100644 index 000000000..433041e43 --- /dev/null +++ b/nDPId-test.c @@ -0,0 +1,283 @@ +#include <fcntl.h> +#include <pthread.h> +#include <stdarg.h> +#include <unistd.h> + +#define NO_MAIN 1 +#include "nDPIsrvd.c" +#include "nDPId.c" + +enum +{ + PIPE_nDPId = 1, /* nDPId mock pipefd array index */ + PIPE_nDPIsrvd = 0, /* nDPIsrvd mock pipefd array index */ + PIPE_WRITE = 1, + PIPE_READ = 0, + PIPE_COUNT = 2 +}; + +static int epollfd = -1; +static int mock_pipefds[PIPE_COUNT] = {}; +static int mock_servfds[PIPE_COUNT] = {}; +static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + +void mock_syslog_stderr(int p, const char * format, ...) +{ + va_list ap; + + (void)p; + va_start(ap, format); + pthread_mutex_lock(&log_mutex); + vfprintf(stderr, format, ap); + fprintf(stderr, "%s\n", ""); + pthread_mutex_unlock(&log_mutex); + va_end(ap); +} + +static int setup_pipe(int pipefd[PIPE_COUNT]) +{ + if (pipe(pipefd) != 0) + { + return -1; + } + + return 0; +} + +static void * nDPIsrvd_mainloop_thread(void * const arg) +{ + (void)arg; + struct remote_desc * mock_json_desc = NULL; + struct remote_desc * mock_serv_desc = NULL; + + mock_json_desc = get_unused_remote_descriptor(JSON_SOCK, mock_pipefds[PIPE_nDPIsrvd]); + if (mock_json_desc == NULL) + { + goto error; + } + + mock_serv_desc = get_unused_remote_descriptor(SERV_SOCK, mock_servfds[PIPE_WRITE]); + if (mock_serv_desc == NULL) + { + goto error; + } + strncpy(mock_serv_desc->event_serv.peer_addr, "0.0.0.0", sizeof(mock_serv_desc->event_serv.peer_addr)); + mock_serv_desc->event_serv.peer.sin_port = 0; + + if (add_event(epollfd, mock_pipefds[PIPE_nDPIsrvd], mock_json_desc) != 0) + { + goto error; + } + + if (add_event(epollfd, mock_servfds[PIPE_WRITE], mock_serv_desc) != 0) + { + goto error; + } + + if (mainloop(epollfd) != 0) + { + goto error; + } + + while (handle_incoming_data(epollfd, mock_json_desc) == 0) {} + +error: + close(mock_servfds[PIPE_WRITE]); + + return NULL; +} + +static void * distributor_mainloop_thread(void * const arg) +{ + char buf[NETWORK_BUFFER_MAX_SIZE]; + + (void)arg; + + int dis_thread_shutdown = 0; + int dis_epollfd = create_evq(); + int signalfd = setup_signalfd(dis_epollfd); + + struct epoll_event events[32]; + size_t const events_size = sizeof(events) / sizeof(events[0]); + + if (dis_epollfd < 0) + { + goto error; + } + if (add_event(dis_epollfd, mock_servfds[PIPE_READ], NULL) != 0) + { + goto error; + } + if (signalfd < 0) + { + goto error; + } + + while (dis_thread_shutdown == 0) + { + int nready = epoll_wait(dis_epollfd, events, events_size, -1); + + for (int i = 0; i < nready; i++) + { + if ((events[i].events & EPOLLERR) != 0) + { + dis_thread_shutdown = 1; + break; + } + if ((events[i].events & EPOLLIN) == 0) + { + dis_thread_shutdown = 1; + break; + } + + if (events[i].data.fd == mock_servfds[PIPE_READ]) + { + ssize_t bytes_read = read(mock_servfds[PIPE_READ], buf, sizeof(buf)); + if (bytes_read <= 0) + { + dis_thread_shutdown = 1; + break; + } + printf("%.*s", (int)bytes_read, buf); + } + else if (events[i].data.fd == signalfd) + { + struct signalfd_siginfo fdsi; + ssize_t s; + + s = read(signalfd, &fdsi, sizeof(struct signalfd_siginfo)); + if (s != sizeof(struct signalfd_siginfo)) + { + dis_thread_shutdown = 1; + break; + } + + if (fdsi.ssi_signo == SIGINT || fdsi.ssi_signo == SIGTERM || fdsi.ssi_signo == SIGQUIT) + { + dis_thread_shutdown = 1; + break; + } + } + else + { + dis_thread_shutdown = 1; + break; + } + } + } + ssize_t bytes_read; + while ((bytes_read = read(mock_servfds[PIPE_READ], buf, sizeof(buf))) > 0) + { + printf("%.*s", (int)bytes_read, buf); + } + +error: + del_event(dis_epollfd, signalfd); + del_event(dis_epollfd, mock_servfds[PIPE_READ]); + close(dis_epollfd); + close(signalfd); + + return NULL; +} + +static void * nDPId_mainloop_thread(void * const arg) +{ + (void)arg; + + if (setup_reader_threads() != 0) + { + exit(EXIT_FAILURE); + } + + /* Replace nDPId JSON socket fd with the one in our pipe and hope that no socket specific code-path triggered. */ + reader_threads[0].json_sockfd = mock_pipefds[PIPE_nDPId]; + reader_threads[0].json_sock_reconnect = 0; + + run_pcap_loop(&reader_threads[0]); + free_reader_threads(); + + close(mock_pipefds[PIPE_nDPId]); + + return NULL; +} + +static void usage(char const * const arg0) +{ + printf("usage: %s [path-to-pcap-file]\n", arg0); +} + +int main(int argc, char **argv) +{ + if (argc != 2) + { + usage(argv[0]); + return -1; + } + + nDPId_options.reader_thread_count = 1; /* Please do not change this! Generating meaningful pcap diff's relies on a single reader thread! */ + nDPId_options.instance_alias = strdup("nDPId-test"); + nDPId_options.pcap_file_or_interface = strdup(argv[1]); + if (validate_options(argv[0]) != 0) + { + return -1; + } + + if (setup_pipe(mock_pipefds) != 0 || setup_pipe(mock_servfds) != 0) + { + return -1; + } + + /* We do not have any sockets, any socket operation must fail! */ + json_sockfd = -1; + serv_sockfd = -1; + + if (setup_remote_descriptors(2) != 0) + { + return -1; + } + + epollfd = create_evq(); + if (epollfd < 0) + { + return -1; + } + + pthread_t nDPId_thread; + if (pthread_create(&nDPId_thread, NULL, nDPId_mainloop_thread, NULL) != 0) + { + return -1; + } + + pthread_t nDPIsrvd_thread; + if (pthread_create(&nDPIsrvd_thread, NULL, nDPIsrvd_mainloop_thread, NULL) != 0) + { + return -1; + } + + pthread_t distributor_thread; + if (pthread_create(&distributor_thread, NULL, distributor_mainloop_thread, NULL) != 0) + { + return -1; + } + + if (pthread_join(nDPId_thread, NULL) != 0) + { + return -1; + } + + pthread_kill(nDPIsrvd_thread, SIGINT); + + if (pthread_join(nDPIsrvd_thread, NULL) != 0) + { + return -1; + } + + pthread_kill(distributor_thread, SIGINT); + + if (pthread_join(distributor_thread, NULL) != 0) + { + return -1; + } + + return 0; +} @@ -3,7 +3,6 @@ #include <fcntl.h> #include <ifaddrs.h> #include <linux/if_ether.h> -#include <linux/un.h> #include <net/if.h> #include <netinet/in.h> #include <ndpi_api.h> @@ -16,12 +15,17 @@ #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> +#include <sys/un.h> #include <syslog.h> #include <unistd.h> #include "config.h" #include "utils.h" +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + #if ((NDPI_MAJOR == 3 && NDPI_MINOR < 6) || NDPI_MAJOR < 3) && NDPI_API_VERSION < 4087 #error "nDPI >= 3.6.0 or API version >= 4087 required" #endif @@ -257,7 +261,7 @@ static char const * const daemon_event_name_table[DAEMON_EVENT_COUNT] = { }; static struct nDPId_reader_thread reader_threads[nDPId_MAX_READER_THREADS] = {}; -int main_thread_shutdown = 0; +static int nDPId_main_thread_shutdown = 0; static uint64_t global_flow_id = 1; #ifdef ENABLE_MEMORY_PROFILING @@ -267,33 +271,46 @@ static uint64_t ndpi_memory_free_count = 0; static uint64_t ndpi_memory_free_bytes = 0; #endif -static char * pcap_file_or_interface = NULL; -static union nDPId_ip pcap_dev_ip = {}; -static union nDPId_ip pcap_dev_netmask = {}; -static union nDPId_ip pcap_dev_subnet = {}; -static uint8_t process_internal_initial_direction = 0; -static uint8_t process_external_initial_direction = 0; -static char * bpf_str = NULL; -static int log_to_stderr = 0; -static char pidfile[UNIX_PATH_MAX] = nDPId_PIDFILE; -static char * user = "nobody"; -static char * group = NULL; -static char * custom_protocols_file = NULL; -static char * custom_categories_file = NULL; -static char * custom_ja3_file = NULL; -static char * custom_sha1_file = NULL; -static char json_sockpath[UNIX_PATH_MAX] = COLLECTOR_UNIX_SOCKET; - -/* subopts */ -static char * instance_alias = NULL; -static unsigned long long int max_flows_per_thread = nDPId_MAX_FLOWS_PER_THREAD / 2; -static unsigned long long int max_idle_flows_per_thread = nDPId_MAX_IDLE_FLOWS_PER_THREAD / 2; -static unsigned long long int tick_resolution = nDPId_TICK_RESOLUTION; -static unsigned long long int reader_thread_count = nDPId_MAX_READER_THREADS / 2; -static unsigned long long int idle_scan_period = nDPId_IDLE_SCAN_PERIOD; -static unsigned long long int max_idle_time = nDPId_IDLE_TIME; -static unsigned long long int tcp_max_post_end_flow_time = nDPId_TCP_POST_END_FLOW_TIME; -static unsigned long long int max_packets_per_flow_to_send = nDPId_PACKETS_PER_FLOW_TO_SEND; +static struct +{ + /* opts */ + char * pcap_file_or_interface; + union nDPId_ip pcap_dev_ip; + union nDPId_ip pcap_dev_netmask; + union nDPId_ip pcap_dev_subnet; + uint8_t process_internal_initial_direction; + uint8_t process_external_initial_direction; + char * bpf_str; + int log_to_stderr; + char pidfile[UNIX_PATH_MAX]; + char * user; + char * group; + char * custom_protocols_file; + char * custom_categories_file; + char * custom_ja3_file; + char * custom_sha1_file; + char json_sockpath[UNIX_PATH_MAX]; + /* subopts */ + char * instance_alias; + unsigned long long int max_flows_per_thread; + unsigned long long int max_idle_flows_per_thread; + unsigned long long int tick_resolution; + unsigned long long int reader_thread_count; + unsigned long long int idle_scan_period; + unsigned long long int max_idle_time; + unsigned long long int tcp_max_post_end_flow_time; + unsigned long long int max_packets_per_flow_to_send; +} nDPId_options = {.pidfile = nDPId_PIDFILE, + .user = "nobody", + .json_sockpath = COLLECTOR_UNIX_SOCKET, + .max_flows_per_thread = nDPId_MAX_FLOWS_PER_THREAD / 2, + .max_idle_flows_per_thread = nDPId_MAX_IDLE_FLOWS_PER_THREAD / 2, + .tick_resolution = nDPId_TICK_RESOLUTION, + .reader_thread_count = nDPId_MAX_READER_THREADS / 2, + .idle_scan_period = nDPId_IDLE_SCAN_PERIOD, + .max_idle_time = nDPId_IDLE_TIME, + .tcp_max_post_end_flow_time = nDPId_TCP_POST_END_FLOW_TIME, + .max_packets_per_flow_to_send = nDPId_PACKETS_PER_FLOW_TO_SEND}; enum nDPId_subopts { @@ -434,7 +451,7 @@ static int get_ip_netmask_from_pcap_dev(char const * const pcap_dev) { break; } - get_v4_ip_from_sockaddr((struct sockaddr_in *)&ifr.ifr_netmask, &pcap_dev_netmask); + get_v4_ip_from_sockaddr((struct sockaddr_in *)&ifr.ifr_netmask, &nDPId_options.pcap_dev_netmask); memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, ifa->ifa_name, ifnamelen); @@ -444,19 +461,22 @@ static int get_ip_netmask_from_pcap_dev(char const * const pcap_dev) { break; } - get_v4_ip_from_sockaddr((struct sockaddr_in *)&ifr.ifr_netmask, &pcap_dev_ip); + get_v4_ip_from_sockaddr((struct sockaddr_in *)&ifr.ifr_netmask, &nDPId_options.pcap_dev_ip); - ip_netmask_to_subnet(&pcap_dev_ip, &pcap_dev_netmask, &pcap_dev_subnet, type); + ip_netmask_to_subnet(&nDPId_options.pcap_dev_ip, + &nDPId_options.pcap_dev_netmask, + &nDPId_options.pcap_dev_subnet, + type); char addr[INET_ADDRSTRLEN]; char netm[INET_ADDRSTRLEN]; char subn[INET_ADDRSTRLEN]; - void * saddr = &pcap_dev_ip.v4.ip; - void * snetm = &pcap_dev_netmask.v4.ip; - void * ssubn = &pcap_dev_subnet.v4.ip; + void * saddr = &nDPId_options.pcap_dev_ip.v4.ip; + void * snetm = &nDPId_options.pcap_dev_netmask.v4.ip; + void * ssubn = &nDPId_options.pcap_dev_subnet.v4.ip; syslog(LOG_DAEMON, "%s address/netmask/subnet: %s/%s/%s", - pcap_file_or_interface, + nDPId_options.pcap_file_or_interface, inet_ntop(ifa->ifa_addr->sa_family, saddr, addr, sizeof(addr)), inet_ntop(ifa->ifa_addr->sa_family, snetm, netm, sizeof(netm)), inet_ntop(ifa->ifa_addr->sa_family, ssubn, subn, sizeof(subn))); @@ -544,10 +564,10 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device) return NULL; } - if (bpf_str != NULL) + if (nDPId_options.bpf_str != NULL) { struct bpf_program fp; - if (pcap_compile(workflow->pcap_handle, &fp, bpf_str, 1, PCAP_NETMASK_UNKNOWN) != 0) + if (pcap_compile(workflow->pcap_handle, &fp, nDPId_options.bpf_str, 1, PCAP_NETMASK_UNKNOWN) != 0) { syslog(LOG_DAEMON | LOG_ERR, "pcap_compile: %s", pcap_geterr(workflow->pcap_handle)); free_workflow(&workflow); @@ -573,7 +593,7 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device) workflow->total_skipped_flows = 0; workflow->total_active_flows = 0; - workflow->max_active_flows = max_flows_per_thread; + workflow->max_active_flows = nDPId_options.max_flows_per_thread; workflow->ndpi_flows_active = (void **)ndpi_calloc(workflow->max_active_flows, sizeof(void *)); if (workflow->ndpi_flows_active == NULL) { @@ -582,7 +602,7 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device) } workflow->total_idle_flows = 0; - workflow->max_idle_flows = max_idle_flows_per_thread; + workflow->max_idle_flows = nDPId_options.max_idle_flows_per_thread; workflow->ndpi_flows_idle = (void **)ndpi_calloc(workflow->max_idle_flows, sizeof(void *)); if (workflow->ndpi_flows_idle == NULL) { @@ -593,21 +613,21 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device) NDPI_PROTOCOL_BITMASK protos; NDPI_BITMASK_SET_ALL(protos); ndpi_set_protocol_detection_bitmask2(workflow->ndpi_struct, &protos); - if (custom_protocols_file != NULL) + if (nDPId_options.custom_protocols_file != NULL) { - ndpi_load_protocols_file(workflow->ndpi_struct, custom_protocols_file); + ndpi_load_protocols_file(workflow->ndpi_struct, nDPId_options.custom_protocols_file); } - if (custom_categories_file != NULL) + if (nDPId_options.custom_categories_file != NULL) { - ndpi_load_categories_file(workflow->ndpi_struct, custom_categories_file); + ndpi_load_categories_file(workflow->ndpi_struct, nDPId_options.custom_categories_file); } - if (custom_ja3_file != NULL) + if (nDPId_options.custom_ja3_file != NULL) { - ndpi_load_malicious_ja3_file(workflow->ndpi_struct, custom_ja3_file); + ndpi_load_malicious_ja3_file(workflow->ndpi_struct, nDPId_options.custom_ja3_file); } - if (custom_sha1_file != NULL) + if (nDPId_options.custom_sha1_file != NULL) { - ndpi_load_malicious_sha1_file(workflow->ndpi_struct, custom_sha1_file); + ndpi_load_malicious_sha1_file(workflow->ndpi_struct, nDPId_options.custom_sha1_file); } ndpi_finalize_initialization(workflow->ndpi_struct); @@ -684,52 +704,52 @@ static int setup_reader_threads(void) { char pcap_error_buffer[PCAP_ERRBUF_SIZE]; - if (reader_thread_count > nDPId_MAX_READER_THREADS) + if (nDPId_options.reader_thread_count > nDPId_MAX_READER_THREADS) { return 1; } - if (pcap_file_or_interface == NULL) + if (nDPId_options.pcap_file_or_interface == NULL) { - pcap_file_or_interface = get_default_pcapdev(pcap_error_buffer); - if (pcap_file_or_interface == NULL) + nDPId_options.pcap_file_or_interface = get_default_pcapdev(pcap_error_buffer); + if (nDPId_options.pcap_file_or_interface == NULL) { syslog(LOG_DAEMON | LOG_ERR, "pcap_lookupdev: %.*s", (int)PCAP_ERRBUF_SIZE, pcap_error_buffer); return 1; } - syslog(LOG_DAEMON, "Capturing packets from default device: %s", pcap_file_or_interface); + syslog(LOG_DAEMON, "Capturing packets from default device: %s", nDPId_options.pcap_file_or_interface); } errno = 0; - if (access(pcap_file_or_interface, R_OK) != 0 && errno == ENOENT) + if (access(nDPId_options.pcap_file_or_interface, R_OK) != 0 && errno == ENOENT) { errno = 0; - if (get_ip_netmask_from_pcap_dev(pcap_file_or_interface) != 0) + if (get_ip_netmask_from_pcap_dev(nDPId_options.pcap_file_or_interface) != 0) { syslog(LOG_DAEMON | LOG_ERR, "Could not get netmask for pcap device %s: %s", - pcap_file_or_interface, + nDPId_options.pcap_file_or_interface, strerror(errno)); return 1; } } else { - if (process_internal_initial_direction != 0) + if (nDPId_options.process_internal_initial_direction != 0) { syslog(LOG_DAEMON | LOG_ERR, "You are processing a PCAP file, `-I' ignored"); - process_internal_initial_direction = 0; + nDPId_options.process_internal_initial_direction = 0; } - if (process_external_initial_direction != 0) + if (nDPId_options.process_external_initial_direction != 0) { syslog(LOG_DAEMON | LOG_ERR, "You are processing a PCAP file, `-E' ignored"); - process_external_initial_direction = 0; + nDPId_options.process_external_initial_direction = 0; } } - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { - reader_threads[i].workflow = init_workflow(pcap_file_or_interface); + reader_threads[i].workflow = init_workflow(nDPId_options.pcap_file_or_interface); if (reader_threads[i].workflow == NULL) { return 1; @@ -816,16 +836,16 @@ static void ndpi_idle_scan_walker(void const * const A, ndpi_VISIT which, int de return; } - if (workflow->cur_idle_flows == max_idle_flows_per_thread) + if (workflow->cur_idle_flows == nDPId_options.max_idle_flows_per_thread) { return; } if (which == ndpi_preorder || which == ndpi_leaf) { - if (flow_basic->last_seen + max_idle_time < workflow->last_time || + if (flow_basic->last_seen + nDPId_options.max_idle_time < workflow->last_time || (flow_basic->tcp_fin_rst_seen == 1 && - flow_basic->last_seen + tcp_max_post_end_flow_time < workflow->last_time)) + flow_basic->last_seen + nDPId_options.tcp_max_post_end_flow_time < workflow->last_time)) { workflow->ndpi_flows_idle[workflow->cur_idle_flows++] = flow_basic; if (flow_basic->type == FT_INFO) @@ -925,7 +945,7 @@ static void check_for_idle_flows(struct nDPId_reader_thread * const reader_threa { struct nDPId_workflow * const workflow = reader_thread->workflow; - if (workflow->last_idle_scan_time + idle_scan_period < workflow->last_time) + if (workflow->last_idle_scan_time + nDPId_options.idle_scan_period < workflow->last_time) { #ifdef ENABLE_MEMORY_PROFILING if (reader_thread->array_index == 0) @@ -1032,8 +1052,8 @@ static void jsonize_basic(struct nDPId_reader_thread * const reader_thread) ndpi_serialize_string_int32(&workflow->ndpi_serializer, "thread_id", reader_thread->array_index); ndpi_serialize_string_uint32(&workflow->ndpi_serializer, "packet_id", workflow->packets_captured); - ndpi_serialize_string_string(&workflow->ndpi_serializer, "source", pcap_file_or_interface); - ndpi_serialize_string_string(&workflow->ndpi_serializer, "alias", instance_alias); + ndpi_serialize_string_string(&workflow->ndpi_serializer, "source", nDPId_options.pcap_file_or_interface); + ndpi_serialize_string_string(&workflow->ndpi_serializer, "alias", nDPId_options.instance_alias); } static void jsonize_daemon(struct nDPId_reader_thread * const reader_thread, enum daemon_event event) @@ -1055,18 +1075,24 @@ static void jsonize_daemon(struct nDPId_reader_thread * const reader_thread, enu if (event == DAEMON_EVENT_INIT) { - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "max-flows-per-thread", max_flows_per_thread); - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "max-idle-flows-per-thread", max_idle_flows_per_thread); - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "tick-resolution", tick_resolution); - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "reader-thread-count", reader_thread_count); - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "idle-scan-period", idle_scan_period); - ndpi_serialize_string_int64(&workflow->ndpi_serializer, "max-idle-time", max_idle_time); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, + "max-flows-per-thread", + nDPId_options.max_flows_per_thread); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, + "max-idle-flows-per-thread", + nDPId_options.max_idle_flows_per_thread); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, "tick-resolution", nDPId_options.tick_resolution); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, + "reader-thread-count", + nDPId_options.reader_thread_count); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, "idle-scan-period", nDPId_options.idle_scan_period); + ndpi_serialize_string_int64(&workflow->ndpi_serializer, "max-idle-time", nDPId_options.max_idle_time); ndpi_serialize_string_int64(&workflow->ndpi_serializer, "tcp-max-post-end-flow-time", - tcp_max_post_end_flow_time); + nDPId_options.tcp_max_post_end_flow_time); ndpi_serialize_string_int64(&workflow->ndpi_serializer, "max-packets-per-flow-to-send", - max_packets_per_flow_to_send); + nDPId_options.max_packets_per_flow_to_send); } serialize_and_send(reader_thread); } @@ -1103,7 +1129,7 @@ static int connect_to_json_socket(struct nDPId_reader_thread * const reader_thre } saddr.sun_family = AF_UNIX; - if (snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", json_sockpath) < 0 || + if (snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", nDPId_options.json_sockpath) < 0 || connect(reader_thread->json_sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { reader_thread->json_sock_reconnect = 1; @@ -1342,7 +1368,7 @@ static void jsonize_packet_event(struct nDPId_reader_thread * const reader_threa reader_thread->array_index); return; } - if (flow->packets_processed > max_packets_per_flow_to_send) + if (flow->packets_processed > nDPId_options.max_packets_per_flow_to_send) { return; } @@ -1420,7 +1446,9 @@ static void jsonize_flow_event(struct nDPId_reader_thread * const reader_thread, ndpi_serialize_string_int32(&workflow->ndpi_serializer, "flow_datalink", pcap_datalink(reader_thread->workflow->pcap_handle)); - ndpi_serialize_string_uint32(&workflow->ndpi_serializer, "flow_max_packets", max_packets_per_flow_to_send); + ndpi_serialize_string_uint32(&workflow->ndpi_serializer, + "flow_max_packets", + nDPId_options.max_packets_per_flow_to_send); break; case FLOW_EVENT_NOT_DETECTED: @@ -1878,7 +1906,8 @@ static void ndpi_process_packet(uint8_t * const args, } workflow->packets_captured++; - time_ms = ((uint64_t)header->ts.tv_sec) * tick_resolution + header->ts.tv_usec / (1000000 / tick_resolution); + time_ms = ((uint64_t)header->ts.tv_sec) * nDPId_options.tick_resolution + + header->ts.tv_usec / (1000000 / nDPId_options.tick_resolution); workflow->last_time = time_ms; check_for_idle_flows(reader_thread); @@ -2045,7 +2074,7 @@ static void ndpi_process_packet(uint8_t * const args, /* distribute flows to threads while keeping stability (same flow goes always to same thread) */ thread_index += (flow_basic.src_port < flow_basic.dst_port ? flow_basic.dst_port : flow_basic.src_port); - thread_index %= reader_thread_count; + thread_index %= nDPId_options.reader_thread_count; if (thread_index != reader_thread->array_index) { return; @@ -2123,9 +2152,12 @@ static void ndpi_process_packet(uint8_t * const args, { /* flow still not found, must be new */ - if (process_internal_initial_direction != 0) + if (nDPId_options.process_internal_initial_direction != 0) { - if (is_ip_in_subnet(&flow_basic.src, &pcap_dev_netmask, &pcap_dev_subnet, flow_basic.l3_type) == 0) + if (is_ip_in_subnet(&flow_basic.src, + &nDPId_options.pcap_dev_netmask, + &nDPId_options.pcap_dev_subnet, + flow_basic.l3_type) == 0) { if (add_new_flow(workflow, &flow_basic, FT_SKIPPED, hashed_index) == NULL) { @@ -2147,9 +2179,12 @@ static void ndpi_process_packet(uint8_t * const args, return; } } - else if (process_external_initial_direction != 0) + else if (nDPId_options.process_external_initial_direction != 0) { - if (is_ip_in_subnet(&flow_basic.src, &pcap_dev_netmask, &pcap_dev_subnet, flow_basic.l3_type) != 0) + if (is_ip_in_subnet(&flow_basic.src, + &nDPId_options.pcap_dev_netmask, + &nDPId_options.pcap_dev_subnet, + flow_basic.l3_type) != 0) { if (add_new_flow(workflow, &flow_basic, FT_SKIPPED, hashed_index) == NULL) { @@ -2372,7 +2407,7 @@ static void * processing_thread(void * const ndpi_thread_arg) syslog(LOG_DAEMON | LOG_ERR, "Thread %u: Could not connect to JSON sink %s, will try again later. Error: %s", reader_thread->array_index, - json_sockpath, + nDPId_options.json_sockpath, (errno != 0 ? strerror(errno) : "Internal Error.")); } else @@ -2388,7 +2423,7 @@ static void * processing_thread(void * const ndpi_thread_arg) static int processing_threads_error_or_eof(void) { - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { if (__sync_fetch_and_add(&reader_threads[i].workflow->error_or_eof, 0) == 0) { @@ -2411,15 +2446,15 @@ static int start_reader_threads(void) return 1; } - if (daemonize_with_pidfile(pidfile) != 0) + if (daemonize_with_pidfile(nDPId_options.pidfile) != 0) { return 1; } closelog(); - openlog("nDPId", LOG_CONS | (log_to_stderr != 0 ? LOG_PERROR : 0), LOG_DAEMON); + openlog("nDPId", LOG_CONS | (nDPId_options.log_to_stderr != 0 ? LOG_PERROR : 0), LOG_DAEMON); errno = 0; - if (change_user_group(user, group, pidfile, NULL, NULL) != 0) + if (change_user_group(nDPId_options.user, nDPId_options.group, nDPId_options.pidfile, NULL, NULL) != 0) { if (errno != 0) { @@ -2432,7 +2467,7 @@ static int start_reader_threads(void) return 1; } - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { reader_threads[i].array_index = i; @@ -2470,7 +2505,7 @@ static void ndpi_shutdown_walker(void const * const A, ndpi_VISIT which, int dep return; } - if (workflow->cur_idle_flows == max_idle_flows_per_thread) + if (workflow->cur_idle_flows == nDPId_options.max_idle_flows_per_thread) { return; } @@ -2494,13 +2529,13 @@ static int stop_reader_threads(void) unsigned long long int total_flows_idle = 0; unsigned long long int total_flows_detected = 0; - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { break_pcap_loop(&reader_threads[i]); } printf("------------------------------------ Stopping reader threads\n"); - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { if (reader_threads[i].workflow == NULL) { @@ -2514,7 +2549,7 @@ static int stop_reader_threads(void) } printf("------------------------------------ Processing remaining flows\n"); - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { for (size_t idle_scan_index = 0; idle_scan_index < reader_threads[i].workflow->max_active_flows; ++idle_scan_index) @@ -2531,7 +2566,7 @@ static int stop_reader_threads(void) } printf("------------------------------------ Results\n"); - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { if (reader_threads[i].workflow == NULL) { @@ -2570,7 +2605,7 @@ static int stop_reader_threads(void) static void free_reader_threads(void) { - for (unsigned long long int i = 0; i < reader_thread_count; ++i) + for (unsigned long long int i = 0; i < nDPId_options.reader_thread_count; ++i) { if (reader_threads[i].workflow == NULL) { @@ -2585,9 +2620,9 @@ static void sighandler(int signum) { (void)signum; - if (__sync_fetch_and_add(&main_thread_shutdown, 0) == 0) + if (__sync_fetch_and_add(&nDPId_main_thread_shutdown, 0) == 0) { - __sync_fetch_and_add(&main_thread_shutdown, 1); + __sync_fetch_and_add(&nDPId_main_thread_shutdown, 1); } } @@ -2605,28 +2640,28 @@ static void print_subopt_usage(void) switch (index++) { case MAX_FLOWS_PER_THREAD: - fprintf(stderr, "%llu\n", max_flows_per_thread); + fprintf(stderr, "%llu\n", nDPId_options.max_flows_per_thread); break; case MAX_IDLE_FLOWS_PER_THREAD: - fprintf(stderr, "%llu\n", max_idle_flows_per_thread); + fprintf(stderr, "%llu\n", nDPId_options.max_idle_flows_per_thread); break; case TICK_RESOLUTION: - fprintf(stderr, "%llu\n", tick_resolution); + fprintf(stderr, "%llu\n", nDPId_options.tick_resolution); break; case MAX_READER_THREADS: - fprintf(stderr, "%llu\n", reader_thread_count); + fprintf(stderr, "%llu\n", nDPId_options.reader_thread_count); break; case IDLE_SCAN_PERIOD: - fprintf(stderr, "%llu\n", idle_scan_period); + fprintf(stderr, "%llu\n", nDPId_options.idle_scan_period); break; case MAX_IDLE_TIME: - fprintf(stderr, "%llu\n", max_idle_time); + fprintf(stderr, "%llu\n", nDPId_options.max_idle_time); break; case TCP_MAX_POST_END_FLOW_TIME: - fprintf(stderr, "%llu\n", tcp_max_post_end_flow_time); + fprintf(stderr, "%llu\n", nDPId_options.tcp_max_post_end_flow_time); break; case MAX_PACKETS_PER_FLOW_TO_SEND: - fprintf(stderr, "%llu\n", max_packets_per_flow_to_send); + fprintf(stderr, "%llu\n", nDPId_options.max_packets_per_flow_to_send); break; default: break; @@ -2640,7 +2675,7 @@ static void print_subopt_usage(void) } while (1); } -static int parse_options(int argc, char ** argv) +static int nDPId_parse_options(int argc, char ** argv) { int opt; @@ -2685,19 +2720,19 @@ static int parse_options(int argc, char ** argv) switch (opt) { case 'i': - pcap_file_or_interface = strdup(optarg); + nDPId_options.pcap_file_or_interface = strdup(optarg); break; case 'I': - process_internal_initial_direction = 1; + nDPId_options.process_internal_initial_direction = 1; break; case 'E': - process_external_initial_direction = 1; + nDPId_options.process_external_initial_direction = 1; break; case 'B': - bpf_str = strdup(optarg); + nDPId_options.bpf_str = strdup(optarg); break; case 'l': - log_to_stderr = 1; + nDPId_options.log_to_stderr = 1; if (setvbuf(stderr, NULL, _IOLBF, 0) != 0) { fprintf(stderr, @@ -2707,36 +2742,36 @@ static int parse_options(int argc, char ** argv) } break; case 'c': - strncpy(json_sockpath, optarg, sizeof(json_sockpath) - 1); - json_sockpath[sizeof(json_sockpath) - 1] = '\0'; + strncpy(nDPId_options.json_sockpath, optarg, sizeof(nDPId_options.json_sockpath) - 1); + nDPId_options.json_sockpath[sizeof(nDPId_options.json_sockpath) - 1] = '\0'; break; case 'd': daemonize_enable(); break; case 'p': - strncpy(pidfile, optarg, sizeof(pidfile) - 1); - pidfile[sizeof(pidfile) - 1] = '\0'; + strncpy(nDPId_options.pidfile, optarg, sizeof(nDPId_options.pidfile) - 1); + nDPId_options.pidfile[sizeof(nDPId_options.pidfile) - 1] = '\0'; break; case 'u': - user = strdup(optarg); + nDPId_options.user = strdup(optarg); break; case 'g': - group = strdup(optarg); + nDPId_options.group = strdup(optarg); break; case 'P': - custom_protocols_file = strdup(optarg); + nDPId_options.custom_protocols_file = strdup(optarg); break; case 'C': - custom_categories_file = strdup(optarg); + nDPId_options.custom_categories_file = strdup(optarg); break; case 'J': - custom_ja3_file = strdup(optarg); + nDPId_options.custom_ja3_file = strdup(optarg); break; case 'S': - custom_sha1_file = strdup(optarg); + nDPId_options.custom_sha1_file = strdup(optarg); break; case 'a': - instance_alias = strdup(optarg); + nDPId_options.instance_alias = strdup(optarg); break; case 'o': { @@ -2774,28 +2809,28 @@ static int parse_options(int argc, char ** argv) switch ((enum nDPId_subopts)subopt) { case MAX_FLOWS_PER_THREAD: - max_flows_per_thread = value_llu; + nDPId_options.max_flows_per_thread = value_llu; break; case MAX_IDLE_FLOWS_PER_THREAD: - max_idle_flows_per_thread = value_llu; + nDPId_options.max_idle_flows_per_thread = value_llu; break; case TICK_RESOLUTION: - tick_resolution = value_llu; + nDPId_options.tick_resolution = value_llu; break; case MAX_READER_THREADS: - reader_thread_count = value_llu; + nDPId_options.reader_thread_count = value_llu; break; case IDLE_SCAN_PERIOD: - idle_scan_period = value_llu; + nDPId_options.idle_scan_period = value_llu; break; case MAX_IDLE_TIME: - max_idle_time = value_llu; + nDPId_options.max_idle_time = value_llu; break; case TCP_MAX_POST_END_FLOW_TIME: - tcp_max_post_end_flow_time = value_llu; + nDPId_options.tcp_max_post_end_flow_time = value_llu; break; case MAX_PACKETS_PER_FLOW_TO_SEND: - max_packets_per_flow_to_send = value_llu; + nDPId_options.max_packets_per_flow_to_send = value_llu; break; } } @@ -2823,7 +2858,7 @@ static int validate_options(char const * const arg0) { int retval = 0; - if (instance_alias == NULL) + if (nDPId_options.instance_alias == NULL) { char hname[256]; @@ -2835,73 +2870,80 @@ static int validate_options(char const * const arg0) } else { - instance_alias = strdup(hname); - fprintf(stderr, "%s: No instance alias given, using your hostname '%s'\n", arg0, instance_alias); - if (instance_alias == NULL) + nDPId_options.instance_alias = strdup(hname); + fprintf(stderr, + "%s: No instance alias given, using your hostname '%s'\n", + arg0, + nDPId_options.instance_alias); + if (nDPId_options.instance_alias == NULL) { retval = 1; } } } - if (max_flows_per_thread < 128 || max_flows_per_thread > nDPId_MAX_FLOWS_PER_THREAD) + if (nDPId_options.max_flows_per_thread < 128 || nDPId_options.max_flows_per_thread > nDPId_MAX_FLOWS_PER_THREAD) { fprintf(stderr, "%s: Value not in range: 128 < max-flows-per-thread[%llu] < %d\n", arg0, - max_flows_per_thread, + nDPId_options.max_flows_per_thread, nDPId_MAX_FLOWS_PER_THREAD); retval = 1; } - if (max_idle_flows_per_thread < 64 || max_idle_flows_per_thread > nDPId_MAX_IDLE_FLOWS_PER_THREAD) + if (nDPId_options.max_idle_flows_per_thread < 64 || + nDPId_options.max_idle_flows_per_thread > nDPId_MAX_IDLE_FLOWS_PER_THREAD) { fprintf(stderr, "%s: Value not in range: 64 < max-idle-flows-per-thread[%llu] < %d\n", arg0, - max_idle_flows_per_thread, + nDPId_options.max_idle_flows_per_thread, nDPId_MAX_IDLE_FLOWS_PER_THREAD); retval = 1; } - if (tick_resolution < 1) + if (nDPId_options.tick_resolution < 1) { - fprintf(stderr, "%s: Value not in range: tick-resolution[%llu] > 1\n", arg0, tick_resolution); + fprintf(stderr, "%s: Value not in range: tick-resolution[%llu] > 1\n", arg0, nDPId_options.tick_resolution); retval = 1; } - if (reader_thread_count < 1 || reader_thread_count > nDPId_MAX_READER_THREADS) + if (nDPId_options.reader_thread_count < 1 || nDPId_options.reader_thread_count > nDPId_MAX_READER_THREADS) { fprintf(stderr, "%s: Value not in range: 1 < reader-thread-count[%llu] < %d\n", arg0, - reader_thread_count, + nDPId_options.reader_thread_count, nDPId_MAX_READER_THREADS); retval = 1; } - if (idle_scan_period < 1000) + if (nDPId_options.idle_scan_period < 1000) { - fprintf(stderr, "%s: Value not in range: idle-scan-period[%llu] > 1000\n", arg0, idle_scan_period); + fprintf(stderr, + "%s: Value not in range: idle-scan-period[%llu] > 1000\n", + arg0, + nDPId_options.idle_scan_period); retval = 1; } - if (max_idle_time < 60) + if (nDPId_options.max_idle_time < 60) { - fprintf(stderr, "%s: Value not in range: max-idle-time[%llu] > 60\n", arg0, max_idle_time); + fprintf(stderr, "%s: Value not in range: max-idle-time[%llu] > 60\n", arg0, nDPId_options.max_idle_time); retval = 1; } - if (tcp_max_post_end_flow_time > max_idle_time) + if (nDPId_options.tcp_max_post_end_flow_time > nDPId_options.max_idle_time) { fprintf(stderr, "%s: Value not in range: max-post-end-flow-time[%llu] < max_idle_time[%llu]\n", arg0, - tcp_max_post_end_flow_time, - max_idle_time); + nDPId_options.tcp_max_post_end_flow_time, + nDPId_options.max_idle_time); retval = 1; } - if (process_internal_initial_direction != 0 && process_external_initial_direction != 0) + if (nDPId_options.process_internal_initial_direction != 0 && nDPId_options.process_external_initial_direction != 0) { fprintf(stderr, "%s: Internal and External packet processing does not make sense as this is the default.\n", arg0); retval = 1; } - if (process_internal_initial_direction != 0 || process_external_initial_direction != 0) + if (nDPId_options.process_internal_initial_direction != 0 || nDPId_options.process_external_initial_direction != 0) { fprintf(stderr, "%s: Internal and External packet processing may lead to incorrect results for flows that were active " @@ -2912,6 +2954,7 @@ static int validate_options(char const * const arg0) return retval; } +#ifndef NO_MAIN int main(int argc, char ** argv) { if (argc == 0) @@ -2919,7 +2962,7 @@ int main(int argc, char ** argv) return 1; } - if (parse_options(argc, argv) != 0) + if (nDPId_parse_options(argc, argv) != 0) { return 1; } @@ -2965,20 +3008,21 @@ int main(int argc, char ** argv) signal(SIGTERM, sighandler); signal(SIGPIPE, SIG_IGN); - while (main_thread_shutdown == 0 && processing_threads_error_or_eof() == 0) + while (nDPId_main_thread_shutdown == 0 && processing_threads_error_or_eof() == 0) { sleep(1); } - if (main_thread_shutdown == 1 && stop_reader_threads() != 0) + if (nDPId_main_thread_shutdown == 1 && stop_reader_threads() != 0) { return 1; } free_reader_threads(); - daemonize_shutdown(pidfile); + daemonize_shutdown(nDPId_options.pidfile); syslog(LOG_DAEMON | LOG_NOTICE, "Bye."); closelog(); return 0; } +#endif diff --git a/nDPIsrvd.c b/nDPIsrvd.c index 27e68ecf2..105fec2b0 100644 --- a/nDPIsrvd.c +++ b/nDPIsrvd.c @@ -59,18 +59,22 @@ static struct remotes size_t desc_used; } remotes = {NULL, 0, 0}; -static int main_thread_shutdown = 0; -static int log_to_stderr = 0; -static char * pidfile = NULL; -static char * json_sockpath = NULL; -static char * serv_optarg = NULL; +static int nDPIsrvd_main_thread_shutdown = 0; +static int json_sockfd; +static int serv_sockfd; static struct nDPIsrvd_address serv_address = { .raw.sa_family = 0xFFFF, }; -static int json_sockfd; -static int serv_sockfd; -static char * user = NULL; -static char * group = NULL; + +static struct +{ + int log_to_stderr; + char * pidfile; + char * json_sockpath; + char * serv_optarg; + char * user; + char * group; +} nDPIsrvd_options = {}; static int fcntl_add_flags(int fd, int flags) { @@ -116,7 +120,7 @@ static int create_listen_sockets(void) struct sockaddr_un json_addr; json_addr.sun_family = AF_UNIX; - if (snprintf(json_addr.sun_path, sizeof(json_addr.sun_path), "%s", json_sockpath) <= 0) + if (snprintf(json_addr.sun_path, sizeof(json_addr.sun_path), "%s", nDPIsrvd_options.json_sockpath) <= 0) { syslog(LOG_DAEMON | LOG_ERR, "snprintf failed: %s", strerror(errno)); return 1; @@ -124,31 +128,34 @@ static int create_listen_sockets(void) if (bind(json_sockfd, (struct sockaddr *)&json_addr, sizeof(json_addr)) < 0) { - unlink(json_sockpath); + unlink(nDPIsrvd_options.json_sockpath); syslog(LOG_DAEMON | LOG_ERR, "Error on binding UNIX socket (collector) to %s: %s", - json_sockpath, + nDPIsrvd_options.json_sockpath, strerror(errno)); return 1; } if (bind(serv_sockfd, &serv_address.raw, serv_address.size) < 0) { - syslog(LOG_DAEMON | LOG_ERR, "Error on binding socket (distributor) to %s: %s", serv_optarg, strerror(errno)); - unlink(json_sockpath); + syslog(LOG_DAEMON | LOG_ERR, + "Error on binding socket (distributor) to %s: %s", + nDPIsrvd_options.serv_optarg, + strerror(errno)); + unlink(nDPIsrvd_options.json_sockpath); return 1; } if (listen(json_sockfd, 16) < 0 || listen(serv_sockfd, 16) < 0) { - unlink(json_sockpath); + unlink(nDPIsrvd_options.json_sockpath); syslog(LOG_DAEMON | LOG_ERR, "Error on listen: %s", strerror(errno)); return 1; } if (fcntl_add_flags(json_sockfd, O_NONBLOCK) != 0) { - unlink(json_sockpath); + unlink(nDPIsrvd_options.json_sockpath); syslog(LOG_DAEMON | LOG_ERR, "Error setting fd flags for the collector socket: %s", strerror(errno)); return 1; } @@ -223,7 +230,7 @@ static void disconnect_client(int epollfd, struct remote_desc * const current) current->buf.ptr = NULL; } -static int parse_options(int argc, char ** argv) +static int nDPIsrvd_parse_options(int argc, char ** argv) { int opt; @@ -232,30 +239,30 @@ static int parse_options(int argc, char ** argv) switch (opt) { case 'l': - log_to_stderr = 1; + nDPIsrvd_options.log_to_stderr = 1; break; case 'c': - free(json_sockpath); - json_sockpath = strdup(optarg); + free(nDPIsrvd_options.json_sockpath); + nDPIsrvd_options.json_sockpath = strdup(optarg); break; case 'd': daemonize_enable(); break; case 'p': - free(pidfile); - pidfile = strdup(optarg); + free(nDPIsrvd_options.pidfile); + nDPIsrvd_options.pidfile = strdup(optarg); break; case 's': - free(serv_optarg); - serv_optarg = strdup(optarg); + free(nDPIsrvd_options.serv_optarg); + nDPIsrvd_options.serv_optarg = strdup(optarg); break; case 'u': - free(user); - user = strdup(optarg); + free(nDPIsrvd_options.user); + nDPIsrvd_options.user = strdup(optarg); break; case 'g': - free(group); - group = strdup(optarg); + free(nDPIsrvd_options.group); + nDPIsrvd_options.group = strdup(optarg); break; default: fprintf(stderr, @@ -266,24 +273,24 @@ static int parse_options(int argc, char ** argv) } } - if (pidfile == NULL) + if (nDPIsrvd_options.pidfile == NULL) { - pidfile = strdup(nDPIsrvd_PIDFILE); + nDPIsrvd_options.pidfile = strdup(nDPIsrvd_PIDFILE); } - if (json_sockpath == NULL) + if (nDPIsrvd_options.json_sockpath == NULL) { - json_sockpath = strdup(COLLECTOR_UNIX_SOCKET); + nDPIsrvd_options.json_sockpath = strdup(COLLECTOR_UNIX_SOCKET); } - if (serv_optarg == NULL) + if (nDPIsrvd_options.serv_optarg == NULL) { - serv_optarg = strdup(DISTRIBUTOR_UNIX_SOCKET); + nDPIsrvd_options.serv_optarg = strdup(DISTRIBUTOR_UNIX_SOCKET); } - if (nDPIsrvd_setup_address(&serv_address, serv_optarg) != 0) + if (nDPIsrvd_setup_address(&serv_address, nDPIsrvd_options.serv_optarg) != 0) { - fprintf(stderr, "%s: Could not parse address `%s'\n", argv[0], serv_optarg); + fprintf(stderr, "%s: Could not parse address `%s'\n", argv[0], nDPIsrvd_options.serv_optarg); return 1; } @@ -680,7 +687,7 @@ static int mainloop(int epollfd) size_t const events_size = sizeof(events) / sizeof(events[0]); int signalfd = setup_signalfd(epollfd); - while (main_thread_shutdown == 0) + while (nDPIsrvd_main_thread_shutdown == 0) { int nready = epoll_wait(epollfd, events, events_size, -1); @@ -724,7 +731,7 @@ static int mainloop(int epollfd) if (fdsi.ssi_signo == SIGINT || fdsi.ssi_signo == SIGTERM || fdsi.ssi_signo == SIGQUIT) { - main_thread_shutdown = 1; + nDPIsrvd_main_thread_shutdown = 1; break; } } @@ -805,34 +812,35 @@ static int setup_remote_descriptors(size_t max_descriptors) int main(int argc, char ** argv) { int retval = 1; + int epollfd; if (argc == 0) { return 1; } - if (parse_options(argc, argv) != 0) + if (nDPIsrvd_parse_options(argc, argv) != 0) { return 1; } openlog("nDPIsrvd", LOG_CONS | LOG_PERROR, LOG_DAEMON); - if (access(json_sockpath, F_OK) == 0) + if (access(nDPIsrvd_options.json_sockpath, F_OK) == 0) { syslog(LOG_DAEMON | LOG_ERR, "UNIX socket %s exists; nDPIsrvd already running? " "Please remove the socket manually or change socket path.", - json_sockpath); + nDPIsrvd_options.json_sockpath); return 1; } - if (daemonize_with_pidfile(pidfile) != 0) + if (daemonize_with_pidfile(nDPIsrvd_options.pidfile) != 0) { goto error; } closelog(); - openlog("nDPIsrvd", LOG_CONS | (log_to_stderr != 0 ? LOG_PERROR : 0), LOG_DAEMON); + openlog("nDPIsrvd", LOG_CONS | (nDPIsrvd_options.log_to_stderr != 0 ? LOG_PERROR : 0), LOG_DAEMON); if (setup_remote_descriptors(32) != 0) { @@ -843,26 +851,30 @@ int main(int argc, char ** argv) { goto error; } - syslog(LOG_DAEMON, "collector listen on %s", json_sockpath); + + syslog(LOG_DAEMON, "collector listen on %s", nDPIsrvd_options.json_sockpath); switch (serv_address.raw.sa_family) { default: goto error; case AF_INET: case AF_INET6: - syslog(LOG_DAEMON, "distributor listen on %s", serv_optarg); + syslog(LOG_DAEMON, "distributor listen on %s", nDPIsrvd_options.serv_optarg); syslog(LOG_DAEMON | LOG_ERR, "Please keep in mind that using a TCP Socket may leak sensitive information to " "everyone with access to the device/network. You've been warned!"); break; case AF_UNIX: - syslog(LOG_DAEMON, "distributor listen on %s", json_sockpath); + syslog(LOG_DAEMON, "distributor listen on %s", nDPIsrvd_options.json_sockpath); break; } errno = 0; - if (change_user_group( - user, group, pidfile, json_sockpath, (serv_address.raw.sa_family == AF_UNIX ? serv_optarg : NULL)) != 0) + if (change_user_group(nDPIsrvd_options.user, + nDPIsrvd_options.group, + nDPIsrvd_options.pidfile, + nDPIsrvd_options.json_sockpath, + (serv_address.raw.sa_family == AF_UNIX ? nDPIsrvd_options.serv_optarg : NULL)) != 0) { if (errno != 0) { @@ -877,7 +889,7 @@ int main(int argc, char ** argv) signal(SIGPIPE, SIG_IGN); - int epollfd = setup_event_queue(); + epollfd = setup_event_queue(); if (epollfd < 0) { goto error; @@ -889,12 +901,12 @@ error: close(json_sockfd); close(serv_sockfd); - daemonize_shutdown(pidfile); + daemonize_shutdown(nDPIsrvd_options.pidfile); syslog(LOG_DAEMON | LOG_NOTICE, "Bye."); closelog(); - unlink(json_sockpath); - unlink(serv_optarg); + unlink(nDPIsrvd_options.json_sockpath); + unlink(nDPIsrvd_options.serv_optarg); return retval; } diff --git a/schema/flow_event_schema.json b/schema/flow_event_schema.json index 60d36a4de..701ebcfb8 100644 --- a/schema/flow_event_schema.json +++ b/schema/flow_event_schema.json @@ -97,12 +97,12 @@ ] }, "l4_proto": { - "type": "string", "oneOf": [ { - "pattern": "^[0-9]+$" + "type": "number" }, { + "type": "string", "enum": [ "tcp", "udp", diff --git a/test/run_tests.sh b/test/run_tests.sh new file mode 100755 index 000000000..918767c8d --- /dev/null +++ b/test/run_tests.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env sh + +set -e + +MYDIR="$(realpath "$(dirname ${0})")" +nDPId_test_EXEC="${2:-"$(realpath "${MYDIR}/../nDPId-test")"}" +nDPI_SOURCE_ROOT="${1}" + +if [ $# -ne 1 -a $# -ne 2 ]; then +cat <<EOF +usage: ${0} [path-to-nDPI-source-root] [path-to-nDPId-test-exec] + + path-to-nDPId-test-exec defaults to ${nDPId_test_EXEC} +EOF +exit 1 +fi + +nDPI_TEST_DIR="${nDPI_SOURCE_ROOT}/tests/pcap" + +cat <<EOF +nDPId-test......: ${nDPId_test_EXEC} +nDPI source root: ${nDPI_TEST_DIR} +EOF + +cd "${nDPI_TEST_DIR}" +mkdir -p /tmp/nDPId-test-stderr +set +e +for pcap_file in $(ls *.pcap*); do + printf '%s' "${pcap_file}" + + ${nDPId_test_EXEC} "${pcap_file}" \ + >"${MYDIR}/results/${pcap_file}.out.new" \ + 2>"/tmp/nDPId-test-stderr/${pcap_file}.out" + + if [ $? -eq 0 ]; then + if diff -u0 "${MYDIR}/results/${pcap_file}.out" \ + "${MYDIR}/results/${pcap_file}.out.new" >/dev/null; then + printf ' [%s]\n' 'OK' + else + printf ' [%s]\n' 'DIFF' + diff -u0 "${MYDIR}/results/${pcap_file}.out" \ + "${MYDIR}/results/${pcap_file}.out.new" + mv -v "${MYDIR}/results/${pcap_file}.out.new" \ + "${MYDIR}/results/${pcap_file}.out" + fi + else + printf ' [%s]\n' 'FAIL' + fi + + rm -f "${MYDIR}/results/${pcap_file}.out.new" +done |