summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt62
-rw-r--r--Makefile.old32
-rw-r--r--nDPId-test.c283
-rw-r--r--nDPId.c358
-rw-r--r--nDPIsrvd.c116
-rw-r--r--schema/flow_event_schema.json4
-rwxr-xr-xtest/run_tests.sh51
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;
+}
diff --git a/nDPId.c b/nDPId.c
index 7ee1ba28c..6182fc171 100644
--- a/nDPId.c
+++ b/nDPId.c
@@ -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