]> 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>
Tue, 14 Jun 2022 19:12:44 +0000 (21:12 +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.
(cherry picked from commit 8580499deda6bae88034f1961c01179ded91246e)

17 files changed:
src/Makefile.am
src/app-layer-parser.c
src/app-layer.c
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 d2d35f4648eb7d05b727a9d4c8b0de690d562197..1beadab102201bb4033013a68a4e5c6e17a12932 100755 (executable)
@@ -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 \
index 2a41fd35b9ed088e7d8fd6e03b5a388d33ea49a2..e5732cb260133738074ee7b542a3715ae2c72370 100644 (file)
@@ -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();
index 3b9465d67e0261bf0dfbee7e5034000e76cd8d49..08100e240ecd35afae4e24f79bac444d3813d43c 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
@@ -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);
 }
index 317a0122d0c5a5b5e30937a64d47241722d31e1c..c3597abb86b5e2b6af6881bfff24cb56be1260cf 100644 (file)
@@ -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;
             }
 
index 2716a6c9010e8aafc8fc44046bfbe033c029daac..19a9fc0dc6bf3cf6a4dd2b6cfd505047c0e0b488 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 8bda7230d24d3e13df7dfa062ebc7100d4d3d0cd..010bb02121e9932568b83a5eb68bca17bdaea841 100644 (file)
@@ -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;
     }
 
index d4129bfbc6cba88e6f3a47830174491a6856659d..cf539840dc6590e6ab26976875b11de44d3a1983 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;
 
@@ -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;
 
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 8e361251880e1f9a76c4509cc46d14a2d6617bce..36c37847afb16eae4930164d3e5668fab0d198b6 100644 (file)
@@ -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);
index 9adba466df599fea711d09ad194c6ce4a06d0559..e5729ff1d1db2d8bb761e68326d3e75d1e363219 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 558af5c54172ed5b73df510619e7ac8afdaa9eb6..20e4892fce56a39db413aa326625db9fc748b1d9 100644 (file)
@@ -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();
 
index 29d06c7febd1870c70c9a782380f6bbdb5e1ddb3..01306a802e032b486ff64eccc47e213fcc75d30b 100644 (file)
@@ -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);
         }
 
index 57db84c8a770898ddc4ab2a7d40ddb3d10966540..37805eedaabe6149c37a1d620c095c15a1519f00 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"
 
@@ -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;
index 653e8d3aa1d76a6fb2f35adee3aaca14ceded249..a414994a3a9341306a64296478b2e1414e90b9bd 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 33168e1418e66ae0b985f58b646ef9c4407fac05..bb7e929f28210a82f7f1cdb872b019be1653a479 100644 (file)
@@ -86,6 +86,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-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 (file)
index 0000000..0f54cf0
--- /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);
+                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 (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