From d1e9db7bb8d74ecbc494d548e2a598cc64445373 Mon Sep 17 00:00:00 2001 From: TripleA150 <46062533+TripleA150@users.noreply.github.com> Date: Tue, 22 Sep 2026 14:02:50 +0700 Subject: [PATCH] ndpi: add ndpi.license configuration option nDPI 6.0 requires the application to declare under which license the library is used and, for for-profit use without an agreement with ntop, does not load its dual-licensed dissectors (DHCP, DNS, QUIC and TLS as of 6.0), so rules on those protocols never fire. Expose this as "ndpi.license" with the values not-for-profit (the default), for-profit and for-profit-dual. An invalid value is fatal. Older nDPI releases accept and ignore the option with a warning. Ticket: 8077 --- plugins/ndpi/ndpi-compat.h | 30 ++++++++++++++++++-------- plugins/ndpi/ndpi.c | 43 +++++++++++++++++++++++++++++++++++++- suricata.yaml.in | 17 +++++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/plugins/ndpi/ndpi-compat.h b/plugins/ndpi/ndpi-compat.h index 689944f000..37eb5a6801 100644 --- a/plugins/ndpi/ndpi-compat.h +++ b/plugins/ndpi/ndpi-compat.h @@ -35,18 +35,30 @@ #error "the nDPI plugin requires nDPI 4.12 or later" #endif -/** - * \brief Allocate a detection module. - * - * nDPI 6.0 requires the embedding application to declare under which - * license it uses the library. Not-for-profit use enables every dissector. - */ +/* nDPI 6.0 requires the embedding application to declare under which + * license it uses the library; earlier releases have no notion of it. */ +#if NDPI_MAJOR >= 6 +#define NDPI_COMPAT_HAS_LICENSE 1 +typedef enum ndpi_license_type NdpiCompatLicense; +#define NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT NDPI_LICENSE_NOT_FOR_PROFIT_LGPL +#define NDPI_COMPAT_LICENSE_FOR_PROFIT NDPI_LICENSE_FOR_PROFIT_LGPL +#define NDPI_COMPAT_LICENSE_FOR_PROFIT_DUAL NDPI_LICENSE_FOR_PROFIT_DUAL_LICENSE +#else +#define NDPI_COMPAT_HAS_LICENSE 0 +/* placeholder so the license plumbing builds, the value is ignored */ +typedef int NdpiCompatLicense; +#define NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT 0 +#endif + +/* Allocate a detection module. The license is ignored on nDPI releases + * predating the declaration. */ static inline struct ndpi_detection_module_struct *NdpiCompatInitModule( - struct ndpi_global_context *g_ctx) + struct ndpi_global_context *g_ctx, NdpiCompatLicense license) { -#if NDPI_MAJOR >= 6 - return ndpi_init_detection_module(g_ctx, NDPI_LICENSE_NOT_FOR_PROFIT_LGPL); +#if NDPI_COMPAT_HAS_LICENSE + return ndpi_init_detection_module(g_ctx, license); #else + (void)license; return ndpi_init_detection_module(g_ctx); #endif } diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index d3c717b645..af5e63f5f5 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -22,6 +22,7 @@ #include "suricata-common.h" #include "suricata-plugin.h" +#include "conf.h" #include "detect-engine-helper.h" #include "detect-parse.h" #include "flow.h" @@ -39,6 +40,7 @@ static SCFlowStorageId flow_storage_id = { .id = -1 }; static int ndpi_protocol_keyword_id = -1; static int ndpi_risk_keyword_id = -1; static struct ndpi_global_context *ndpi_g_ctx = NULL; +static NdpiCompatLicense ndpi_license = NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT; struct NdpiThreadContext { struct ndpi_detection_module_struct *ndpi; @@ -82,6 +84,43 @@ static inline struct NdpiFlowContext *NdpiGetFlowContext(const Flow *f) return SCFlowGetStorageById(f, flow_storage_id); } +/** + * Resolve "ndpi.license". nDPI 6.0 does not load its dual-licensed + * dissectors (DHCP, DNS, QUIC and TLS as of 6.0) for for-profit use + * without an agreement with ntop. Not-for-profit is the default so the + * plugin works out of the box; an invalid value is fatal. + */ +static NdpiCompatLicense NdpiResolveLicense(void) +{ + const char *license = NULL; + + if (SCConfGet("ndpi.license", &license) != 1 || license == NULL) + return NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT; + +#if NDPI_COMPAT_HAS_LICENSE + if (strcmp(license, "not-for-profit") == 0) + return NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT; + + if (strcmp(license, "for-profit") == 0) { + SCLogWarning("ndpi.license is \"for-profit\": nDPI will not load its dual-licensed " + "dissectors (DHCP, DNS, QUIC and TLS as of nDPI 6.0)"); + return NDPI_COMPAT_LICENSE_FOR_PROFIT; + } + + if (strcmp(license, "for-profit-dual") == 0) + return NDPI_COMPAT_LICENSE_FOR_PROFIT_DUAL; + + FatalError("invalid ndpi.license value \"%s\": expected \"not-for-profit\", " + "\"for-profit\" or \"for-profit-dual\"", + license); +#else + SCLogWarning("ndpi.license has no effect with nDPI %d.%d, the license declaration was " + "introduced in nDPI 6.0", + NDPI_MAJOR, NDPI_MINOR); +#endif + return NDPI_COMPAT_LICENSE_NOT_FOR_PROFIT; +} + /* nDPI correlates flows through LRU caches (DNS to TLS, STUN to RTP, ...) * that are private to a detection module unless made global through a * shared context. Suricata spreads a host's flows over all its workers, @@ -110,7 +149,7 @@ static const char *ndpi_shared_lru_caches[] = { static struct ndpi_detection_module_struct *NdpiModuleNew(bool shared_caches) { struct ndpi_global_context *g_ctx = shared_caches ? ndpi_g_ctx : NULL; - struct ndpi_detection_module_struct *ndpi = NdpiCompatInitModule(g_ctx); + struct ndpi_detection_module_struct *ndpi = NdpiCompatInitModule(g_ctx, ndpi_license); if (ndpi == NULL) return NULL; @@ -609,6 +648,8 @@ static void NdpiInit(void) { SCLogDebug("Initializing nDPI plugin"); + ndpi_license = NdpiResolveLicense(); + /* Global context shared by the worker modules, see NdpiModuleNew(). * NULL if nDPI was built without global context support. There is * no plugin deinit hook, so it is never released. */ diff --git a/suricata.yaml.in b/suricata.yaml.in index bb96a4fa27..34e25fa040 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -85,6 +85,23 @@ plugins: @ndpi_comment@- @prefix@/lib/@PACKAGE_NAME@/ndpi.so # - /path/to/plugin.so +# nDPI plugin settings, only used when the nDPI plugin is loaded above. +ndpi: + # nDPI 6.0 and later require the application to declare under which + # license the library is used. Has no effect with older nDPI releases. + # + # not-for-profit : not-for-profit use, every dissector is enabled. + # This is the default. + # for-profit : for-profit use without a license agreement with + # ntop. nDPI does not load its dual-licensed + # dissectors (DHCP, DNS, QUIC and TLS as of nDPI 6.0), + # so rules matching on those protocols never fire. + # for-profit-dual : for-profit use with a license agreement signed + # with ntop for the dual-licensed components. + # + # See https://www.ntop.org/announcing-ndpi-dual-license-change/ + #license: not-for-profit + # Configure the type of alert (and other) logging you would like. outputs: # a line based alerts log similar to Snort's fast.log