From 58acd822bb39c4734e301cfe1239ed209a2b83f8 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 10:28:04 +0200 Subject: [PATCH 01/17] protodetect: make expectation_proto part of AppLayerProtoDetectCtx instead of a global variable. For easier initialization with dynamic number of protocols --- src/app-layer-detect-proto.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index a65a98c88a41..dd17b560c5fb 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -160,6 +160,9 @@ typedef struct AppLayerProtoDetectCtx_ { * for protocol detection. This table is independent of the * ipproto. */ const char *alproto_names[ALPROTO_MAX]; + + /* Protocol expectations, like ftp-data on tcp */ + uint8_t expectation_proto[ALPROTO_MAX]; } AppLayerProtoDetectCtx; typedef struct AppLayerProtoDetectAliases_ { @@ -2111,27 +2114,25 @@ void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos) SCReturn; } -uint8_t expectation_proto[ALPROTO_MAX]; - static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto, uint8_t *ipprotos) { - if (expectation_proto[alproto] == IPPROTO_TCP) { + if (alpd_ctx.expectation_proto[alproto] == IPPROTO_TCP) { ipprotos[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); } - if (expectation_proto[alproto] == IPPROTO_UDP) { + if (alpd_ctx.expectation_proto[alproto] == IPPROTO_UDP) { ipprotos[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8); } } void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto) { - if (expectation_proto[alproto]) { - if (proto != expectation_proto[alproto]) { + if (alpd_ctx.expectation_proto[alproto]) { + if (proto != alpd_ctx.expectation_proto[alproto]) { SCLogError("Expectation on 2 IP protocols are not supported"); } } - expectation_proto[alproto] = proto; + alpd_ctx.expectation_proto[alproto] = proto; } /***** Unittests *****/ From f64ad70391bb52b3f356118216f94e65a9e7a400 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 10:35:11 +0200 Subject: [PATCH 02/17] protodetect: use dynamic number of app-layer protos for expectation_proto Ticket: 5053 --- src/app-layer-detect-proto.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index dd17b560c5fb..01d5536ece06 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -161,8 +161,11 @@ typedef struct AppLayerProtoDetectCtx_ { * ipproto. */ const char *alproto_names[ALPROTO_MAX]; - /* Protocol expectations, like ftp-data on tcp */ - uint8_t expectation_proto[ALPROTO_MAX]; + /* Protocol expectations, like ftp-data on tcp. + * It should be allocated to contain ALPROTO_MAX + * app-layer protocols. For each protocol, an iptype + * is referenced (or 0 if there is no expectation). */ + uint8_t *expectation_proto; } AppLayerProtoDetectCtx; typedef struct AppLayerProtoDetectAliases_ { @@ -1721,6 +1724,11 @@ int AppLayerProtoDetectSetup(void) } } + // to realloc when dynamic protos are added + alpd_ctx.expectation_proto = SCCalloc(ALPROTO_MAX, sizeof(uint8_t)); + if (unlikely(alpd_ctx.expectation_proto == NULL)) { + FatalError("Unable to alloc expectation_proto."); + } AppLayerExpectationSetup(); SCReturnInt(0); @@ -1752,6 +1760,9 @@ int AppLayerProtoDetectDeSetup(void) } } + SCFree(alpd_ctx.expectation_proto); + alpd_ctx.expectation_proto = NULL; + SpmDestroyGlobalThreadCtx(alpd_ctx.spm_global_thread_ctx); AppLayerProtoDetectFreeAliases(); From b377ea18aa56dfe8c09e8aab8870ab78c1ac9a93 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 10:40:19 +0200 Subject: [PATCH 03/17] protodetect: use dynamic number of app-layer protos for alproto_names Ticket: 5053 --- src/app-layer-detect-proto.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 01d5536ece06..52e1f2922c02 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -158,8 +158,9 @@ typedef struct AppLayerProtoDetectCtx_ { /* Indicates the protocols that have registered themselves * for protocol detection. This table is independent of the - * ipproto. */ - const char *alproto_names[ALPROTO_MAX]; + * ipproto. It should be allocated to contain ALPROTO_MAX + * protocols. */ + const char **alproto_names; /* Protocol expectations, like ftp-data on tcp. * It should be allocated to contain ALPROTO_MAX @@ -1724,6 +1725,10 @@ int AppLayerProtoDetectSetup(void) } } + alpd_ctx.alproto_names = SCCalloc(ALPROTO_MAX, sizeof(char *)); + if (unlikely(alpd_ctx.alproto_names == NULL)) { + FatalError("Unable to alloc alproto_names."); + } // to realloc when dynamic protos are added alpd_ctx.expectation_proto = SCCalloc(ALPROTO_MAX, sizeof(uint8_t)); if (unlikely(alpd_ctx.expectation_proto == NULL)) { @@ -1760,6 +1765,8 @@ int AppLayerProtoDetectDeSetup(void) } } + SCFree(alpd_ctx.alproto_names); + alpd_ctx.alproto_names = NULL; SCFree(alpd_ctx.expectation_proto); alpd_ctx.expectation_proto = NULL; @@ -1776,6 +1783,7 @@ void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_n { SCEnter(); + // should have just been realloced when dynamic protos is added if (alpd_ctx.alproto_names[alproto] == NULL) alpd_ctx.alproto_names[alproto] = alproto_name; From b7757696bb6396908fc1f7bd93328731aa9d6807 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 10:47:07 +0200 Subject: [PATCH 04/17] frames: use dynamic number of app-layer protos Ticket: 5053 --- src/app-layer-frames.c | 12 +++++++++++- src/app-layer-frames.h | 1 + src/app-layer.c | 2 ++ src/suricata.c | 1 - 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app-layer-frames.c b/src/app-layer-frames.c index 432bd020d230..3fa4c345474a 100644 --- a/src/app-layer-frames.c +++ b/src/app-layer-frames.c @@ -33,15 +33,25 @@ struct FrameConfig { SC_ATOMIC_DECLARE(uint64_t, types); }; -static struct FrameConfig frame_config[ALPROTO_MAX]; +/* This array should be allocated to contain ALPROTO_MAX protocols. */ +static struct FrameConfig *frame_config; void FrameConfigInit(void) { + frame_config = SCCalloc(ALPROTO_MAX, sizeof(struct FrameConfig)); + if (unlikely(frame_config == NULL)) { + FatalError("Unable to alloc frame_config."); + } for (AppProto p = 0; p < ALPROTO_MAX; p++) { SC_ATOMIC_INIT(frame_config[p].types); } } +void FrameConfigDeInit(void) +{ + SCFree(frame_config); +} + void FrameConfigEnableAll(void) { const uint64_t bits = UINT64_MAX; diff --git a/src/app-layer-frames.h b/src/app-layer-frames.h index f49bead57b41..f1d5135d8953 100644 --- a/src/app-layer-frames.h +++ b/src/app-layer-frames.h @@ -106,6 +106,7 @@ FramesContainer *AppLayerFramesGetContainer(Flow *f); FramesContainer *AppLayerFramesSetupContainer(Flow *f); void FrameConfigInit(void); +void FrameConfigDeInit(void); void FrameConfigEnableAll(void); void FrameConfigEnable(const AppProto p, const uint8_t type); diff --git a/src/app-layer.c b/src/app-layer.c index 94f99f44f83e..d17ec45ec5f8 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -1039,6 +1039,7 @@ int AppLayerSetup(void) AppLayerProtoDetectPrepareState(); AppLayerSetupCounters(); + FrameConfigInit(); SCReturnInt(0); } @@ -1051,6 +1052,7 @@ int AppLayerDeSetup(void) AppLayerParserDeSetup(); AppLayerDeSetupCounters(); + FrameConfigDeInit(); SCReturnInt(0); } diff --git a/src/suricata.c b/src/suricata.c index 6bdd6edb90f6..2442ac4ad20f 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -361,7 +361,6 @@ void GlobalsInitPreConfig(void) SupportFastPatternForSigMatchTypes(); SCThresholdConfGlobalInit(); SCProtoNameInit(); - FrameConfigInit(); } void GlobalsDestroy(void) From 050b5992933304956070ef433d9c690c7cdd2b54 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 13:16:00 +0200 Subject: [PATCH 05/17] util: parenthesis for macro so that we can use safely EXCEPTION_POLICY_MAX*sizeof(x) --- src/flow-private.h | 2 +- src/util-exception-policy-types.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flow-private.h b/src/flow-private.h index bd7aac73644e..ebd4e11961b4 100644 --- a/src/flow-private.h +++ b/src/flow-private.h @@ -74,7 +74,7 @@ enum { FLOW_PROTO_MAX, }; /* max used in app-layer (counters) */ -#define FLOW_PROTO_APPLAYER_MAX FLOW_PROTO_UDP + 1 +#define FLOW_PROTO_APPLAYER_MAX (FLOW_PROTO_UDP + 1) /* * Variables diff --git a/src/util-exception-policy-types.h b/src/util-exception-policy-types.h index b5295d19305b..a6139acc8934 100644 --- a/src/util-exception-policy-types.h +++ b/src/util-exception-policy-types.h @@ -33,7 +33,7 @@ enum ExceptionPolicy { EXCEPTION_POLICY_REJECT, }; -#define EXCEPTION_POLICY_MAX EXCEPTION_POLICY_REJECT + 1 +#define EXCEPTION_POLICY_MAX (EXCEPTION_POLICY_REJECT + 1) /* Max length = possible exception policy scenarios + counter names * + exception policy type. E.g.: From 9d2bc8d786487a2bf14e169f65ed3c34d64bc2d3 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 10:52:04 +0200 Subject: [PATCH 06/17] app-layer/stats: use dynamic number of app-layer protos Ticket: 5053 --- src/app-layer.c | 137 ++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/src/app-layer.c b/src/app-layer.c index d17ec45ec5f8..bd5920ab5c52 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -95,9 +95,9 @@ typedef struct AppLayerCounters_ { } AppLayerCounters; /* counter names. Only used at init. */ -AppLayerCounterNames applayer_counter_names[FLOW_PROTO_APPLAYER_MAX][ALPROTO_MAX]; +AppLayerCounterNames (*applayer_counter_names)[FLOW_PROTO_APPLAYER_MAX]; /* counter id's. Used that runtime. */ -AppLayerCounters applayer_counters[FLOW_PROTO_APPLAYER_MAX][ALPROTO_MAX]; +AppLayerCounters (*applayer_counters)[FLOW_PROTO_APPLAYER_MAX]; /* Exception policy global counters ids */ ExceptionPolicyCounters eps_error_summary; @@ -144,7 +144,7 @@ static inline int ProtoDetectDone(const Flow *f, const TcpSession *ssn, uint8_t */ static void AppLayerIncFlowCounter(ThreadVars *tv, Flow *f) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].counter_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].counter_id; if (likely(tv && id > 0)) { StatsIncr(tv, id); } @@ -152,7 +152,7 @@ static void AppLayerIncFlowCounter(ThreadVars *tv, Flow *f) void AppLayerIncTxCounter(ThreadVars *tv, Flow *f, uint64_t step) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].counter_tx_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].counter_tx_id; if (likely(tv && id > 0)) { StatsAddUI64(tv, id, step); } @@ -160,7 +160,7 @@ void AppLayerIncTxCounter(ThreadVars *tv, Flow *f, uint64_t step) void AppLayerIncGapErrorCounter(ThreadVars *tv, Flow *f) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].gap_error_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].gap_error_id; if (likely(tv && id > 0)) { StatsIncr(tv, id); } @@ -168,7 +168,7 @@ void AppLayerIncGapErrorCounter(ThreadVars *tv, Flow *f) void AppLayerIncAllocErrorCounter(ThreadVars *tv, Flow *f) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].alloc_error_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].alloc_error_id; if (likely(tv && id > 0)) { StatsIncr(tv, id); } @@ -176,7 +176,7 @@ void AppLayerIncAllocErrorCounter(ThreadVars *tv, Flow *f) void AppLayerIncParserErrorCounter(ThreadVars *tv, Flow *f) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].parser_error_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].parser_error_id; if (likely(tv && id > 0)) { StatsIncr(tv, id); } @@ -184,7 +184,7 @@ void AppLayerIncParserErrorCounter(ThreadVars *tv, Flow *f) void AppLayerIncInternalErrorCounter(ThreadVars *tv, Flow *f) { - const uint16_t id = applayer_counters[f->protomap][f->alproto].internal_error_id; + const uint16_t id = applayer_counters[f->alproto][f->protomap].internal_error_id; if (likely(tv && id > 0)) { StatsIncr(tv, id); } @@ -197,7 +197,7 @@ static void AppLayerIncrErrorExcPolicyCounter(ThreadVars *tv, Flow *f, enum Exce return; } #endif - uint16_t id = applayer_counters[f->protomap][f->alproto].eps_error.eps_id[policy]; + uint16_t id = applayer_counters[f->alproto][f->protomap].eps_error.eps_id[policy]; /* for the summary values */ uint16_t g_id = eps_error_summary.eps_id[policy]; @@ -1132,8 +1132,8 @@ static void AppLayerSetupExceptionPolicyPerProtoCounters( g_applayerparser_error_policy != EXCEPTION_POLICY_NOT_SET) { for (enum ExceptionPolicy i = EXCEPTION_POLICY_NOT_SET + 1; i < EXCEPTION_POLICY_MAX; i++) { if (IsAppLayerErrorExceptionPolicyStatsValid(i)) { - snprintf(applayer_counter_names[ipproto_map][alproto].eps_name[i], - sizeof(applayer_counter_names[ipproto_map][alproto].eps_name[i]), + snprintf(applayer_counter_names[alproto][ipproto_map].eps_name[i], + sizeof(applayer_counter_names[alproto][ipproto_map].eps_name[i]), "app_layer.error.%s%s.exception_policy.%s", alproto_str, ipproto_suffix, ExceptionPolicyEnumToString(i, true)); } @@ -1148,6 +1148,15 @@ void AppLayerSetupCounters(void) const char *str = "app_layer.flow."; const char *estr = "app_layer.error."; + applayer_counter_names = + SCCalloc(ALPROTO_MAX, sizeof(AppLayerCounterNames[FLOW_PROTO_APPLAYER_MAX])); + if (unlikely(applayer_counter_names == NULL)) { + FatalError("Unable to alloc applayer_counter_names."); + } + applayer_counters = SCCalloc(ALPROTO_MAX, sizeof(AppLayerCounters[FLOW_PROTO_APPLAYER_MAX])); + if (unlikely(applayer_counters == NULL)) { + FatalError("Unable to alloc applayer_counters."); + } /* We don't log stats counters if exception policy is `ignore`/`not set` */ if (g_applayerparser_error_policy != EXCEPTION_POLICY_NOT_SET) { /* Register global counters for app layer error exception policy summary */ @@ -1178,62 +1187,62 @@ void AppLayerSetupCounters(void) AppLayerProtoDetectSupportedIpprotos(alproto, ipprotos_all); if ((ipprotos_all[IPPROTO_TCP / 8] & (1 << (IPPROTO_TCP % 8))) && (ipprotos_all[IPPROTO_UDP / 8] & (1 << (IPPROTO_UDP % 8)))) { - snprintf(applayer_counter_names[ipproto_map][alproto].name, - sizeof(applayer_counter_names[ipproto_map][alproto].name), - "%s%s%s", str, alproto_str, ipproto_suffix); - snprintf(applayer_counter_names[ipproto_map][alproto].tx_name, - sizeof(applayer_counter_names[ipproto_map][alproto].tx_name), - "%s%s%s", tx_str, alproto_str, ipproto_suffix); + snprintf(applayer_counter_names[alproto][ipproto_map].name, + sizeof(applayer_counter_names[alproto][ipproto_map].name), "%s%s%s", + str, alproto_str, ipproto_suffix); + snprintf(applayer_counter_names[alproto][ipproto_map].tx_name, + sizeof(applayer_counter_names[alproto][ipproto_map].tx_name), "%s%s%s", + tx_str, alproto_str, ipproto_suffix); if (ipproto == IPPROTO_TCP) { - snprintf(applayer_counter_names[ipproto_map][alproto].gap_error, - sizeof(applayer_counter_names[ipproto_map][alproto].gap_error), + snprintf(applayer_counter_names[alproto][ipproto_map].gap_error, + sizeof(applayer_counter_names[alproto][ipproto_map].gap_error), "%s%s%s.gap", estr, alproto_str, ipproto_suffix); } - snprintf(applayer_counter_names[ipproto_map][alproto].alloc_error, - sizeof(applayer_counter_names[ipproto_map][alproto].alloc_error), + snprintf(applayer_counter_names[alproto][ipproto_map].alloc_error, + sizeof(applayer_counter_names[alproto][ipproto_map].alloc_error), "%s%s%s.alloc", estr, alproto_str, ipproto_suffix); - snprintf(applayer_counter_names[ipproto_map][alproto].parser_error, - sizeof(applayer_counter_names[ipproto_map][alproto].parser_error), + snprintf(applayer_counter_names[alproto][ipproto_map].parser_error, + sizeof(applayer_counter_names[alproto][ipproto_map].parser_error), "%s%s%s.parser", estr, alproto_str, ipproto_suffix); - snprintf(applayer_counter_names[ipproto_map][alproto].internal_error, - sizeof(applayer_counter_names[ipproto_map][alproto].internal_error), + snprintf(applayer_counter_names[alproto][ipproto_map].internal_error, + sizeof(applayer_counter_names[alproto][ipproto_map].internal_error), "%s%s%s.internal", estr, alproto_str, ipproto_suffix); AppLayerSetupExceptionPolicyPerProtoCounters( ipproto_map, alproto, alproto_str, ipproto_suffix); } else { - snprintf(applayer_counter_names[ipproto_map][alproto].name, - sizeof(applayer_counter_names[ipproto_map][alproto].name), - "%s%s", str, alproto_str); - snprintf(applayer_counter_names[ipproto_map][alproto].tx_name, - sizeof(applayer_counter_names[ipproto_map][alproto].tx_name), - "%s%s", tx_str, alproto_str); + snprintf(applayer_counter_names[alproto][ipproto_map].name, + sizeof(applayer_counter_names[alproto][ipproto_map].name), "%s%s", str, + alproto_str); + snprintf(applayer_counter_names[alproto][ipproto_map].tx_name, + sizeof(applayer_counter_names[alproto][ipproto_map].tx_name), "%s%s", + tx_str, alproto_str); if (ipproto == IPPROTO_TCP) { - snprintf(applayer_counter_names[ipproto_map][alproto].gap_error, - sizeof(applayer_counter_names[ipproto_map][alproto].gap_error), + snprintf(applayer_counter_names[alproto][ipproto_map].gap_error, + sizeof(applayer_counter_names[alproto][ipproto_map].gap_error), "%s%s.gap", estr, alproto_str); } - snprintf(applayer_counter_names[ipproto_map][alproto].alloc_error, - sizeof(applayer_counter_names[ipproto_map][alproto].alloc_error), + snprintf(applayer_counter_names[alproto][ipproto_map].alloc_error, + sizeof(applayer_counter_names[alproto][ipproto_map].alloc_error), "%s%s.alloc", estr, alproto_str); - snprintf(applayer_counter_names[ipproto_map][alproto].parser_error, - sizeof(applayer_counter_names[ipproto_map][alproto].parser_error), + snprintf(applayer_counter_names[alproto][ipproto_map].parser_error, + sizeof(applayer_counter_names[alproto][ipproto_map].parser_error), "%s%s.parser", estr, alproto_str); - snprintf(applayer_counter_names[ipproto_map][alproto].internal_error, - sizeof(applayer_counter_names[ipproto_map][alproto].internal_error), + snprintf(applayer_counter_names[alproto][ipproto_map].internal_error, + sizeof(applayer_counter_names[alproto][ipproto_map].internal_error), "%s%s.internal", estr, alproto_str); AppLayerSetupExceptionPolicyPerProtoCounters( ipproto_map, alproto, alproto_str, ""); } } else if (alproto == ALPROTO_FAILED) { - snprintf(applayer_counter_names[ipproto_map][alproto].name, - sizeof(applayer_counter_names[ipproto_map][alproto].name), - "%s%s%s", str, "failed", ipproto_suffix); + snprintf(applayer_counter_names[alproto][ipproto_map].name, + sizeof(applayer_counter_names[alproto][ipproto_map].name), "%s%s%s", str, + "failed", ipproto_suffix); if (ipproto == IPPROTO_TCP) { - snprintf(applayer_counter_names[ipproto_map][alproto].gap_error, - sizeof(applayer_counter_names[ipproto_map][alproto].gap_error), + snprintf(applayer_counter_names[alproto][ipproto_map].gap_error, + sizeof(applayer_counter_names[alproto][ipproto_map].gap_error), "%sfailed%s.gap", estr, ipproto_suffix); } } @@ -1264,41 +1273,41 @@ void AppLayerRegisterThreadCounters(ThreadVars *tv) for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { if (alprotos[alproto] == 1) { - applayer_counters[ipproto_map][alproto].counter_id = - StatsRegisterCounter(applayer_counter_names[ipproto_map][alproto].name, tv); + applayer_counters[alproto][ipproto_map].counter_id = + StatsRegisterCounter(applayer_counter_names[alproto][ipproto_map].name, tv); - applayer_counters[ipproto_map][alproto].counter_tx_id = - StatsRegisterCounter(applayer_counter_names[ipproto_map][alproto].tx_name, tv); + applayer_counters[alproto][ipproto_map].counter_tx_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].tx_name, tv); if (ipproto == IPPROTO_TCP) { - applayer_counters[ipproto_map][alproto].gap_error_id = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].gap_error, tv); + applayer_counters[alproto][ipproto_map].gap_error_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].gap_error, tv); } - applayer_counters[ipproto_map][alproto].alloc_error_id = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].alloc_error, tv); - applayer_counters[ipproto_map][alproto].parser_error_id = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].parser_error, tv); - applayer_counters[ipproto_map][alproto].internal_error_id = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].internal_error, tv); + applayer_counters[alproto][ipproto_map].alloc_error_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].alloc_error, tv); + applayer_counters[alproto][ipproto_map].parser_error_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].parser_error, tv); + applayer_counters[alproto][ipproto_map].internal_error_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].internal_error, tv); /* We don't log stats counters if exception policy is `ignore`/`not set` */ if (g_stats_eps_per_app_proto_errors && g_applayerparser_error_policy != EXCEPTION_POLICY_NOT_SET) { for (enum ExceptionPolicy i = EXCEPTION_POLICY_NOT_SET + 1; i < EXCEPTION_POLICY_MAX; i++) { if (IsAppLayerErrorExceptionPolicyStatsValid(i)) { - applayer_counters[ipproto_map][alproto] + applayer_counters[alproto][ipproto_map] .eps_error.eps_id[i] = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].eps_name[i], tv); + applayer_counter_names[alproto][ipproto_map].eps_name[i], tv); } } } } else if (alproto == ALPROTO_FAILED) { - applayer_counters[ipproto_map][alproto].counter_id = - StatsRegisterCounter(applayer_counter_names[ipproto_map][alproto].name, tv); + applayer_counters[alproto][ipproto_map].counter_id = + StatsRegisterCounter(applayer_counter_names[alproto][ipproto_map].name, tv); if (ipproto == IPPROTO_TCP) { - applayer_counters[ipproto_map][alproto].gap_error_id = StatsRegisterCounter( - applayer_counter_names[ipproto_map][alproto].gap_error, tv); + applayer_counters[alproto][ipproto_map].gap_error_id = StatsRegisterCounter( + applayer_counter_names[alproto][ipproto_map].gap_error, tv); } } } @@ -1307,8 +1316,8 @@ void AppLayerRegisterThreadCounters(ThreadVars *tv) void AppLayerDeSetupCounters(void) { - memset(applayer_counter_names, 0, sizeof(applayer_counter_names)); - memset(applayer_counters, 0, sizeof(applayer_counters)); + SCFree(applayer_counter_names); + SCFree(applayer_counters); } /***** Unittests *****/ From 6881109e879dc50350495e4dbfa0fc8c74fc6d66 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 11:13:21 +0200 Subject: [PATCH 07/17] fuzz: use dynamic number of app-layer protos Ticket: 5053 delay after initialization so that StringToAppProto works --- src/tests/fuzz/fuzz_applayerparserparse.c | 36 ++++++++++------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/tests/fuzz/fuzz_applayerparserparse.c b/src/tests/fuzz/fuzz_applayerparserparse.c index f20c566e399a..5e71243e047f 100644 --- a/src/tests/fuzz/fuzz_applayerparserparse.c +++ b/src/tests/fuzz/fuzz_applayerparserparse.c @@ -36,32 +36,15 @@ extern const char *configNoChecksum; const uint8_t separator[] = {0x01, 0xD5, 0xCA, 0x7A}; SCInstance surifuzz; AppProto forceLayer = 0; +char *target_suffix = NULL; SC_ATOMIC_EXTERN(unsigned int, engine_stage); int LLVMFuzzerInitialize(int *argc, char ***argv) { - char *target_suffix = strrchr((*argv)[0], '_'); - if (target_suffix != NULL) { - AppProto applayer = StringToAppProto(target_suffix + 1); - if (applayer != ALPROTO_UNKNOWN) { - forceLayer = applayer; - printf("Forcing %s=%" PRIu16 "\n", AppProtoToString(forceLayer), forceLayer); - return 0; - } - } + target_suffix = strrchr((*argv)[0], '_'); // else - const char *forceLayerStr = getenv("FUZZ_APPLAYER"); - if (forceLayerStr) { - if (ByteExtractStringUint16(&forceLayer, 10, 0, forceLayerStr) < 0) { - forceLayer = 0; - printf("Invalid numeric value for FUZZ_APPLAYER environment variable"); - } else { - printf("Forcing %s\n", AppProtoToString(forceLayer)); - } - } - // http is the output name, but we want to fuzz HTTP1 - if (forceLayer == ALPROTO_HTTP) { - forceLayer = ALPROTO_HTTP1; + if (!target_suffix) { + target_suffix = getenv("FUZZ_APPLAYER"); } return 0; } @@ -96,6 +79,17 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) PostConfLoadedSetup(&surifuzz); alp_tctx = AppLayerParserThreadCtxAlloc(); SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME); + if (target_suffix != NULL) { + AppProto applayer = StringToAppProto(target_suffix + 1); + if (applayer != ALPROTO_UNKNOWN) { + forceLayer = applayer; + printf("Forcing %s=%" PRIu16 "\n", AppProtoToString(forceLayer), forceLayer); + } + } + // http is the output name, but we want to fuzz HTTP1 + if (forceLayer == ALPROTO_HTTP) { + forceLayer = ALPROTO_HTTP1; + } } if (size < HEADER_LEN) { From 7b40d1514dc69a160799c1bd02f18ad650c95133 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 2 Sep 2024 11:00:37 +0200 Subject: [PATCH 08/17] app-layer/parser: use dynamic number of app-layer protos Ticket: 5053 --- src/app-layer-parser.c | 206 +++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 102 deletions(-) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index d1cacc572f40..ecd032191a8d 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -56,7 +56,7 @@ #include "app-layer-imap.h" struct AppLayerParserThreadCtx_ { - void *alproto_local_storage[FLOW_PROTO_MAX][ALPROTO_MAX]; + void *(*alproto_local_storage)[FLOW_PROTO_MAX]; }; @@ -123,7 +123,7 @@ typedef struct AppLayerParserProtoCtx_ } AppLayerParserProtoCtx; typedef struct AppLayerParserCtx_ { - AppLayerParserProtoCtx ctxs[FLOW_PROTO_MAX][ALPROTO_MAX]; + AppLayerParserProtoCtx (*ctxs)[FLOW_PROTO_MAX]; } AppLayerParserCtx; struct AppLayerParserState_ { @@ -218,7 +218,7 @@ int AppLayerParserProtoIsRegistered(uint8_t ipproto, AppProto alproto) { uint8_t ipproto_map = FlowGetProtoMapping(ipproto); - return (alp_ctx.ctxs[ipproto_map][alproto].StateAlloc != NULL) ? 1 : 0; + return (alp_ctx.ctxs[alproto][ipproto_map].StateAlloc != NULL) ? 1 : 0; } AppLayerParserState *AppLayerParserStateAlloc(void) @@ -248,7 +248,12 @@ void AppLayerParserStateFree(AppLayerParserState *pstate) int AppLayerParserSetup(void) { SCEnter(); - memset(&alp_ctx, 0, sizeof(alp_ctx)); + // initial allocation that will later be grown using realloc, + // when new protocols register themselves and make ALPROTO_MAX grow + alp_ctx.ctxs = SCCalloc(ALPROTO_MAX, sizeof(AppLayerParserProtoCtx[FLOW_PROTO_MAX])); + if (unlikely(alp_ctx.ctxs == NULL)) { + FatalError("Unable to alloc alp_ctx.ctxs."); + } SCReturnInt(0); } @@ -257,10 +262,9 @@ void AppLayerParserPostStreamSetup(void) /* lets set a default value for stream_depth */ for (int flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { - if (!(alp_ctx.ctxs[flow_proto][alproto].internal_flags & + if (!(alp_ctx.ctxs[alproto][flow_proto].internal_flags & APP_LAYER_PARSER_INT_STREAM_DEPTH_SET)) { - alp_ctx.ctxs[flow_proto][alproto].stream_depth = - stream_config.reassembly_depth; + alp_ctx.ctxs[alproto][flow_proto].stream_depth = stream_config.reassembly_depth; } } } @@ -270,6 +274,8 @@ int AppLayerParserDeSetup(void) { SCEnter(); + SCFree(alp_ctx.ctxs); + FTPParserCleanup(); SMTPParserCleanup(); @@ -284,12 +290,18 @@ AppLayerParserThreadCtx *AppLayerParserThreadCtxAlloc(void) if (tctx == NULL) goto end; + tctx->alproto_local_storage = SCCalloc(ALPROTO_MAX, sizeof(void *[FLOW_PROTO_MAX])); + if (unlikely(tctx->alproto_local_storage == NULL)) { + SCFree(tctx); + tctx = NULL; + goto end; + } for (uint8_t flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); - tctx->alproto_local_storage[flow_proto][alproto] = - AppLayerParserGetProtocolParserLocalStorage(ipproto, alproto); + tctx->alproto_local_storage[alproto][flow_proto] = + AppLayerParserGetProtocolParserLocalStorage(ipproto, alproto); } } @@ -305,11 +317,12 @@ void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx) for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); - AppLayerParserDestroyProtocolParserLocalStorage(ipproto, alproto, - tctx->alproto_local_storage[flow_proto][alproto]); + AppLayerParserDestroyProtocolParserLocalStorage( + ipproto, alproto, tctx->alproto_local_storage[alproto][flow_proto]); } } + SCFree(tctx->alproto_local_storage); SCFree(tctx); SCReturn; } @@ -381,8 +394,8 @@ int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - Parser[(direction & STREAM_TOSERVER) ? 0 : 1] = Parser; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)] + .Parser[(direction & STREAM_TOSERVER) ? 0 : 1] = Parser; SCReturnInt(0); } @@ -392,8 +405,8 @@ void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppPro { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].first_data_dir |= - (direction & (STREAM_TOSERVER | STREAM_TOCLIENT)); + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].first_data_dir |= + (direction & (STREAM_TOSERVER | STREAM_TOCLIENT)); SCReturn; } @@ -403,7 +416,7 @@ void AppLayerParserRegisterOptionFlags(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].option_flags |= flags; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].option_flags |= flags; SCReturn; } @@ -413,10 +426,8 @@ void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateAlloc = - StateAlloc; - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateFree = - StateFree; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateAlloc = StateAlloc; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateFree = StateFree; SCReturn; } @@ -427,10 +438,8 @@ void AppLayerParserRegisterLocalStorageFunc(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageAlloc = - LocalStorageAlloc; - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageFree = - LocalStorageFree; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageAlloc = LocalStorageAlloc; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageFree = LocalStorageFree; SCReturn; } @@ -440,7 +449,7 @@ void AppLayerParserRegisterGetTxFilesFunc( { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxFiles = GetTxFiles; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetTxFiles = GetTxFiles; SCReturn; } @@ -449,7 +458,7 @@ void AppLayerParserRegisterLoggerBits(uint8_t ipproto, AppProto alproto, LoggerI { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger_bits = bits; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].logger_bits = bits; SCReturn; } @@ -458,7 +467,7 @@ void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto) { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger = true; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].logger = true; SCReturn; } @@ -468,8 +477,7 @@ void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alprot { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateGetProgress = StateGetProgress; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetProgress = StateGetProgress; SCReturn; } @@ -479,8 +487,7 @@ void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateTransactionFree = StateTransactionFree; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateTransactionFree = StateTransactionFree; SCReturn; } @@ -490,8 +497,7 @@ void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateGetTxCnt = StateGetTxCnt; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetTxCnt = StateGetTxCnt; SCReturn; } @@ -501,8 +507,7 @@ void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateGetTx = StateGetTx; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetTx = StateGetTx; SCReturn; } @@ -511,7 +516,7 @@ void AppLayerParserRegisterGetTxIterator(uint8_t ipproto, AppProto alproto, AppLayerGetTxIteratorFunc Func) { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetTxIterator = Func; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetTxIterator = Func; SCReturn; } @@ -521,13 +526,13 @@ void AppLayerParserRegisterStateProgressCompletionStatus( BUG_ON(ts == 0); BUG_ON(tc == 0); BUG_ON(!AppProtoIsValid(alproto)); - BUG_ON(alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_ts != 0 && - alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_ts != ts); - BUG_ON(alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_tc != 0 && - alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_tc != tc); + BUG_ON(alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_ts != 0 && + alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_ts != ts); + BUG_ON(alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_tc != 0 && + alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_tc != tc); - alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_ts = ts; - alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_tc = tc; + alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_ts = ts; + alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_tc = tc; } void AppLayerParserRegisterGetEventInfoById(uint8_t ipproto, AppProto alproto, @@ -536,8 +541,8 @@ void AppLayerParserRegisterGetEventInfoById(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateGetEventInfoById = StateGetEventInfoById; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetEventInfoById = + StateGetEventInfoById; SCReturn; } @@ -547,8 +552,8 @@ void AppLayerParserRegisterGetFrameFuncs(uint8_t ipproto, AppProto alproto, AppLayerParserGetFrameNameByIdFn GetNameByIdFunc) { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameIdByName = GetIdByNameFunc; - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameNameById = GetNameByIdFunc; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameIdByName = GetIdByNameFunc; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameNameById = GetNameByIdFunc; SCReturn; } @@ -558,8 +563,7 @@ void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - StateGetEventInfo = StateGetEventInfo; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetEventInfo = StateGetEventInfo; SCReturn; } @@ -569,7 +573,7 @@ void AppLayerParserRegisterTxDataFunc(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxData = GetTxData; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetTxData = GetTxData; SCReturn; } @@ -579,7 +583,7 @@ void AppLayerParserRegisterStateDataFunc( { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetStateData = GetStateData; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetStateData = GetStateData; SCReturn; } @@ -589,7 +593,7 @@ void AppLayerParserRegisterApplyTxConfigFunc(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].ApplyTxConfig = ApplyTxConfig; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].ApplyTxConfig = ApplyTxConfig; SCReturn; } @@ -599,7 +603,7 @@ void AppLayerParserRegisterSetStreamDepthFlag(uint8_t ipproto, AppProto alproto, { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetStreamDepthFlag = SetStreamDepthFlag; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].SetStreamDepthFlag = SetStreamDepthFlag; SCReturn; } @@ -611,11 +615,8 @@ void *AppLayerParserGetProtocolParserLocalStorage(uint8_t ipproto, AppProto alpr SCEnter(); void * r = NULL; - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - LocalStorageAlloc != NULL) - { - r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - LocalStorageAlloc(); + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageAlloc != NULL) { + r = alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageAlloc(); } SCReturnPtr(r, "void *"); @@ -626,11 +627,8 @@ void AppLayerParserDestroyProtocolParserLocalStorage(uint8_t ipproto, AppProto a { SCEnter(); - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - LocalStorageFree != NULL) - { - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. - LocalStorageFree(local_data); + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageFree != NULL) { + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].LocalStorageFree(local_data); } SCReturn; @@ -675,7 +673,7 @@ AppLayerGetTxIteratorFunc AppLayerGetTxIterator(const uint8_t ipproto, const AppProto alproto) { AppLayerGetTxIteratorFunc Func = - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetTxIterator; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetTxIterator; return Func ? Func : AppLayerDefaultGetTxIterator; } @@ -855,8 +853,8 @@ AppLayerGetFileState AppLayerParserGetTxFiles(const Flow *f, void *tx, const uin { SCEnter(); - if (alp_ctx.ctxs[f->protomap][f->alproto].GetTxFiles != NULL) { - return alp_ctx.ctxs[f->protomap][f->alproto].GetTxFiles(tx, direction); + if (alp_ctx.ctxs[f->alproto][f->protomap].GetTxFiles != NULL) { + return alp_ctx.ctxs[f->alproto][f->protomap].GetTxFiles(tx, direction); } AppLayerGetFileState files = { .fc = NULL, .cfg = NULL }; @@ -886,7 +884,7 @@ void AppLayerParserTransactionsCleanup(Flow *f, const uint8_t pkt_dir) SCEnter(); DEBUG_ASSERT_FLOW_LOCKED(f); - AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][f->alproto]; + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->alproto][f->protomap]; if (unlikely(p->StateTransactionFree == NULL)) SCReturn; @@ -1048,9 +1046,9 @@ void AppLayerParserTransactionsCleanup(Flow *f, const uint8_t pkt_dir) static inline int StateGetProgressCompletionStatus(const AppProto alproto, const uint8_t flags) { if (flags & STREAM_TOSERVER) { - return alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_ts; + return alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_ts; } else if (flags & STREAM_TOCLIENT) { - return alp_ctx.ctxs[FLOW_PROTO_DEFAULT][alproto].complete_tc; + return alp_ctx.ctxs[alproto][FLOW_PROTO_DEFAULT].complete_tc; } else { DEBUG_VALIDATE_BUG_ON(1); return 0; @@ -1071,7 +1069,7 @@ int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, r = StateGetProgressCompletionStatus(alproto, flags); } else { uint8_t direction = flags & (STREAM_TOCLIENT | STREAM_TOSERVER); - r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetProgress( + r = alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetProgress( alstate, direction); } SCReturnInt(r); @@ -1080,14 +1078,14 @@ int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate) { SCEnter(); - uint64_t r = alp_ctx.ctxs[f->protomap][f->alproto].StateGetTxCnt(alstate); + uint64_t r = alp_ctx.ctxs[f->alproto][f->protomap].StateGetTxCnt(alstate); SCReturnCT(r, "uint64_t"); } void *AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id) { SCEnter(); - void *r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetTx(alstate, tx_id); + void *r = alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].StateGetTx(alstate, tx_id); SCReturnPtr(r, "void *"); } @@ -1104,8 +1102,10 @@ int AppLayerParserGetEventInfo(uint8_t ipproto, AppProto alproto, const char *ev { SCEnter(); const int ipproto_map = FlowGetProtoMapping(ipproto); - int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo == NULL) ? - -1 : alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo(event_name, event_id, event_type); + int r = (alp_ctx.ctxs[alproto][ipproto_map].StateGetEventInfo == NULL) + ? -1 + : alp_ctx.ctxs[alproto][ipproto_map].StateGetEventInfo( + event_name, event_id, event_type); SCReturnInt(r); } @@ -1115,15 +1115,17 @@ int AppLayerParserGetEventInfoById(uint8_t ipproto, AppProto alproto, uint8_t ev SCEnter(); const int ipproto_map = FlowGetProtoMapping(ipproto); *event_name = (const char *)NULL; - int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfoById == NULL) ? - -1 : alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfoById(event_id, event_name, event_type); + int r = (alp_ctx.ctxs[alproto][ipproto_map].StateGetEventInfoById == NULL) + ? -1 + : alp_ctx.ctxs[alproto][ipproto_map].StateGetEventInfoById( + event_id, event_name, event_type); SCReturnInt(r); } uint8_t AppLayerParserGetFirstDataDir(uint8_t ipproto, AppProto alproto) { SCEnter(); - uint8_t r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].first_data_dir; + uint8_t r = alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].first_data_dir; SCReturnCT(r, "uint8_t"); } @@ -1135,7 +1137,7 @@ uint64_t AppLayerParserGetTransactionActive(const Flow *f, uint64_t active_id; uint64_t log_id = pstate->log_id; uint64_t inspect_id = pstate->inspect_id[(direction & STREAM_TOSERVER) ? 0 : 1]; - if (alp_ctx.ctxs[f->protomap][f->alproto].logger == true) { + if (alp_ctx.ctxs[f->alproto][f->protomap].logger == true) { active_id = MIN(log_id, inspect_id); } else { active_id = inspect_id; @@ -1151,22 +1153,22 @@ bool AppLayerParserSupportsFiles(uint8_t ipproto, AppProto alproto) return AppLayerParserSupportsFiles(ipproto, ALPROTO_HTTP1) || AppLayerParserSupportsFiles(ipproto, ALPROTO_HTTP2); } - return alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxFiles != NULL; + return alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetTxFiles != NULL; } AppLayerTxData *AppLayerParserGetTxData(uint8_t ipproto, AppProto alproto, void *tx) { SCEnter(); - AppLayerTxData *d = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxData(tx); + AppLayerTxData *d = alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetTxData(tx); SCReturnPtr(d, "AppLayerTxData"); } AppLayerStateData *AppLayerParserGetStateData(uint8_t ipproto, AppProto alproto, void *state) { SCEnter(); - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetStateData) { + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetStateData) { AppLayerStateData *d = - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetStateData(state); + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetStateData(state); SCReturnPtr(d, "AppLayerStateData"); } SCReturnPtr(NULL, "AppLayerStateData"); @@ -1177,8 +1179,8 @@ void AppLayerParserApplyTxConfig(uint8_t ipproto, AppProto alproto, { SCEnter(); const int ipproto_map = FlowGetProtoMapping(ipproto); - if (alp_ctx.ctxs[ipproto_map][alproto].ApplyTxConfig) { - alp_ctx.ctxs[ipproto_map][alproto].ApplyTxConfig(state, tx, mode, config); + if (alp_ctx.ctxs[alproto][ipproto_map].ApplyTxConfig) { + alp_ctx.ctxs[alproto][ipproto_map].ApplyTxConfig(state, tx, mode, config); } SCReturn; } @@ -1270,7 +1272,7 @@ int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow BUG_ON(f->protomap != FlowGetProtoMapping(f->proto)); #endif AppLayerParserState *pstate = f->alparser; - AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][alproto]; + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[alproto][f->protomap]; StreamSlice stream_slice; void *alstate = NULL; uint64_t p_tx_cnt = 0; @@ -1359,7 +1361,7 @@ int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow #endif /* invoke the parser */ AppLayerResult res = p->Parser[direction](f, alstate, pstate, stream_slice, - alp_tctx->alproto_local_storage[f->protomap][alproto]); + alp_tctx->alproto_local_storage[alproto][f->protomap]); if (res.status < 0) { AppLayerIncParserErrorCounter(tv, f); goto error; @@ -1505,7 +1507,7 @@ bool AppLayerParserHasDecoderEvents(AppLayerParserState *pstate) int AppLayerParserIsEnabled(AppProto alproto) { for (int i = 0; i < FLOW_PROTO_APPLAYER_MAX; i++) { - if (alp_ctx.ctxs[i][alproto].StateGetProgress != NULL) { + if (alp_ctx.ctxs[alproto][i].StateGetProgress != NULL) { return 1; } } @@ -1516,7 +1518,7 @@ int AppLayerParserProtocolHasLogger(uint8_t ipproto, AppProto alproto) { SCEnter(); int ipproto_map = FlowGetProtoMapping(ipproto); - int r = (alp_ctx.ctxs[ipproto_map][alproto].logger == false) ? 0 : 1; + int r = (alp_ctx.ctxs[alproto][ipproto_map].logger == false) ? 0 : 1; SCReturnInt(r); } @@ -1524,7 +1526,7 @@ LoggerId AppLayerParserProtocolGetLoggerBits(uint8_t ipproto, AppProto alproto) { SCEnter(); const int ipproto_map = FlowGetProtoMapping(ipproto); - LoggerId r = alp_ctx.ctxs[ipproto_map][alproto].logger_bits; + LoggerId r = alp_ctx.ctxs[alproto][ipproto_map].logger_bits; SCReturnUInt(r); } @@ -1543,16 +1545,16 @@ void AppLayerParserSetStreamDepth(uint8_t ipproto, AppProto alproto, uint32_t st { SCEnter(); - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].stream_depth = stream_depth; - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].internal_flags |= - APP_LAYER_PARSER_INT_STREAM_DEPTH_SET; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].stream_depth = stream_depth; + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].internal_flags |= + APP_LAYER_PARSER_INT_STREAM_DEPTH_SET; SCReturn; } uint32_t AppLayerParserGetStreamDepth(const Flow *f) { - SCReturnInt(alp_ctx.ctxs[f->protomap][f->alproto].stream_depth); + SCReturnInt(alp_ctx.ctxs[f->alproto][f->protomap].stream_depth); } void AppLayerParserSetStreamDepthFlag(uint8_t ipproto, AppProto alproto, void *state, uint64_t tx_id, uint8_t flags) @@ -1561,8 +1563,8 @@ void AppLayerParserSetStreamDepthFlag(uint8_t ipproto, AppProto alproto, void *s void *tx = NULL; if (state != NULL) { if ((tx = AppLayerParserGetTx(ipproto, alproto, state, tx_id)) != NULL) { - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetStreamDepthFlag != NULL) { - alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetStreamDepthFlag(tx, flags); + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].SetStreamDepthFlag != NULL) { + alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].SetStreamDepthFlag(tx, flags); } } } @@ -1571,8 +1573,8 @@ void AppLayerParserSetStreamDepthFlag(uint8_t ipproto, AppProto alproto, void *s int AppLayerParserGetFrameIdByName(uint8_t ipproto, AppProto alproto, const char *name) { - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameIdByName != NULL) { - return alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameIdByName(name); + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameIdByName != NULL) { + return alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameIdByName(name); } else { return -1; } @@ -1580,8 +1582,8 @@ int AppLayerParserGetFrameIdByName(uint8_t ipproto, AppProto alproto, const char const char *AppLayerParserGetFrameNameById(uint8_t ipproto, AppProto alproto, const uint8_t id) { - if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameNameById != NULL) { - return alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetFrameNameById(id); + if (alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameNameById != NULL) { + return alp_ctx.ctxs[alproto][FlowGetProtoMapping(ipproto)].GetFrameNameById(id); } else { return NULL; } @@ -1594,7 +1596,7 @@ void AppLayerParserStateProtoCleanup( { SCEnter(); - AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[protomap][alproto]; + AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[alproto][protomap]; if (ctx->StateFree != NULL && alstate != NULL) ctx->StateFree(alstate); @@ -1614,7 +1616,7 @@ void AppLayerParserStateCleanup(const Flow *f, void *alstate, AppLayerParserStat static void ValidateParserProtoDump(AppProto alproto, uint8_t ipproto) { uint8_t map = FlowGetProtoMapping(ipproto); - const AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[map][alproto]; + const AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[alproto][map]; printf("ERROR: incomplete app-layer registration\n"); printf("AppLayer protocol %s ipproto %u\n", AppProtoToString(alproto), ipproto); printf("- option flags %"PRIx32"\n", ctx->option_flags); @@ -1640,7 +1642,7 @@ static void ValidateParserProtoDump(AppProto alproto, uint8_t ipproto) static void ValidateParserProto(AppProto alproto, uint8_t ipproto) { uint8_t map = FlowGetProtoMapping(ipproto); - const AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[map][alproto]; + const AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[alproto][map]; if (ctx->Parser[0] == NULL && ctx->Parser[1] == NULL) return; @@ -1787,7 +1789,7 @@ void AppLayerParserRegisterUnittests(void) for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { - ctx = &alp_ctx.ctxs[ip][alproto]; + ctx = &alp_ctx.ctxs[alproto][ip]; if (ctx->RegisterUnittests == NULL) continue; ctx->RegisterUnittests(); From 8ae806e93e94cd501a18ed8d3916e556b8c41535 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 29 Oct 2024 11:27:30 +0100 Subject: [PATCH 09/17] profiling: use dynamic number of app-layer protos Ticket: 5053 --- src/util-profiling.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/util-profiling.c b/src/util-profiling.c index 80d3938bd41f..20fcc7f98016 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -72,8 +72,8 @@ SCProfilePacketData packet_profile_data6[257]; /**< all proto's + tunnel */ SCProfilePacketData packet_profile_tmm_data4[TMM_SIZE][257]; SCProfilePacketData packet_profile_tmm_data6[TMM_SIZE][257]; -SCProfilePacketData packet_profile_app_data4[ALPROTO_MAX][257]; -SCProfilePacketData packet_profile_app_data6[ALPROTO_MAX][257]; +SCProfilePacketData *packet_profile_app_data4; +SCProfilePacketData *packet_profile_app_data6; SCProfilePacketData packet_profile_app_pd_data4[257]; SCProfilePacketData packet_profile_app_pd_data6[257]; @@ -158,8 +158,14 @@ SCProfilingInit(void) memset(&packet_profile_data6, 0, sizeof(packet_profile_data6)); memset(&packet_profile_tmm_data4, 0, sizeof(packet_profile_tmm_data4)); memset(&packet_profile_tmm_data6, 0, sizeof(packet_profile_tmm_data6)); - memset(&packet_profile_app_data4, 0, sizeof(packet_profile_app_data4)); - memset(&packet_profile_app_data6, 0, sizeof(packet_profile_app_data6)); + packet_profile_app_data4 = SCCalloc(ALPROTO_MAX * 257, sizeof(SCProfilePacketData)); + if (packet_profile_app_data4 == NULL) { + FatalError("Failed to allocate packet_profile_app_data4"); + } + packet_profile_app_data6 = SCCalloc(ALPROTO_MAX * 257, sizeof(SCProfilePacketData)); + if (packet_profile_app_data6 == NULL) { + FatalError("Failed to allocate packet_profile_app_data6"); + } memset(&packet_profile_app_pd_data4, 0, sizeof(packet_profile_app_pd_data4)); memset(&packet_profile_app_pd_data6, 0, sizeof(packet_profile_app_pd_data6)); memset(&packet_profile_detect_data4, 0, sizeof(packet_profile_detect_data4)); @@ -269,6 +275,15 @@ SCProfilingInit(void) void SCProfilingDestroy(void) { + if (packet_profile_app_data4) { + SCFree(packet_profile_app_data4); + packet_profile_app_data4 = NULL; + } + if (packet_profile_app_data6) { + SCFree(packet_profile_app_data6); + packet_profile_app_data6 = NULL; + } + if (profiling_packets_enabled) { pthread_mutex_destroy(&packet_profile_lock); } @@ -490,16 +505,16 @@ void SCProfilingDumpPacketStats(void) total = 0; for (AppProto a = 0; a < ALPROTO_MAX; a++) { for (int p = 0; p < 257; p++) { - SCProfilePacketData *pd = &packet_profile_app_data4[a][p]; + SCProfilePacketData *pd = &packet_profile_app_data4[a * 257 + p]; total += pd->tot; - pd = &packet_profile_app_data6[a][p]; + pd = &packet_profile_app_data6[a * 257 + p]; total += pd->tot; } } for (AppProto a = 0; a < ALPROTO_MAX; a++) { for (int p = 0; p < 257; p++) { - SCProfilePacketData *pd = &packet_profile_app_data4[a][p]; + SCProfilePacketData *pd = &packet_profile_app_data4[a * 257 + p]; if (pd->cnt == 0) { continue; } @@ -518,7 +533,7 @@ void SCProfilingDumpPacketStats(void) for (AppProto a = 0; a < ALPROTO_MAX; a++) { for (int p = 0; p < 257; p++) { - SCProfilePacketData *pd = &packet_profile_app_data6[a][p]; + SCProfilePacketData *pd = &packet_profile_app_data6[a * 257 + p]; if (pd->cnt == 0) { continue; } @@ -918,9 +933,9 @@ static void SCProfilingUpdatePacketAppRecord(int alproto, uint8_t ipproto, PktPr SCProfilePacketData *pd; if (ipver == 4) - pd = &packet_profile_app_data4[alproto][ipproto]; + pd = &packet_profile_app_data4[alproto * 257 + ipproto]; else - pd = &packet_profile_app_data6[alproto][ipproto]; + pd = &packet_profile_app_data6[alproto * 257 + ipproto]; if (pd->min == 0 || pdt->ticks_spent < pd->min) { pd->min = pdt->ticks_spent; From be464889996946bd651f3c2b4eca46700391c92e Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 29 Oct 2024 22:26:37 +0100 Subject: [PATCH 10/17] fuzz: better init for protocol detection --- src/tests/fuzz/fuzz_applayerprotodetectgetproto.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c index e30f729bc28f..6dcaf1793461 100644 --- a/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c +++ b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c @@ -9,7 +9,7 @@ #include "suricata.h" #include "app-layer-detect-proto.h" #include "flow-util.h" -#include "app-layer-parser.h" +#include "app-layer.h" #include "util-unittest-helper.h" #include "conf-yaml-loader.h" @@ -43,9 +43,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) MpmTableSetup(); SpmTableSetup(); EngineModeSetIDS(); - AppLayerProtoDetectSetup(); - AppLayerParserSetup(); - AppLayerParserRegisterProtocolParsers(); + AppLayerSetup(); alpd_tctx = AppLayerProtoDetectGetCtxThread(); SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME); } From a3f0ab83a5491921fe307ff7f6c26e7978cee87d Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 29 Oct 2024 22:28:18 +0100 Subject: [PATCH 11/17] fuzz: simplify target for protocol detection As too many cases are found when splitting tcp payload --- .../fuzz/fuzz_applayerprotodetectgetproto.c | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c index 6dcaf1793461..4473de00941c 100644 --- a/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c +++ b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c @@ -30,8 +30,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) Flow *f; TcpSession ssn; bool reverse; - AppProto alproto; - AppProto alproto2; if (alpd_tctx == NULL) { //global init @@ -66,31 +64,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (data[0] & STREAM_TOSERVER) { flags = STREAM_TOSERVER; } - alproto = AppLayerProtoDetectGetProto( + AppLayerProtoDetectGetProto( alpd_tctx, f, data + HEADER_LEN, size - HEADER_LEN, f->proto, flags, &reverse); - if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED && f->proto == IPPROTO_TCP) { - /* If we find a valid protocol at the start of a stream : - * check that with smaller input - * we find the same protocol or ALPROTO_UNKNOWN. - * Otherwise, we have evasion with TCP splitting - */ - for (size_t i = 0; i < size-HEADER_LEN && i < PROTO_DETECT_MAX_LEN; i++) { - // reset detection at each try cf probing_parser_toserver_alproto_masks - AppLayerProtoDetectReset(f); - alproto2 = AppLayerProtoDetectGetProto( - alpd_tctx, f, data + HEADER_LEN, i, f->proto, flags, &reverse); - if (alproto2 != ALPROTO_UNKNOWN && alproto2 != alproto) { - printf("Failed with input length %" PRIuMAX " versus %" PRIuMAX - ", found %s instead of %s\n", - (uintmax_t)i, (uintmax_t)size - HEADER_LEN, AppProtoToString(alproto2), - AppProtoToString(alproto)); - printf("Assertion failure: %s-%s\n", AppProtoToString(alproto2), - AppProtoToString(alproto)); - fflush(stdout); - abort(); - } - } - } FlowFree(f); return 0; From 783bbe01ba4077f96e9c972dfbab4c7250fb1988 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 29 Oct 2024 22:29:06 +0100 Subject: [PATCH 12/17] sip: remove UPDATE method for detection As it is also used for HTTP/1 Remove it only for TCP and keep it for UDP. --- rust/src/sip/sip.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index 1a73d4e46a66..5f52e0c8db07 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -496,7 +496,6 @@ fn register_pattern_probe(proto: u8) -> i8 { "ACK\0", "BYE\0", "CANCEL\0", - "UPDATE\0", "REFER\0", "PRACK\0", "SUBSCRIBE\0", @@ -526,6 +525,16 @@ fn register_pattern_probe(proto: u8) -> i8 { 0, core::Direction::ToClient as u8, ); + if proto == core::IPPROTO_UDP { + r |= AppLayerProtoDetectPMRegisterPatternCS( + proto, + ALPROTO_SIP, + "UPDATE\0".as_ptr() as *const std::os::raw::c_char, + "UPDATE".len() as u16, + 0, + core::Direction::ToServer as u8, + ); + } } if r == 0 { From 40b7eeef819bfeb96502fea6a51dc033eef06441 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 11 Nov 2024 07:21:03 +0100 Subject: [PATCH 13/17] app-layer: move ALPROTO_FAILED definition Because some alprotos will remain static and defined as a constant, such as ALPROTO_UNKNOWN=0, or ALPROTO_FAILED. The regular already used protocols keep for now their static identifier such as ALPROTO_SNMP, but this could be made more dynamic in a later commit. ALPROTO_FAILED was used in comparison and these needed to change to use either ALPROTO_MAX or use standard function AppProtoIsValid --- rust/src/core.rs | 3 +-- rust/src/ldap/ldap.rs | 4 ++-- rust/src/modbus/modbus.rs | 2 +- rust/src/ntp/ntp.rs | 2 +- rust/src/smb/smb.rs | 2 +- src/app-layer-protos.c | 2 +- src/app-layer-protos.h | 10 ++++++---- src/app-layer-register.c | 2 +- src/detect-engine-prefilter.c | 2 +- src/detect-engine.c | 2 +- src/detect-parse.c | 3 +-- src/util-profiling.c | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/rust/src/core.rs b/rust/src/core.rs index a628b300384a..8dafc73eec81 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -109,7 +109,7 @@ impl From for u8 { pub type AppProto = u16; pub const ALPROTO_UNKNOWN : AppProto = 0; -pub static mut ALPROTO_FAILED : AppProto = 0; // updated during init +pub const ALPROTO_FAILED : AppProto = 1; pub const IPPROTO_TCP : u8 = 6; pub const IPPROTO_UDP : u8 = 17; @@ -252,7 +252,6 @@ pub fn init_ffi(context: &'static SuricataContext) { unsafe { SC = Some(context); - ALPROTO_FAILED = StringToAppProto("failed\0".as_ptr()); } } diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index 4c9c3947d7a8..4fea91547658 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -469,7 +469,7 @@ fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> AppProto { Ok((_, msg)) => { let ldap_msg = LdapMessage::from(msg); if ldap_msg.is_unknown() { - return unsafe { ALPROTO_FAILED }; + return ALPROTO_FAILED; } if direction == Direction::ToServer && !ldap_msg.is_request() { unsafe { @@ -487,7 +487,7 @@ fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> AppProto { return ALPROTO_UNKNOWN; } Err(_e) => { - return unsafe { ALPROTO_FAILED }; + return ALPROTO_FAILED; } } } diff --git a/rust/src/modbus/modbus.rs b/rust/src/modbus/modbus.rs index 0d0c73371ef0..9401fc6922d0 100644 --- a/rust/src/modbus/modbus.rs +++ b/rust/src/modbus/modbus.rs @@ -281,7 +281,7 @@ pub extern "C" fn rs_modbus_probe( match MODBUS_PARSER.probe(slice, Direction::Unknown) { Status::Recognized => unsafe { ALPROTO_MODBUS }, Status::Incomplete => ALPROTO_UNKNOWN, - Status::Unrecognized => unsafe { ALPROTO_FAILED }, + Status::Unrecognized => ALPROTO_FAILED, } } diff --git a/rust/src/ntp/ntp.rs b/rust/src/ntp/ntp.rs index ae723bbb21cd..e17648c4c960 100644 --- a/rust/src/ntp/ntp.rs +++ b/rust/src/ntp/ntp.rs @@ -259,7 +259,7 @@ pub extern "C" fn ntp_probing_parser(_flow: *const Flow, return ALPROTO_UNKNOWN; }, Err(_) => { - return unsafe{ALPROTO_FAILED}; + return ALPROTO_FAILED; }, } } diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 901ed2a23927..c9471c34f603 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -2154,7 +2154,7 @@ fn smb_probe_tcp(flags: u8, slice: &[u8], rdir: *mut u8, begins: bool) -> AppPro } } SCLogDebug!("no smb"); - unsafe { return ALPROTO_FAILED; } + return ALPROTO_FAILED; } // probing confirmation parser diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 03736554c7b6..999e4c52322e 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -32,6 +32,7 @@ typedef struct AppProtoStringTuple { const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_UNKNOWN, "unknown" }, + { ALPROTO_FAILED, "failed" }, { ALPROTO_HTTP1, "http1" }, { ALPROTO_FTP, "ftp" }, { ALPROTO_SMTP, "smtp" }, @@ -69,7 +70,6 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_BITTORRENT_DHT, "bittorrent-dht" }, { ALPROTO_POP3, "pop3" }, { ALPROTO_HTTP, "http" }, - { ALPROTO_FAILED, "failed" }, }; const char *AppProtoToString(AppProto alproto) diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 10b8959772c4..6515571c68b7 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -27,6 +27,11 @@ enum AppProtoEnum { ALPROTO_UNKNOWN = 0, + /* used by the probing parser when alproto detection fails + * permanently for that particular stream */ + ALPROTO_FAILED = 1, + + // Beginning of real/normal protocols ALPROTO_HTTP1, ALPROTO_FTP, ALPROTO_SMTP, @@ -69,9 +74,6 @@ enum AppProtoEnum { // HTTP for any version (ALPROTO_HTTP1 (version 1) or ALPROTO_HTTP2) ALPROTO_HTTP, - /* used by the probing parser when alproto detection fails - * permanently for that particular stream */ - ALPROTO_FAILED, /* keep last */ ALPROTO_MAX, }; @@ -82,7 +84,7 @@ typedef uint16_t AppProto; static inline bool AppProtoIsValid(AppProto a) { - return ((a > ALPROTO_UNKNOWN && a < ALPROTO_FAILED)); + return ((a > ALPROTO_FAILED && a < ALPROTO_MAX)); } // whether a signature AppProto matches a flow (or signature) AppProto diff --git a/src/app-layer-register.c b/src/app-layer-register.c index 1e4986b361ea..9a33a38eea76 100644 --- a/src/app-layer-register.c +++ b/src/app-layer-register.c @@ -101,7 +101,7 @@ int AppLayerRegisterParser(const struct AppLayerParser *p, AppProto alproto) if (p == NULL) FatalError("Call to %s with NULL pointer.", __FUNCTION__); - if (alproto == ALPROTO_UNKNOWN || alproto >= ALPROTO_FAILED) + if (!AppProtoIsValid(alproto)) FatalError("Unknown or invalid AppProto '%s'.", p->name); BUG_ON(strcmp(p->name, AppProtoToString(alproto)) != 0); diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index feff1251e4e2..e5bdbfd3d792 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -521,7 +521,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) /* per alproto to set is_last_for_progress per alproto because the inspect * loop skips over engines that are not the correct alproto */ - for (AppProto a = 1; a < ALPROTO_FAILED; a++) { + for (AppProto a = ALPROTO_FAILED + 1; a < ALPROTO_MAX; a++) { int last_tx_progress = 0; bool last_tx_progress_set = false; PrefilterEngine *prev_engine = NULL; diff --git a/src/detect-engine.c b/src/detect-engine.c index 77c25a1cf3a9..d3d0ef527f14 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -181,7 +181,7 @@ static void AppLayerInspectEngineRegisterInternal(const char *name, AppProto alp } SCLogDebug("name %s id %d", name, sm_list); - if ((alproto >= ALPROTO_FAILED) || (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) || + if ((alproto == ALPROTO_FAILED) || (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) || (sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (progress < 0 || progress >= SHRT_MAX) || (Callback == NULL)) { SCLogError("Invalid arguments"); diff --git a/src/detect-parse.c b/src/detect-parse.c index 984501c1dd8a..a7da0b25f448 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -1736,8 +1736,7 @@ int DetectSignatureAddTransform(Signature *s, int transform, void *options) int DetectSignatureSetAppProto(Signature *s, AppProto alproto) { - if (alproto == ALPROTO_UNKNOWN || - alproto >= ALPROTO_FAILED) { + if (!AppProtoIsValid(alproto)) { SCLogError("invalid alproto %u", alproto); return -1; } diff --git a/src/util-profiling.c b/src/util-profiling.c index 20fcc7f98016..73e0c8489057 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -820,7 +820,7 @@ void SCProfilingPrintPacketProfile(Packet *p) /* count ticks for app layer */ uint64_t app_total = 0; - for (AppProto i = 1; i < ALPROTO_FAILED; i++) { + for (AppProto i = 0; i < ALPROTO_MAX; i++) { const PktProfilingAppData *pdt = &p->profile->app[i]; if (p->proto == IPPROTO_TCP) { From d535a4e5ed3323d0a69f970ec82bbd59cc46bff3 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 11 Nov 2024 07:26:11 +0100 Subject: [PATCH 14/17] app-layer: make number of alprotos dynamic Ticket: 5053 The names are now dynamically registered at runtime. The AppProto alproto enum identifiers are still static for now. This is the final step before app-layer plugins. --- scripts/setup-app-layer.py | 2 +- src/app-layer-detect-proto.c | 40 ++++++------ src/app-layer-frames.c | 8 +-- src/app-layer-parser.c | 16 ++--- src/app-layer-protos.c | 76 ++++++++++------------- src/app-layer-protos.h | 7 ++- src/app-layer.c | 59 +++++++++++++++--- src/detect-engine-build.c | 7 ++- src/detect-engine-mpm.c | 2 +- src/detect-engine-prefilter.c | 2 +- src/detect-file-data.c | 4 +- src/output-tx.c | 14 ++--- src/output.c | 4 +- src/runmodes.c | 8 +-- src/tests/fuzz/fuzz_applayerparserparse.c | 2 +- src/util-profiling.c | 16 ++--- src/util-profiling.h | 12 ++-- 17 files changed, 158 insertions(+), 121 deletions(-) diff --git a/scripts/setup-app-layer.py b/scripts/setup-app-layer.py index bb3d23e83944..f94e68ae7d7e 100755 --- a/scripts/setup-app-layer.py +++ b/scripts/setup-app-layer.py @@ -129,7 +129,7 @@ def patch_app_layer_protos_h(protoname): open(filename, "w").write(output.getvalue()) def patch_app_layer_protos_c(protoname): - filename = "src/app-layer-protos.c" + filename = "src/app-layer.c" print("Patching %s." % (filename)) output = io.StringIO() diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 52e1f2922c02..5ae28b137bb5 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -292,7 +292,7 @@ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, } /* alproto bit field */ - uint8_t pm_results_bf[(ALPROTO_MAX / 8) + 1]; + uint8_t pm_results_bf[(AlprotoMax / 8) + 1]; memset(pm_results_bf, 0, sizeof(pm_results_bf)); /* loop through unique pattern id's. Can't use search_cnt here, @@ -324,7 +324,7 @@ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, /** \internal * \brief Run Pattern Sigs against buffer * \param direction direction for the patterns - * \param pm_results[out] AppProto array of size ALPROTO_MAX */ + * \param pm_results[out] AppProto array of size AlprotoMax */ static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx, Flow *f, const uint8_t *buf, uint32_t buflen, uint8_t flags, AppProto *pm_results, bool *rflow) { @@ -804,7 +804,7 @@ static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParser "register the probing parser. min_depth >= max_depth"); goto error; } - if (alproto <= ALPROTO_UNKNOWN || alproto >= ALPROTO_MAX) { + if (alproto <= ALPROTO_UNKNOWN || alproto >= AlprotoMax) { SCLogError("Invalid arguments sent to register " "the probing parser. Invalid alproto - %d", alproto); @@ -1411,7 +1411,7 @@ AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx, Flow *f AppProto pm_alproto = ALPROTO_UNKNOWN; if (!FLOW_IS_PM_DONE(f, flags)) { - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; uint16_t pm_matches = AppLayerProtoDetectPMGetProto( tctx, f, buf, buflen, flags, pm_results, reverse_flow); if (pm_matches > 0) { @@ -1725,12 +1725,12 @@ int AppLayerProtoDetectSetup(void) } } - alpd_ctx.alproto_names = SCCalloc(ALPROTO_MAX, sizeof(char *)); + alpd_ctx.alproto_names = SCCalloc(AlprotoMax, sizeof(char *)); if (unlikely(alpd_ctx.alproto_names == NULL)) { FatalError("Unable to alloc alproto_names."); } // to realloc when dynamic protos are added - alpd_ctx.expectation_proto = SCCalloc(ALPROTO_MAX, sizeof(uint8_t)); + alpd_ctx.expectation_proto = SCCalloc(AlprotoMax, sizeof(uint8_t)); if (unlikely(alpd_ctx.expectation_proto == NULL)) { FatalError("Unable to alloc expectation_proto."); } @@ -2090,7 +2090,7 @@ AppProto AppLayerProtoDetectGetProtoByName(const char *alproto_name) AppProto a; AppProto b = StringToAppProto(alproto_name); - for (a = 0; a < ALPROTO_MAX; a++) { + for (a = 0; a < AlprotoMax; a++) { if (alpd_ctx.alproto_names[a] != NULL && AppProtoEquals(b, a)) { // That means return HTTP_ANY if HTTP1 or HTTP2 is enabled SCReturnCT(b, "AppProto"); @@ -2121,11 +2121,11 @@ void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos) { SCEnter(); - memset(alprotos, 0, ALPROTO_MAX * sizeof(AppProto)); + memset(alprotos, 0, AlprotoMax * sizeof(AppProto)); int alproto; - for (alproto = 0; alproto != ALPROTO_MAX; alproto++) { + for (alproto = 0; alproto != AlprotoMax; alproto++) { if (alpd_ctx.alproto_names[alproto] != NULL) alprotos[alproto] = 1; } @@ -2229,7 +2229,7 @@ static int AppLayerProtoDetectTest03(void) AppLayerProtoDetectSetup(); uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2276,7 +2276,7 @@ static int AppLayerProtoDetectTest04(void) uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n"; Flow f; memset(&f, 0x00, sizeof(f)); - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); @@ -2314,7 +2314,7 @@ static int AppLayerProtoDetectTest05(void) AppLayerProtoDetectSetup(); uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\nBlahblah"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2358,7 +2358,7 @@ static int AppLayerProtoDetectTest06(void) AppLayerProtoDetectSetup(); uint8_t l7data[] = "220 Welcome to the OISF FTP server\r\n"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2404,7 +2404,7 @@ static int AppLayerProtoDetectTest07(void) Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); const char *buf = "HTTP"; @@ -2458,7 +2458,7 @@ static int AppLayerProtoDetectTest08(void) 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00 }; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2513,7 +2513,7 @@ static int AppLayerProtoDetectTest09(void) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02 }; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2563,7 +2563,7 @@ static int AppLayerProtoDetectTest10(void) 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 }; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2608,7 +2608,7 @@ static int AppLayerProtoDetectTest11(void) uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); @@ -2733,7 +2733,7 @@ static int AppLayerProtoDetectTest13(void) uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; Flow f; memset(&f, 0x00, sizeof(f)); @@ -2804,7 +2804,7 @@ static int AppLayerProtoDetectTest14(void) uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; - AppProto pm_results[ALPROTO_MAX]; + AppProto pm_results[AlprotoMax]; uint32_t cnt; Flow f; memset(&f, 0x00, sizeof(f)); diff --git a/src/app-layer-frames.c b/src/app-layer-frames.c index 3fa4c345474a..0ba738f8b19f 100644 --- a/src/app-layer-frames.c +++ b/src/app-layer-frames.c @@ -33,16 +33,16 @@ struct FrameConfig { SC_ATOMIC_DECLARE(uint64_t, types); }; -/* This array should be allocated to contain ALPROTO_MAX protocols. */ +/* This array should be allocated to contain AlprotoMax protocols. */ static struct FrameConfig *frame_config; void FrameConfigInit(void) { - frame_config = SCCalloc(ALPROTO_MAX, sizeof(struct FrameConfig)); + frame_config = SCCalloc(AlprotoMax, sizeof(struct FrameConfig)); if (unlikely(frame_config == NULL)) { FatalError("Unable to alloc frame_config."); } - for (AppProto p = 0; p < ALPROTO_MAX; p++) { + for (AppProto p = 0; p < AlprotoMax; p++) { SC_ATOMIC_INIT(frame_config[p].types); } } @@ -55,7 +55,7 @@ void FrameConfigDeInit(void) void FrameConfigEnableAll(void) { const uint64_t bits = UINT64_MAX; - for (AppProto p = 0; p < ALPROTO_MAX; p++) { + for (AppProto p = 0; p < AlprotoMax; p++) { struct FrameConfig *fc = &frame_config[p]; SC_ATOMIC_OR(fc->types, bits); } diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index ecd032191a8d..9384864f525b 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -249,8 +249,8 @@ int AppLayerParserSetup(void) { SCEnter(); // initial allocation that will later be grown using realloc, - // when new protocols register themselves and make ALPROTO_MAX grow - alp_ctx.ctxs = SCCalloc(ALPROTO_MAX, sizeof(AppLayerParserProtoCtx[FLOW_PROTO_MAX])); + // when new protocols register themselves and make AlprotoMax grow + alp_ctx.ctxs = SCCalloc(AlprotoMax, sizeof(AppLayerParserProtoCtx[FLOW_PROTO_MAX])); if (unlikely(alp_ctx.ctxs == NULL)) { FatalError("Unable to alloc alp_ctx.ctxs."); } @@ -261,7 +261,7 @@ void AppLayerParserPostStreamSetup(void) { /* lets set a default value for stream_depth */ for (int flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { if (!(alp_ctx.ctxs[alproto][flow_proto].internal_flags & APP_LAYER_PARSER_INT_STREAM_DEPTH_SET)) { alp_ctx.ctxs[alproto][flow_proto].stream_depth = stream_config.reassembly_depth; @@ -290,14 +290,14 @@ AppLayerParserThreadCtx *AppLayerParserThreadCtxAlloc(void) if (tctx == NULL) goto end; - tctx->alproto_local_storage = SCCalloc(ALPROTO_MAX, sizeof(void *[FLOW_PROTO_MAX])); + tctx->alproto_local_storage = SCCalloc(AlprotoMax, sizeof(void *[FLOW_PROTO_MAX])); if (unlikely(tctx->alproto_local_storage == NULL)) { SCFree(tctx); tctx = NULL; goto end; } for (uint8_t flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); tctx->alproto_local_storage[alproto][flow_proto] = @@ -314,7 +314,7 @@ void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx) SCEnter(); for (uint8_t flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); AppLayerParserDestroyProtocolParserLocalStorage( @@ -1688,7 +1688,7 @@ static void ValidateParser(AppProto alproto) static void ValidateParsers(void) { AppProto p = 0; - for ( ; p < ALPROTO_MAX; p++) { + for (; p < AlprotoMax; p++) { ValidateParser(p); } } @@ -1788,7 +1788,7 @@ void AppLayerParserRegisterUnittests(void) AppLayerParserProtoCtx *ctx; for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { - for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (alproto = 0; alproto < AlprotoMax; alproto++) { ctx = &alp_ctx.ctxs[alproto][ip]; if (ctx->RegisterUnittests == NULL) continue; diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 999e4c52322e..e97d94ce12af 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -24,53 +24,18 @@ #include "suricata-common.h" #include "app-layer-protos.h" +#include "rust.h" + +AppProto AlprotoMax = ALPROTO_MAX_STATIC + 1; +#define ARRAY_CAP_STEP 16 +AppProto AppProtoStringsCap = ALPROTO_MAX_STATIC + 1; typedef struct AppProtoStringTuple { AppProto alproto; const char *str; } AppProtoStringTuple; -const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { - { ALPROTO_UNKNOWN, "unknown" }, - { ALPROTO_FAILED, "failed" }, - { ALPROTO_HTTP1, "http1" }, - { ALPROTO_FTP, "ftp" }, - { ALPROTO_SMTP, "smtp" }, - { ALPROTO_TLS, "tls" }, - { ALPROTO_SSH, "ssh" }, - { ALPROTO_IMAP, "imap" }, - { ALPROTO_JABBER, "jabber" }, - { ALPROTO_SMB, "smb" }, - { ALPROTO_DCERPC, "dcerpc" }, - { ALPROTO_IRC, "irc" }, - { ALPROTO_DNS, "dns" }, - { ALPROTO_MODBUS, "modbus" }, - { ALPROTO_ENIP, "enip" }, - { ALPROTO_DNP3, "dnp3" }, - { ALPROTO_NFS, "nfs" }, - { ALPROTO_NTP, "ntp" }, - { ALPROTO_FTPDATA, "ftp-data" }, - { ALPROTO_TFTP, "tftp" }, - { ALPROTO_IKE, "ike" }, - { ALPROTO_KRB5, "krb5" }, - { ALPROTO_QUIC, "quic" }, - { ALPROTO_DHCP, "dhcp" }, - { ALPROTO_SNMP, "snmp" }, - { ALPROTO_SIP, "sip" }, - { ALPROTO_RFB, "rfb" }, - { ALPROTO_MQTT, "mqtt" }, - { ALPROTO_PGSQL, "pgsql" }, - { ALPROTO_TELNET, "telnet" }, - { ALPROTO_WEBSOCKET, "websocket" }, - { ALPROTO_LDAP, "ldap" }, - { ALPROTO_DOH2, "doh2" }, - { ALPROTO_TEMPLATE, "template" }, - { ALPROTO_RDP, "rdp" }, - { ALPROTO_HTTP2, "http2" }, - { ALPROTO_BITTORRENT_DHT, "bittorrent-dht" }, - { ALPROTO_POP3, "pop3" }, - { ALPROTO_HTTP, "http" }, -}; +AppProtoStringTuple *AppProtoStrings = NULL; const char *AppProtoToString(AppProto alproto) { @@ -84,7 +49,7 @@ const char *AppProtoToString(AppProto alproto) proto_name = "http_any"; break; default: - if (alproto < ARRAY_SIZE(AppProtoStrings)) { + if (alproto < AlprotoMax) { BUG_ON(AppProtoStrings[alproto].alproto != alproto); proto_name = AppProtoStrings[alproto].str; } @@ -98,10 +63,35 @@ AppProto StringToAppProto(const char *proto_name) return ALPROTO_UNKNOWN; // We could use a Multi Pattern Matcher - for (size_t i = 0; i < ARRAY_SIZE(AppProtoStrings); i++) { + for (size_t i = 0; i < AlprotoMax; i++) { if (strcmp(proto_name, AppProtoStrings[i].str) == 0) return AppProtoStrings[i].alproto; } return ALPROTO_UNKNOWN; } + +void AppProtoRegisterProtoString(AppProto alproto, const char *proto_name) +{ + if (alproto < ALPROTO_MAX_STATIC) { + if (AppProtoStrings == NULL) { + AppProtoStrings = SCCalloc(AppProtoStringsCap, sizeof(AppProtoStringTuple)); + if (AppProtoStrings == NULL) { + FatalError("Unable to allocate AppProtoStrings"); + } + } + } else if (alproto + 1 == AlprotoMax) { + if (AlprotoMax == AppProtoStringsCap) { + void *tmp = SCRealloc(AppProtoStrings, + sizeof(AppProtoStringTuple) * (AppProtoStringsCap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + FatalError("Unable to reallocate AppProtoStrings"); + } + AppProtoStringsCap += ARRAY_CAP_STEP; + AppProtoStrings = tmp; + } + AlprotoMax++; + } + AppProtoStrings[alproto].str = proto_name; + AppProtoStrings[alproto].alproto = alproto; +} diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 6515571c68b7..79942c8f6b70 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -75,16 +75,17 @@ enum AppProtoEnum { ALPROTO_HTTP, /* keep last */ - ALPROTO_MAX, + ALPROTO_MAX_STATIC, }; // NOTE: if ALPROTO's get >= 256, update SignatureNonPrefilterStore /* not using the enum as that is a unsigned int, so 4 bytes */ typedef uint16_t AppProto; +extern AppProto AlprotoMax; static inline bool AppProtoIsValid(AppProto a) { - return ((a > ALPROTO_FAILED && a < ALPROTO_MAX)); + return ((a > ALPROTO_FAILED && a < AlprotoMax)); } // whether a signature AppProto matches a flow (or signature) AppProto @@ -172,4 +173,6 @@ const char *AppProtoToString(AppProto alproto); */ AppProto StringToAppProto(const char *proto_name); +void AppProtoRegisterProtoString(AppProto alproto, const char *proto_name); + #endif /* SURICATA_APP_LAYER_PROTOS_H */ diff --git a/src/app-layer.c b/src/app-layer.c index bd5920ab5c52..61359c153195 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -1013,12 +1013,12 @@ void AppLayerListSupportedProtocols(void) SCEnter(); AppProto alproto; - AppProto alprotos[ALPROTO_MAX]; + AppProto alprotos[AlprotoMax]; AppLayerProtoDetectSupportedAppProtocols(alprotos); printf("=========Supported App Layer Protocols=========\n"); - for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (alproto = 0; alproto < AlprotoMax; alproto++) { if (alprotos[alproto] == 1) printf("%s\n", AppLayerGetProtoName(alproto)); } @@ -1027,11 +1027,54 @@ void AppLayerListSupportedProtocols(void) } /***** Setup/General Registration *****/ +static void AppLayerNamesSetup(void) +{ + AppProtoRegisterProtoString(ALPROTO_UNKNOWN, "unknown"); + AppProtoRegisterProtoString(ALPROTO_FAILED, "failed"); + AppProtoRegisterProtoString(ALPROTO_HTTP1, "http1"); + AppProtoRegisterProtoString(ALPROTO_FTP, "ftp"); + AppProtoRegisterProtoString(ALPROTO_SMTP, "smtp"); + AppProtoRegisterProtoString(ALPROTO_TLS, "tls"); + AppProtoRegisterProtoString(ALPROTO_SSH, "ssh"); + AppProtoRegisterProtoString(ALPROTO_IMAP, "imap"); + AppProtoRegisterProtoString(ALPROTO_JABBER, "jabber"); + AppProtoRegisterProtoString(ALPROTO_SMB, "smb"); + AppProtoRegisterProtoString(ALPROTO_DCERPC, "dcerpc"); + AppProtoRegisterProtoString(ALPROTO_IRC, "irc"); + AppProtoRegisterProtoString(ALPROTO_DNS, "dns"); + AppProtoRegisterProtoString(ALPROTO_MODBUS, "modbus"); + AppProtoRegisterProtoString(ALPROTO_ENIP, "enip"); + AppProtoRegisterProtoString(ALPROTO_DNP3, "dnp3"); + AppProtoRegisterProtoString(ALPROTO_NFS, "nfs"); + AppProtoRegisterProtoString(ALPROTO_NTP, "ntp"); + AppProtoRegisterProtoString(ALPROTO_FTPDATA, "ftp-data"); + AppProtoRegisterProtoString(ALPROTO_TFTP, "tftp"); + AppProtoRegisterProtoString(ALPROTO_IKE, "ike"); + AppProtoRegisterProtoString(ALPROTO_KRB5, "krb5"); + AppProtoRegisterProtoString(ALPROTO_QUIC, "quic"); + AppProtoRegisterProtoString(ALPROTO_DHCP, "dhcp"); + AppProtoRegisterProtoString(ALPROTO_SNMP, "snmp"); + AppProtoRegisterProtoString(ALPROTO_SIP, "sip"); + AppProtoRegisterProtoString(ALPROTO_RFB, "rfb"); + AppProtoRegisterProtoString(ALPROTO_MQTT, "mqtt"); + AppProtoRegisterProtoString(ALPROTO_PGSQL, "pgsql"); + AppProtoRegisterProtoString(ALPROTO_TELNET, "telnet"); + AppProtoRegisterProtoString(ALPROTO_WEBSOCKET, "websocket"); + AppProtoRegisterProtoString(ALPROTO_LDAP, "ldap"); + AppProtoRegisterProtoString(ALPROTO_DOH2, "doh2"); + AppProtoRegisterProtoString(ALPROTO_TEMPLATE, "template"); + AppProtoRegisterProtoString(ALPROTO_RDP, "rdp"); + AppProtoRegisterProtoString(ALPROTO_HTTP2, "http2"); + AppProtoRegisterProtoString(ALPROTO_BITTORRENT_DHT, "bittorrent-dht"); + AppProtoRegisterProtoString(ALPROTO_POP3, "pop3"); + AppProtoRegisterProtoString(ALPROTO_HTTP, "http"); +} int AppLayerSetup(void) { SCEnter(); + AppLayerNamesSetup(); AppLayerProtoDetectSetup(); AppLayerParserSetup(); @@ -1144,16 +1187,16 @@ static void AppLayerSetupExceptionPolicyPerProtoCounters( void AppLayerSetupCounters(void) { const uint8_t ipprotos[] = { IPPROTO_TCP, IPPROTO_UDP }; - AppProto alprotos[ALPROTO_MAX]; + AppProto alprotos[AlprotoMax]; const char *str = "app_layer.flow."; const char *estr = "app_layer.error."; applayer_counter_names = - SCCalloc(ALPROTO_MAX, sizeof(AppLayerCounterNames[FLOW_PROTO_APPLAYER_MAX])); + SCCalloc(AlprotoMax, sizeof(AppLayerCounterNames[FLOW_PROTO_APPLAYER_MAX])); if (unlikely(applayer_counter_names == NULL)) { FatalError("Unable to alloc applayer_counter_names."); } - applayer_counters = SCCalloc(ALPROTO_MAX, sizeof(AppLayerCounters[FLOW_PROTO_APPLAYER_MAX])); + applayer_counters = SCCalloc(AlprotoMax, sizeof(AppLayerCounters[FLOW_PROTO_APPLAYER_MAX])); if (unlikely(applayer_counters == NULL)) { FatalError("Unable to alloc applayer_counters."); } @@ -1178,7 +1221,7 @@ void AppLayerSetupCounters(void) const char *ipproto_suffix = (ipproto == IPPROTO_TCP) ? "_tcp" : "_udp"; uint8_t ipprotos_all[256 / 8]; - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { if (alprotos[alproto] == 1) { const char *tx_str = "app_layer.tx."; const char *alproto_str = AppLayerGetProtoName(alproto); @@ -1253,7 +1296,7 @@ void AppLayerSetupCounters(void) void AppLayerRegisterThreadCounters(ThreadVars *tv) { const uint8_t ipprotos[] = { IPPROTO_TCP, IPPROTO_UDP }; - AppProto alprotos[ALPROTO_MAX]; + AppProto alprotos[AlprotoMax]; AppLayerProtoDetectSupportedAppProtocols(alprotos); /* We don't log stats counters if exception policy is `ignore`/`not set` */ @@ -1271,7 +1314,7 @@ void AppLayerRegisterThreadCounters(ThreadVars *tv) const uint8_t ipproto = ipprotos[p]; const uint8_t ipproto_map = FlowGetProtoMapping(ipproto); - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { if (alprotos[alproto] == 1) { applayer_counters[alproto][ipproto_map].counter_id = StatsRegisterCounter(applayer_counter_names[alproto][ipproto_map].name, tv); diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index c85f53d5051d..05ab4ff88dd8 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -625,10 +625,11 @@ static json_t *RulesGroupPrintSghStats(const DetectEngineCtx *de_ctx, const SigG } mpm_stats[max_buffer_type_id]; memset(mpm_stats, 0x00, sizeof(mpm_stats)); - uint32_t alstats[ALPROTO_MAX] = {0}; + uint32_t alstats[AlprotoMax]; + memset(alstats, 0, AlprotoMax * sizeof(uint32_t)); uint32_t mpm_sizes[max_buffer_type_id][256]; memset(mpm_sizes, 0, sizeof(mpm_sizes)); - uint32_t alproto_mpm_bufs[ALPROTO_MAX][max_buffer_type_id]; + uint32_t alproto_mpm_bufs[AlprotoMax][max_buffer_type_id]; memset(alproto_mpm_bufs, 0, sizeof(alproto_mpm_bufs)); DEBUG_VALIDATE_BUG_ON(sgh->init == NULL); @@ -790,7 +791,7 @@ static json_t *RulesGroupPrintSghStats(const DetectEngineCtx *de_ctx, const SigG json_object_set_new(types, "any5", json_integer(any5_cnt)); json_object_set_new(stats, "types", types); - for (AppProto i = 0; i < ALPROTO_MAX; i++) { + for (AppProto i = 0; i < AlprotoMax; i++) { if (alstats[i] > 0) { json_t *app = json_object(); json_object_set_new(app, "total", json_integer(alstats[i])); diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 5e8687e34686..b1175b24915b 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -1972,7 +1972,7 @@ static void PrepareMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh) const int max_buffer_id = de_ctx->buffer_type_id + 1; const uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx) / 8 + 1; - AppProto engines[max_buffer_id][ALPROTO_MAX]; + AppProto engines[max_buffer_id][AlprotoMax]; memset(engines, 0, sizeof(engines)); int engines_idx[max_buffer_id]; memset(engines_idx, 0, sizeof(engines_idx)); diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index e5bdbfd3d792..fef89e7c66bf 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -521,7 +521,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) /* per alproto to set is_last_for_progress per alproto because the inspect * loop skips over engines that are not the correct alproto */ - for (AppProto a = ALPROTO_FAILED + 1; a < ALPROTO_MAX; a++) { + for (AppProto a = ALPROTO_FAILED + 1; a < AlprotoMax; a++) { int last_tx_progress = 0; bool last_tx_progress_set = false; PrefilterEngine *prev_engine = NULL; diff --git a/src/detect-file-data.c b/src/detect-file-data.c index a721c08c7cf9..90f305cc9013 100644 --- a/src/detect-file-data.c +++ b/src/detect-file-data.c @@ -97,11 +97,11 @@ static void SetupDetectEngineConfig(DetectEngineCtx *de_ctx) { if (de_ctx->filedata_config) return; - de_ctx->filedata_config = SCMalloc(ALPROTO_MAX * sizeof(DetectFileDataCfg)); + de_ctx->filedata_config = SCMalloc(AlprotoMax * sizeof(DetectFileDataCfg)); if (unlikely(de_ctx->filedata_config == NULL)) return; /* initialize default */ - for (AppProto i = 0; i < ALPROTO_MAX; i++) { + for (AppProto i = 0; i < AlprotoMax; i++) { de_ctx->filedata_config[i].content_limit = FILEDATA_CONTENT_LIMIT; de_ctx->filedata_config[i].content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE; } diff --git a/src/output-tx.c b/src/output-tx.c index 40b887706770..3cf06d6b5935 100644 --- a/src/output-tx.c +++ b/src/output-tx.c @@ -67,7 +67,7 @@ int SCOutputRegisterTxLogger(LoggerId id, const char *name, AppProto alproto, Tx ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit) { if (list == NULL) { - list = SCCalloc(ALPROTO_MAX, sizeof(OutputTxLogger *)); + list = SCCalloc(AlprotoMax, sizeof(OutputTxLogger *)); if (unlikely(list == NULL)) { SCLogError("Failed to allocate OutputTx list"); return -1; @@ -542,14 +542,14 @@ static TmEcode OutputTxLog(ThreadVars *tv, Packet *p, void *thread_data) static TmEcode OutputTxLogThreadInit(ThreadVars *tv, const void *_initdata, void **data) { OutputTxLoggerThreadData *td = - SCCalloc(1, sizeof(*td) + ALPROTO_MAX * sizeof(OutputLoggerThreadStore *)); + SCCalloc(1, sizeof(*td) + AlprotoMax * sizeof(OutputLoggerThreadStore *)); if (td == NULL) return TM_ECODE_FAILED; *data = (void *)td; SCLogDebug("OutputTxLogThreadInit happy (*data %p)", *data); - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { OutputTxLogger *logger = list[alproto]; while (logger) { if (logger->ThreadInit) { @@ -598,7 +598,7 @@ static TmEcode OutputTxLogThreadDeinit(ThreadVars *tv, void *thread_data) { OutputTxLoggerThreadData *op_thread_data = (OutputTxLoggerThreadData *)thread_data; - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { OutputLoggerThreadStore *store = op_thread_data->store[alproto]; OutputTxLogger *logger = list[alproto]; @@ -628,7 +628,7 @@ static TmEcode OutputTxLogThreadDeinit(ThreadVars *tv, void *thread_data) static uint32_t OutputTxLoggerGetActiveCount(void) { uint32_t cnt = 0; - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { for (OutputTxLogger *p = list[alproto]; p != NULL; p = p->next) { cnt++; } @@ -650,7 +650,7 @@ static uint32_t OutputTxLoggerGetActiveCount(void) void OutputTxLoggerRegister (void) { BUG_ON(list); - list = SCCalloc(ALPROTO_MAX, sizeof(OutputTxLogger *)); + list = SCCalloc(AlprotoMax, sizeof(OutputTxLogger *)); if (unlikely(list == NULL)) { FatalError("Failed to allocate OutputTx list"); } @@ -664,7 +664,7 @@ void OutputTxShutdown(void) if (list == NULL) { return; } - for (AppProto alproto = 0; alproto < ALPROTO_MAX; alproto++) { + for (AppProto alproto = 0; alproto < AlprotoMax; alproto++) { OutputTxLogger *logger = list[alproto]; while (logger) { OutputTxLogger *next_logger = logger->next; diff --git a/src/output.c b/src/output.c index 002f33b5abc6..63dcd9eaa132 100644 --- a/src/output.c +++ b/src/output.c @@ -835,7 +835,7 @@ void TmModuleLoggerRegister(void) EveJsonSimpleAppLayerLogger *SCEveJsonSimpleGetLogger(AppProto alproto) { - if (alproto < ALPROTO_MAX) { + if (alproto < AlprotoMax) { return &simple_json_applayer_loggers[alproto]; } return NULL; @@ -857,7 +857,7 @@ static void RegisterSimpleJsonApplayerLogger( */ void OutputRegisterRootLoggers(void) { - simple_json_applayer_loggers = SCCalloc(ALPROTO_MAX, sizeof(EveJsonSimpleAppLayerLogger)); + simple_json_applayer_loggers = SCCalloc(AlprotoMax, sizeof(EveJsonSimpleAppLayerLogger)); if (unlikely(simple_json_applayer_loggers == NULL)) { FatalError("Failed to allocate simple_json_applayer_loggers"); } diff --git a/src/runmodes.c b/src/runmodes.c index b326a96e3a67..0306fd6a1a01 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -757,8 +757,9 @@ void RunModeInitializeOutputs(void) char tls_log_enabled = 0; char tls_store_present = 0; - // ALPROTO_MAX is set to its final value - LoggerId logger_bits[ALPROTO_MAX] = { 0 }; + // AlprotoMax is set to its final value + LoggerId logger_bits[AlprotoMax]; + memset(logger_bits, 0, AlprotoMax * sizeof(LoggerId)); TAILQ_FOREACH(output, &outputs->head, next) { output_config = ConfNodeLookupChild(output, output->val); @@ -884,7 +885,7 @@ void RunModeInitializeOutputs(void) /* register the logger bits to the app-layer */ AppProto a; - for (a = 0; a < ALPROTO_MAX; a++) { + for (a = 0; a < AlprotoMax; a++) { if (AppLayerParserSupportsFiles(IPPROTO_TCP, a)) { if (g_file_logger_enabled) logger_bits[a] |= BIT_U32(LOGGER_FILE); @@ -919,7 +920,6 @@ void RunModeInitializeOutputs(void) AppLayerParserRegisterLoggerBits(IPPROTO_TCP, a, logger_bits[a]); if (udp) AppLayerParserRegisterLoggerBits(IPPROTO_UDP, a, logger_bits[a]); - } OutputSetupActiveLoggers(); } diff --git a/src/tests/fuzz/fuzz_applayerparserparse.c b/src/tests/fuzz/fuzz_applayerparserparse.c index 5e71243e047f..bd2ecfae6ddb 100644 --- a/src/tests/fuzz/fuzz_applayerparserparse.c +++ b/src/tests/fuzz/fuzz_applayerparserparse.c @@ -96,7 +96,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) return 0; } - if (data[0] >= ALPROTO_MAX) { + if (data[0] >= AlprotoMax) { return 0; } //no UTHBuildFlow to have storage diff --git a/src/util-profiling.c b/src/util-profiling.c index 73e0c8489057..04ff786f726c 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -158,11 +158,11 @@ SCProfilingInit(void) memset(&packet_profile_data6, 0, sizeof(packet_profile_data6)); memset(&packet_profile_tmm_data4, 0, sizeof(packet_profile_tmm_data4)); memset(&packet_profile_tmm_data6, 0, sizeof(packet_profile_tmm_data6)); - packet_profile_app_data4 = SCCalloc(ALPROTO_MAX * 257, sizeof(SCProfilePacketData)); + packet_profile_app_data4 = SCCalloc(AlprotoMax * 257, sizeof(SCProfilePacketData)); if (packet_profile_app_data4 == NULL) { FatalError("Failed to allocate packet_profile_app_data4"); } - packet_profile_app_data6 = SCCalloc(ALPROTO_MAX * 257, sizeof(SCProfilePacketData)); + packet_profile_app_data6 = SCCalloc(AlprotoMax * 257, sizeof(SCProfilePacketData)); if (packet_profile_app_data6 == NULL) { FatalError("Failed to allocate packet_profile_app_data6"); } @@ -503,7 +503,7 @@ void SCProfilingDumpPacketStats(void) "--------------------", "------", "-----", "----------", "------------", "------------", "-----------"); total = 0; - for (AppProto a = 0; a < ALPROTO_MAX; a++) { + for (AppProto a = 0; a < AlprotoMax; a++) { for (int p = 0; p < 257; p++) { SCProfilePacketData *pd = &packet_profile_app_data4[a * 257 + p]; total += pd->tot; @@ -512,7 +512,7 @@ void SCProfilingDumpPacketStats(void) total += pd->tot; } } - for (AppProto a = 0; a < ALPROTO_MAX; a++) { + for (AppProto a = 0; a < AlprotoMax; a++) { for (int p = 0; p < 257; p++) { SCProfilePacketData *pd = &packet_profile_app_data4[a * 257 + p]; if (pd->cnt == 0) { @@ -531,7 +531,7 @@ void SCProfilingDumpPacketStats(void) } } - for (AppProto a = 0; a < ALPROTO_MAX; a++) { + for (AppProto a = 0; a < AlprotoMax; a++) { for (int p = 0; p < 257; p++) { SCProfilePacketData *pd = &packet_profile_app_data6[a * 257 + p]; if (pd->cnt == 0) { @@ -820,7 +820,7 @@ void SCProfilingPrintPacketProfile(Packet *p) /* count ticks for app layer */ uint64_t app_total = 0; - for (AppProto i = 0; i < ALPROTO_MAX; i++) { + for (AppProto i = 0; i < AlprotoMax; i++) { const PktProfilingAppData *pdt = &p->profile->app[i]; if (p->proto == IPPROTO_TCP) { @@ -951,7 +951,7 @@ static void SCProfilingUpdatePacketAppRecord(int alproto, uint8_t ipproto, PktPr static void SCProfilingUpdatePacketAppRecords(Packet *p) { int i; - for (i = 0; i < ALPROTO_MAX; i++) { + for (i = 0; i < AlprotoMax; i++) { PktProfilingAppData *pdt = &p->profile->app[i]; if (pdt->ticks_spent > 0) { @@ -1199,7 +1199,7 @@ PktProfiling *SCProfilePacketStart(void) { uint64_t sample = SC_ATOMIC_ADD(samples, 1); if (sample % rate == 0) - return SCCalloc(1, sizeof(PktProfiling) + ALPROTO_MAX * sizeof(PktProfilingAppData)); + return SCCalloc(1, sizeof(PktProfiling) + AlprotoMax * sizeof(PktProfilingAppData)); return NULL; } diff --git a/src/util-profiling.h b/src/util-profiling.h index 1c334bb34f27..284adcced795 100644 --- a/src/util-profiling.h +++ b/src/util-profiling.h @@ -203,12 +203,12 @@ PktProfiling *SCProfilePacketStart(void); (dp)->proto_detect_ticks_spent = 0; \ } -#define PACKET_PROFILING_APP_STORE(dp, p) \ - if (profiling_packets_enabled && (p)->profile != NULL) { \ - if ((dp)->alproto < ALPROTO_MAX) { \ - (p)->profile->app[(dp)->alproto].ticks_spent += (dp)->ticks_spent; \ - (p)->profile->proto_detect += (dp)->proto_detect_ticks_spent; \ - } \ +#define PACKET_PROFILING_APP_STORE(dp, p) \ + if (profiling_packets_enabled && (p)->profile != NULL) { \ + if ((dp)->alproto < AlprotoMax) { \ + (p)->profile->app[(dp)->alproto].ticks_spent += (dp)->ticks_spent; \ + (p)->profile->proto_detect += (dp)->proto_detect_ticks_spent; \ + } \ } #define PACKET_PROFILING_DETECT_START(p, id) \ From c3a5008066d4f7c8230cc361e48e0031bad336ab Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 16 Nov 2023 14:35:49 +0100 Subject: [PATCH 15/17] plugins: app-layer plugins Ticket: 5053 --- src/app-layer-parser.c | 24 +++++++++++ src/app-layer-parser.h | 1 + src/app-layer-protos.c | 6 +-- src/detect-engine-file.h | 3 ++ src/detect-engine-register.c | 25 +++++++++++ src/detect-engine-register.h | 1 + src/detect-parse.c | 81 +++++++++++++++++++++++------------- src/output.c | 33 +++++++++++++++ src/output.h | 9 ++++ src/suricata-plugin.h | 11 +++++ src/util-plugin.c | 33 +++++++++++++++ 11 files changed, 196 insertions(+), 31 deletions(-) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 9384864f525b..4bf99cf9196c 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1693,6 +1693,27 @@ static void ValidateParsers(void) } } +#define ARRAY_CAP_STEP 16 +static void (**preregistered_callbacks)(void) = NULL; +static size_t preregistered_callbacks_nb = 0; +static size_t preregistered_callbacks_cap = 0; + +int AppLayerParserPreRegister(void (*Register)(void)) +{ + if (preregistered_callbacks_nb == preregistered_callbacks_cap) { + void *tmp = SCRealloc(preregistered_callbacks, + sizeof(void *) * (preregistered_callbacks_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_callbacks_cap += ARRAY_CAP_STEP; + preregistered_callbacks = tmp; + } + preregistered_callbacks[preregistered_callbacks_nb] = Register; + preregistered_callbacks_nb++; + return 0; +} + void AppLayerParserRegisterProtocolParsers(void) { SCEnter(); @@ -1745,6 +1766,9 @@ void AppLayerParserRegisterProtocolParsers(void) } else { SCLogInfo("Protocol detection and parser disabled for pop3 protocol."); } + for (size_t i = 0; i < preregistered_callbacks_nb; i++) { + preregistered_callbacks[i](); + } ValidateParsers(); } diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index 58ad4333563c..d233edf9eb1f 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -158,6 +158,7 @@ typedef AppLayerGetTxIterTuple (*AppLayerGetTxIteratorFunc) typedef int (*AppLayerParserGetFrameIdByNameFn)(const char *frame_name); typedef const char *(*AppLayerParserGetFrameNameByIdFn)(const uint8_t id); +int AppLayerParserPreRegister(void (*Register)(void)); /** * \brief Register app layer parser for the protocol. * diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index e97d94ce12af..710f698fc725 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -26,9 +26,9 @@ #include "app-layer-protos.h" #include "rust.h" -AppProto AlprotoMax = ALPROTO_MAX_STATIC + 1; +AppProto AlprotoMax = ALPROTO_MAX_STATIC; #define ARRAY_CAP_STEP 16 -AppProto AppProtoStringsCap = ALPROTO_MAX_STATIC + 1; +AppProto AppProtoStringsCap = ALPROTO_MAX_STATIC; typedef struct AppProtoStringTuple { AppProto alproto; @@ -80,7 +80,7 @@ void AppProtoRegisterProtoString(AppProto alproto, const char *proto_name) FatalError("Unable to allocate AppProtoStrings"); } } - } else if (alproto + 1 == AlprotoMax) { + } else if (alproto == AlprotoMax) { if (AlprotoMax == AppProtoStringsCap) { void *tmp = SCRealloc(AppProtoStrings, sizeof(AppProtoStringTuple) * (AppProtoStringsCap + ARRAY_CAP_STEP)); diff --git a/src/detect-engine-file.h b/src/detect-engine-file.h index 13aa7465f436..10bcf5ca7dbc 100644 --- a/src/detect-engine-file.h +++ b/src/detect-engine-file.h @@ -28,4 +28,7 @@ uint8_t DetectFileInspectGeneric(DetectEngineCtx *de_ctx, DetectEngineThreadCtx const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f, uint8_t flags, void *_alstate, void *tx, uint64_t tx_id); +void DetectFileRegisterProto( + AppProto alproto, int direction, int to_client_progress, int to_server_progress); + #endif /* SURICATA_DETECT_ENGINE_FILE_H */ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 37fbc98d8597..8f74c6164bd1 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -439,6 +439,27 @@ void SigTableCleanup(void) } } +#define ARRAY_CAP_STEP 16 +static void (**preregistered_callbacks)(void) = NULL; +static size_t preregistered_callbacks_nb = 0; +static size_t preregistered_callbacks_cap = 0; + +int SigTablePreRegister(void (*KeywordsRegister)(void)) +{ + if (preregistered_callbacks_nb == preregistered_callbacks_cap) { + void *tmp = SCRealloc(preregistered_callbacks, + sizeof(void *) * (preregistered_callbacks_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_callbacks_cap += ARRAY_CAP_STEP; + preregistered_callbacks = tmp; + } + preregistered_callbacks[preregistered_callbacks_nb] = KeywordsRegister; + preregistered_callbacks_nb++; + return 0; +} + void SigTableSetup(void) { if (sigmatch_table == NULL) { @@ -684,6 +705,10 @@ void SigTableSetup(void) ScDetectSipRegister(); ScDetectTemplateRegister(); + for (size_t i = 0; i < preregistered_callbacks_nb; i++) { + preregistered_callbacks[i](); + } + /* close keyword registration */ DetectBufferTypeCloseRegistration(); } diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index c9134c77b83a..dc7a35ee7d69 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -339,6 +339,7 @@ extern int DETECT_TBLSIZE_IDX; int SigTableList(const char *keyword); void SigTableCleanup(void); void SigTableSetup(void); +int SigTablePreRegister(void (*KeywordsRegister)(void)); void SigTableRegisterTests(void); #endif /* SURICATA_DETECT_ENGINE_REGISTER_H */ diff --git a/src/detect-parse.c b/src/detect-parse.c index a7da0b25f448..7679a67382da 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -68,55 +68,80 @@ #include "string.h" #include "detect-parse.h" #include "detect-engine-iponly.h" +#include "detect-engine-file.h" #include "app-layer-detect-proto.h" #include "action-globals.h" #include "util-validate.h" +// file protocols with common file handling +typedef struct { + AppProto alproto; + int direction; + int to_client_progress; + int to_server_progress; +} DetectFileHandlerProtocol_t; + /* Table with all filehandler registrations */ DetectFileHandlerTableElmt filehandler_table[DETECT_TBLSIZE_STATIC]; +#define ALPROTO_WITHFILES_MAX 16 + +// file protocols with common file handling +DetectFileHandlerProtocol_t al_protocols[ALPROTO_WITHFILES_MAX] = { + { .alproto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, + { .alproto = ALPROTO_HTTP1, + .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, + .to_client_progress = HTP_RESPONSE_BODY, + .to_server_progress = HTP_REQUEST_BODY }, + { .alproto = ALPROTO_HTTP2, + .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, + .to_client_progress = HTTP2StateDataServer, + .to_server_progress = HTTP2StateDataClient }, + { .alproto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER }, { .alproto = ALPROTO_UNKNOWN } +}; + +void DetectFileRegisterProto( + AppProto alproto, int direction, int to_client_progress, int to_server_progress) +{ + size_t i = 0; + while (i < ALPROTO_WITHFILES_MAX && al_protocols[i].alproto != ALPROTO_UNKNOWN) { + i++; + } + if (i == ALPROTO_WITHFILES_MAX) { + return; + } + al_protocols[i].alproto = alproto; + al_protocols[i].direction = direction; + al_protocols[i].to_client_progress = to_client_progress; + al_protocols[i].to_server_progress = to_server_progress; + al_protocols[i + 1].alproto = ALPROTO_UNKNOWN; +} + void DetectFileRegisterFileProtocols(DetectFileHandlerTableElmt *reg) { - // file protocols with common file handling - typedef struct { - AppProto al_proto; - int direction; - int to_client_progress; - int to_server_progress; - } DetectFileHandlerProtocol_t; - static DetectFileHandlerProtocol_t al_protocols[] = { - { .al_proto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT }, - { .al_proto = ALPROTO_HTTP1, - .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, - .to_client_progress = HTP_RESPONSE_BODY, - .to_server_progress = HTP_REQUEST_BODY }, - { .al_proto = ALPROTO_HTTP2, - .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT, - .to_client_progress = HTTP2StateDataServer, - .to_server_progress = HTTP2StateDataClient }, - { .al_proto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER } - }; - - for (size_t i = 0; i < ARRAY_SIZE(al_protocols); i++) { + for (size_t i = 0; i < AlprotoMax; i++) { + if (al_protocols[i].alproto == ALPROTO_UNKNOWN) { + break; + } int direction = al_protocols[i].direction == 0 ? (int)(SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT) : al_protocols[i].direction; if (direction & SIG_FLAG_TOCLIENT) { DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOCLIENT, reg->priority, reg->PrefilterFn, - reg->GetData, al_protocols[i].al_proto, al_protocols[i].to_client_progress); - DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].al_proto, + reg->GetData, al_protocols[i].alproto, al_protocols[i].to_client_progress); + DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto, SIG_FLAG_TOCLIENT, al_protocols[i].to_client_progress, reg->Callback, reg->GetData); } if (direction & SIG_FLAG_TOSERVER) { DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOSERVER, reg->priority, reg->PrefilterFn, - reg->GetData, al_protocols[i].al_proto, al_protocols[i].to_server_progress); - DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].al_proto, + reg->GetData, al_protocols[i].alproto, al_protocols[i].to_server_progress); + DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto, SIG_FLAG_TOSERVER, al_protocols[i].to_server_progress, reg->Callback, reg->GetData); } diff --git a/src/output.c b/src/output.c index 63dcd9eaa132..1a5c90c14af0 100644 --- a/src/output.c +++ b/src/output.c @@ -949,6 +949,28 @@ static int JsonGenericDirFlowLogger(ThreadVars *tv, void *thread_data, const Pac return JsonGenericLogger(tv, thread_data, p, f, state, tx, tx_id, LOG_DIR_FLOW); } +#define ARRAY_CAP_STEP 16 +static EveJsonLoggerRegistrationData *preregistered_loggers = NULL; +static size_t preregistered_loggers_nb = 0; +static size_t preregistered_loggers_cap = 0; + +int OutputPreRegisterLogger(EveJsonLoggerRegistrationData reg_data) +{ + if (preregistered_loggers_nb == preregistered_loggers_cap) { + void *tmp = SCRealloc( + preregistered_loggers, sizeof(EveJsonLoggerRegistrationData) * + (preregistered_loggers_cap + ARRAY_CAP_STEP)); + if (tmp == NULL) { + return 1; + } + preregistered_loggers_cap += ARRAY_CAP_STEP; + preregistered_loggers = tmp; + } + preregistered_loggers[preregistered_loggers_nb] = reg_data; + preregistered_loggers_nb++; + return 0; +} + /** * \brief Register all non-root logging modules. */ @@ -1105,4 +1127,15 @@ void OutputRegisterLoggers(void) } /* ARP JSON logger */ JsonArpLogRegister(); + + for (size_t i = 0; i < preregistered_loggers_nb; i++) { + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", preregistered_loggers[i].logname, + preregistered_loggers[i].confname, OutputJsonLogInitSub, + preregistered_loggers[i].alproto, JsonGenericDirFlowLogger, JsonLogThreadInit, + JsonLogThreadDeinit); + SCLogNotice( + "%s JSON logger registered.", AppProtoToString(preregistered_loggers[i].alproto)); + RegisterSimpleJsonApplayerLogger( + preregistered_loggers[i].alproto, preregistered_loggers[i].LogTx, NULL); + } } diff --git a/src/output.h b/src/output.h index 43bd9d8b8f74..3820ae1665d8 100644 --- a/src/output.h +++ b/src/output.h @@ -170,4 +170,13 @@ typedef struct EveJsonSimpleAppLayerLogger { EveJsonSimpleAppLayerLogger *SCEveJsonSimpleGetLogger(AppProto alproto); +typedef struct EveJsonLoggerRegistrationData { + const char *confname; + const char *logname; + AppProto alproto; + EveJsonSimpleTxLogFunc LogTx; +} EveJsonLoggerRegistrationData; + +int OutputPreRegisterLogger(EveJsonLoggerRegistrationData reg_data); + #endif /* ! SURICATA_OUTPUT_H */ diff --git a/src/suricata-plugin.h b/src/suricata-plugin.h index 639dd7c7313e..c13187e6ca0d 100644 --- a/src/suricata-plugin.h +++ b/src/suricata-plugin.h @@ -52,4 +52,15 @@ typedef struct SCCapturePlugin_ { int SCPluginRegisterCapture(SCCapturePlugin *); +typedef struct SCAppLayerPlugin_ { + char *name; + void (*Register)(void); + void (*KeywordsRegister)(void); + char *logname; + char *confname; + bool (*Logger)(void *tx, void *jb); +} SCAppLayerPlugin; + +int SCPluginRegisterAppLayer(SCAppLayerPlugin *); + #endif /* __SURICATA_PLUGIN_H */ diff --git a/src/util-plugin.c b/src/util-plugin.c index 7a9b467daaac..f1720e7e3f9a 100644 --- a/src/util-plugin.c +++ b/src/util-plugin.c @@ -25,6 +25,11 @@ #ifdef HAVE_PLUGINS +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "detect-engine-register.h" +#include "output.h" + #include typedef struct PluginListNode_ { @@ -148,4 +153,32 @@ SCCapturePlugin *SCPluginFindCaptureByName(const char *name) } return plugin; } + +int SCPluginRegisterAppLayer(SCAppLayerPlugin *plugin) +{ + AppProto alproto = AlprotoMax; + AppProtoRegisterProtoString(alproto, plugin->name); + if (plugin->Register) { + if (AppLayerParserPreRegister(plugin->Register) != 0) { + return 1; + } + } + if (plugin->KeywordsRegister) { + if (SigTablePreRegister(plugin->KeywordsRegister) != 0) { + return 1; + } + } + if (plugin->Logger) { + EveJsonLoggerRegistrationData reg_data = { + .confname = plugin->confname, + .logname = plugin->logname, + .alproto = alproto, + .LogTx = (EveJsonSimpleTxLogFunc)plugin->Logger, + }; + if (OutputPreRegisterLogger(reg_data) != 0) { + return 1; + } + } + return 0; +} #endif From 6e320fc91d052820e65f715454a9d9a3fc19510e Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 11 Nov 2024 08:52:28 +0100 Subject: [PATCH 16/17] app-layer: improve limits on number of probing parsers There was an implicit limit of 32 app-layer protocols used by probing parsers through a mask, meaning that Suricata should not support more than 32 app-layer protocols in total. This limit is relaxed to each flow not being able to run more than 32 probing parsers, meaning that for each source and destination port combination, the sum of registered probing parsers should not exceed 32, even if there are more than 32 in total. --- src/app-layer-detect-proto.c | 297 ++++++++++++++--------------------- 1 file changed, 117 insertions(+), 180 deletions(-) diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 5ae28b137bb5..dbf6eb0b55e8 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -67,8 +67,6 @@ typedef struct AppLayerProtoDetectProbingParserElement_ { AppProto alproto; - /* \todo calculate at runtime and get rid of this var */ - uint32_t alproto_mask; /* the min length of data that has to be supplied to invoke the parser */ uint16_t min_depth; /* the max length of data after which this parser won't be invoked */ @@ -90,8 +88,6 @@ typedef struct AppLayerProtoDetectProbingParserPort_ { // WebSocket has this set to false as it only works with protocol change bool use_ports; - uint32_t alproto_mask; - /* the max depth for all the probing parsers registered for this port */ uint16_t dp_max_depth; uint16_t sp_max_depth; @@ -480,11 +476,20 @@ static AppProto AppLayerProtoDetectPEGetProto(Flow *f, uint8_t flags) } static inline AppProto PPGetProto(const AppLayerProtoDetectProbingParserElement *pe, Flow *f, - uint8_t flags, const uint8_t *buf, uint32_t buflen, uint32_t *alproto_masks, uint8_t *rdir) + uint8_t flags, const uint8_t *buf, uint32_t buflen, uint32_t *alproto_masks, uint8_t *rdir, + uint8_t *nb_tried) { while (pe != NULL) { - if ((buflen < pe->min_depth) || - (alproto_masks[0] & pe->alproto_mask)) { + // callers make alproto_masks and nb_tried are either both defined or both NULL + if (alproto_masks != NULL) { + DEBUG_VALIDATE_BUG_ON(*nb_tried >= 32); + if (buflen < pe->min_depth || (alproto_masks[0] & BIT_U32(*nb_tried))) { + // skip if already failed once + pe = pe->next; + *nb_tried = *nb_tried + 1; + continue; + } + } else if (buflen < pe->min_depth) { pe = pe->next; continue; } @@ -498,9 +503,12 @@ static inline AppProto PPGetProto(const AppLayerProtoDetectProbingParserElement if (AppProtoIsValid(alproto)) { SCReturnUInt(alproto); } - if (alproto == ALPROTO_FAILED || - (pe->max_depth != 0 && buflen > pe->max_depth)) { - alproto_masks[0] |= pe->alproto_mask; + if (alproto_masks != NULL) { + if ((alproto == ALPROTO_FAILED || (pe->max_depth != 0 && buflen > pe->max_depth))) { + // This PE failed, mask it from now on + alproto_masks[0] |= BIT_U32(*nb_tried); + } + *nb_tried = *nb_tried + 1; } pe = pe->next; } @@ -524,8 +532,20 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f, const uint8_t *buf, uint3 const AppLayerProtoDetectProbingParserElement *pe1 = NULL; const AppLayerProtoDetectProbingParserElement *pe2 = NULL; AppProto alproto = ALPROTO_UNKNOWN; + // number of tried protocols : + // used against alproto_masks to see if al tried protocols failed + // Instead of keeping a bitmask for all protocols, we + // use only the protocols relevant to this flow, so as to + // have alproto_masks a u32 but we have more than 32 alprotos + // in Suricata, but we do not allow more than 32 probing parsers + // on one flow. + // alproto_masks is consistent throughout different calls here + // from different packets in the flow. + // We can have up to 4 calls to PPGetProto with a mask : + // destination port (probing parser), source port, + // and again with the reversed flow in case of midstream. + uint8_t nb_tried = 0; uint32_t *alproto_masks = NULL; - uint32_t mask = 0; uint8_t idir = (flags & (STREAM_TOSERVER | STREAM_TOCLIENT)); uint8_t dir = idir; uint16_t dp = f->protodetect_dp ? f->protodetect_dp : FLOW_GET_DP(f); @@ -607,34 +627,30 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f, const uint8_t *buf, uint3 /* run the parser(s): always call with original direction */ uint8_t rdir = 0; - alproto = PPGetProto(pe0, f, flags, buf, buflen, alproto_masks, &rdir); + // pe0 can change based on the flow state, do not use mask for it + alproto = PPGetProto(pe0, f, flags, buf, buflen, NULL, &rdir, NULL); if (AppProtoIsValid(alproto)) goto end; - alproto = PPGetProto(pe1, f, flags, buf, buflen, alproto_masks, &rdir); + alproto = PPGetProto(pe1, f, flags, buf, buflen, alproto_masks, &rdir, &nb_tried); if (AppProtoIsValid(alproto)) goto end; - alproto = PPGetProto(pe2, f, flags, buf, buflen, alproto_masks, &rdir); + alproto = PPGetProto(pe2, f, flags, buf, buflen, alproto_masks, &rdir, &nb_tried); if (AppProtoIsValid(alproto)) goto end; /* get the mask we need for this direction */ if (dir == idir) { - if (pp_port_dp && pp_port_sp) - mask = pp_port_dp->alproto_mask|pp_port_sp->alproto_mask; - else if (pp_port_dp) - mask = pp_port_dp->alproto_mask; - else if (pp_port_sp) - mask = pp_port_sp->alproto_mask; - - if (alproto_masks[0] == mask) { + // if we tried 3 protocols, we set probing parsing done if + // alproto_masks[0] = 7 = 0b111 = BIT_U32(3) - 1 = 1<<3 - 1 + if (alproto_masks[0] == BIT_U32(nb_tried) - 1) { FLOW_SET_PP_DONE(f, dir); SCLogDebug("%s, mask is now %08x, needed %08x, so done", - (dir == STREAM_TOSERVER) ? "toserver":"toclient", - alproto_masks[0], mask); + (dir == STREAM_TOSERVER) ? "toserver" : "toclient", alproto_masks[0], + BIT_U32(nb_tried) - 1); } else { SCLogDebug("%s, mask is now %08x, need %08x", - (dir == STREAM_TOSERVER) ? "toserver":"toclient", - alproto_masks[0], mask); + (dir == STREAM_TOSERVER) ? "toserver" : "toclient", alproto_masks[0], + BIT_U32(nb_tried) - 1); } } @@ -690,17 +706,6 @@ static void AppLayerProtoDetectPPGetIpprotos(AppProto alproto, SCReturn; } -static uint32_t AppLayerProtoDetectProbingParserGetMask(AppProto alproto) -{ - SCEnter(); - - if (!(alproto > ALPROTO_UNKNOWN && alproto < ALPROTO_FAILED)) { - FatalError("Unknown protocol detected - %u", alproto); - } - - SCReturnUInt(BIT_U32(alproto)); -} - static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParserElementAlloc(void) { SCEnter(); @@ -794,7 +799,6 @@ static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParser AppLayerProtoDetectProbingParserElement *pe = AppLayerProtoDetectProbingParserElementAlloc(); pe->alproto = alproto; - pe->alproto_mask = AppLayerProtoDetectProbingParserGetMask(alproto); pe->min_depth = min_depth; pe->max_depth = max_depth; pe->next = NULL; @@ -825,7 +829,6 @@ AppLayerProtoDetectProbingParserElementDuplicate(AppLayerProtoDetectProbingParse AppLayerProtoDetectProbingParserElement *new_pe = AppLayerProtoDetectProbingParserElementAlloc(); new_pe->alproto = pe->alproto; - new_pe->alproto_mask = pe->alproto_mask; new_pe->min_depth = pe->min_depth; new_pe->max_depth = pe->max_depth; new_pe->ProbingParserTs = pe->ProbingParserTs; @@ -859,15 +862,12 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar if (pp_port->dp != NULL) { printf(" Port: %"PRIu16 "\n", pp_port->port); - printf(" Destination port: (max-depth: %"PRIu16 ", " - "mask - %"PRIu32")\n", - pp_port->dp_max_depth, - pp_port->alproto_mask); + printf(" Destination port: (max-depth: %" PRIu16 ")\n", + pp_port->dp_max_depth); pp_pe = pp_port->dp; for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); - printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); @@ -879,15 +879,11 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar continue; } - printf(" Source port: (max-depth: %"PRIu16 ", " - "mask - %"PRIu32")\n", - pp_port->sp_max_depth, - pp_port->alproto_mask); + printf(" Source port: (max-depth: %" PRIu16 ")\n", pp_port->sp_max_depth); pp_pe = pp_port->sp; for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); - printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); @@ -1030,7 +1026,6 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing AppLayerProtoDetectProbingParserElement *dup_pe = AppLayerProtoDetectProbingParserElementDuplicate(zero_pe); AppLayerProtoDetectProbingParserElementAppend(&curr_port->dp, dup_pe); - curr_port->alproto_mask |= dup_pe->alproto_mask; } zero_pe = zero_port->sp; @@ -1047,7 +1042,6 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing AppLayerProtoDetectProbingParserElement *dup_pe = AppLayerProtoDetectProbingParserElementDuplicate(zero_pe); AppLayerProtoDetectProbingParserElementAppend(&curr_port->sp, dup_pe); - curr_port->alproto_mask |= dup_pe->alproto_mask; } } /* if (zero_port != NULL) */ } /* if (curr_port == NULL) */ @@ -1088,7 +1082,6 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing curr_port->dp_max_depth < new_pe->max_depth) { curr_port->dp_max_depth = new_pe->max_depth; } - curr_port->alproto_mask |= new_pe->alproto_mask; head_pe = &curr_port->dp; } else { curr_pe->ProbingParserTs = ProbingParser2; @@ -1101,7 +1094,6 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing curr_port->sp_max_depth < new_pe->max_depth) { curr_port->sp_max_depth = new_pe->max_depth; } - curr_port->alproto_mask |= new_pe->alproto_mask; head_pe = &curr_port->sp; } AppLayerProtoDetectProbingParserElementAppend(head_pe, new_pe); @@ -1119,9 +1111,8 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing temp_port->dp_max_depth < curr_pe->max_depth) { temp_port->dp_max_depth = curr_pe->max_depth; } - AppLayerProtoDetectProbingParserElementAppend(&temp_port->dp, - AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); - temp_port->alproto_mask |= curr_pe->alproto_mask; + AppLayerProtoDetectProbingParserElementAppend( + &temp_port->dp, AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); } else { if (temp_port->sp == NULL) temp_port->sp_max_depth = curr_pe->max_depth; @@ -1131,9 +1122,8 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing temp_port->sp_max_depth < curr_pe->max_depth) { temp_port->sp_max_depth = curr_pe->max_depth; } - AppLayerProtoDetectProbingParserElementAppend(&temp_port->sp, - AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); - temp_port->alproto_mask |= curr_pe->alproto_mask; + AppLayerProtoDetectProbingParserElementAppend( + &temp_port->sp, AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); } temp_port = temp_port->next; } /* while */ @@ -2870,14 +2860,12 @@ typedef struct AppLayerProtoDetectPPTestDataElement_ { const char *alproto_name; AppProto alproto; uint16_t port; - uint32_t alproto_mask; uint32_t min_depth; uint32_t max_depth; } AppLayerProtoDetectPPTestDataElement; typedef struct AppLayerProtoDetectPPTestDataPort_ { uint16_t port; - uint32_t alproto_mask; uint16_t dp_max_depth; uint16_t sp_max_depth; @@ -2912,10 +2900,6 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, for (k = 0; k < ip_proto[i].no_of_port; k++, pp_port = pp_port->next) { if (pp_port->port != ip_proto[i].port[k].port) goto end; - if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask) - goto end; - if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask) - goto end; if (pp_port->dp_max_depth != ip_proto[i].port[k].dp_max_depth) goto end; if (pp_port->sp_max_depth != ip_proto[i].port[k].sp_max_depth) @@ -2931,9 +2915,6 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, if (pp_element->alproto != ip_proto[i].port[k].toserver_element[j].alproto) { goto end; } - if (pp_element->alproto_mask != ip_proto[i].port[k].toserver_element[j].alproto_mask) { - goto end; - } if (pp_element->min_depth != ip_proto[i].port[k].toserver_element[j].min_depth) { goto end; } @@ -2952,9 +2933,6 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, if (pp_element->alproto != ip_proto[i].port[k].toclient_element[j].alproto) { goto end; } - if (pp_element->alproto_mask != ip_proto[i].port[k].toclient_element[j].alproto_mask) { - goto end; - } if (pp_element->min_depth != ip_proto[i].port[k].toclient_element[j].min_depth) { goto end; } @@ -3126,100 +3104,76 @@ static int AppLayerProtoDetectTest15(void) ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPTestDataElement element_ts_80[] = { - { "http", ALPROTO_HTTP1, 80, 1 << ALPROTO_HTTP1, 5, 8 }, - { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 }, - { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, + { "http", ALPROTO_HTTP1, 80, 5, 8 }, + { "smb", ALPROTO_SMB, 80, 5, 6 }, + { "ftp", ALPROTO_FTP, 80, 7, 10 }, + { "smtp", ALPROTO_SMTP, 0, 12, 0 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "irc", ALPROTO_IRC, 0, 12, 25 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, }; - AppLayerProtoDetectPPTestDataElement element_tc_80[] = { { "http", ALPROTO_HTTP1, 80, - 1 << ALPROTO_HTTP1, 5, 8 }, - { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 }, - { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; + AppLayerProtoDetectPPTestDataElement element_tc_80[] = { { "http", ALPROTO_HTTP1, 80, 5, 8 }, + { "smb", ALPROTO_SMB, 80, 5, 6 }, { "ftp", ALPROTO_FTP, 80, 7, 10 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, { "irc", ALPROTO_IRC, 0, 12, 14 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_81[] = { - { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 }, - { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - }; - AppLayerProtoDetectPPTestDataElement element_tc_81[] = { - { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 }, - { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } - }; + { "dcerpc", ALPROTO_DCERPC, 81, 9, 10 }, + { "ftp", ALPROTO_FTP, 81, 7, 15 }, + { "smtp", ALPROTO_SMTP, 0, 12, 0 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "irc", ALPROTO_IRC, 0, 12, 25 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + }; + AppLayerProtoDetectPPTestDataElement element_tc_81[] = { { "ftp", ALPROTO_FTP, 81, 7, 15 }, + { "dcerpc", ALPROTO_DCERPC, 81, 9, 10 }, { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + { "irc", ALPROTO_IRC, 0, 12, 14 }, { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "smtp", ALPROTO_SMTP, 0, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_85[] = { - { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 }, - { "ftp", ALPROTO_FTP, 85, 1 << ALPROTO_FTP, 7, 15 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - }; - AppLayerProtoDetectPPTestDataElement element_tc_85[] = { - { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } - }; + { "dcerpc", ALPROTO_DCERPC, 85, 9, 10 }, + { "ftp", ALPROTO_FTP, 85, 7, 15 }, + { "smtp", ALPROTO_SMTP, 0, 12, 0 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "irc", ALPROTO_IRC, 0, 12, 25 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + }; + AppLayerProtoDetectPPTestDataElement element_tc_85[] = { { "dcerpc", ALPROTO_DCERPC, 85, 9, + 10 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, { "irc", ALPROTO_IRC, 0, 12, 14 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_90[] = { - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - }; - AppLayerProtoDetectPPTestDataElement element_tc_90[] = { - { "ftp", ALPROTO_FTP, 90, 1 << ALPROTO_FTP, 7, 15 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } - }; + { "smtp", ALPROTO_SMTP, 0, 12, 0 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "irc", ALPROTO_IRC, 0, 12, 25 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + }; + AppLayerProtoDetectPPTestDataElement element_tc_90[] = { { "ftp", ALPROTO_FTP, 90, 7, 15 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, { "irc", ALPROTO_IRC, 0, 12, 14 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_0[] = { - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - }; - AppLayerProtoDetectPPTestDataElement element_tc_0[] = { - { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, - { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, - { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, - { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } - }; - + { "smtp", ALPROTO_SMTP, 0, 12, 0 }, + { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "irc", ALPROTO_IRC, 0, 12, 25 }, + { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + }; + AppLayerProtoDetectPPTestDataElement element_tc_0[] = { { "jabber", ALPROTO_JABBER, 0, 12, 23 }, + { "irc", ALPROTO_IRC, 0, 12, 14 }, { "tls", ALPROTO_TLS, 0, 12, 18 }, + { "smtp", ALPROTO_SMTP, 0, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_85_udp[] = { - { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 }, - }; + { "imap", ALPROTO_IMAP, 85, 12, 23 }, + }; AppLayerProtoDetectPPTestDataElement element_tc_85_udp[] = { - { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 }, - }; + { "imap", ALPROTO_IMAP, 85, 12, 23 }, + }; AppLayerProtoDetectPPTestDataPort ports_tcp[] = { { 80, - ((1 << ALPROTO_HTTP1) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) | - (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | - (1 << ALPROTO_JABBER)), - ((1 << ALPROTO_HTTP1) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) | - (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | - (1 << ALPROTO_SMTP)), + 23, 23, element_ts_80, element_tc_80, @@ -3228,52 +3182,35 @@ static int AppLayerProtoDetectTest15(void) }, { 81, - ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) | (1 << ALPROTO_SMTP) | - (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), - ((1 << ALPROTO_FTP) | (1 << ALPROTO_DCERPC) | (1 << ALPROTO_JABBER) | - (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), + 23, 23, element_ts_81, element_tc_81, sizeof(element_ts_81) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_81) / sizeof(AppLayerProtoDetectPPTestDataElement), }, - { 85, - ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) | (1 << ALPROTO_SMTP) | - (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), - ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | - (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), - 23, element_ts_85, element_tc_85, + { 85, 23, 23, element_ts_85, element_tc_85, sizeof(element_ts_85) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_85) / sizeof(AppLayerProtoDetectPPTestDataElement) }, - { 90, - ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | - (1 << ALPROTO_JABBER)), - ((1 << ALPROTO_FTP) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | - (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), - 23, element_ts_90, element_tc_90, + { 90, 23, 23, element_ts_90, element_tc_90, sizeof(element_ts_90) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_90) / sizeof(AppLayerProtoDetectPPTestDataElement) }, - { 0, - ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | - (1 << ALPROTO_JABBER)), - ((1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | - (1 << ALPROTO_SMTP)), - 23, element_ts_0, element_tc_0, + { 0, 23, 23, element_ts_0, element_tc_0, sizeof(element_ts_0) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_0) / sizeof(AppLayerProtoDetectPPTestDataElement) } }; AppLayerProtoDetectPPTestDataPort ports_udp[] = { - { 85, - (1 << ALPROTO_IMAP), - (1 << ALPROTO_IMAP), - 23, - element_ts_85_udp, element_tc_85_udp, - sizeof(element_ts_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), - sizeof(element_tc_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), - }, - }; + { + 85, + 23, + 23, + element_ts_85_udp, + element_tc_85_udp, + sizeof(element_ts_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), + sizeof(element_tc_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), + }, + }; AppLayerProtoDetectPPTestDataIPProto ip_proto[] = { { IPPROTO_TCP, From 89cb933c1e4ffb768a8ac2467e35de58165c205d Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Wed, 13 Nov 2024 11:07:03 +0100 Subject: [PATCH 17/17] plugin: add in-tree zabbix plugin for testing --- .github/workflows/builds.yml | 9 + examples/plugins/README.md | 4 + examples/plugins/zabbix/Cargo.toml | 17 + examples/plugins/zabbix/LICENSE | 21 + examples/plugins/zabbix/ndpi_zabbix.pcap | Bin 0 -> 899 bytes examples/plugins/zabbix/src/detect.rs | 160 +++++++ examples/plugins/zabbix/src/lib.rs | 7 + examples/plugins/zabbix/src/log.rs | 76 ++++ examples/plugins/zabbix/src/parser.rs | 68 +++ examples/plugins/zabbix/src/plugin.rs | 34 ++ examples/plugins/zabbix/src/suricata.rs | 550 +++++++++++++++++++++++ examples/plugins/zabbix/src/util.rs | 55 +++ examples/plugins/zabbix/src/zabbix.rs | 507 +++++++++++++++++++++ examples/plugins/zabbix/zabbix.rules | 3 + examples/plugins/zabbix/zabbix.yaml | 20 + 15 files changed, 1531 insertions(+) create mode 100644 examples/plugins/zabbix/Cargo.toml create mode 100644 examples/plugins/zabbix/LICENSE create mode 100644 examples/plugins/zabbix/ndpi_zabbix.pcap create mode 100644 examples/plugins/zabbix/src/detect.rs create mode 100644 examples/plugins/zabbix/src/lib.rs create mode 100644 examples/plugins/zabbix/src/log.rs create mode 100644 examples/plugins/zabbix/src/parser.rs create mode 100644 examples/plugins/zabbix/src/plugin.rs create mode 100644 examples/plugins/zabbix/src/suricata.rs create mode 100644 examples/plugins/zabbix/src/util.rs create mode 100644 examples/plugins/zabbix/src/zabbix.rs create mode 100644 examples/plugins/zabbix/zabbix.rules create mode 100644 examples/plugins/zabbix/zabbix.yaml diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index b5f6786c73b6..c830565f8972 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -186,6 +186,15 @@ jobs: cat eve.json | jq -c 'select(.dns)' test $(cat eve.json | jq -c 'select(.dns)' | wc -l) = "1" + - name: Test app-layer plugin + working-directory: examples/plugins/zabbix + run: | + RUSTFLAGS=-Clink-args=-Wl,-undefined,dynamic_lookup cargo build + ../../../src/suricata -S zabbix.rules --set plugins.0=./target/debug/libsuricata_zabbix.so --runmode=single -l . -c zabbix.yaml -k none -r ndpi_zabbix.pcap + cat eve.json | jq -c 'select(.zabbix)' + test $(cat eve.json | jq -c 'select(.zabbix)' | wc -l) = "6" + # we get 4 alerts and 2 zabbix events + - name: Test library build in tree working-directory: examples/lib/simple run: make clean all diff --git a/examples/plugins/README.md b/examples/plugins/README.md index 5300f750342b..1d0c9abee6f2 100644 --- a/examples/plugins/README.md +++ b/examples/plugins/README.md @@ -9,3 +9,7 @@ is useful if you want to send EVE output to custom destinations. A minimal capture plugin that can be used as a template, but also used for testing capture plugin loading and registration in CI. + +## zabbix + +An app-layer plugin for Zabbix protocol. diff --git a/examples/plugins/zabbix/Cargo.toml b/examples/plugins/zabbix/Cargo.toml new file mode 100644 index 000000000000..82711ca89cab --- /dev/null +++ b/examples/plugins/zabbix/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "suricata-zabbix" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +nom7 = { version="7.0", package="nom" } +libc = "~0.2.82" +flate2 = "1.0.34" + +[features] +default = ["suricata8"] +suricata7 = [] +suricata8 = [] diff --git a/examples/plugins/zabbix/LICENSE b/examples/plugins/zabbix/LICENSE new file mode 100644 index 000000000000..144c2324afc2 --- /dev/null +++ b/examples/plugins/zabbix/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Catena cyber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/plugins/zabbix/ndpi_zabbix.pcap b/examples/plugins/zabbix/ndpi_zabbix.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8e3d32f5e8919162a62003fd6273f8bdac0b51f9 GIT binary patch literal 899 zcmca|c+)~A1{MYcU}0bca>_PtilPL!4leCOrk6qDjLgjJAd|zq z5GF&+gqRO8nSlXhve5^SsUXwUo$h-Te5^xx|8_1&SLbO0h1qR9+ zpwk~`8bU3C1jrSTiL0YI8G?W|fiQ-NF5BC|CMIAtQG(%dy&Bxa-@qWg0(Lqx#N`Y| zX2u{JwgPQ{1viEbCfhr}Hsqt(PzrXlAkfV+s5XF1)Q>@!h!NZ-Pq@Ir{T3~_-vA@9 z6fFX;0weG@$i$TicunL6oA?HciDeCfC`kft;*msziCBEy3HEg!ny<@%zJ3diDG9i* J{{qAHDgc)!5KRC8 literal 0 HcmV?d00001 diff --git a/examples/plugins/zabbix/src/detect.rs b/examples/plugins/zabbix/src/detect.rs new file mode 100644 index 000000000000..bebf97d816da --- /dev/null +++ b/examples/plugins/zabbix/src/detect.rs @@ -0,0 +1,160 @@ +use crate::suricata::{ + rs_detect_u8_match, rs_detect_u8_parse, DetectBufferSetActiveList, + DetectHelperBufferMpmRegister, DetectHelperBufferRegister, DetectHelperGetData, + DetectHelperKeywordRegister, DetectHelperKeywordSetup, DetectSignatureSetAppProto, + DetectUintData, Level, SCSigTableElmt, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT, +}; +use crate::util::ctor_pointer; +use crate::util::SCLog; +use crate::zabbix::{ZabbixTransaction, ALPROTO_ZABBIX}; +use std::os::raw::{c_int, c_void}; + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_keywords_register() { + SCLog!(Level::Info, "registering Zabbix keywords"); + zabbix_register_flags_keyword(); + zabbix_register_data_keyword(); +} + +static mut G_ZABBIX_FLAGS_KWID: c_int = 0; +static mut G_ZABBIX_FLAGS_BUFFER_ID: c_int = 0; + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_flags_setup( + de: *mut c_void, + s: *mut c_void, + raw: *const std::os::raw::c_char, +) -> c_int { + let ctx = rs_detect_u8_parse(raw) as *mut std::os::raw::c_void; + if ctx.is_null() { + return -1; + } + let r = DetectHelperKeywordSetup( + de, + ALPROTO_ZABBIX, + G_ZABBIX_FLAGS_KWID, + G_ZABBIX_FLAGS_BUFFER_ID, + s, + ctx, + ); + if r < 0 { + rs_zabbix_flags_free(std::ptr::null_mut(), ctx); + } + r +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_flags_match( + _de: *mut c_void, + _f: *mut c_void, + _flags: u8, + _state: *mut c_void, + tx: *mut c_void, + _sig: *const c_void, + ctx: *const c_void, +) -> c_int { + let tx = ctor_pointer!(tx, ZabbixTransaction); + rs_detect_u8_match(tx.zabbix.flags, ctx) +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_flags_free(_de: *mut c_void, ctx: *mut c_void) { + // Just unbox... + let ctx = ctor_pointer!(ctx, DetectUintData); + std::mem::drop(Box::from_raw(ctx)); +} + +fn zabbix_register_flags_keyword() { + let kw = SCSigTableElmt { + name: b"zabbix.flags\0".as_ptr() as *const libc::c_char, + desc: b"match on zabbix header flags\0".as_ptr() as *const libc::c_char, + url: b"\0".as_ptr() as *const libc::c_char, + flags: 0, + AppLayerTxMatch: Some(rs_zabbix_flags_match), + Setup: rs_zabbix_flags_setup, + Free: Some(rs_zabbix_flags_free), + }; + unsafe { + G_ZABBIX_FLAGS_KWID = DetectHelperKeywordRegister(&kw); + G_ZABBIX_FLAGS_BUFFER_ID = DetectHelperBufferRegister( + b"zabbix_flags\0".as_ptr() as *const libc::c_char, + ALPROTO_ZABBIX, + true, + true, + ); + } +} + +static mut G_ZABBIX_DATA_BUFID: c_int = 0; + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_data_setup( + de: *mut c_void, + s: *mut c_void, + _raw: *const std::os::raw::c_char, +) -> c_int { + if DetectBufferSetActiveList(de, s, G_ZABBIX_DATA_BUFID) < 0 { + return -1; + } + if DetectSignatureSetAppProto(s, ALPROTO_ZABBIX) != 0 { + return -1; + } + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_data( + tx: *const c_void, + _flow_flags: u8, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> bool { + let tx = ctor_pointer!(tx, ZabbixTransaction); + *buffer = tx.zabbix.data.as_ptr(); + *buffer_len = tx.zabbix.data.len() as u32; + true +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_get_data( + de: *mut c_void, + transforms: *const c_void, + flow: *const c_void, + flow_flags: u8, + tx: *const c_void, + list_id: c_int, +) -> *mut c_void { + DetectHelperGetData( + de, + transforms, + flow, + flow_flags, + tx, + list_id, + rs_zabbix_data, + ) +} + +pub(super) fn zabbix_register_data_keyword() { + let kw = SCSigTableElmt { + name: b"zabbix.data\0".as_ptr() as *const libc::c_char, + desc: b"match on zabbix data\0".as_ptr() as *const libc::c_char, + url: b"\0".as_ptr() as *const libc::c_char, + Setup: rs_zabbix_data_setup, + flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, + AppLayerTxMatch: None, + Free: None, + }; + unsafe { + DetectHelperKeywordRegister(&kw); + G_ZABBIX_DATA_BUFID = DetectHelperBufferMpmRegister( + b"zabbix_data\0".as_ptr() as *const libc::c_char, + b"zabbix data\0".as_ptr() as *const libc::c_char, + ALPROTO_ZABBIX, + true, + true, + rs_zabbix_get_data, + ); + } +} diff --git a/examples/plugins/zabbix/src/lib.rs b/examples/plugins/zabbix/src/lib.rs new file mode 100644 index 000000000000..2969e0c90341 --- /dev/null +++ b/examples/plugins/zabbix/src/lib.rs @@ -0,0 +1,7 @@ +mod detect; +mod log; +mod parser; +pub mod plugin; +mod suricata; +mod util; +mod zabbix; diff --git a/examples/plugins/zabbix/src/log.rs b/examples/plugins/zabbix/src/log.rs new file mode 100644 index 000000000000..4359177219f0 --- /dev/null +++ b/examples/plugins/zabbix/src/log.rs @@ -0,0 +1,76 @@ +use crate::suricata::JsonBuilder; +use crate::suricata::{jb_close, jb_open_object, jb_set_string, jb_set_uint}; +use crate::util::ctor_pointer; +use crate::zabbix::ZabbixTransaction; + +use std::ffi::CString; + +#[derive(Debug, PartialEq, Eq)] +pub enum JsonError { + SuricataError, +} + +impl std::error::Error for JsonError {} + +impl std::fmt::Display for JsonError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + JsonError::SuricataError => write!(f, "suricata returned error"), + } + } +} + +fn jb_set_string_sc(jb: &mut JsonBuilder, key: &str, val: &str) -> Result<(), JsonError> { + let keyc = CString::new(key).unwrap(); + let valc = CString::new(val.escape_default().to_string()).unwrap(); + if unsafe { !jb_set_string(jb, keyc.as_ptr(), valc.as_ptr()) } { + return Err(JsonError::SuricataError); + } + Ok(()) +} + +fn jb_close_sc(jb: &mut JsonBuilder) -> Result<(), JsonError> { + if unsafe { !jb_close(jb) } { + return Err(JsonError::SuricataError); + } + Ok(()) +} + +fn jb_open_object_sc(jb: &mut JsonBuilder, key: &str) -> Result<(), JsonError> { + let keyc = CString::new(key).unwrap(); + if unsafe { !jb_open_object(jb, keyc.as_ptr()) } { + return Err(JsonError::SuricataError); + } + Ok(()) +} + +fn jb_set_uint_sc(jb: &mut JsonBuilder, key: &str, val: u64) -> Result<(), JsonError> { + let keyc = CString::new(key).unwrap(); + if unsafe { !jb_set_uint(jb, keyc.as_ptr(), val) } { + return Err(JsonError::SuricataError); + } + Ok(()) +} + +fn log_zabbix(tx: &ZabbixTransaction, jb: &mut JsonBuilder) -> Result<(), JsonError> { + jb_open_object_sc(jb, "zabbix")?; + jb_set_uint_sc(jb, "flags", tx.zabbix.flags.into())?; + //TODO make configurable + if tx.zabbix.data.len() < 256 { + jb_set_string_sc(jb, "data", &String::from_utf8_lossy(&tx.zabbix.data))?; + } else { + jb_set_string_sc(jb, "data", &String::from_utf8_lossy(&tx.zabbix.data[..256]))?; + } + jb_close_sc(jb)?; + Ok(()) +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_log( + tx: *mut std::os::raw::c_void, + jb: *mut std::os::raw::c_void, +) -> bool { + let tx = ctor_pointer!(tx, ZabbixTransaction); + let jb = ctor_pointer!(jb, JsonBuilder); + log_zabbix(tx, jb).is_ok() +} diff --git a/examples/plugins/zabbix/src/parser.rs b/examples/plugins/zabbix/src/parser.rs new file mode 100644 index 000000000000..ec666f1ab42d --- /dev/null +++ b/examples/plugins/zabbix/src/parser.rs @@ -0,0 +1,68 @@ +use flate2::bufread::ZlibDecoder; +use nom7::bytes::streaming::take as takes; +use nom7::combinator::verify; +use nom7::number::streaming::{le_u32, le_u64, le_u8}; +use nom7::IResult; +use std::io::Read; + +#[derive(Clone, Debug, Default)] +pub struct ZabbixPdu { + pub flags: u8, + pub data: Vec, + pub wrong_decompressed_len: bool, + pub error_decompression: bool, + pub rem_len: u64, +} + +pub fn check_zabbix(i: &[u8]) -> bool { + let r = verify(le_u32::<&[u8], nom7::error::Error<&[u8]>>, |&v| { + v == 0x4458425a + })(i); + r.is_ok() +} + +pub fn parse_zabbix(i: &[u8]) -> IResult<&[u8], ZabbixPdu> { + let (i, _magic) = le_u32(i)?; + let (i, flags) = le_u8(i)?; + let large = (flags & 4) != 0; + let (i, (pdu_len, decompressed_len)) = if large { + let (i2, pdu_len) = le_u64(i)?; + let (i2, decompressed_len) = le_u64(i2)?; + Ok((i2, (pdu_len, decompressed_len))) + } else { + let (i2, pdu_len) = le_u32(i)?; + let (i2, decompressed_len) = le_u32(i2)?; + Ok((i2, (pdu_len as u64, decompressed_len as u64))) + }?; + let mut wrong_decompressed_len = false; + let mut error_decompression = false; + //TODO make configurable + let take_len = std::cmp::min(pdu_len, 4096); + let (i, data) = takes(pdu_len as usize)(i)?; + let rem_len = take_len - pdu_len; + let data = if (flags & 2) != 0 { + let mut z = ZlibDecoder::new(data); + let mut dec_data = Vec::new(); + if let Ok(n) = z.read_to_end(&mut dec_data) { + if n as u64 != decompressed_len { + wrong_decompressed_len = true; + } + dec_data + } else { + error_decompression = true; + data.to_vec() + } + } else { + data.to_vec() + }; + Ok(( + i, + ZabbixPdu { + flags, + data, + wrong_decompressed_len, + error_decompression, + rem_len, + }, + )) +} diff --git a/examples/plugins/zabbix/src/plugin.rs b/examples/plugins/zabbix/src/plugin.rs new file mode 100644 index 000000000000..e66f852d8a80 --- /dev/null +++ b/examples/plugins/zabbix/src/plugin.rs @@ -0,0 +1,34 @@ +use super::suricata; +use super::zabbix::rs_zabbix_register_parser; +use crate::detect::rs_zabbix_keywords_register; +use crate::log::rs_zabbix_log; +use crate::suricata::{SCAppLayerPlugin, SCPlugin, SCPluginRegisterAppLayer}; +use crate::util::SCLog; + +extern "C" fn zabbix_plugin_init() { + SCLog!(suricata::Level::Notice, "Initializing zabbix plugin"); + let plugin = SCAppLayerPlugin { + name: b"zabbix\0".as_ptr() as *const libc::c_char, + logname: b"JsonZabbixLog\0".as_ptr() as *const libc::c_char, + confname: b"eve-log.zabbix\0".as_ptr() as *const libc::c_char, + Register: rs_zabbix_register_parser, + Logger: rs_zabbix_log, + KeywordsRegister: rs_zabbix_keywords_register, + }; + unsafe { + if SCPluginRegisterAppLayer(Box::into_raw(Box::new(plugin))) != 0 { + println!("Failed to register zabbix plugin"); + } + } +} + +#[no_mangle] +extern "C" fn SCPluginRegister() -> *const SCPlugin { + let plugin = SCPlugin { + name: b"zabbix\0".as_ptr() as *const libc::c_char, + license: b"MIT\0".as_ptr() as *const libc::c_char, + author: b"Philippe Antoine\0".as_ptr() as *const libc::c_char, + Init: zabbix_plugin_init, + }; + Box::into_raw(Box::new(plugin)) +} diff --git a/examples/plugins/zabbix/src/suricata.rs b/examples/plugins/zabbix/src/suricata.rs new file mode 100644 index 000000000000..4ef3a3a73ca9 --- /dev/null +++ b/examples/plugins/zabbix/src/suricata.rs @@ -0,0 +1,550 @@ +// This file is kind of the include required by API +// completed by helper functions + +use std::os::raw::{c_char, c_int, c_void}; + +pub type AppProto = u16; +pub const ALPROTO_UNKNOWN: AppProto = 0; + +#[repr(C)] +#[allow(non_snake_case)] +pub struct SCPlugin { + pub name: *const libc::c_char, + pub license: *const libc::c_char, + pub author: *const libc::c_char, + pub Init: extern "C" fn(), +} + +#[repr(C)] +#[derive(Default, Debug, PartialEq, Eq)] +pub struct AppLayerTxConfig { + log_flags: u8, +} + +#[repr(C)] +#[derive(Default, Debug, PartialEq, Eq)] +pub struct LoggerFlags { + flags: u32, +} + +pub enum DetectEngineState {} +pub enum AppLayerDecoderEvents {} + +#[repr(C)] +#[derive(Debug, PartialEq, Eq)] +pub struct AppLayerTxData { + pub config: AppLayerTxConfig, + logged: LoggerFlags, + pub files_opened: u32, + pub files_logged: u32, + pub files_stored: u32, + + pub file_flags: u16, + pub file_tx: u8, + + detect_flags_ts: u64, + detect_flags_tc: u64, + + de_state: *mut DetectEngineState, + pub events: *mut AppLayerDecoderEvents, +} + +#[repr(C)] +#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] +pub struct AppLayerStateData { + pub file_flags: u16, +} + +#[repr(C)] +#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] +pub struct AppLayerResult { + pub status: i32, + pub consumed: u32, + pub needed: u32, +} + +pub enum Flow {} + +#[repr(C)] +pub struct StreamSlice { + input: *const u8, + input_len: u32, + flags: u8, + offset: u64, +} + +#[repr(C)] +pub struct AppLayerGetTxIterTuple { + tx_ptr: *mut std::os::raw::c_void, + tx_id: u64, + has_next: bool, +} + +pub const IPPROTO_TCP: u8 = 6; + +pub type AppLayerEventType = c_int; + +pub const APP_LAYER_TX_SKIP_INSPECT_FLAG: u64 = 0x4000000000000000; +pub const APP_LAYER_PARSER_OPT_ACCEPT_GAPS: u32 = 0x00000001; + +pub const STREAM_TOSERVER: u8 = 0x04; +//pub const STREAM_TOCLIENT: u8 = 0x08; + +// only in Suricata 7 +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Direction { + ToServer = 0x04, + ToClient = 0x08, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FileContainer { + pub head: *mut c_void, + pub tail: *mut c_void, +} + +// exists also in 6 but only constructed in 7 for this plugin +#[repr(C)] +pub struct StreamingBufferConfig { + pub buf_size: u32, + + pub max_regions: u16, + pub region_gap: u32, + // do not bother with real prototypes + pub calloc: Option, + pub realloc: Option, + pub free: Option, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct AppLayerGetFileState { + pub fc: *mut FileContainer, + pub cfg: *const StreamingBufferConfig, +} + +pub type ParseFn = unsafe extern "C" fn( + flow: *const Flow, + state: *mut c_void, + pstate: *mut c_void, + stream_slice: StreamSlice, + data: *const c_void, +) -> AppLayerResult; +pub type ProbeFn = unsafe extern "C" fn( + flow: *const Flow, + flags: u8, + input: *const u8, + input_len: u32, + rdir: *mut u8, +) -> AppProto; +pub type StateAllocFn = extern "C" fn(*mut c_void, AppProto) -> *mut c_void; +pub type StateFreeFn = unsafe extern "C" fn(*mut c_void); +pub type StateTxFreeFn = unsafe extern "C" fn(*mut c_void, u64); +pub type StateGetTxFn = unsafe extern "C" fn(*mut c_void, u64) -> *mut c_void; +pub type StateGetTxCntFn = unsafe extern "C" fn(*mut c_void) -> u64; + +pub type StateGetProgressFn = unsafe extern "C" fn(*mut c_void, u8) -> c_int; +pub type GetEventInfoFn = + unsafe extern "C" fn(*const c_char, *mut c_int, *mut AppLayerEventType) -> c_int; +pub type GetEventInfoByIdFn = + unsafe extern "C" fn(c_int, *mut *const c_char, *mut AppLayerEventType) -> i8; +pub type LocalStorageNewFn = extern "C" fn() -> *mut c_void; +pub type LocalStorageFreeFn = extern "C" fn(*mut c_void); + +pub type GetTxFilesFn = unsafe extern "C" fn(*mut c_void, *mut c_void, u8) -> AppLayerGetFileState; + +pub type GetTxIteratorFn = unsafe extern "C" fn( + ipproto: u8, + alproto: AppProto, + state: *mut c_void, + min_tx_id: u64, + max_tx_id: u64, + istate: &mut u64, +) -> AppLayerGetTxIterTuple; +pub type GetTxDataFn = unsafe extern "C" fn(*mut c_void) -> *mut AppLayerTxData; + +pub type GetStateDataFn = unsafe extern "C" fn(*mut c_void) -> *mut AppLayerStateData; + +pub type ApplyTxConfigFn = unsafe extern "C" fn(*mut c_void, *mut c_void, c_int, AppLayerTxConfig); + +pub type GetFrameIdByName = unsafe extern "C" fn(*const c_char) -> c_int; +pub type GetFrameNameById = unsafe extern "C" fn(u8) -> *const c_char; + +#[repr(C)] +pub struct RustParser { + pub name: *const c_char, + pub default_port: *const c_char, + pub ipproto: u8, + pub probe_ts: Option, + pub probe_tc: Option, + pub min_depth: u16, + pub max_depth: u16, + pub state_new: StateAllocFn, + pub state_free: StateFreeFn, + pub parse_ts: ParseFn, + pub parse_tc: ParseFn, + pub get_tx_count: StateGetTxCntFn, + pub get_tx: StateGetTxFn, + pub tx_free: StateTxFreeFn, + + pub tx_comp_st_ts: c_int, + pub tx_comp_st_tc: c_int, + pub tx_get_progress: StateGetProgressFn, + + pub get_eventinfo: Option, + pub get_eventinfo_byid: Option, + pub localstorage_new: Option, + pub localstorage_free: Option, + + pub get_tx_files: Option, + + pub get_tx_iterator: Option, + + pub get_state_data: GetStateDataFn, + + pub get_tx_data: GetTxDataFn, + pub apply_tx_config: Option, + pub flags: u32, + pub get_frame_id_by_name: Option, + pub get_frame_name_by_id: Option, +} + +pub const APP_LAYER_EVENT_TYPE_TRANSACTION: i32 = 1; + +pub const SIGMATCH_NOOPT: u16 = 1; +pub const SIGMATCH_INFO_STICKY_BUFFER: u16 = 0x200; + +#[allow(dead_code)] +#[derive(Debug)] +#[repr(C)] +pub enum Level { + NotSet = -1, + None = 0, + + Error, + Warning, + Notice, + Info, + Perf, + Config, + Debug, +} + +#[repr(C)] +#[allow(non_snake_case)] +pub struct SCAppLayerPlugin { + pub name: *const libc::c_char, + pub Register: unsafe extern "C" fn(), + pub KeywordsRegister: unsafe extern "C" fn(), + pub logname: *const libc::c_char, + pub confname: *const libc::c_char, + pub Logger: + unsafe extern "C" fn(tx: *mut std::os::raw::c_void, jb: *mut std::os::raw::c_void) -> bool, +} + +#[repr(C)] +#[allow(non_snake_case)] +pub struct SCSigTableElmt { + pub name: *const libc::c_char, + pub desc: *const libc::c_char, + pub url: *const libc::c_char, + pub flags: u16, + pub Setup: unsafe extern "C" fn( + de: *mut c_void, + s: *mut c_void, + raw: *const std::os::raw::c_char, + ) -> c_int, + pub Free: Option, + pub AppLayerTxMatch: Option< + unsafe extern "C" fn( + de: *mut c_void, + f: *mut c_void, + flags: u8, + state: *mut c_void, + tx: *mut c_void, + sig: *const c_void, + ctx: *const c_void, + ) -> c_int, + >, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +#[repr(u8)] +pub enum DetectUintMode { + _DetectUintModeEqual, +} + +#[derive(Debug)] +#[repr(C)] +pub struct DetectUintData { + pub arg1: T, + pub arg2: T, + pub mode: DetectUintMode, +} + +extern "C" { + pub fn StringToAppProto(proto_name: *const u8) -> AppProto; + pub fn SCLogMessage( + level: c_int, + filename: *const std::os::raw::c_char, + line: std::os::raw::c_uint, + function: *const std::os::raw::c_char, + subsystem: *const std::os::raw::c_char, + message: *const std::os::raw::c_char, + ) -> c_int; + + pub fn AppLayerProtoDetectConfProtoDetectionEnabled( + ipproto: *const c_char, + proto: *const c_char, + ) -> c_int; + + pub fn AppLayerRegisterProtocolDetection( + parser: *const RustParser, + enable_default: c_int, + ) -> AppProto; + pub fn AppLayerProtoDetectPMRegisterPatternCS( + ipproto: u8, + alproto: AppProto, + pattern: *const c_char, + depth: u16, + offset: u16, + direction: u8, + ) -> c_int; + pub fn AppLayerParserConfParserEnabled(ipproto: *const c_char, proto: *const c_char) -> c_int; + pub fn AppLayerRegisterParser(parser: *const RustParser, alproto: AppProto) -> c_int; + pub fn SCPluginRegisterAppLayer(plugin: *const SCAppLayerPlugin) -> c_int; + pub fn AppLayerDecoderEventsSetEventRaw(events: *mut *mut AppLayerDecoderEvents, event: u8); + pub fn AppLayerParserRegisterLogger(pproto: u8, alproto: AppProto); + pub fn DetectHelperBufferRegister( + name: *const c_char, + alproto: AppProto, + toclient: bool, + toserver: bool, + ) -> c_int; + pub fn DetectHelperBufferMpmRegister( + name: *const c_char, + desc: *const c_char, + alproto: AppProto, + toclient: bool, + toserver: bool, + get_data: unsafe extern "C" fn( + *mut c_void, + *const c_void, + *const c_void, + u8, + *const c_void, + i32, + ) -> *mut c_void, + ) -> c_int; + pub fn DetectHelperGetData( + de: *mut c_void, + transforms: *const c_void, + flow: *const c_void, + flow_flags: u8, + tx: *const c_void, + list_id: c_int, + get_buf: unsafe extern "C" fn(*const c_void, u8, *mut *const u8, *mut u32) -> bool, + ) -> *mut c_void; + pub fn DetectHelperKeywordRegister(kw: *const SCSigTableElmt) -> c_int; + pub fn DetectSignatureSetAppProto(s: *mut c_void, alproto: AppProto) -> c_int; + pub fn DetectBufferSetActiveList(de: *mut c_void, s: *mut c_void, bufid: c_int) -> c_int; + + pub fn SigMatchAppendSMToList( + de_ctx: *mut c_void, + s: *mut c_void, + kw_id: c_int, + ctx: *mut c_void, + buf_id: c_int, + ) -> *mut c_void; + pub fn rs_detect_u8_match(parg: u8, ctx: *const c_void) -> c_int; + pub fn rs_detect_u8_parse(raw: *const c_char) -> *mut DetectUintData; +} + +// jsonbuilder +pub enum JsonBuilder {} + +extern "C" { + pub fn jb_set_string(jb: &mut JsonBuilder, key: *const c_char, val: *const c_char) -> bool; + pub fn jb_close(jb: &mut JsonBuilder) -> bool; + pub fn jb_open_object(jb: &mut JsonBuilder, key: *const c_char) -> bool; + pub fn jb_set_uint(jb: &mut JsonBuilder, key: *const c_char, val: u64) -> bool; +} + +// Helper functions + +#[allow(non_snake_case)] +pub unsafe fn DetectHelperKeywordSetup( + de_ctx: *mut c_void, + alproto: AppProto, + kw_id: c_int, + buf_id: c_int, + s: *mut c_void, + ctx: *mut c_void, +) -> c_int { + if DetectSignatureSetAppProto(s, alproto) != 0 { + return -1; + } + if SigMatchAppendSMToList(de_ctx, s, kw_id, ctx, buf_id).is_null() { + return -1; + } + 0 +} + +impl AppLayerResult { + pub fn ok() -> Self { + Default::default() + } + pub fn err() -> Self { + Self { + status: -1, + ..Default::default() + } + } + pub fn incomplete(consumed: u32, needed: u32) -> Self { + Self { + status: 1, + consumed, + needed, + } + } +} + +impl Direction { + pub fn index(&self) -> usize { + match self { + Direction::ToServer => 0, + Direction::ToClient => 1, + } + } +} + +impl StreamSlice { + pub fn as_slice(&self) -> &[u8] { + if self.input.is_null() && self.input_len == 0 { + unsafe { + return std::slice::from_raw_parts( + std::ptr::NonNull::::dangling().as_ptr(), + self.input_len as usize, + ); + } + } + unsafe { std::slice::from_raw_parts(self.input, self.input_len as usize) } + } + pub fn flags(&self) -> u8 { + self.flags + } + pub fn is_gap(&self) -> bool { + self.input.is_null() && self.input_len > 0 + } +} + +impl AppLayerGetTxIterTuple { + pub fn with_values( + tx_ptr: *mut std::os::raw::c_void, + tx_id: u64, + has_next: bool, + ) -> AppLayerGetTxIterTuple { + AppLayerGetTxIterTuple { + tx_ptr, + tx_id, + has_next, + } + } + pub fn not_found() -> AppLayerGetTxIterTuple { + AppLayerGetTxIterTuple { + tx_ptr: std::ptr::null_mut(), + tx_id: 0, + has_next: false, + } + } +} + +impl AppLayerTxData { + pub fn for_direction(direction: Direction) -> Self { + let (detect_flags_ts, detect_flags_tc) = match direction { + Direction::ToServer => (0, APP_LAYER_TX_SKIP_INSPECT_FLAG), + Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_FLAG, 0), + }; + Self { + config: AppLayerTxConfig::default(), + logged: LoggerFlags::default(), + files_opened: 0, + files_logged: 0, + files_stored: 0, + file_flags: 0, + file_tx: 0, + detect_flags_ts, + detect_flags_tc, + de_state: std::ptr::null_mut(), + events: std::ptr::null_mut(), + } + } + pub fn set_event(&mut self, event: u8) { + unsafe { + AppLayerDecoderEventsSetEventRaw(&mut self.events, event); + } + } +} + +pub struct Frame { + pub _id: i64, +} + +impl Frame { + #[allow(clippy::not_unsafe_ptr_arg_deref)] + pub fn new( + flow: *const Flow, + stream_slice: &StreamSlice, + frame_start: &[u8], + frame_len: i64, + frame_type: u8, + tx_id: u64, + ) -> Option { + let offset = frame_start.as_ptr() as usize - stream_slice.as_slice().as_ptr() as usize; + let frame = unsafe { + AppLayerFrameNewByRelativeOffset( + flow, + stream_slice, + offset as u32, + frame_len, + (stream_slice.flags() & STREAM_TOSERVER == 0).into(), + frame_type, + ) + }; + let id = unsafe { AppLayerFrameGetId(frame) }; + if id > 0 { + let direction = if stream_slice.flags() & STREAM_TOSERVER != 0 { + 0 + } else { + 1 + }; + unsafe { + AppLayerFrameSetTxIdById(flow, direction, id, tx_id); + }; + Some(Self { _id: id }) + } else { + None + } + } +} + +#[repr(C)] +struct CFrame { + _private: [u8; 0], +} + +// Defined in app-layer-register.h +extern "C" { + fn AppLayerFrameNewByRelativeOffset( + flow: *const Flow, + stream_slice: *const StreamSlice, + frame_start_rel: u32, + len: i64, + dir: i32, + frame_type: u8, + ) -> *const CFrame; + pub fn AppLayerFrameSetTxIdById(flow: *const Flow, dir: i32, id: i64, tx_id: u64); + fn AppLayerFrameGetId(frame: *const CFrame) -> i64; +} diff --git a/examples/plugins/zabbix/src/util.rs b/examples/plugins/zabbix/src/util.rs new file mode 100644 index 000000000000..31096329c5ee --- /dev/null +++ b/examples/plugins/zabbix/src/util.rs @@ -0,0 +1,55 @@ +// Helper macros +use crate::suricata::{Level, SCLogMessage}; + +use std::ffi::CString; + +macro_rules! ctor_pointer { + ($ptr:ident, $ty:ty) => { + &mut *($ptr as *mut $ty) + }; +} +pub(crate) use ctor_pointer; + +// This macro returns the function name. +// +// This macro has been borrowed from https://github.com/popzxc/stdext-rs, which +// is released under the MIT license as there is currently no macro in Rust +// to provide the function name. +macro_rules! function { + () => {{ + // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust. + fn __f() {} + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(__f); + &name[..name.len() - 5] + }}; +} +pub(crate) use function; + +macro_rules!SCLog { + ($level:expr, $($arg:tt)*) => { + $crate::util::sclog($level, file!(), line!(), crate::util::function!(), + &(format!($($arg)*))); + } +} + +pub(crate) use SCLog; + +pub fn sclog(level: Level, filename: &str, line: u32, function: &str, message: &str) { + let filenamec = CString::new(filename).unwrap(); + let functionc = CString::new(function).unwrap(); + let modulec = CString::new("zabbix").unwrap(); + let messagec = CString::new(message).unwrap(); + unsafe { + SCLogMessage( + level as i32, + filenamec.as_ptr(), + line, + (functionc).as_ptr(), + (modulec).as_ptr(), + (messagec).as_ptr(), + ); + } +} diff --git a/examples/plugins/zabbix/src/zabbix.rs b/examples/plugins/zabbix/src/zabbix.rs new file mode 100644 index 000000000000..c41943ecd73d --- /dev/null +++ b/examples/plugins/zabbix/src/zabbix.rs @@ -0,0 +1,507 @@ +use super::parser; +use super::suricata::{ + AppLayerGetTxIterTuple, AppLayerParserConfParserEnabled, AppLayerParserRegisterLogger, + AppLayerProtoDetectConfProtoDetectionEnabled, AppLayerProtoDetectPMRegisterPatternCS, + AppLayerRegisterParser, AppLayerRegisterProtocolDetection, AppLayerResult, AppLayerStateData, + AppLayerTxData, AppProto, Direction, Flow, Frame, Level, RustParser, StreamSlice, + StringToAppProto, ALPROTO_UNKNOWN, APP_LAYER_EVENT_TYPE_TRANSACTION, + APP_LAYER_PARSER_OPT_ACCEPT_GAPS, IPPROTO_TCP, +}; +use crate::util::{ctor_pointer, SCLog}; +use std::collections::VecDeque; +use std::ffi::CStr; +use std::ffi::CString; +use std::os::raw::{c_char, c_int, c_void}; + +pub(crate) static mut ALPROTO_ZABBIX: AppProto = ALPROTO_UNKNOWN; +static mut ALPROTO_FAILED: AppProto = 0xFFFF; + +#[derive(Debug, PartialEq, Eq)] +pub enum ZabbixEvent { + ErrorDecompression, + WrongDecompressedLen, +} + +impl ZabbixEvent { + fn from_id(id: i32) -> Option { + match id { + 0 => Some(ZabbixEvent::ErrorDecompression), + 1 => Some(ZabbixEvent::WrongDecompressedLen), + _ => None, + } + } + + fn to_cstring(&self) -> &str { + match *self { + ZabbixEvent::ErrorDecompression => "error_decompression\0", + ZabbixEvent::WrongDecompressedLen => "wrong_decompressed_len\0", + } + } + + fn as_i32(&self) -> i32 { + match *self { + ZabbixEvent::ErrorDecompression => 0, + ZabbixEvent::WrongDecompressedLen => 1, + } + } + + fn from_string(s: &str) -> Option { + match s { + "error_decompression" => Some(ZabbixEvent::ErrorDecompression), + "wrong_decompressed_len" => Some(ZabbixEvent::WrongDecompressedLen), + _ => None, + } + } + + pub unsafe extern "C" fn get_event_info( + event_name: *const std::os::raw::c_char, + event_id: *mut std::os::raw::c_int, + event_type: *mut std::os::raw::c_int, + ) -> std::os::raw::c_int { + if event_name.is_null() { + return -1; + } + + let event = match CStr::from_ptr(event_name) + .to_str() + .map(ZabbixEvent::from_string) + { + Ok(Some(event)) => event.as_i32(), + _ => { + return -1; + } + }; + *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; + *event_id = event as std::os::raw::c_int; + 0 + } + + pub unsafe extern "C" fn get_event_info_by_id( + event_id: std::os::raw::c_int, + event_name: *mut *const std::os::raw::c_char, + event_type: *mut std::os::raw::c_int, + ) -> i8 { + if let Some(e) = ZabbixEvent::from_id(event_id) { + *event_name = e.to_cstring().as_ptr() as *const std::os::raw::c_char; + *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; + return 0; + } + -1 + } +} + +pub enum ZabbixFrameType { + Pdu, + Hdr, + Data, +} + +impl ZabbixFrameType { + fn from_u8(id: u8) -> Option { + match id { + 0 => Some(ZabbixFrameType::Pdu), + 1 => Some(ZabbixFrameType::Hdr), + 2 => Some(ZabbixFrameType::Data), + _ => None, + } + } + + fn to_cstring(&self) -> *const std::os::raw::c_char { + let s = match *self { + ZabbixFrameType::Pdu => "pdu\0", + ZabbixFrameType::Hdr => "hdr\0", + ZabbixFrameType::Data => "data\0", + }; + s.as_ptr() as *const std::os::raw::c_char + } + + fn as_u8(&self) -> u8 { + match *self { + ZabbixFrameType::Pdu => 0, + ZabbixFrameType::Hdr => 1, + ZabbixFrameType::Data => 2, + } + } + + fn from_str(s: &str) -> Option { + match s { + "pdu" => Some(ZabbixFrameType::Pdu), + "hdr" => Some(ZabbixFrameType::Hdr), + "data" => Some(ZabbixFrameType::Data), + _ => None, + } + } + + pub unsafe extern "C" fn ffi_id_from_name(name: *const std::os::raw::c_char) -> i32 { + if name.is_null() { + return -1; + } + let frame_id = if let Ok(s) = std::ffi::CStr::from_ptr(name).to_str() { + ZabbixFrameType::from_str(s) + .map(|t| t.as_u8() as i32) + .unwrap_or(-1) + } else { + -1 + }; + frame_id + } + + pub unsafe extern "C" fn ffi_name_from_id(id: u8) -> *const std::os::raw::c_char { + ZabbixFrameType::from_u8(id) + .map(|s| s.to_cstring()) + .unwrap_or_else(std::ptr::null) + } +} + +pub struct ZabbixTransaction { + tx_id: u64, + pub zabbix: parser::ZabbixPdu, + tx_data: AppLayerTxData, +} + +impl ZabbixTransaction { + pub fn new(dir: Direction) -> ZabbixTransaction { + Self { + tx_id: 0, + zabbix: parser::ZabbixPdu::default(), + tx_data: AppLayerTxData::for_direction(dir), + } + } + pub fn set_event(&mut self, event: u8) { + self.tx_data.set_event(event); + } +} + +#[derive(Default)] +pub struct ZabbixState { + state_data: AppLayerStateData, + tx_id: u64, + transactions: VecDeque, + in_gap: [bool; 2], + to_skip: [usize; 2], +} + +impl ZabbixState { + pub fn new() -> Self { + Default::default() + } + + // Free a transaction by ID. + fn free_tx(&mut self, tx_id: u64) { + let len = self.transactions.len(); + let mut found = false; + let mut index = 0; + for i in 0..len { + let tx = &self.transactions[i]; + if tx.tx_id == tx_id + 1 { + found = true; + index = i; + break; + } + } + if found { + self.transactions.remove(index); + } + } + + pub fn get_tx(&mut self, tx_id: u64) -> Option<&ZabbixTransaction> { + self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1) + } + + fn process_frames( + zabbix: &parser::ZabbixPdu, + stream_slice: &StreamSlice, + flow: *const Flow, + input: &[u8], + remlen: usize, + tx_id: u64, + ) { + let hdrlen = if (zabbix.flags & 4) != 0 { + 4 + 1 + 2 * 8 + } else { + 4 + 1 + 2 * 4 + }; + let _pdu = Frame::new( + flow, + stream_slice, + input, + (input.len() - remlen) as i64, + ZabbixFrameType::Pdu as u8, + tx_id, + ); + let _hdr = Frame::new( + flow, + stream_slice, + input, + hdrlen as i64, + ZabbixFrameType::Hdr as u8, + tx_id, + ); + let _data = Frame::new( + flow, + stream_slice, + &input[hdrlen..], + (input.len() - remlen - hdrlen) as i64, + ZabbixFrameType::Data as u8, + tx_id, + ); + } + + fn parse_zabbix( + &mut self, + dir: Direction, + flow: *const Flow, + stream_slice: StreamSlice, + ) -> AppLayerResult { + if stream_slice.is_gap() { + self.in_gap[dir.index()] = true; + return AppLayerResult::ok(); + } + let input = stream_slice.as_slice(); + if self.in_gap[dir.index()] { + if parser::check_zabbix(input) { + self.in_gap[dir.index()] = false; + } else { + return AppLayerResult::ok(); + } + } + let mut start = input; + while !start.is_empty() { + if self.to_skip[dir.index()] > start.len() { + self.to_skip[dir.index()] -= start.len(); + return AppLayerResult::ok(); + } + start = &start[self.to_skip[dir.index()]..]; + match parser::parse_zabbix(start) { + Ok((rem, h)) => { + let mut tx = ZabbixTransaction::new(dir); + self.tx_id += 1; + tx.tx_id = self.tx_id; + ZabbixState::process_frames( + &h, + &stream_slice, + flow, + start, + rem.len(), + tx.tx_id, + ); + if h.error_decompression { + tx.set_event(ZabbixEvent::ErrorDecompression as u8); + } + if h.wrong_decompressed_len { + tx.set_event(ZabbixEvent::WrongDecompressedLen as u8); + } + self.to_skip[dir.index()] = h.rem_len as usize; + tx.zabbix = h; + self.transactions.push_back(tx); + start = rem; + } + Err(nom7::Err::Incomplete(n1)) => { + if let nom7::Needed::Size(n2) = n1 { + let consumed = input.len() - start.len(); + let needed = start.len() + usize::from(n2); + return AppLayerResult::incomplete(consumed as u32, needed as u32); + } else { + return AppLayerResult::err(); + } + } + Err(_) => { + return AppLayerResult::err(); + } + } + } + + // Input was fully consumed. + AppLayerResult::ok() + } +} + +// C exports. + +extern "C" fn rs_zabbix_state_new(_orig_state: *mut c_void, _orig_proto: AppProto) -> *mut c_void { + let state = ZabbixState::new(); + let boxed = Box::new(state); + Box::into_raw(boxed) as *mut c_void +} + +unsafe extern "C" fn rs_zabbix_state_free(state: *mut c_void) { + std::mem::drop(Box::from_raw(state as *mut ZabbixState)); +} + +unsafe extern "C" fn rs_zabbix_state_tx_free(state: *mut c_void, tx_id: u64) { + let state = ctor_pointer!(state, ZabbixState); + state.free_tx(tx_id); +} + +unsafe extern "C" fn rs_zabbix_parse_request( + _flow: *const Flow, + state: *mut c_void, + _pstate: *mut c_void, + stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = ctor_pointer!(state, ZabbixState); + state.parse_zabbix(Direction::ToServer, _flow, stream_slice) +} + +unsafe extern "C" fn rs_zabbix_parse_response( + _flow: *const Flow, + state: *mut c_void, + _pstate: *mut c_void, + stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = ctor_pointer!(state, ZabbixState); + state.parse_zabbix(Direction::ToClient, _flow, stream_slice) +} + +unsafe extern "C" fn rs_zabbix_state_get_tx(state: *mut c_void, tx_id: u64) -> *mut c_void { + let state = ctor_pointer!(state, ZabbixState); + match state.get_tx(tx_id) { + Some(tx) => tx as *const _ as *mut _, + None => std::ptr::null_mut(), + } +} + +unsafe extern "C" fn rs_zabbix_state_get_tx_count(state: *mut c_void) -> u64 { + let state = ctor_pointer!(state, ZabbixState); + state.tx_id +} + +unsafe extern "C" fn rs_zabbix_tx_get_alstate_progress(_tx: *mut c_void, _direction: u8) -> c_int { + 1 +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_get_tx_data( + tx: *mut std::os::raw::c_void, +) -> *mut AppLayerTxData { + let tx = &mut *(tx as *mut ZabbixTransaction); + &mut tx.tx_data +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_get_state_data( + state: *mut std::os::raw::c_void, +) -> *mut AppLayerStateData { + let state = &mut *(state as *mut ZabbixState); + &mut state.state_data +} + +pub unsafe extern "C" fn zabbix_get_tx_iterator( + _ipproto: u8, + _alproto: AppProto, + state: *mut std::os::raw::c_void, + min_tx_id: u64, + _max_tx_id: u64, + istate: &mut u64, +) -> AppLayerGetTxIterTuple { + let state = ctor_pointer!(state, ZabbixState); + let mut index = *istate as usize; + let len = state.transactions.len(); + while index < len { + let tx = state.transactions.get(index).unwrap(); + if tx.tx_id < min_tx_id + 1 { + index += 1; + continue; + } + *istate = index as u64; + return AppLayerGetTxIterTuple::with_values( + tx as *const _ as *mut _, + tx.tx_id - 1, + len - index > 1, + ); + } + AppLayerGetTxIterTuple::not_found() +} + +#[no_mangle] +pub extern "C" fn rs_zabbix_state_progress_completion_status( + _direction: u8, +) -> std::os::raw::c_int { + 1 +} + +#[no_mangle] +pub unsafe extern "C" fn rs_zabbix_register_parser() { + SCLog!(Level::Notice, "Registering zabbix parser"); + //let default_port = CString::new("[10050]").unwrap(); + let parser = RustParser { + name: b"zabbix\0".as_ptr() as *const c_char, + default_port: std::ptr::null(), + ipproto: IPPROTO_TCP, + probe_ts: None, + probe_tc: None, + min_depth: 16, + max_depth: 16, + state_new: rs_zabbix_state_new, + state_free: rs_zabbix_state_free, + tx_free: rs_zabbix_state_tx_free, + parse_ts: rs_zabbix_parse_request, + parse_tc: rs_zabbix_parse_response, + get_tx_count: rs_zabbix_state_get_tx_count, + get_tx: rs_zabbix_state_get_tx, + + // unidirectional, always complete after creation + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: rs_zabbix_tx_get_alstate_progress, + + get_eventinfo: Some(ZabbixEvent::get_event_info), + get_eventinfo_byid: Some(ZabbixEvent::get_event_info_by_id), + + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + + get_tx_iterator: Some(zabbix_get_tx_iterator), + get_tx_data: rs_zabbix_get_tx_data, + get_state_data: rs_zabbix_get_state_data, + apply_tx_config: None, + flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, + get_frame_id_by_name: Some(ZabbixFrameType::ffi_id_from_name), + get_frame_name_by_id: Some(ZabbixFrameType::ffi_name_from_id), + }; + + let ip_proto_str = CString::new("tcp").unwrap(); + ALPROTO_FAILED = StringToAppProto("failed\0".as_ptr()); + + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_ZABBIX = alproto; + if AppLayerProtoDetectPMRegisterPatternCS( + IPPROTO_TCP, + ALPROTO_ZABBIX, + b"ZBXD\0".as_ptr() as *const std::os::raw::c_char, + 4, + 0, + Direction::ToServer as u8, + ) < 0 + { + SCLog!(Level::Warning, "Rust zabbix failed to register detection."); + } + + if AppLayerProtoDetectPMRegisterPatternCS( + IPPROTO_TCP, + ALPROTO_ZABBIX, + b"ZBXD\0".as_ptr() as *const std::os::raw::c_char, + 4, + 0, + Direction::ToClient as u8, + ) < 0 + { + SCLog!(Level::Warning, "Rust zabbix failed to register detection."); + } + + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_ZABBIX); + SCLog!(Level::Notice, "Rust zabbix parser registered."); + } else { + SCLog!( + Level::Notice, + "Protocol detector and parser disabled for zabbix." + ); + } +} diff --git a/examples/plugins/zabbix/zabbix.rules b/examples/plugins/zabbix/zabbix.rules new file mode 100644 index 000000000000..407a82e4d39f --- /dev/null +++ b/examples/plugins/zabbix/zabbix.rules @@ -0,0 +1,3 @@ +alert zabbix any any -> any any (msg: "zabbix frame"; frame: zabbix.data; content: "proc"; startswith; sid:10;) +alert zabbix any any -> any any (msg: "zabbix payload"; zabbix.data; content: "proc"; startswith; sid:11;) +alert zabbix any any -> any any (msg: "zabbix flags"; zabbix.flags: &1=1; sid:12;) diff --git a/examples/plugins/zabbix/zabbix.yaml b/examples/plugins/zabbix/zabbix.yaml new file mode 100644 index 000000000000..4c699773b6c5 --- /dev/null +++ b/examples/plugins/zabbix/zabbix.yaml @@ -0,0 +1,20 @@ +%YAML 1.1 +--- + +outputs: + - eve-log: + enabled: true + types: + - alert + - anomaly + - files + - flow + - stats: + enabled: no + - zabbix: + enabled: yes + +app-layer: + protocols: + zabbix: + enabled: true