diff options
-rw-r--r-- | .gitlab-ci.yml | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | nDPId-test.c | 26 | ||||
-rw-r--r-- | nDPId.c | 230 | ||||
-rw-r--r-- | nDPIsrvd.c | 2 | ||||
-rwxr-xr-x | scripts/daemon.sh | 3 |
6 files changed, 231 insertions, 37 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index df509dc95..169933917 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ build_and_test: # static linked build - mkdir build-cmake-submodule - cd build-cmake-submodule - - cmake .. -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DENABLE_SANITIZER=ON -DENABLE_ZLIB=ON + - env CMAKE_C_FLAGS='-Werror' cmake .. -DBUILD_EXAMPLES=ON -DBUILD_NDPI=ON -DENABLE_SANITIZER=ON -DENABLE_ZLIB=ON - make libnDPI - tree libnDPI - make install VERBOSE=1 DESTDIR="$(realpath ../_install)" @@ -33,7 +33,7 @@ build_and_test: - mkdir build - cd build - export PKG_CONFIG_PATH="$(realpath ../build-cmake-submodule/libnDPI/lib/pkgconfig)" - - cmake .. -DBUILD_EXAMPLES=ON -DENABLE_SANITIZER=ON -DENABLE_MEMORY_PROFILING=ON -DENABLE_ZLIB=ON + - env CMAKE_C_FLAGS='-Werror' cmake .. -DBUILD_EXAMPLES=ON -DENABLE_SANITIZER=ON -DENABLE_MEMORY_PROFILING=ON -DENABLE_ZLIB=ON - make all VERBOSE=1 - cd .. - ./build/nDPId-test || test $? -eq 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index d09494b61..267b03593 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") set(NDPID_DEFS -DJSMN_STATIC=1 -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1) if(ENABLE_MEMORY_PROFILING) + message(WARNING "ENABLE_MEMORY_PROFILING should not be used in production environments.") add_definitions("-DENABLE_MEMORY_PROFILING=1" "-Duthash_malloc=nDPIsrvd_uthash_malloc" "-Duthash_free=nDPIsrvd_uthash_free") @@ -255,7 +256,7 @@ target_compile_definitions(nDPId-test PRIVATE ${NDPID_DEFS}) target_include_directories(nDPId-test PRIVATE "${STATIC_LIBNDPI_INC}" "${NDPI_INCLUDEDIR}" "${NDPI_INCLUDEDIR}/ndpi" "${CMAKE_SOURCE_DIR}/dependencies/uthash/src") -target_compile_definitions(nDPId-test PRIVATE "-D_GNU_SOURCE=1" "-DNO_MAIN=1" "-Dsyslog=mock_syslog_stderr" ${NDPID_TEST_MPROF_DEFS}) +target_compile_definitions(nDPId-test PRIVATE "-D_GNU_SOURCE=1" "-DNO_MAIN=1" ${NDPID_TEST_MPROF_DEFS}) target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" "${pkgcfg_lib_NDPI_ndpi}" "${pkgcfg_lib_PCRE_pcre}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" diff --git a/nDPId-test.c b/nDPId-test.c index 38dcd9be7..d83fcac42 100644 --- a/nDPId-test.c +++ b/nDPId-test.c @@ -3,6 +3,19 @@ #include <stdarg.h> #include <unistd.h> +/* + * Mock some syslog variables and functions. + * This way, we do not spam any syslog daemon on the host. + */ +#define LOG_DAEMON 0x1 +#define LOG_ERR 0x2 +#define LOG_CONS 0x4 +#define LOG_PERROR 0x8 + +static void openlog(const char *ident, int option, int facility); +static void syslog(int p, const char * format, ...); +static void closelog(void); + #define NO_MAIN 1 #include "nDPIsrvd.c" #include "nDPId.c" @@ -63,7 +76,14 @@ static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; goto error; \ } while (0); -void mock_syslog_stderr(int p, const char * format, ...) +static void openlog(const char *ident, int option, int facility) +{ + (void)ident; + (void)option; + (void)facility; +} + +static void syslog(int p, const char * format, ...) { va_list ap; @@ -76,6 +96,10 @@ void mock_syslog_stderr(int p, const char * format, ...) va_end(ap); } +static void closelog(void) +{ +} + static int setup_pipe(int pipefd[PIPE_COUNT]) { if (pipe(pipefd) != 0) @@ -16,7 +16,9 @@ #include <stdlib.h> #include <sys/ioctl.h> #include <sys/un.h> +#ifndef NO_MAIN #include <syslog.h> +#endif #include <unistd.h> #ifdef ENABLE_ZLIB #include <zlib.h> @@ -930,46 +932,205 @@ static void ndpi_free_wrapper(void * const freeable) free(p); } -static void log_memory_usage(struct nDPId_reader_thread * const reader_thread) +static void log_memory_usage(struct nDPId_reader_thread const * const reader_thread) { - struct nDPId_workflow * const workflow = reader_thread->workflow; + if (reader_thread->array_index == 0) + { + uint64_t alloc_count = __sync_fetch_and_add(&ndpi_memory_alloc_count, 0); + uint64_t free_count = __sync_fetch_and_add(&ndpi_memory_free_count, 0); + uint64_t alloc_bytes = __sync_fetch_and_add(&ndpi_memory_alloc_bytes, 0); + uint64_t free_bytes = __sync_fetch_and_add(&ndpi_memory_free_bytes, 0); - if (workflow->last_memory_usage_log_time + nDPId_options.memory_profiling_print_every < workflow->last_time) + syslog(LOG_DAEMON, + "MemoryProfiler: %llu allocs, %llu frees, %llu bytes allocated, %llu bytes freed, %llu blocks in " + "use, " + "%llu bytes in use", + (long long unsigned int)alloc_count, + (long long unsigned int)free_count, + (long long unsigned int)alloc_bytes, + (long long unsigned int)free_bytes, + (long long unsigned int)(alloc_count - free_count), + (long long unsigned int)(alloc_bytes - free_bytes)); +#ifdef ENABLE_ZLIB + uint64_t zlib_compression_count = __sync_fetch_and_add(&zlib_compressions, 0); + uint64_t zlib_decompression_count = __sync_fetch_and_add(&zlib_decompressions, 0); + uint64_t zlib_bytes_diff = __sync_fetch_and_add(&zlib_compression_diff, 0); + + syslog(LOG_DAEMON, + "MemoryProfiler (zLib): %llu compressions, %llu decompressions, %llu compressed blocks in use, %llu " + "bytes " + "diff", + (long long unsigned int)zlib_compression_count, + (long long unsigned int)zlib_decompression_count, + (long long unsigned int)zlib_compression_count - (long long unsigned int)zlib_decompression_count, + (long long unsigned int)zlib_bytes_diff); +#endif + } +} + +struct log_flows_user_data +{ + struct nDPId_workflow * workflow; + unsigned long long int flows_ukn; + unsigned long long int flows_skp; + unsigned long long int flows_fin; + unsigned long long int flows_inf; + struct nDPId_flow_basic const * flows_active_fin[nDPId_MAX_FLOWS_PER_THREAD]; + struct nDPId_flow_basic const * flows_active_inf[nDPId_MAX_FLOWS_PER_THREAD]; +}; + +static void log_flows_flow_walker(void const * const A, ndpi_VISIT which, int depth, void * const user_data) +{ + struct log_flows_user_data * const log_user_data = (struct log_flows_user_data *)user_data; + struct nDPId_flow_basic * const flow_basic = *(struct nDPId_flow_basic **)A; + + (void)depth; + + if (log_user_data->workflow == NULL || flow_basic == NULL) { - if (reader_thread->array_index == 0) + return; + } + + if (which == ndpi_preorder || which == ndpi_leaf) + { + switch (flow_basic->type) { - uint64_t alloc_count = __sync_fetch_and_add(&ndpi_memory_alloc_count, 0); - uint64_t free_count = __sync_fetch_and_add(&ndpi_memory_free_count, 0); - uint64_t alloc_bytes = __sync_fetch_and_add(&ndpi_memory_alloc_bytes, 0); - uint64_t free_bytes = __sync_fetch_and_add(&ndpi_memory_free_bytes, 0); + case FT_UNKNOWN: + log_user_data->flows_ukn++; + break; - syslog(LOG_DAEMON, - "MemoryProfiler: %llu allocs, %llu frees, %llu bytes allocated, %llu bytes freed, %llu blocks in " - "use, " - "%llu bytes in use", - (long long unsigned int)alloc_count, - (long long unsigned int)free_count, - (long long unsigned int)alloc_bytes, - (long long unsigned int)free_bytes, - (long long unsigned int)(alloc_count - free_count), - (long long unsigned int)(alloc_bytes - free_bytes)); -#ifdef ENABLE_ZLIB - uint64_t zlib_compression_count = __sync_fetch_and_add(&zlib_compressions, 0); - uint64_t zlib_decompression_count = __sync_fetch_and_add(&zlib_decompressions, 0); - uint64_t zlib_bytes_diff = __sync_fetch_and_add(&zlib_compression_diff, 0); + case FT_SKIPPED: + log_user_data->flows_skp++; + break; - syslog(LOG_DAEMON, - "MemoryProfiler (zLib): %llu compressions, %llu decompressions, %llu compressed blocks in use, %llu " - "bytes " - "diff", - (long long unsigned int)zlib_compression_count, - (long long unsigned int)zlib_decompression_count, - (long long unsigned int)zlib_compression_count - (long long unsigned int)zlib_decompression_count, - (long long unsigned int)zlib_bytes_diff); + case FT_FINISHED: + { + log_user_data->flows_fin++; + log_user_data->flows_active_fin[log_user_data->flows_fin - 1] = flow_basic; + break; + } + + case FT_INFO: + { + log_user_data->flows_inf++; + log_user_data->flows_active_inf[log_user_data->flows_inf - 1] = flow_basic; + break; + } + } + } +} + +static size_t log_flows_to_str(struct nDPId_flow_basic const * flows[nDPId_MAX_FLOWS_PER_THREAD], + unsigned long long int flows_used, + char output[BUFSIZ]) +{ + size_t output_used = 0; + int written; + + output[0] = '\0'; + + for (size_t flow_index = 0; flow_index < flows_used; ++flow_index) + { + struct nDPId_flow_basic const * const flow_basic = flows[flow_index]; + + written = -1; + + switch (flow_basic->type) + { + case FT_UNKNOWN: + case FT_SKIPPED: + break; + + case FT_FINISHED: + { + struct nDPId_flow_finished const * const flow_finished = (struct nDPId_flow_finished const *)flow_basic; + +#if 1 + written = snprintf(output + output_used, BUFSIZ, "%u,", flow_finished->flow_info.flow_extended.flow_id); +#else + written = + snprintf(output + output_used, + BUFSIZ, + "[%u, %u, %llu],", + flow_finished->flow_info.flow_extended.flow_id, + flow_finished->flow_info.flow_extended.flow_basic.l4_protocol, + (unsigned long long int const)flow_finished->flow_info.flow_extended.flow_basic.last_seen); #endif + break; + } + + case FT_INFO: + { + struct nDPId_flow_info const * const flow_info = (struct nDPId_flow_info const *)flow_basic; + +#if 1 + written = snprintf(output + output_used, BUFSIZ, "%u,", flow_info->flow_extended.flow_id); +#else + written = snprintf(output + output_used, + BUFSIZ, + "[%u, %u, %llu],", + flow_info->flow_extended.flow_id, + flow_info->flow_extended.flow_basic.l4_protocol, + (unsigned long long int const)flow_info->flow_extended.flow_basic.last_seen); +#endif + break; + } } - workflow->last_memory_usage_log_time = workflow->last_time; + if (written <= 0) + { + break; + } + output_used += written; + } + + return (output_used > 0 ? output_used - 1 : 0); +} + +static void log_flows(struct nDPId_reader_thread const * const reader_thread) +{ + struct nDPId_workflow * const workflow = reader_thread->workflow; + struct log_flows_user_data log_user_data; + char flows_log_str[BUFSIZ]; + size_t flows_log_str_used; + + log_user_data.workflow = reader_thread->workflow; + log_user_data.flows_ukn = 0; + log_user_data.flows_skp = 0; + log_user_data.flows_fin = 0; + log_user_data.flows_inf = 0; + + for (size_t scan_index = 0; scan_index < workflow->max_active_flows; ++scan_index) + { + ndpi_twalk(workflow->ndpi_flows_active[scan_index], log_flows_flow_walker, &log_user_data); + } + + syslog(LOG_DAEMON, + "MemoryProfiler flow stats: [thread: %d][unknown: %llu][skipped: %llu][finished: %llu][info: %llu]", + reader_thread->array_index, + log_user_data.flows_ukn, + log_user_data.flows_skp, + log_user_data.flows_fin, + log_user_data.flows_inf); + + flows_log_str_used = log_flows_to_str(log_user_data.flows_active_fin, log_user_data.flows_fin, flows_log_str); + if (flows_log_str_used > 0) + { + syslog(LOG_DAEMON, + "MemoryProfiler flows active (finished): [thread: %d][%.*s]", + reader_thread->array_index, + (int)flows_log_str_used, + flows_log_str); + } + + flows_log_str_used = log_flows_to_str(log_user_data.flows_active_inf, log_user_data.flows_inf, flows_log_str); + if (flows_log_str_used > 0) + { + syslog(LOG_DAEMON, + "MemoryProfiler flows active (info): [thread: %d][%.*s]", + reader_thread->array_index, + (int)flows_log_str_used, + flows_log_str); } } #endif @@ -2591,7 +2752,12 @@ static void ndpi_process_packet(uint8_t * const args, check_for_idle_flows(reader_thread); #ifdef ENABLE_MEMORY_PROFILING - log_memory_usage(reader_thread); + if (workflow->last_memory_usage_log_time + nDPId_options.memory_profiling_print_every < workflow->last_time) + { + log_memory_usage(reader_thread); + log_flows(reader_thread); + workflow->last_memory_usage_log_time = workflow->last_time; + } #endif if (process_datalink_layer(reader_thread, header, packet, &ip_offset, &type) != 0) diff --git a/nDPIsrvd.c b/nDPIsrvd.c index 58fbfae5d..b9e71183f 100644 --- a/nDPIsrvd.c +++ b/nDPIsrvd.c @@ -8,7 +8,9 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#ifndef NO_MAIN #include <syslog.h> +#endif #include <sys/epoll.h> #include <sys/signalfd.h> #include <sys/socket.h> diff --git a/scripts/daemon.sh b/scripts/daemon.sh index 2cd7b4389..68661aaac 100755 --- a/scripts/daemon.sh +++ b/scripts/daemon.sh @@ -5,6 +5,7 @@ NUSER="nobody" NSUFFIX="${NSUFFIX:-daemon-test}" +nDPId_THREADS="${nDPId_THREADS:-4}" if [ x"${1}" = x -o x"${2}" = x ]; then printf '%s\n' "usage: ${0} [nDPId-path] [nDPIsrvd-path]" >&2 @@ -39,7 +40,7 @@ else ${2} -p "/tmp/nDPIsrvd-${NSUFFIX}.pid" -c "/tmp/nDPIsrvd-${NSUFFIX}-collector.sock" -s "/tmp/nDPIsrvd-${NSUFFIX}-distributor.sock" -d sudo chgrp "$(id -n -g "${NUSER}")" "/tmp/nDPIsrvd-${NSUFFIX}-collector.sock" sudo chmod g+w "/tmp/nDPIsrvd-${NSUFFIX}-collector.sock" - sudo ${1} -p "/tmp/nDPId-${NSUFFIX}.pid" -c "/tmp/nDPIsrvd-${NSUFFIX}-collector.sock" -d -u "${NUSER}" + sudo ${1} -p "/tmp/nDPId-${NSUFFIX}.pid" -c "/tmp/nDPIsrvd-${NSUFFIX}-collector.sock" -d -u "${NUSER}" -o max-reader-threads=${nDPId_THREADS} set +x printf '%s\n' "daemons started" >&2 printf '%s\n' "You may now run examples e.g.: $(realpath --relative-to="$(pwd)" $(dirname "${0}")/../examples/py-flow-info/flow-info.py) --unix /tmp/nDPIsrvd-${NSUFFIX}-distributor.sock" |