suricata
util-exception-policy.c
Go to the documentation of this file.
1/* Copyright (C) 2022-2025 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 */
21
23#include "suricata-common.h"
24#include "suricata.h"
25#include "packet.h"
26#include "util-misc.h"
28#include "action-globals.h"
29#include "conf.h"
30#include "flow.h"
31#include "stream-tcp.h"
32#include "defrag-hash.h"
33#include "app-layer-parser.h"
34
36/** true if exception policy was defined in config */
37static bool g_eps_have_exception_policy = false;
38
39const char *ExceptionPolicyEnumToString(enum ExceptionPolicy policy, bool is_json)
40{
41 switch (policy) {
43 return "ignore";
45 return "auto";
47 return "reject";
49 return "bypass";
51 return is_json ? "drop_flow" : "drop-flow";
53 return is_json ? "drop_packet" : "drop-packet";
55 return is_json ? "pass_packet" : "pass-packet";
57 return is_json ? "pass_flow" : "pass-flow";
58 }
59 // TODO we shouldn't reach this, but if we do, better not to leave this as simply null...
60 return "not set";
61}
62
64{
65 g_eps_master_switch = ExceptionPolicyParse("exception-policy", true);
66}
67
68static enum ExceptionPolicy GetMasterExceptionPolicy(void)
69{
71}
72
73static uint8_t ExceptionPolicyFlag(enum PacketDropReason drop_reason)
74{
75 switch (drop_reason) {
88 default:
89 return 0;
90 }
91
92 return 0;
93}
94
95const char *ExceptionPolicyTargetFlagToString(uint8_t target_flag)
96{
97 switch (target_flag) {
99 return "defrag_memcap";
101 return "stream_memcap";
103 return "stream_reassembly_memcap";
105 return "flow_memcap";
107 return "stream_midstream";
109 return "app_layer_error";
110 default:
111 return "none";
112 }
113}
114
135
136void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason)
137{
138 SCLogDebug("start: pcap_cnt %" PRIu64 ", policy %u", p->pcap_cnt, policy);
139 if (p->flow) {
140 p->flow->applied_exception_policy |= ExceptionPolicyFlag(drop_reason);
141 }
142 switch (policy) {
144 break;
146 break;
148 SCLogDebug("EXCEPTION_POLICY_REJECT");
149 PacketDrop(p, ACTION_REJECT, drop_reason);
150 if (!EngineModeIsIPS()) {
151 break;
152 }
153 /* fall through */
155 SCLogDebug("EXCEPTION_POLICY_DROP_FLOW");
156 if (p->flow) {
158 FlowSetNoPayloadInspectionFlag(p->flow);
160 }
161 /* fall through */
163 SCLogDebug("EXCEPTION_POLICY_DROP_PACKET");
164 DecodeSetNoPayloadInspectionFlag(p);
165 DecodeSetNoPacketInspectionFlag(p);
166 PacketDrop(p, ACTION_DROP, drop_reason);
167 break;
170 /* fall through */
172 SCLogDebug("EXCEPTION_POLICY_PASS_FLOW");
173 if (p->flow) {
175 }
176 /* fall through */
178 SCLogDebug("EXCEPTION_POLICY_PASS_PACKET");
179 DecodeSetNoPayloadInspectionFlag(p);
180 DecodeSetNoPacketInspectionFlag(p);
181 break;
182 }
183 SCLogDebug("end");
184}
185
186static enum ExceptionPolicy PickPacketAction(const char *option, enum ExceptionPolicy p)
187{
188 switch (p) {
191 "flow actions not supported for %s, defaulting to \"drop-packet\"", option);
195 "flow actions not supported for %s, defaulting to \"pass-packet\"", option);
198 SCLogWarning("flow actions not supported for %s, defaulting to \"ignore\"", option);
200 /* add all cases, to make sure new cases not handle will raise
201 * errors */
203 break;
205 break;
207 break;
209 break;
211 break;
212 }
213 return p;
214}
215
216static enum ExceptionPolicy ExceptionPolicyConfigValueParse(
217 const char *option, const char *value_str)
218{
220 if (strcmp(value_str, "drop-flow") == 0) {
222 } else if (strcmp(value_str, "pass-flow") == 0) {
224 } else if (strcmp(value_str, "bypass") == 0) {
226 } else if (strcmp(value_str, "drop-packet") == 0) {
228 } else if (strcmp(value_str, "pass-packet") == 0) {
230 } else if (strcmp(value_str, "reject") == 0) {
232 } else if (strcmp(value_str, "ignore") == 0) { // TODO name?
234 } else if (strcmp(value_str, "auto") == 0) {
235 policy = EXCEPTION_POLICY_AUTO;
236 } else {
238 "\"%s\" is not a valid exception policy value. Valid options are drop-flow, "
239 "pass-flow, bypass, reject, drop-packet, pass-packet, ignore or auto.",
240 value_str);
241 }
242
243 return policy;
244}
245
246/* Select an exception policy in case the configuration value was set to 'auto' */
247static enum ExceptionPolicy ExceptionPolicyPickAuto(bool midstream_enabled, bool support_flow)
248{
250 if (!midstream_enabled && EngineModeIsIPS()) {
251 if (support_flow) {
253 } else {
255 }
256 }
257 return policy;
258}
259
260static enum ExceptionPolicy ExceptionPolicyMasterParse(const char *value)
261{
262 enum ExceptionPolicy policy = ExceptionPolicyConfigValueParse("exception-policy", value);
263 if (!EngineModeIsIPS() &&
266 }
267 g_eps_have_exception_policy = true;
268
269 SCLogInfo("master exception-policy set to: %s", ExceptionPolicyEnumToString(policy, false));
270
271 return policy;
272}
273
274static enum ExceptionPolicy ExceptionPolicyGetDefault(
275 const char *option, bool support_flow, bool midstream)
276{
278 if (g_eps_have_exception_policy) {
279 p = GetMasterExceptionPolicy();
280
281 if (p == EXCEPTION_POLICY_AUTO) {
282 p = ExceptionPolicyPickAuto(midstream, support_flow);
283 }
284
285 if (!support_flow) {
286 p = PickPacketAction(option, p);
287 }
288 SCLogConfig("%s: %s (defined via 'exception-policy' master switch)", option,
290 return p;
291 } else if (EngineModeIsIPS() && !midstream) {
293 }
294 SCLogConfig("%s: %s (defined via 'built-in default' for %s-mode)", option,
295 ExceptionPolicyEnumToString(p, false), EngineModeIsIPS() ? "IPS" : "IDS");
296
297 return p;
298}
299
300enum ExceptionPolicy ExceptionPolicyParse(const char *option, bool support_flow)
301{
303 const char *value_str = NULL;
304
305 if ((SCConfGet(option, &value_str) == 1) && value_str != NULL) {
306 if (strcmp(option, "exception-policy") == 0) {
307 policy = ExceptionPolicyMasterParse(value_str);
308 } else {
309 policy = ExceptionPolicyConfigValueParse(option, value_str);
310 if (policy == EXCEPTION_POLICY_AUTO) {
311 policy = ExceptionPolicyPickAuto(false, support_flow);
312 }
313 if (!support_flow) {
314 policy = PickPacketAction(option, policy);
315 }
316 SCLogConfig("%s: %s", option, ExceptionPolicyEnumToString(policy, false));
317 }
318 } else {
319 policy = ExceptionPolicyGetDefault(option, support_flow, false);
320 }
321
322 return policy;
323}
324
326{
328 const char *value_str = NULL;
329 /* policy was set directly */
330 if ((SCConfGet("stream.midstream-policy", &value_str)) == 1 && value_str != NULL) {
331 policy = ExceptionPolicyConfigValueParse("midstream-policy", value_str);
332 if (policy == EXCEPTION_POLICY_AUTO) {
333 policy = ExceptionPolicyPickAuto(midstream_enabled, true);
334 } else if (midstream_enabled) {
335 if (policy != EXCEPTION_POLICY_NOT_SET && policy != EXCEPTION_POLICY_PASS_FLOW) {
337 "Error parsing stream.midstream-policy from config file. \"%s\" is "
338 "not a valid exception policy when midstream is enabled. Valid options "
339 "are pass-flow and ignore.",
340 value_str);
341 }
342 }
343 if (!EngineModeIsIPS()) {
344 if (policy == EXCEPTION_POLICY_DROP_FLOW) {
346 "Error parsing stream.midstream-policy from config file. \"%s\" is "
347 "not a valid exception policy in IDS mode. See our documentation for a "
348 "list of all possible values.",
349 value_str);
350 }
351 }
352 } else {
353 policy = ExceptionPolicyGetDefault("stream.midstream-policy", true, midstream_enabled);
354 }
355
357 FatalErrorOnInit("Error parsing stream.midstream-policy from config file. \"%s\" is "
358 "not valid for this exception policy. See our documentation for a list of "
359 "all possible values.",
360 value_str);
361 }
362
363 return policy;
364}
365
367 ExceptionPolicyStatsSetts *setting, enum ExceptionPolicy conf_policy,
368 const char *default_str, bool (*isExceptionPolicyValid)(enum ExceptionPolicy))
369{
370 if (conf_policy != EXCEPTION_POLICY_NOT_SET) {
371 /* set-up policy counters */
373 if (isExceptionPolicyValid(i)) {
374 snprintf(setting->eps_name[i], sizeof(setting->eps_name[i]), "%s%s", default_str,
376 counter->eps_id[i] = StatsRegisterCounter(setting->eps_name[i], tv);
377 }
378 }
379 }
380}
381
382#ifndef DEBUG
383
384int ExceptionSimulationCommandLineParser(const char *name, const char *arg)
385{
386 return 0;
387}
388
389#else
390
391/* exception policy simulation (eps) handling */
392
393uint64_t g_eps_applayer_error_offset_ts = UINT64_MAX;
394uint64_t g_eps_applayer_error_offset_tc = UINT64_MAX;
395uint64_t g_eps_pcap_packet_loss = UINT64_MAX;
396uint64_t g_eps_stream_ssn_memcap = UINT64_MAX;
397uint64_t g_eps_stream_reassembly_memcap = UINT64_MAX;
398uint64_t g_eps_flow_memcap = UINT64_MAX;
399uint64_t g_eps_defrag_memcap = UINT64_MAX;
400bool g_eps_is_alert_queue_fail_mode = false;
401
402/* 1: parsed, 0: not for us, -1: error */
403int ExceptionSimulationCommandLineParser(const char *name, const char *arg)
404{
405 if (strcmp(name, "simulate-applayer-error-at-offset-ts") == 0) {
406 BUG_ON(arg == NULL);
407 uint64_t offset = 0;
408 if (ParseSizeStringU64(arg, &offset) < 0) {
409 return -1;
410 }
411 g_eps_applayer_error_offset_ts = offset;
412 } else if (strcmp(name, "simulate-applayer-error-at-offset-tc") == 0) {
413 BUG_ON(arg == NULL);
414 uint64_t offset = 0;
415 if (ParseSizeStringU64(arg, &offset) < 0) {
416 return TM_ECODE_FAILED;
417 }
418 g_eps_applayer_error_offset_tc = offset;
419 } else if (strcmp(name, "simulate-packet-loss") == 0) {
420 BUG_ON(arg == NULL);
421 uint64_t pkt_num = 0;
422 if (ParseSizeStringU64(arg, &pkt_num) < 0) {
423 return TM_ECODE_FAILED;
424 }
425 g_eps_pcap_packet_loss = pkt_num;
426 } else if (strcmp(name, "simulate-packet-tcp-reassembly-memcap") == 0) {
427 BUG_ON(arg == NULL);
428 uint64_t pkt_num = 0;
429 if (ParseSizeStringU64(arg, &pkt_num) < 0) {
430 return TM_ECODE_FAILED;
431 }
432 g_eps_stream_reassembly_memcap = pkt_num;
433 } else if (strcmp(name, "simulate-packet-tcp-ssn-memcap") == 0) {
434 BUG_ON(arg == NULL);
435 uint64_t pkt_num = 0;
436 if (ParseSizeStringU64(arg, &pkt_num) < 0) {
437 return TM_ECODE_FAILED;
438 }
439 g_eps_stream_ssn_memcap = pkt_num;
440 } else if (strcmp(name, "simulate-packet-flow-memcap") == 0) {
441 BUG_ON(arg == NULL);
442 uint64_t pkt_num = 0;
443 if (ParseSizeStringU64(arg, &pkt_num) < 0) {
444 return TM_ECODE_FAILED;
445 }
446 g_eps_flow_memcap = pkt_num;
447 } else if (strcmp(name, "simulate-packet-defrag-memcap") == 0) {
448 BUG_ON(arg == NULL);
449 uint64_t pkt_num = 0;
450 if (ParseSizeStringU64(arg, &pkt_num) < 0) {
451 return TM_ECODE_FAILED;
452 }
453 g_eps_defrag_memcap = pkt_num;
454 } else if (strcmp(name, "simulate-alert-queue-realloc-failure") == 0) {
455 g_eps_is_alert_queue_fail_mode = true;
456 } else {
457 // not for us
458 return 0;
459 }
460 return 1;
461}
462#endif
#define ACTION_REJECT
#define ACTION_DROP
enum ExceptionPolicy AppLayerErrorGetExceptionPolicy(void)
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition conf.c:350
uint16_t StatsRegisterCounter(const char *name, struct ThreadVars_ *tv)
Registers a normal, unqualified counter.
Definition counters.c:952
PacketDropReason
Definition decode.h:380
@ PKT_DROP_REASON_STREAM_MEMCAP
Definition decode.h:392
@ PKT_DROP_REASON_DEFRAG_MEMCAP
Definition decode.h:384
@ PKT_DROP_REASON_STREAM_REASSEMBLY
Definition decode.h:394
@ PKT_DROP_REASON_APPLAYER_ERROR
Definition decode.h:387
@ PKT_DROP_REASON_STREAM_MIDSTREAM
Definition decode.h:393
@ PKT_DROP_REASON_FLOW_MEMCAP
Definition decode.h:385
enum ExceptionPolicy DefragGetMemcapExceptionPolicy(void)
Definition defrag-hash.c:80
enum ExceptionPolicy FlowGetMemcapExceptionPolicy(void)
Definition flow.c:134
#define FLOW_ACTION_DROP
Definition flow.h:70
#define FLOW_ACTION_PASS
Definition flow.h:117
ThreadVars * tv
void PacketBypassCallback(Packet *p)
Definition decode.c:530
void PacketDrop(Packet *p, const uint8_t action, enum PacketDropReason r)
issue drop action
Definition packet.c:33
void StreamTcpDisableAppLayer(Flow *f)
enum ExceptionPolicy StreamTcpSsnMemcapGetExceptionPolicy(void)
Definition stream-tcp.c:899
enum ExceptionPolicy StreamTcpReassemblyMemcapGetExceptionPolicy(void)
Definition stream-tcp.c:904
enum ExceptionPolicy StreamMidstreamGetExceptionPolicy(void)
Definition stream-tcp.c:909
uint16_t eps_id[EXCEPTION_POLICY_MAX]
char eps_name[EXCEPTION_POLICY_MAX][EXCEPTION_POLICY_COUNTER_MAX_LEN]
uint8_t applied_exception_policy
Definition flow.h:473
uint32_t flags
Definition flow.h:421
uint64_t pcap_cnt
Definition decode.h:626
struct Flow_ * flow
Definition decode.h:546
Per thread variable structure.
Definition threadvars.h:58
#define BUG_ON(x)
int EngineModeIsIPS(void)
Definition suricata.c:242
@ TM_ECODE_FAILED
const char * name
#define FatalErrorOnInit(...)
Fatal error IF we're starting up, and configured to consider errors to be fatal errors.
Definition util-debug.h:519
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogConfig(...)
Definition util-debug.h:229
#define EXCEPTION_TARGET_FLAG_DEFRAG_MEMCAP
#define EXCEPTION_TARGET_FLAG_REASSEMBLY_MEMCAP
#define EXCEPTION_TARGET_FLAG_APPLAYER_ERROR
#define EXCEPTION_TARGET_FLAG_MIDSTREAM
#define EXCEPTION_POLICY_MAX
#define EXCEPTION_TARGET_FLAG_FLOW_MEMCAP
#define EXCEPTION_TARGET_FLAG_SESSION_MEMCAP
@ EXCEPTION_POLICY_BYPASS_FLOW
@ EXCEPTION_POLICY_PASS_FLOW
@ EXCEPTION_POLICY_PASS_PACKET
@ EXCEPTION_POLICY_DROP_FLOW
@ EXCEPTION_POLICY_DROP_PACKET
const char * ExceptionPolicyTargetFlagToString(uint8_t target_flag)
enum ExceptionPolicy ExceptionPolicyTargetPolicy(uint8_t target_flag)
const char * ExceptionPolicyEnumToString(enum ExceptionPolicy policy, bool is_json)
enum ExceptionPolicy ExceptionPolicyMidstreamParse(bool midstream_enabled)
int ExceptionSimulationCommandLineParser(const char *name, const char *arg)
enum ExceptionPolicy ExceptionPolicyParse(const char *option, bool support_flow)
enum ExceptionPolicy g_eps_master_switch
void ExceptionPolicySetStatsCounters(ThreadVars *tv, ExceptionPolicyCounters *counter, ExceptionPolicyStatsSetts *setting, enum ExceptionPolicy conf_policy, const char *default_str, bool(*isExceptionPolicyValid)(enum ExceptionPolicy))
void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDropReason drop_reason)
void SetMasterExceptionPolicy(void)
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition util-misc.c:190
uint64_t offset