]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
exceptions: initial exception-policy implementation
authorVictor Julien <vjulien@oisf.net>
Mon, 14 Mar 2022 06:20:11 +0000 (07:20 +0100)
committerVictor Julien <vjulien@oisf.net>
Thu, 9 Jun 2022 05:26:57 +0000 (07:26 +0200)
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.

18 files changed:
src/Makefile.am
src/app-layer-parser.c
src/app-layer.c
src/app-layer.h
src/defrag-hash.c
src/defrag-hash.h
src/defrag.c
src/flow-hash.c
src/flow-hash.h
src/flow.c
src/flow.h
src/source-pcap-file-helper.c
src/stream-tcp-reassemble.c
src/stream-tcp.c
src/stream-tcp.h
src/suricata.c
src/util-exception-policy.c [new file with mode: 0644]
src/util-exception-policy.h [new file with mode: 0644]

index 959d27b1a91aa515ee54705e755bf7503a33c36b..d2a76b08e6a5582940b1477fec3b765053b59f48 100755 (executable)
@@ -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 \
index cc6e65cc1376c803a8ca8758e8a3ab1af9013087..59a0c13aeac73cda9cd4a88b7dca7c6e78b339b0 100644 (file)
@@ -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();
index 87a2d12d5e2f24c8b2aa6687f592e92e2439b045..9cf276f2353e7e1007df8b918e34f612cc4125b6 100644 (file)
 #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);
 }
index b220092a5ca00105d0ddcf8b396dbb6a74cb476a..27532dc8b40c7c8bfbcca68d54f1c0a80715e1fc 100644 (file)
@@ -175,5 +175,4 @@ static inline uint32_t StreamSliceGetGapSize(const StreamSlice *stream_slice)
 {
     return StreamSliceGetDataLen(stream_slice);
 }
-
 #endif
index 24afa76cf4ede1fae2266048b664d584caa27971..decc8fd7550172230e84c518832a085c1b76fd93 100644 (file)
@@ -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;
             }
 
index 9f5f2686eee63138670e536645fdbe0b421b8b18..2307ef9710ed62efb659c042c021b75b2f1f4d97 100644 (file)
@@ -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
index f2c98dc1706376e26a152a82bd919aa67e9d3455..1bd34b5152187f9f842e81115d67f2300ee96144 100644 (file)
@@ -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;
     }
 
index 3f551f05479de0663569505e337f6dba36c57ea6..ced2f799287cc8ee81e5c15a9ee61a08754bd8fe 100644 (file)
@@ -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;
 
index e1026ffa310a440dbf74f77a6bef0aa53afe7b4f..cad46652b1b88818dcca9976a6a28a3c67b65996 100644 (file)
@@ -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);
index b77c318d1ad5dee183d5b87dd4bfbce8c984d2c3..a785290800a2791467515078a0caa731e0c23c6b 100644 (file)
@@ -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);
index 349ec53e5b7fe4000a15d6d5d5a497706da0ee46..9887380459233e4e6c8ca1fe19ded23e045314e5 100644 (file)
@@ -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;
 
index 00d99308a2854dadb980e12c6307fba19ce7e2ce..d19c456244d55cc98ceeabf1ec568c6652cd4120 100644 (file)
@@ -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();
 
index f394fc71a459c67fb6413eb4c9f52e996c3c2157..a986e27248edb5624ee6f824f01dd127db2e71ac 100644 (file)
@@ -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);
         }
 
index 1ef011b1db29971dd5a146e89b37cf849ed47824..60c154f8d211b871da8fd04556279d78ed724098 100644 (file)
@@ -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;
index 7918bf468f093863e7187d9aa57ad5cc11235a5e..58b97c9b2a0c2fda592895a588c0e0f590f6a3e9 100644 (file)
@@ -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;
 
index 82fd121b572e1a03bd3f682bea0af64a3d3de3f5..de6d7e22c6893355a8c015be40aa83b8d78bfe7a 100644 (file)
@@ -82,6 +82,7 @@
 
 #include "source-pcap.h"
 #include "source-pcap-file.h"
+#include "source-pcap-file-helper.h"
 
 #include "source-pfring.h"
 
 #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 (file)
index 0000000..a214900
--- /dev/null
@@ -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 (file)
index 0000000..7fd782c
--- /dev/null
@@ -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