diff --git a/src/Makefile.am b/src/Makefile.am index d2d35f4648..1beadab102 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -483,6 +483,7 @@ util-device.c util-device.h \ util-ebpf.c util-ebpf.h \ util-enum.c util-enum.h \ util-error.c util-error.h \ +util-exception-policy.c util-exception-policy.h \ util-file.c util-file.h \ util-file-decompression.c util-file-decompression.h \ util-file-swf-decompression.c util-file-swf-decompression.h \ diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 2a41fd35b9..e5732cb260 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -169,6 +169,13 @@ struct AppLayerParserState_ { AppLayerDecoderEvents *decoder_events; }; +enum ExceptionPolicy g_applayerparser_error_policy = EXCEPTION_POLICY_IGNORE; + +static void AppLayerConfg(void) +{ + g_applayerparser_error_policy = ExceptionPolicyParse("app-layer.error-policy", true); +} + #ifdef UNITTESTS void UTHAppLayerParserStateGetIds(void *ptr, uint64_t *i1, uint64_t *i2, uint64_t *log, uint64_t *min) { @@ -1281,6 +1288,24 @@ int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow /* invoke the recursive parser, but only on data. We may get empty msgs on EOF */ if (input_len > 0 || (flags & STREAM_EOF)) { +#ifdef DEBUG + uint64_t offset = 0; + if (f->proto == IPPROTO_TCP && f->protoctx != NULL) { + TcpSession *ssn = f->protoctx; + TcpStream *stream = (flags & STREAM_TOSERVER) ? &ssn->client : &ssn->server; + offset = STREAM_APP_PROGRESS(stream); + } + if (((flags & STREAM_TOSERVER) && 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); + goto error; + } + if (((flags & STREAM_TOCLIENT) && 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); + goto error; + } +#endif /* invoke the parser */ AppLayerResult res = p->Parser[direction](f, alstate, pstate, input, input_len, @@ -1614,6 +1639,8 @@ void AppLayerParserRegisterProtocolParsers(void) { SCEnter(); + AppLayerConfg(); + RegisterHTPParsers(); RegisterSSLParsers(); RegisterDCERPCParsers(); diff --git a/src/app-layer.c b/src/app-layer.c index 3b9465d67e..08100e240e 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -40,14 +40,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 @@ -292,6 +291,8 @@ static int TCPProtoDetectTriggerOpposingSide(ThreadVars *tv, return ret; } +extern enum ExceptionPolicy g_applayerparser_error_policy; + /** \todo data const * \retval int -1 error * \retval int 0 ok @@ -402,8 +403,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 @@ -439,8 +439,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 @@ -468,7 +467,7 @@ static int TCPProtoDetect(ThreadVars *tv, flags, data, data_len); PACKET_PROFILING_APP_END(app_tctx, f->alproto); if (r < 0) { - SCReturnInt(-1); + goto parser_error; } else if (r == 0) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); } @@ -520,8 +519,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 @@ -553,7 +551,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; @@ -579,6 +577,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. @@ -640,6 +644,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; } @@ -719,6 +727,10 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, PACKET_PROFILING_APP_END(app_tctx, f->alproto); if (r == 0) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); + } else if (r < 0) { + ExceptionPolicyApply( + p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); + SCReturnInt(-1); } } } @@ -807,6 +819,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/defrag-hash.c b/src/defrag-hash.c index 317a0122d0..c3597abb86 100644 --- a/src/defrag-hash.c +++ b/src/defrag-hash.c @@ -181,6 +181,7 @@ void DefragInitConfig(char 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; @@ -472,6 +473,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 */ @@ -491,6 +500,7 @@ static DefragTracker *DefragTrackerGetNew(Packet *p) dt = DefragTrackerGetUsedDefragTracker(); if (dt == NULL) { + ExceptionPolicyApply(p, defrag_config.memcap_policy, PKT_DROP_REASON_DEFRAG_MEMCAP); return NULL; } @@ -499,6 +509,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 2716a6c901..19a9fc0dc6 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 8bda7230d2..010bb02121 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -62,6 +62,8 @@ #include "util-unittest.h" #endif +#include "util-validate.h" + #define DEFAULT_DEFRAG_HASH_SIZE 0xffff #define DEFAULT_DEFRAG_POOL_SIZE 0xffff @@ -620,8 +622,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 d4129bfbc6..cf539840dc 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; @@ -526,6 +527,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 * @@ -537,10 +543,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; } @@ -561,6 +571,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 @@ -585,6 +596,7 @@ static Flow *FlowGetNew(ThreadVars *tv, FlowLookupStruct *fls, const Packet *p) #ifdef UNITTESTS } #endif + NoFlowHandleIPS(p); return NULL; } @@ -600,9 +612,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) { @@ -724,8 +735,7 @@ static inline void FromHashLockCMP(Flow *f) * * \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 8e36125188..36c37847af 100644 --- a/src/flow.c +++ b/src/flow.c @@ -589,6 +589,9 @@ void FlowInitConfig(char 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 9adba466df..e5729ff1d1 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 558af5c541..20e4892fce 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -27,6 +27,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; @@ -59,7 +60,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 29d06c7feb..01306a802e 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -66,6 +66,7 @@ #include "util-profiling.h" #include "util-validate.h" +#include "util-exception-policy.h" #ifdef DEBUG static SCMutex segment_pool_memuse_mutex; @@ -73,6 +74,10 @@ static uint64_t segment_pool_memuse = 0; static uint64_t segment_pool_memcnt = 0; #endif +#ifdef DEBUG +thread_local uint64_t t_pcapcnt = UINT64_MAX; +#endif + static PoolThread *segment_thread_pool = NULL; /* init only, protect initializing and growing pool */ static SCMutex segment_thread_pool_mutex = SCMUTEX_INITIALIZER; @@ -145,6 +150,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) @@ -1888,6 +1900,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 57db84c8a7..37805eedaa 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" @@ -107,7 +108,9 @@ static inline int StreamTcpValidateAck(TcpSession *ssn, TcpStream *, Packet *); static int StreamTcpStateDispatch(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn, PacketQueueNoLock *pq, uint8_t state); - +#ifdef DEBUG +extern thread_local uint64_t t_pcapcnt; +#endif extern int g_detect_disabled; static PoolThread *ssn_pool = NULL; @@ -468,6 +471,11 @@ void StreamTcpInitConfig(char 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); + SCLogConfig("memcap-policy: %u/%u", stream_config.ssn_memcap_policy, + stream_config.reassembly_memcap_policy); if (!quiet) { SCLogConfig("stream.\"inline\": %s", @@ -700,11 +708,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; } @@ -5223,6 +5238,9 @@ TmEcode StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueueNoLock *pq) StreamTcpThread *stt = (StreamTcpThread *)data; SCLogDebug("p->pcap_cnt %"PRIu64, p->pcap_cnt); +#ifdef DEBUG + t_pcapcnt = p->pcap_cnt; +#endif if (!(PKT_IS_TCP(p))) { return TM_ECODE_OK; diff --git a/src/stream-tcp.h b/src/stream-tcp.h index 653e8d3aa1..a414994a3a 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 33168e1418..bb7e929f28 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -86,6 +86,7 @@ #include "source-pcap.h" #include "source-pcap-file.h" +#include "source-pcap-file-helper.h" #include "source-pfring.h" @@ -176,6 +177,8 @@ #include "util-plugin.h" +#include "util-exception-policy.h" + #include "rust.h" /* @@ -1293,6 +1296,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 @@ -1662,6 +1673,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..0f54cf0787 --- /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); + PACKET_PASS(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