From: Victor Julien Date: Mon, 14 Mar 2022 06:20:11 +0000 (+0100) Subject: exceptions: initial exception-policy implementation X-Git-Tag: suricata-7.0.0-beta1~483 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8580499deda6bae88034f1961c01179ded91246e;p=thirdparty%2Fsuricata.git exceptions: initial exception-policy implementation Adds a framework for setting exception policies. These would be called when the engine reaches some kind of exception condition, like hitting a memcap or some traffic processing error. The policy gives control over what should happen next: drop the packet, drop the packet and flow, bypass, etc. Implements the policy for: stream: If stream session or reassembly memcaps are hit call the memcap policy on the packet and flow. flow: Apply policy when memcap is reached and no flow could be freed up. defrag: Apply policy when no tracker could be picked up. app-layer: Apply ppolicy if a parser reaches an error state. All options default to 'ignore', which means the default behavior is unchanged. Adds commandline options: add simulation options for exceptions. These are only exposed if compiled with `--enable-debug`. Ticket: #5214. Ticket: #5215. Ticket: #5216. Ticket: #5218. Ticket: #5194. --- diff --git a/src/Makefile.am b/src/Makefile.am index 959d27b1a9..d2a76b08e6 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -526,6 +526,7 @@ noinst_HEADERS = \ util-ebpf.h \ util-enum.h \ util-error.h \ + util-exception-policy.h \ util-file-decompression.h \ util-file.h \ util-file-swf-decompression.h \ @@ -1106,6 +1107,7 @@ libsuricata_c_a_SOURCES = \ util-ebpf.c \ util-enum.c \ util-error.c \ + util-exception-policy.c \ util-file.c \ util-file-decompression.c \ util-file-swf-decompression.c \ diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index cc6e65cc13..59a0c13aea 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -172,6 +172,13 @@ struct AppLayerParserState_ { FramesContainer *frames; }; +enum ExceptionPolicy g_applayerparser_error_policy = EXCEPTION_POLICY_IGNORE; + +static void AppLayerConfg(void) +{ + g_applayerparser_error_policy = ExceptionPolicyParse("app-layer.error-policy", true); +} + static void AppLayerParserFramesFreeContainer(FramesContainer *frames) { if (frames != NULL) { @@ -1300,6 +1307,22 @@ int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow if (input_len > 0 || (flags & STREAM_EOF)) { Setup(f, flags & (STREAM_TOSERVER | STREAM_TOCLIENT), input, input_len, flags, &stream_slice); +#ifdef DEBUG + if (((stream_slice.flags & STREAM_TOSERVER) && + stream_slice.offset >= g_eps_applayer_error_offset_ts)) { + SCLogNotice("putting parser %s into an error state from toserver offset %" PRIu64, + AppProtoToString(alproto), g_eps_applayer_error_offset_ts); + AppLayerIncParserErrorCounter(tv, f); + goto error; + } + if (((stream_slice.flags & STREAM_TOCLIENT) && + stream_slice.offset >= g_eps_applayer_error_offset_tc)) { + SCLogNotice("putting parser %s into an error state from toclient offset %" PRIu64, + AppProtoToString(alproto), g_eps_applayer_error_offset_tc); + AppLayerIncParserErrorCounter(tv, f); + goto error; + } +#endif /* invoke the parser */ AppLayerResult res = p->Parser[direction](f, alstate, pstate, stream_slice, alp_tctx->alproto_local_storage[f->protomap][alproto]); @@ -1637,6 +1660,8 @@ void AppLayerParserRegisterProtocolParsers(void) { SCEnter(); + AppLayerConfg(); + RegisterHTPParsers(); RegisterSSLParsers(); rs_dcerpc_register_parser(); diff --git a/src/app-layer.c b/src/app-layer.c index 87a2d12d5e..9cf276f235 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -41,14 +41,13 @@ #include "flow-util.h" #include "flow-private.h" #include "ippair.h" - #include "util-debug.h" #include "util-print.h" #include "util-profiling.h" #include "util-validate.h" #include "decode-events.h" - #include "app-layer-htp-mem.h" +#include "util-exception-policy.h" /** * \brief This is for the app layer in general and it contains per thread @@ -333,6 +332,8 @@ static int TCPProtoDetectTriggerOpposingSide(ThreadVars *tv, TcpReassemblyThread return ret; } +extern enum ExceptionPolicy g_applayerparser_error_policy; + /** \todo data const * \retval int -1 error * \retval int 0 ok @@ -441,8 +442,7 @@ static int TCPProtoDetect(ThreadVars *tv, if (TCPProtoDetectTriggerOpposingSide(tv, ra_ctx, p, ssn, *stream) != 0) { - DisableAppLayer(tv, f, p); - SCReturnInt(-1); + goto detect_error; } if (FlowChangeProto(f)) { /* We have the first data which requested a protocol change from P1 to P2 @@ -478,8 +478,7 @@ static int TCPProtoDetect(ThreadVars *tv, if (first_data_dir && !(first_data_dir & ssn->data_first_seen_dir)) { AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, APPLAYER_WRONG_DIRECTION_FIRST_DATA); - DisableAppLayer(tv, f, p); - SCReturnInt(-1); + goto detect_error; } /* This can happen if the current direction is not the * right direction, and the data from the other(also @@ -510,7 +509,7 @@ static int TCPProtoDetect(ThreadVars *tv, StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); } if (r < 0) { - SCReturnInt(-1); + goto parser_error; } } else { /* if the ssn is midstream, we may end up with a case where the @@ -560,8 +559,7 @@ static int TCPProtoDetect(ThreadVars *tv, if ((ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) && (first_data_dir) && !(first_data_dir & flags)) { - DisableAppLayer(tv, f, p); - SCReturnInt(-1); + goto detect_error; } /* if protocol detection is marked done for our direction we @@ -593,7 +591,7 @@ static int TCPProtoDetect(ThreadVars *tv, SCLogDebug("packet %"PRIu64": pd done(us %u them %u), parser called (r==%d), APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION set", p->pcap_cnt, *alproto, *alproto_otherdir, r); if (r < 0) { - SCReturnInt(-1); + goto parser_error; } } *alproto = ALPROTO_FAILED; @@ -619,6 +617,12 @@ static int TCPProtoDetect(ThreadVars *tv, } } SCReturnInt(0); +parser_error: + ExceptionPolicyApply(p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); + SCReturnInt(-1); +detect_error: + DisableAppLayer(tv, f, p); + SCReturnInt(-2); } /** \brief handle TCP data for the app-layer. @@ -680,6 +684,10 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, PACKET_PROFILING_APP_END(app_tctx, f->alproto); /* ignore parser result for gap */ StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); + if (r < 0) { + ExceptionPolicyApply(p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); + SCReturnInt(-1); + } goto end; } @@ -760,6 +768,11 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, PACKET_PROFILING_APP_END(app_tctx, f->alproto); if (r != 1) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); + if (r < 0) { + ExceptionPolicyApply( + p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); + SCReturnInt(-1); + } } } } @@ -894,6 +907,10 @@ int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *tctx, Packet *p, Flow * PACKET_PROFILING_APP_END(tctx, f->alproto); PACKET_PROFILING_APP_STORE(tctx, p); } + if (r < 0) { + ExceptionPolicyApply(p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); + SCReturnInt(-1); + } SCReturnInt(r); } diff --git a/src/app-layer.h b/src/app-layer.h index b220092a5c..27532dc8b4 100644 --- a/src/app-layer.h +++ b/src/app-layer.h @@ -175,5 +175,4 @@ static inline uint32_t StreamSliceGetGapSize(const StreamSlice *stream_slice) { return StreamSliceGetDataLen(stream_slice); } - #endif diff --git a/src/defrag-hash.c b/src/defrag-hash.c index 24afa76cf4..decc8fd755 100644 --- a/src/defrag-hash.c +++ b/src/defrag-hash.c @@ -181,6 +181,7 @@ void DefragInitConfig(bool quiet) defrag_config.hash_size = DEFRAG_DEFAULT_HASHSIZE; defrag_config.prealloc = DEFRAG_DEFAULT_PREALLOC; SC_ATOMIC_SET(defrag_config.memcap, DEFRAG_DEFAULT_MEMCAP); + defrag_config.memcap_policy = ExceptionPolicyParse("defrag.memcap-policy", false); /* Check if we have memcap and hash_size defined at config */ const char *conf_val; @@ -470,6 +471,14 @@ static inline int DefragTrackerCompare(DefragTracker *t, Packet *p) */ static DefragTracker *DefragTrackerGetNew(Packet *p) { +#ifdef DEBUG + if (g_eps_defrag_memcap != UINT64_MAX && g_eps_defrag_memcap == p->pcap_cnt) { + SCLogNotice("simulating memcap hit for packet %" PRIu64, p->pcap_cnt); + ExceptionPolicyApply(p, defrag_config.memcap_policy, PKT_DROP_REASON_DEFRAG_MEMCAP); + return NULL; + } +#endif + DefragTracker *dt = NULL; /* get a tracker from the spare queue */ @@ -489,6 +498,7 @@ static DefragTracker *DefragTrackerGetNew(Packet *p) dt = DefragTrackerGetUsedDefragTracker(); if (dt == NULL) { + ExceptionPolicyApply(p, defrag_config.memcap_policy, PKT_DROP_REASON_DEFRAG_MEMCAP); return NULL; } @@ -497,6 +507,7 @@ static DefragTracker *DefragTrackerGetNew(Packet *p) /* now see if we can alloc a new tracker */ dt = DefragTrackerAlloc(); if (dt == NULL) { + ExceptionPolicyApply(p, defrag_config.memcap_policy, PKT_DROP_REASON_DEFRAG_MEMCAP); return NULL; } diff --git a/src/defrag-hash.h b/src/defrag-hash.h index 9f5f2686ee..2307ef9710 100644 --- a/src/defrag-hash.h +++ b/src/defrag-hash.h @@ -72,6 +72,7 @@ typedef struct DefragConfig_ { uint32_t hash_rand; uint32_t hash_size; uint32_t prealloc; + enum ExceptionPolicy memcap_policy; } DefragConfig; /** \brief check if a memory alloc would fit in the memcap diff --git a/src/defrag.c b/src/defrag.c index f2c98dc170..1bd34b5152 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -63,6 +63,8 @@ #include "util-unittest.h" #endif +#include "util-validate.h" + #define DEFAULT_DEFRAG_HASH_SIZE 0xffff #define DEFAULT_DEFRAG_POOL_SIZE 0xffff @@ -621,8 +623,7 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, } } else { - /* Abort - should not happen. */ - SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid address family, aborting."); + DEBUG_VALIDATE_BUG_ON(1); return NULL; } diff --git a/src/flow-hash.c b/src/flow-hash.c index 3f551f0547..ced2f79928 100644 --- a/src/flow-hash.c +++ b/src/flow-hash.c @@ -49,6 +49,7 @@ #include "output.h" #include "output-flow.h" #include "stream-tcp.h" +#include "util-exception-policy.h" extern TcpStreamCnf stream_config; @@ -549,6 +550,11 @@ static inline Flow *FlowSpareSync(ThreadVars *tv, FlowLookupStruct *fls, return f; } +static inline void NoFlowHandleIPS(Packet *p) +{ + ExceptionPolicyApply(p, flow_config.memcap_policy, PKT_DROP_REASON_FLOW_MEMCAP); +} + /** * \brief Get a new flow * @@ -560,10 +566,14 @@ static inline Flow *FlowSpareSync(ThreadVars *tv, FlowLookupStruct *fls, * * \retval f *LOCKED* flow on succes, NULL on error. */ -static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, const Packet *p) +static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, Packet *p) { const bool emerg = ((SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY) != 0); - +#ifdef DEBUG + if (g_eps_flow_memcap != UINT64_MAX && g_eps_flow_memcap == p->pcap_cnt) { + return NULL; + } +#endif if (FlowCreateCheck(p, emerg) == 0) { return NULL; } @@ -584,6 +594,7 @@ static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, const Packet *p) f = FlowGetUsedFlow(tv, fls->dtv, &p->ts); if (f == NULL) { + NoFlowHandleIPS(p); return NULL; } #ifdef UNITTESTS @@ -608,6 +619,7 @@ static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, const Packet *p) #ifdef UNITTESTS } #endif + NoFlowHandleIPS(p); return NULL; } @@ -623,9 +635,8 @@ static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, const Packet *p) return f; } -static Flow *TcpReuseReplace(ThreadVars *tv, FlowLookupStruct *fls, - FlowBucket *fb, Flow *old_f, - const uint32_t hash, const Packet *p) +static Flow *TcpReuseReplace(ThreadVars *tv, FlowLookupStruct *fls, FlowBucket *fb, Flow *old_f, + const uint32_t hash, Packet *p) { #ifdef UNITTESTS if (tv != NULL && fls->dtv != NULL) { @@ -734,8 +745,7 @@ static inline bool FlowIsTimedOut(const Flow *f, const uint32_t sec, const bool * * \retval f *LOCKED* flow or NULL */ -Flow *FlowGetFlowFromHash(ThreadVars *tv, FlowLookupStruct *fls, - const Packet *p, Flow **dest) +Flow *FlowGetFlowFromHash(ThreadVars *tv, FlowLookupStruct *fls, Packet *p, Flow **dest) { Flow *f = NULL; diff --git a/src/flow-hash.h b/src/flow-hash.h index e1026ffa31..cad46652b1 100644 --- a/src/flow-hash.h +++ b/src/flow-hash.h @@ -77,8 +77,7 @@ typedef struct FlowBucket_ { /* prototypes */ -Flow *FlowGetFlowFromHash(ThreadVars *tv, FlowLookupStruct *tctx, - const Packet *, Flow **); +Flow *FlowGetFlowFromHash(ThreadVars *tv, FlowLookupStruct *tctx, Packet *, Flow **); Flow *FlowGetFromFlowKey(FlowKey *key, struct timespec *ttime, const uint32_t hash); Flow *FlowGetExistingFlowFromHash(FlowKey * key, uint32_t hash); diff --git a/src/flow.c b/src/flow.c index b77c318d1a..a785290800 100644 --- a/src/flow.c +++ b/src/flow.c @@ -596,6 +596,9 @@ void FlowInitConfig(bool quiet) flow_config.prealloc = configval; } } + + flow_config.memcap_policy = ExceptionPolicyParse("flow.memcap-policy", false); + SCLogDebug("Flow config from suricata.yaml: memcap: %"PRIu64", hash-size: " "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(flow_config.memcap), flow_config.hash_size, flow_config.prealloc); diff --git a/src/flow.h b/src/flow.h index 349ec53e5b..9887380459 100644 --- a/src/flow.h +++ b/src/flow.h @@ -28,6 +28,7 @@ typedef struct FlowStorageId FlowStorageId; #include "decode.h" +#include "util-exception-policy.h" #include "util-var.h" #include "util-atomic.h" #include "util-device.h" @@ -299,6 +300,8 @@ typedef struct FlowCnf_ uint32_t emerg_timeout_est; uint32_t emergency_recovery; + enum ExceptionPolicy memcap_policy; + SC_ATOMIC_DECLARE(uint64_t, memcap); } FlowConfig; diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index 00d99308a2..d19c456244 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -28,6 +28,7 @@ #include "util-checksum.h" #include "util-profiling.h" #include "source-pcap-file.h" +#include "util-exception-policy.h" extern int max_pending_packets; extern PcapFileGlobalVars pcap_g; @@ -60,7 +61,13 @@ void CleanupPcapFileFileVars(PcapFileFileVars *pfv) void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) { SCEnter(); - +#ifdef DEBUG + if (unlikely((pcap_g.cnt + 1ULL) == g_eps_pcap_packet_loss)) { + SCLogNotice("skipping packet %" PRIu64, g_eps_pcap_packet_loss); + pcap_g.cnt++; + SCReturn; + } +#endif PcapFileFileVars *ptv = (PcapFileFileVars *)user; Packet *p = PacketGetFromQueueOrAlloc(); diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index f394fc71a4..a986e27248 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -67,6 +67,7 @@ #include "util-profiling.h" #include "util-validate.h" +#include "util-exception-policy.h" #ifdef DEBUG static SCMutex segment_pool_memuse_mutex; @@ -74,6 +75,8 @@ static uint64_t segment_pool_memuse = 0; static uint64_t segment_pool_memcnt = 0; #endif +thread_local uint64_t t_pcapcnt = UINT64_MAX; + static PoolThread *segment_thread_pool = NULL; /* init only, protect initializing and growing pool */ static SCMutex segment_thread_pool_mutex = SCMUTEX_INITIALIZER; @@ -158,6 +161,13 @@ uint64_t StreamTcpReassembleMemuseGlobalCounter(void) */ int StreamTcpReassembleCheckMemcap(uint64_t size) { +#ifdef DEBUG + if (unlikely((g_eps_stream_reassembly_memcap != UINT64_MAX && + g_eps_stream_reassembly_memcap == t_pcapcnt))) { + SCLogNotice("simulating memcap reached condition for packet %" PRIu64, t_pcapcnt); + return 0; + } +#endif uint64_t memcapcopy = SC_ATOMIC_GET(stream_config.reassembly_memcap); if (memcapcopy == 0 || (uint64_t)((uint64_t)size + SC_ATOMIC_GET(ra_memuse)) <= memcapcopy) @@ -1964,6 +1974,9 @@ int StreamTcpReassembleHandleSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) { SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error"); + /* failure can only be because of memcap hit, so see if this should lead to a drop */ + ExceptionPolicyApply( + p, stream_config.reassembly_memcap_policy, PKT_DROP_REASON_STREAM_MEMCAP); SCReturnInt(-1); } diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 1ef011b1db..60c154f8d2 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -74,6 +74,7 @@ #include "util-validate.h" #include "util-runmodes.h" #include "util-random.h" +#include "util-exception-policy.h" #include "source-pcap-file.h" @@ -100,6 +101,7 @@ static int StreamTcpStateDispatch(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn, PacketQueueNoLock *pq, uint8_t state); +extern thread_local uint64_t t_pcapcnt; extern int g_detect_disabled; static PoolThread *ssn_pool = NULL; @@ -462,6 +464,11 @@ void StreamTcpInitConfig(bool quiet) stream_config.flags |= STREAMTCP_INIT_FLAG_INLINE; } } + stream_config.ssn_memcap_policy = ExceptionPolicyParse("stream.memcap-policy", true); + stream_config.reassembly_memcap_policy = + ExceptionPolicyParse("stream.reassembly.memcap-policy", true); + SCLogNotice("memcap-policy: %u/%u", stream_config.ssn_memcap_policy, + stream_config.reassembly_memcap_policy); if (!quiet) { SCLogConfig("stream.\"inline\": %s", @@ -694,11 +701,18 @@ static TcpSession *StreamTcpNewSession (Packet *p, int id) if (p->flow->protoctx != NULL) ssn_pool_cnt++; SCMutexUnlock(&ssn_pool_mutex); -#endif + if (unlikely((g_eps_stream_ssn_memcap != UINT64_MAX && + g_eps_stream_ssn_memcap == t_pcapcnt))) { + SCLogNotice("simulating memcap reached condition for packet %" PRIu64, t_pcapcnt); + ExceptionPolicyApply(p, stream_config.ssn_memcap_policy, PKT_DROP_REASON_STREAM_MEMCAP); + return NULL; + } +#endif ssn = (TcpSession *)p->flow->protoctx; if (ssn == NULL) { SCLogDebug("ssn_pool is empty"); + ExceptionPolicyApply(p, stream_config.ssn_memcap_policy, PKT_DROP_REASON_STREAM_MEMCAP); return NULL; } @@ -5296,6 +5310,7 @@ TmEcode StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueueNoLock *pq) SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s", p->pcap_cnt, p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient") : "noflow"); + t_pcapcnt = p->pcap_cnt; if (!(PKT_IS_TCP(p))) { return TM_ECODE_OK; diff --git a/src/stream-tcp.h b/src/stream-tcp.h index 7918bf468f..58b97c9b2a 100644 --- a/src/stream-tcp.h +++ b/src/stream-tcp.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2007-2022 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -64,6 +64,9 @@ typedef struct TcpStreamCnf_ { bool streaming_log_api; + enum ExceptionPolicy ssn_memcap_policy; + enum ExceptionPolicy reassembly_memcap_policy; + StreamingBufferConfig sbcnf; } TcpStreamCnf; diff --git a/src/suricata.c b/src/suricata.c index 82fd121b57..de6d7e22c6 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -82,6 +82,7 @@ #include "source-pcap.h" #include "source-pcap-file.h" +#include "source-pcap-file-helper.h" #include "source-pfring.h" @@ -175,6 +176,7 @@ #include "util-plugin.h" #include "util-dpdk.h" +#include "util-exception-policy.h" #include "rust.h" @@ -1351,6 +1353,14 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri) #ifdef HAVE_NFLOG {"nflog", optional_argument, 0, 0}, #endif + {"simulate-packet-flow-memcap", required_argument, 0, 0}, + {"simulate-applayer-error-at-offset-ts", required_argument, 0, 0}, + {"simulate-applayer-error-at-offset-tc", required_argument, 0, 0}, + {"simulate-packet-loss", required_argument, 0, 0}, + {"simulate-packet-tcp-reassembly-memcap", required_argument, 0, 0}, + {"simulate-packet-tcp-ssn-memcap", required_argument, 0, 0}, + {"simulate-packet-defrag-memcap", required_argument, 0, 0}, + {NULL, 0, NULL, 0} }; // clang-format on @@ -1719,6 +1729,11 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri) if (suri->strict_rule_parsing_string == NULL) { FatalError(SC_ERR_MEM_ALLOC, "failed to duplicate 'strict' string"); } + } else { + int r = ExceptionSimulationCommandlineParser( + (long_opts[option_index]).name, optarg); + if (r < 0) + return TM_ECODE_FAILED; } break; case 'c': diff --git a/src/util-exception-policy.c b/src/util-exception-policy.c new file mode 100644 index 0000000000..a214900702 --- /dev/null +++ b/src/util-exception-policy.c @@ -0,0 +1,185 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#include "suricata-common.h" +#include "util-exception-policy.h" +#include "util-misc.h" + +void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason) +{ + SCLogDebug("start: pcap_cnt %" PRIu64 ", policy %u", p->pcap_cnt, policy); + if (EngineModeIsIPS()) { + switch (policy) { + case EXCEPTION_POLICY_IGNORE: + break; + case EXCEPTION_POLICY_DROP_FLOW: + SCLogDebug("EXCEPTION_POLICY_DROP_FLOW"); + if (p->flow) { + p->flow->flags |= FLOW_ACTION_DROP; + } + /* fall through */ + case EXCEPTION_POLICY_DROP_PACKET: + SCLogDebug("EXCEPTION_POLICY_DROP_PACKET"); + DecodeSetNoPayloadInspectionFlag(p); + DecodeSetNoPacketInspectionFlag(p); + PacketDrop(p, drop_reason); + break; + case EXCEPTION_POLICY_BYPASS_FLOW: + PacketBypassCallback(p); + /* fall through */ + case EXCEPTION_POLICY_PASS_FLOW: + SCLogDebug("EXCEPTION_POLICY_PASS_FLOW"); + if (p->flow) { + p->flow->flags |= FLOW_ACTION_PASS; + FlowSetNoPacketInspectionFlag(p->flow); // TODO util func + } + /* fall through */ + case EXCEPTION_POLICY_PASS_PACKET: + SCLogDebug("EXCEPTION_POLICY_PASS_PACKET"); + DecodeSetNoPayloadInspectionFlag(p); + DecodeSetNoPacketInspectionFlag(p); + PacketPass(p); + break; + } + } + SCLogDebug("end"); +} + +enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support_flow) +{ + enum ExceptionPolicy policy = EXCEPTION_POLICY_IGNORE; + const char *value_str = NULL; + if ((ConfGet(option, &value_str)) == 1 && value_str != NULL) { + if (strcmp(value_str, "drop-flow") == 0) { + policy = EXCEPTION_POLICY_DROP_FLOW; + SCLogConfig("%s: %s", option, value_str); + } else if (strcmp(value_str, "pass-flow") == 0) { + policy = EXCEPTION_POLICY_PASS_FLOW; + SCLogConfig("%s: %s", option, value_str); + } else if (strcmp(value_str, "bypass") == 0) { + policy = EXCEPTION_POLICY_BYPASS_FLOW; + SCLogConfig("%s: %s", option, value_str); + } else if (strcmp(value_str, "drop-packet") == 0) { + policy = EXCEPTION_POLICY_DROP_PACKET; + SCLogConfig("%s: %s", option, value_str); + } else if (strcmp(value_str, "pass-packet") == 0) { + policy = EXCEPTION_POLICY_PASS_PACKET; + SCLogConfig("%s: %s", option, value_str); + } else if (strcmp(value_str, "ignore") == 0) { // TODO name? + policy = EXCEPTION_POLICY_IGNORE; + SCLogConfig("%s: %s", option, value_str); + } else { + SCLogConfig("%s: ignore", option); + } + + if (!support_flow) { + if (policy == EXCEPTION_POLICY_DROP_FLOW || policy == EXCEPTION_POLICY_PASS_FLOW || + policy == EXCEPTION_POLICY_BYPASS_FLOW) { + SCLogWarning(SC_WARN_COMPATIBILITY, + "flow actions not supported for %s, defaulting to \"ignore\"", option); + policy = EXCEPTION_POLICY_IGNORE; + } + } + + } else { + SCLogConfig("%s: ignore", option); + } + return policy; +} + +#ifndef DEBUG + +int ExceptionSimulationCommandlineParser(const char *name, const char *arg) +{ + return 0; +} + +#else + +/* exception policy simulation (eps) handling */ + +uint64_t g_eps_applayer_error_offset_ts = UINT64_MAX; +uint64_t g_eps_applayer_error_offset_tc = UINT64_MAX; +uint64_t g_eps_pcap_packet_loss = UINT64_MAX; +uint64_t g_eps_stream_ssn_memcap = UINT64_MAX; +uint64_t g_eps_stream_reassembly_memcap = UINT64_MAX; +uint64_t g_eps_flow_memcap = UINT64_MAX; +uint64_t g_eps_defrag_memcap = UINT64_MAX; + +/* 1: parsed, 0: not for us, -1: error */ +int ExceptionSimulationCommandlineParser(const char *name, const char *arg) +{ + if (strcmp(name, "simulate-applayer-error-at-offset-ts") == 0) { + BUG_ON(arg == NULL); + uint64_t offset = 0; + if (ParseSizeStringU64(arg, &offset) < 0) { + return -1; + } + g_eps_applayer_error_offset_ts = offset; + } else if (strcmp(name, "simulate-applayer-error-at-offset-tc") == 0) { + BUG_ON(arg == NULL); + uint64_t offset = 0; + if (ParseSizeStringU64(arg, &offset) < 0) { + return TM_ECODE_FAILED; + } + g_eps_applayer_error_offset_tc = offset; + } else if (strcmp(name, "simulate-packet-loss") == 0) { + BUG_ON(arg == NULL); + uint64_t pkt_num = 0; + if (ParseSizeStringU64(arg, &pkt_num) < 0) { + return TM_ECODE_FAILED; + } + g_eps_pcap_packet_loss = pkt_num; + } else if (strcmp(name, "simulate-packet-tcp-reassembly-memcap") == 0) { + BUG_ON(arg == NULL); + uint64_t pkt_num = 0; + if (ParseSizeStringU64(arg, &pkt_num) < 0) { + return TM_ECODE_FAILED; + } + g_eps_stream_reassembly_memcap = pkt_num; + } else if (strcmp(name, "simulate-packet-tcp-ssn-memcap") == 0) { + BUG_ON(arg == NULL); + uint64_t pkt_num = 0; + if (ParseSizeStringU64(arg, &pkt_num) < 0) { + return TM_ECODE_FAILED; + } + g_eps_stream_ssn_memcap = pkt_num; + } else if (strcmp(name, "simulate-packet-flow-memcap") == 0) { + BUG_ON(arg == NULL); + uint64_t pkt_num = 0; + if (ParseSizeStringU64(arg, &pkt_num) < 0) { + return TM_ECODE_FAILED; + } + g_eps_flow_memcap = pkt_num; + } else if (strcmp(name, "simulate-packet-defrag-memcap") == 0) { + BUG_ON(arg == NULL); + uint64_t pkt_num = 0; + if (ParseSizeStringU64(arg, &pkt_num) < 0) { + return TM_ECODE_FAILED; + } + g_eps_defrag_memcap = pkt_num; + } else { + // not for us + return 0; + } + return 1; +} +#endif diff --git a/src/util-exception-policy.h b/src/util-exception-policy.h new file mode 100644 index 0000000000..7fd782caba --- /dev/null +++ b/src/util-exception-policy.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#ifndef __UTIL_EXCEPTION_POLICY_H__ +#define __UTIL_EXCEPTION_POLICY_H__ + +enum ExceptionPolicy { + EXCEPTION_POLICY_IGNORE = 0, + EXCEPTION_POLICY_PASS_PACKET, + EXCEPTION_POLICY_PASS_FLOW, + EXCEPTION_POLICY_BYPASS_FLOW, + EXCEPTION_POLICY_DROP_PACKET, + EXCEPTION_POLICY_DROP_FLOW, +}; + +void ExceptionPolicyApply( + Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason); +enum ExceptionPolicy ExceptionPolicyParse(const char *option, const bool support_flow); + +#ifdef DEBUG +extern uint64_t g_eps_applayer_error_offset_ts; +extern uint64_t g_eps_applayer_error_offset_tc; +extern uint64_t g_eps_pcap_packet_loss; +extern uint64_t g_eps_stream_ssn_memcap; +extern uint64_t g_eps_stream_reassembly_memcap; +extern uint64_t g_eps_flow_memcap; +extern uint64_t g_eps_defrag_memcap; +#endif + +int ExceptionSimulationCommandlineParser(const char *name, const char *arg); + +#endif