diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2024-10-19 17:23:23 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2024-10-21 16:10:09 +0200 |
commit | 8c5ee1f7bb7f2dff8959a021283dd200c64a3d2c (patch) | |
tree | 89d82d51c0eb9ca56c1ccde2ada069fd332a2c74 | |
parent | 9969f955dc0062582256570960277ad86f40523b (diff) |
Added config testing script.
* nDPId-test may now make use of an optional config file as cmd arg
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r-- | .github/workflows/build.yml | 6 | ||||
-rw-r--r-- | nDPId-test.c | 121 | ||||
-rw-r--r-- | nDPId.c | 2 | ||||
-rw-r--r-- | test/configs/default.conf | 21 | ||||
-rw-r--r-- | test/configs/ndpi.conf | 31 | ||||
-rwxr-xr-x | test/run_config_tests.sh | 127 |
6 files changed, 264 insertions, 44 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61b2daba6..fe6ff8f6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -250,6 +250,7 @@ jobs: if: startsWith(matrix.os, 'macOS') == false && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=OFF') run: | ./test/run_tests.sh ./libnDPI ./build/nDPId-test + ./test/run_config_tests.sh ./libnDPI ./build/nDPId-test - name: Daemon if: startsWith(matrix.compiler, 'gcc') || endsWith(matrix.compiler, 'clang') run: | @@ -266,7 +267,10 @@ jobs: - name: CPack DEB if: startsWith(matrix.os, 'macOS') == false run: | - cd ./build && cpack -G DEB && sudo dpkg -i nDPId-*.deb && cd .. + cd ./build && cpack -G DEB && \ + sudo dpkg -i nDPId-*.deb && \ + sudo apt purge ndpid && \ + sudo dpkg -i nDPId-*.deb && cd .. - name: Upload DEB if: startsWith(matrix.os, 'macOS') == false && matrix.upload uses: actions/upload-artifact@v4 diff --git a/nDPId-test.c b/nDPId-test.c index d48784f38..39b42d591 100644 --- a/nDPId-test.c +++ b/nDPId-test.c @@ -44,6 +44,8 @@ struct nDPId_return_value { struct thread_return_value thread_return_value; + size_t thread_index; + unsigned long long int packets_captured; unsigned long long int packets_processed; unsigned long long int total_skipped_flows; @@ -1287,64 +1289,66 @@ static void * nDPId_mainloop_thread(void * const arg) if (thread_block_signals() != 0) { logger(1, "nDPId block signals failed: %s", strerror(errno)); + THREAD_ERROR_GOTO(trr); } if (setup_reader_threads() != 0) { - THREAD_ERROR(trr); - goto error; + logger(1, "%s", "nDPId setup reader threads failed"); + THREAD_ERROR_GOTO(trr); } /* Replace nDPId JSON socket fd with the one in our pipe and hope that no socket specific code-path triggered. */ - reader_threads[0].collector_sockfd = mock_pipefds[PIPE_nDPId]; - reader_threads[0].collector_sock_last_errno = 0; - if (set_collector_block(&reader_threads[0]) != 0) + reader_threads[nrv->thread_index].collector_sockfd = mock_pipefds[PIPE_nDPId]; + reader_threads[nrv->thread_index].collector_sock_last_errno = 0; + if (set_collector_block(&reader_threads[nrv->thread_index]) != 0) { - goto error; + THREAD_ERROR_GOTO(trr); } logger(0, "nDPId thread initialize done"); thread_signal(&start_condition); thread_wait(&start_condition); - jsonize_daemon(&reader_threads[0], DAEMON_EVENT_INIT); + jsonize_daemon(&reader_threads[nrv->thread_index], DAEMON_EVENT_INIT); /* restore SIGPIPE to the default handler (Termination) */ if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { - goto error; + logger(1, "nDPId restore SIGPIPE to the default handler: %s", strerror(errno)); + THREAD_ERROR_GOTO(trr); } - run_capture_loop(&reader_threads[0]); + + run_capture_loop(&reader_threads[nrv->thread_index]); process_remaining_flows(); - for (size_t i = 0; i < GET_CMDARG_ULL(nDPId_options.reader_thread_count); ++i) - { - nrv->packets_captured += reader_threads[i].workflow->packets_captured; - nrv->packets_processed += reader_threads[i].workflow->packets_processed; - nrv->total_skipped_flows += reader_threads[i].workflow->total_skipped_flows; - nrv->total_l4_payload_len += reader_threads[i].workflow->total_l4_payload_len; - nrv->not_detected_flow_protocols += reader_threads[i].workflow->total_not_detected_flows; - nrv->guessed_flow_protocols += reader_threads[i].workflow->total_guessed_flows; - nrv->detected_flow_protocols += reader_threads[i].workflow->total_detected_flows; - nrv->flow_detection_updates += reader_threads[i].workflow->total_flow_detection_updates; - nrv->flow_updates += reader_threads[i].workflow->total_flow_updates; + nrv->packets_captured += reader_threads[nrv->thread_index].workflow->packets_captured; + nrv->packets_processed += reader_threads[nrv->thread_index].workflow->packets_processed; + nrv->total_skipped_flows += reader_threads[nrv->thread_index].workflow->total_skipped_flows; + nrv->total_l4_payload_len += reader_threads[nrv->thread_index].workflow->total_l4_payload_len; - nrv->total_active_flows += reader_threads[i].workflow->total_active_flows; - nrv->total_idle_flows += reader_threads[i].workflow->total_idle_flows; - nrv->cur_active_flows += reader_threads[i].workflow->cur_active_flows; - nrv->cur_idle_flows += reader_threads[i].workflow->cur_idle_flows; + nrv->not_detected_flow_protocols += reader_threads[nrv->thread_index].workflow->total_not_detected_flows; + nrv->guessed_flow_protocols += reader_threads[nrv->thread_index].workflow->total_guessed_flows; + nrv->detected_flow_protocols += reader_threads[nrv->thread_index].workflow->total_detected_flows; + nrv->flow_detection_updates += reader_threads[nrv->thread_index].workflow->total_flow_detection_updates; + nrv->flow_updates += reader_threads[nrv->thread_index].workflow->total_flow_updates; + + nrv->total_active_flows += reader_threads[nrv->thread_index].workflow->total_active_flows; + nrv->total_idle_flows += reader_threads[nrv->thread_index].workflow->total_idle_flows; + nrv->cur_active_flows += reader_threads[nrv->thread_index].workflow->cur_active_flows; + nrv->cur_idle_flows += reader_threads[nrv->thread_index].workflow->cur_idle_flows; #ifdef ENABLE_ZLIB - nrv->total_compressions += reader_threads[i].workflow->total_compressions; - nrv->total_compression_diff += reader_threads[i].workflow->total_compression_diff; - nrv->current_compression_diff += reader_threads[i].workflow->current_compression_diff; + nrv->total_compressions += reader_threads[nrv->thread_index].workflow->total_compressions; + nrv->total_compression_diff += reader_threads[nrv->thread_index].workflow->total_compression_diff; + nrv->current_compression_diff += reader_threads[nrv->thread_index].workflow->current_compression_diff; #endif - nrv->total_events_serialized += reader_threads[i].workflow->total_events_serialized; - } + nrv->total_events_serialized += reader_threads[nrv->thread_index].workflow->total_events_serialized; error: - free_reader_threads(); + thread_signal(&start_condition); close(mock_pipefds[PIPE_nDPId]); + mock_pipefds[PIPE_nDPId] = -1; logger(0, "%s", "nDPId worker thread exits.."); return NULL; @@ -1352,7 +1356,7 @@ error: static void usage(char const * const arg0) { - fprintf(stderr, "usage: %s [path-to-pcap-file]\n", arg0); + fprintf(stderr, "usage: %s [path-to-pcap-file] [optional-nDPId-config-file]\n", arg0); } static int thread_wait_for_termination(pthread_t thread, time_t wait_time_secs, struct thread_return_value * const trv) @@ -1639,7 +1643,7 @@ error: distributor_return.thread_return_value.val != 0) int main(int argc, char ** argv) { - if (argc != 1 && argc != 2) + if (argc != 1 && argc != 2 && argc != 3) { usage(argv[0]); return 1; @@ -1667,7 +1671,47 @@ int main(int argc, char ** argv) return retval; } + if (access(argv[1], R_OK) != 0) + { + logger(1, "%s: pcap file `%s' does not exist or is not readable", argv[0], argv[1]); + return 1; + } + + set_config_defaults(&config_map[0], nDPIsrvd_ARRAY_LENGTH(config_map)); + set_config_defaults(&general_config_map[0], nDPIsrvd_ARRAY_LENGTH(general_config_map)); + set_config_defaults(&tuning_config_map[0], nDPIsrvd_ARRAY_LENGTH(tuning_config_map)); + { + int ret; + + if (argc == 3) + { + set_cmdarg_string(&nDPId_options.config_file, argv[2]); + } + + if (IS_CMDARG_SET(nDPId_options.config_file) != 0 && + (ret = parse_config_file(GET_CMDARG_STR(nDPId_options.config_file), nDPId_parsed_config_line, NULL)) != 0) + { + if (ret > 0) + { + logger(1, "Config file `%s' is malformed", GET_CMDARG_STR(nDPId_options.config_file)); + } + else if (ret == -ENOENT) + { + logger(1, "Path `%s' is not a regular file", GET_CMDARG_STR(nDPId_options.config_file)); + } + else + { + logger(1, + "Could not open file `%s' for reading: %s", + GET_CMDARG_STR(nDPId_options.config_file), + strerror(errno)); + } + return 1; + } + } + set_cmdarg_ull(&nDPIsrvd_options.max_write_buffers, 32); + set_cmdarg_string(&nDPId_options.pcap_file_or_interface, argv[1]); set_cmdarg_boolean(&nDPId_options.enable_data_analysis, 1); set_cmdarg_ull(&nDPId_options.max_packets_per_flow_to_send, 5); #ifdef ENABLE_ZLIB @@ -1682,15 +1726,7 @@ int main(int argc, char ** argv) set_cmdarg_ull(&nDPId_options.reader_thread_count, 1); /* Please do not change this! Generating meaningful pcap diff's relies on a single reader thread! */ set_cmdarg_string(&nDPId_options.instance_alias, "nDPId-test"); - if (access(argv[1], R_OK) != 0) - { - logger(1, "%s: pcap file `%s' does not exist or is not readable", argv[0], argv[1]); - return 1; - } - set_cmdarg_string(&nDPId_options.pcap_file_or_interface, argv[1]); - set_config_defaults(&config_map[0], nDPIsrvd_ARRAY_LENGTH(config_map)); - set_config_defaults(&general_config_map[0], nDPIsrvd_ARRAY_LENGTH(general_config_map)); - set_config_defaults(&tuning_config_map[0], nDPIsrvd_ARRAY_LENGTH(tuning_config_map)); + if (validate_options() != 0) { return 1; @@ -1713,7 +1749,7 @@ int main(int argc, char ** argv) } pthread_t nDPId_thread; - struct nDPId_return_value nDPId_return = {}; + struct nDPId_return_value nDPId_return = {.thread_index = 0}; if (pthread_create(&nDPId_thread, NULL, nDPId_mainloop_thread, &nDPId_return) != 0) { return 1; @@ -1759,6 +1795,7 @@ int main(int argc, char ** argv) } logger(0, "%s", "All worker threads terminated.."); + free_reader_threads(); if (THREADS_RETURNED_ERROR() != 0) { @@ -5609,7 +5609,7 @@ static int validate_options(void) if (GET_CMDARG_ULL(nDPId_options.flow_scan_interval) >= GET_CMDARG_ULL(nDPId_options.udp_max_idle_time)) { logger_early(1, - "Value not in range:flow-scan-interval[%llu] < udp-max-idle-time[%llu]", + "Value not in range: flow-scan-interval[%llu] < udp-max-idle-time[%llu]", GET_CMDARG_ULL(nDPId_options.flow_scan_interval), GET_CMDARG_ULL(nDPId_options.udp_max_idle_time)); retval = 1; diff --git a/test/configs/default.conf b/test/configs/default.conf new file mode 100644 index 000000000..172166666 --- /dev/null +++ b/test/configs/default.conf @@ -0,0 +1,21 @@ +[general] +compression = true +analysis = true + +[tuning] +max-flows-per-thread = 128 +max-idle-flows-per-thread = 128 +daemon-status-interval = 5000000 +compression-scan-interval = 1000000 +compression-flow-inactivity = 3000000 +flow-scan-interval = 5000001 +generic-max-idle-time = 5000002 +icmp-max-idle-time = 5000002 +tcp-max-idle-time = 5000002 +udp-max-idle-time = 5000002 +tcp-max-post-end-flow-time = 1000000 +max-packets-per-flow-to-send = 5 +max-packets-per-flow-to-process = 8 +max-packets-per-flow-to-analyse = 16 +error-event-threshold-n = 8 +error-event-threshold-time = 1000000 diff --git a/test/configs/ndpi.conf b/test/configs/ndpi.conf new file mode 100644 index 000000000..a0c00ca66 --- /dev/null +++ b/test/configs/ndpi.conf @@ -0,0 +1,31 @@ +[general] +compression = true +analysis = true + +[tuning] +max-flows-per-thread = 128 +max-idle-flows-per-thread = 128 +max-packets-per-flow-to-send = 5 +max-packets-per-flow-to-process = 8 +max-packets-per-flow-to-analyse = 16 +error-event-threshold-n = 8 +error-event-threshold-time = 100000 + +[ndpi] +packets_limit_per_flow = 8 +flow.direction_detection = enable +flow.track_payload = enable +tcp_ack_payload_heuristic = enable +fully_encrypted_heuristic = enable +libgcrypt.init = 1 +dpi.compute_entropy = 1 +fpc = enable +dpi.guess_on_giveup = 0x01 +flow_risk_lists.load = 1 +flow_risk.crawler_bot.list.load = 1 +log.level = 0 + +[protos] +tls.certificate_expiration_threshold = 1 +tls.application_blocks_tracking = enable +stun.max_packets_extra_dissection = 2 diff --git a/test/run_config_tests.sh b/test/run_config_tests.sh new file mode 100755 index 000000000..2f86fb75e --- /dev/null +++ b/test/run_config_tests.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash + +set -e + +LINE_SPACES=${LINE_SPACES:-48} +MYDIR="$(realpath "$(dirname ${0})")" +nDPId_test_EXEC="$(realpath "${2:-"${MYDIR}/../nDPId-test"}")" +IS_GIT=$(test -d "${MYDIR}/../.git" -o -f "${MYDIR}/../.git" && printf '1' || printf '0') + +function usage() +{ +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 +return 0 +} + +test -z "$(which flock)" && { printf '%s\n' 'flock not found'; exit 1; } +test -z "$(which pkill)" && { printf '%s\n' 'pkill not found'; exit 1; } + +if [ $# -eq 0 -a -x "${MYDIR}/../libnDPI/tests/cfgs" ]; then + nDPI_SOURCE_ROOT="${MYDIR}/../libnDPI" +elif [ $# -ne 1 -a $# -ne 2 -a $# -ne 3 -a $# -ne 4 ]; then + usage + exit 2 +else + nDPI_SOURCE_ROOT="$(realpath "${1}")" +fi + +if [ ! -x "${nDPI_SOURCE_ROOT}/tests/cfgs" ]; then + printf 'Test config directory %s does not exist or you do not have the permission to access it.\n' "${nDPI_SOURCE_ROOT}/tests/cfgs" >&2 + printf '%s\n' 'Please make also sure that your nDPI library is not too old.' + exit 2 +fi + +LOCKFILE="$(realpath "${0}").lock" + +touch "${LOCKFILE}" +exec 42< "${LOCKFILE}" +$(which flock) -x -n 42 || { + printf '%s\n' "Could not aquire file lock for ${0}. Already running instance?" >&2; + exit 3; +} +function sighandler() +{ + printf '%s\n' ' Received shutdown SIGNAL, bye' >&2 + $(which pkill) -P $$ + wait + rm -f "${LOCKFILE}" + exit 4 +} +trap sighandler SIGINT SIGTERM + +if [ ! -x "${nDPId_test_EXEC}" ]; then + usage + printf '\n%s\n' "Required nDPId-test executable does not exist; ${nDPId_test_EXEC}" + exit 5 +fi + +nDPI_TEST_DIR="$(realpath "${nDPI_SOURCE_ROOT}/tests")" +cd "${nDPI_TEST_DIR}" + +cat <<EOF +nDPId-test: ${nDPId_test_EXEC} +nDPI pcaps: ${nDPI_TEST_DIR} ($(ls -l cfgs/*/pcap/*.pcap cfgs/*/pcap/*.pcapng cfgs/*/pcap/*.cap 2>/dev/null | wc -l) total) + +----------------------------- +-- nDPId PCAP config tests -- +----------------------------- + +EOF + +if ! `ls -l cfgs/*/pcap/*.pcap cfgs/*/pcap/*.pcapng cfgs/*/pcap/*.cap >/dev/null 2>/dev/null`; then + printf '\n%s\n' "Could not find any PCAP files." + exit 7 +fi + +mkdir -p /tmp/nDPId-cfgtest-stderr +mkdir -p /tmp/nDPId-cfgtest-stdout + +set +e +TESTS_FAILED=0 + +${nDPId_test_EXEC} -h 2>/dev/null +if [ $? -ne 1 ]; then + printf '%s\n' "nDPId-test: ${nDPId_test_EXEC} seems to be an invalid executable" + exit 7 +fi + +for cfg_file in ${MYDIR}/configs/*.conf; do + cfg_name="$(basename ${cfg_file})" + printf 'Config: %s\n' "${cfg_name}" + for pcap_file in cfgs/*/pcap/*.pcap cfgs/*/pcap/*.pcapng cfgs/*/pcap/*.cap; do + if [ ! -r "${pcap_file}" ]; then + printf '%s: %s\n' "${0}" "${pcap_file} does not exist!" + TESTS_FAILED=$((TESTS_FAILED + 1)) + continue + fi + pcap_cfg="$(basename $(dirname $(dirname ${pcap_file})))" + pcap_name="$(basename ${pcap_file})" + stdout_file="/tmp/nDPId-cfgtest-stdout/${pcap_cfg}_${pcap_name}.out.new" + stderr_file="/tmp/nDPId-cfgtest-stderr/${pcap_name}.out" + printf '%s\n' "-- CMD: ${nDPId_test_EXEC} ${pcap_path}" \ + >${stderr_file} + + timeout --foreground -k 3 -s SIGINT 60 ${nDPId_test_EXEC} "${pcap_file}" "${cfg_file}" \ + >${stdout_file} \ + 2>>${stderr_file} + nDPId_test_RETVAL=$? + + if [ ${nDPId_test_RETVAL} -eq 0 ]; then + printf '%s' '.' + else + printf '%s\n' '[FAIL]' + printf '%s\n' '----------------------------------------' + printf '%s\n' "-- STDERR of ${pcap_file}: ${stderr_file}" + cat "${stderr_file}" + test -r "/tmp/nDPId-test-stderr/${pcap_name}.strace.out" && cat "/tmp/nDPId-test-stderr/${pcap_name}.strace.out" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi + done + + printf '%s\n' 'OK' +done |