suricata
output-json-drop.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2023 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 * \author Tom DeCanio <td@npulsetech.com>
22 *
23 * JSON Drop log module to log the dropped packet information
24 *
25 */
26
27#include "suricata-common.h"
28#include "packet.h"
29#include "detect.h"
30#include "flow.h"
31#include "conf.h"
32
33#include "threads.h"
34#include "tm-threads.h"
35#include "threadvars.h"
36#include "util-debug.h"
37
38#include "decode-ipv4.h"
39#include "detect-parse.h"
40#include "detect-engine.h"
41#include "detect-engine-mpm.h"
42#include "detect-reference.h"
43
44#include "output.h"
45#include "output-json.h"
46#include "output-json-alert.h"
47#include "output-json-drop.h"
48
49#include "util-unittest.h"
52#include "util-privs.h"
53#include "util-print.h"
54#include "util-proto-name.h"
55#include "util-logopenfile.h"
56#include "util-time.h"
57#include "util-buffer.h"
58
59#include "action-globals.h"
60
61#define MODULE_NAME "JsonDropLog"
62
63#define LOG_DROP_ALERTS BIT_U8(1)
64#define LOG_DROP_VERDICT BIT_U8(2)
65
70
75
76/* default to true as this has been the default behavior for a long time */
77static int g_droplog_flows_start = 1;
78
79/**
80 * \brief Log the dropped packets in netfilter format when engine is running
81 * in inline mode
82 *
83 * \param tv Pointer the current thread variables
84 * \param p Pointer the packet which is being logged
85 *
86 * \return return TM_ECODE_OK on success
87 */
88static int DropLogJSON(ThreadVars *tv, JsonDropLogThread *aft, const Packet *p)
89{
90 JsonDropOutputCtx *drop_ctx = aft->drop_ctx;
91
94
95 SCJsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, "drop", &addr, drop_ctx->eve_ctx);
96 if (unlikely(js == NULL))
97 return TM_ECODE_OK;
98
99 if (p->flow != NULL) {
100 if (p->flowflags & FLOW_PKT_TOSERVER) {
101 SCJbSetString(js, "direction", "to_server");
102 } else {
103 SCJbSetString(js, "direction", "to_client");
104 }
105 }
106
107 SCJbOpenObject(js, "drop");
108
109 uint16_t proto = 0;
110 if (PacketIsIPv4(p)) {
111 const IPV4Hdr *ip4h = PacketGetIPv4(p);
112 SCJbSetUint(js, "len", IPV4_GET_RAW_IPLEN(ip4h));
113 SCJbSetUint(js, "tos", IPV4_GET_RAW_IPTOS(ip4h));
114 SCJbSetUint(js, "ttl", IPV4_GET_RAW_IPTTL(ip4h));
115 SCJbSetUint(js, "ipid", IPV4_GET_RAW_IPID(ip4h));
117 } else if (PacketIsIPv6(p)) {
118 const IPV6Hdr *ip6h = PacketGetIPv6(p);
119 SCJbSetUint(js, "len", IPV6_GET_RAW_PLEN(ip6h));
120 SCJbSetUint(js, "tc", IPV6_GET_RAW_CLASS(ip6h));
121 SCJbSetUint(js, "hoplimit", IPV6_GET_RAW_HLIM(ip6h));
122 SCJbSetUint(js, "flowlbl", IPV6_GET_RAW_FLOW(ip6h));
124 }
125 switch (proto) {
126 case IPPROTO_TCP:
127 if (PacketIsTCP(p)) {
128 const TCPHdr *tcph = PacketGetTCP(p);
129 SCJbSetUint(js, "tcpseq", TCP_GET_RAW_SEQ(tcph));
130 SCJbSetUint(js, "tcpack", TCP_GET_RAW_ACK(tcph));
131 SCJbSetUint(js, "tcpwin", TCP_GET_RAW_WINDOW(tcph));
132 SCJbSetBool(js, "syn", TCP_ISSET_FLAG_RAW_SYN(tcph) ? true : false);
133 SCJbSetBool(js, "ack", TCP_ISSET_FLAG_RAW_ACK(tcph) ? true : false);
134 SCJbSetBool(js, "psh", TCP_ISSET_FLAG_RAW_PUSH(tcph) ? true : false);
135 SCJbSetBool(js, "rst", TCP_ISSET_FLAG_RAW_RST(tcph) ? true : false);
136 SCJbSetBool(js, "urg", TCP_ISSET_FLAG_RAW_URG(tcph) ? true : false);
137 SCJbSetBool(js, "fin", TCP_ISSET_FLAG_RAW_FIN(tcph) ? true : false);
138 SCJbSetUint(js, "tcpres", TCP_GET_RAW_X2(tcph));
139 SCJbSetUint(js, "tcpurgp", TCP_GET_RAW_URG_POINTER(tcph));
140 }
141 break;
142 case IPPROTO_UDP:
143 if (PacketIsUDP(p)) {
144 const UDPHdr *udph = PacketGetUDP(p);
145 SCJbSetUint(js, "udplen", UDP_GET_RAW_LEN(udph));
146 }
147 break;
148 case IPPROTO_ICMP:
149 if (PacketIsICMPv4(p)) {
150 SCJbSetUint(js, "icmp_id", ICMPV4_GET_ID(p));
151 SCJbSetUint(js, "icmp_seq", ICMPV4_GET_SEQ(p));
152 } else if (PacketIsICMPv6(p)) {
153 SCJbSetUint(js, "icmp_id", ICMPV6_GET_ID(p));
154 SCJbSetUint(js, "icmp_seq", ICMPV6_GET_SEQ(p));
155 }
156 break;
157 }
158 if (p->drop_reason != 0) {
159 const char *str = PacketDropReasonToString(p->drop_reason);
160 SCJbSetString(js, "reason", str);
161 }
162
163 /* Close drop. */
164 SCJbClose(js);
165
166 if (aft->drop_ctx->flags & LOG_DROP_VERDICT) {
167 EveAddVerdict(js, p);
168 }
169
170 if (aft->drop_ctx->flags & LOG_DROP_ALERTS) {
171 int logged = 0;
172 int i;
173 for (i = 0; i < p->alerts.cnt; i++) {
174 const PacketAlert *pa = &p->alerts.alerts[i];
175 if (unlikely(pa->s == NULL)) {
176 continue;
177 }
179 ((pa->action & ACTION_DROP) && EngineModeIsIPS()))
180 {
181 AlertJsonHeader(p, pa, js, 0, &addr, NULL);
182 logged = 1;
183 break;
184 }
185 }
186 if (logged == 0) {
187 if (p->alerts.drop.action != 0) {
188 const PacketAlert *pa = &p->alerts.drop;
189 AlertJsonHeader(p, pa, js, 0, &addr, NULL);
190 }
191 }
192 }
193
194 OutputJsonBuilderBuffer(tv, p, p->flow, js, aft->ctx);
195 SCJbFree(js);
196
197 return TM_ECODE_OK;
198}
199
200static TmEcode JsonDropLogThreadInit(ThreadVars *t, const void *initdata, void **data)
201{
203 if (unlikely(aft == NULL))
204 return TM_ECODE_FAILED;
205
206 if(initdata == NULL)
207 {
208 SCLogDebug("Error getting context for EveLogDrop. \"initdata\" argument NULL");
209 goto error_exit;
210 }
211
212 /** Use the Output Context (file pointer and mutex) */
213 aft->drop_ctx = ((OutputCtx *)initdata)->data;
214 aft->ctx = CreateEveThreadCtx(t, aft->drop_ctx->eve_ctx);
215 if (!aft->ctx) {
216 goto error_exit;
217 }
218
219 *data = (void *)aft;
220 return TM_ECODE_OK;
221
222error_exit:
223 SCFree(aft);
224 return TM_ECODE_FAILED;
225}
226
227static TmEcode JsonDropLogThreadDeinit(ThreadVars *t, void *data)
228{
230 if (aft == NULL) {
231 return TM_ECODE_OK;
232 }
233
234 FreeEveThreadCtx(aft->ctx);
235
236 /* clear memory */
237 memset(aft, 0, sizeof(*aft));
238
239 SCFree(aft);
240 return TM_ECODE_OK;
241}
242
243static void JsonDropOutputCtxFree(JsonDropOutputCtx *drop_ctx)
244{
245 if (drop_ctx != NULL) {
246 SCFree(drop_ctx);
247 }
248}
249
250static void JsonDropLogDeInitCtxSub(OutputCtx *output_ctx)
251{
253
254 JsonDropOutputCtx *drop_ctx = output_ctx->data;
255 SCFree(drop_ctx);
256 SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
257 SCFree(output_ctx);
258}
259
260static OutputInitResult JsonDropLogInitCtxSub(SCConfNode *conf, OutputCtx *parent_ctx)
261{
262 OutputInitResult result = { NULL, false };
263 if (OutputDropLoggerEnable() != 0) {
264 SCLogError("only one 'drop' logger "
265 "can be enabled");
266 return result;
267 }
268
269 OutputJsonCtx *ajt = parent_ctx->data;
270
271 JsonDropOutputCtx *drop_ctx = SCCalloc(1, sizeof(*drop_ctx));
272 if (drop_ctx == NULL)
273 return result;
274
275 OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
276 if (unlikely(output_ctx == NULL)) {
277 JsonDropOutputCtxFree(drop_ctx);
278 return result;
279 }
280
281 if (conf) {
282 const char *extended = SCConfNodeLookupChildValue(conf, "alerts");
283 if (extended != NULL) {
284 if (SCConfValIsTrue(extended)) {
285 drop_ctx->flags |= LOG_DROP_ALERTS;
286 }
287 }
288 extended = SCConfNodeLookupChildValue(conf, "flows");
289 if (extended != NULL) {
290 if (strcasecmp(extended, "start") == 0) {
291 g_droplog_flows_start = 1;
292 } else if (strcasecmp(extended, "all") == 0) {
293 g_droplog_flows_start = 0;
294 } else {
295 SCLogWarning("valid options for "
296 "'flow' are 'start' and 'all'");
297 }
298 }
299 extended = SCConfNodeLookupChildValue(conf, "verdict");
300 if (extended != NULL) {
301 if (SCConfValIsTrue(extended)) {
302 drop_ctx->flags |= LOG_DROP_VERDICT;
303 }
304 }
305 }
306
307 drop_ctx->eve_ctx = ajt;
308
309 output_ctx->data = drop_ctx;
310 output_ctx->DeInit = JsonDropLogDeInitCtxSub;
311
312 result.ctx = output_ctx;
313 result.ok = true;
314 return result;
315}
316
317/**
318 * \brief Log the dropped packets when engine is running in inline mode
319 *
320 * \param tv Pointer the current thread variables
321 * \param data Pointer to the droplog struct
322 * \param p Pointer the packet which is being logged
323 *
324 * \retval 0 on success
325 */
326static int JsonDropLogger(ThreadVars *tv, void *thread_data, const Packet *p)
327{
328 JsonDropLogThread *td = thread_data;
329 int r = DropLogJSON(tv, td, p);
330 if (r < 0)
331 return -1;
332
333 if (!g_droplog_flows_start)
334 return 0;
335
336 if (p->flow) {
337 if (p->flow->flags & FLOW_ACTION_DROP) {
340 else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
342 }
343 }
344 return 0;
345}
346
347/**
348 * \brief Check if we need to drop-log this packet
349 *
350 * \param tv Pointer the current thread variables
351 * \param p Pointer the packet which is tested
352 *
353 * \retval bool true or false
354 */
355static bool JsonDropLogCondition(ThreadVars *tv, void *data, const Packet *p)
356{
357 if (!EngineModeIsIPS()) {
358 SCLogDebug("engine is not running in inline mode, so returning");
359 return false;
360 }
361 if (PKT_IS_PSEUDOPKT(p)) {
362 SCLogDebug("drop log doesn't log pseudo packets");
363 return false;
364 }
365
366 if (!(PacketCheckAction(p, ACTION_DROP))) {
367 return false;
368 }
369
370 if (g_droplog_flows_start && p->flow != NULL) {
371 bool ret = false;
372
373 /* for a flow that will be dropped fully, log just once per direction */
374 if (p->flow->flags & FLOW_ACTION_DROP) {
376 ret = true;
377 else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
378 ret = true;
379 }
380
381 /* if drop is caused by signature, log anyway */
382 if (p->alerts.drop.action != 0)
383 ret = true;
384
385 return ret;
386 }
387
388 return true;
389}
390
392{
393 OutputPacketLoggerFunctions output_logger_functions = {
394 .LogFunc = JsonDropLogger,
395 .FlushFunc = OutputJsonLogFlush,
396 .ConditionFunc = JsonDropLogCondition,
397 .ThreadInitFunc = JsonDropLogThreadInit,
398 .ThreadDeinitFunc = JsonDropLogThreadDeinit,
399 .ThreadExitPrintStatsFunc = NULL,
400 };
401
403 JsonDropLogInitCtxSub, &output_logger_functions);
404}
#define ACTION_REJECT
#define ACTION_REJECT_BOTH
#define ACTION_REJECT_DST
#define ACTION_DROP
int logged
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
#define ICMPV4_GET_ID(p)
#define ICMPV4_GET_SEQ(p)
#define ICMPV6_GET_ID(p)
#define ICMPV6_GET_SEQ(p)
#define IPV4_GET_RAW_IPTOS(ip4h)
Definition decode-ipv4.h:97
#define IPV4_GET_RAW_IPLEN(ip4h)
Definition decode-ipv4.h:98
#define IPV4_GET_RAW_IPTTL(ip4h)
#define IPV4_GET_RAW_IPID(ip4h)
Definition decode-ipv4.h:99
#define IPV4_GET_RAW_IPPROTO(ip4h)
#define IPV6_GET_RAW_PLEN(ip6h)
Definition decode-ipv6.h:66
#define IPV6_GET_RAW_HLIM(ip6h)
Definition decode-ipv6.h:67
#define IPV6_GET_L4PROTO(p)
Definition decode-ipv6.h:75
#define IPV6_GET_RAW_FLOW(ip6h)
Definition decode-ipv6.h:64
#define IPV6_GET_RAW_CLASS(ip6h)
Definition decode-ipv6.h:63
#define TCP_GET_RAW_SEQ(tcph)
Definition decode-tcp.h:80
#define TCP_ISSET_FLAG_RAW_PUSH(tcph)
Definition decode-tcp.h:122
#define TCP_ISSET_FLAG_RAW_ACK(tcph)
Definition decode-tcp.h:123
#define TCP_GET_RAW_ACK(tcph)
Definition decode-tcp.h:81
#define TCP_ISSET_FLAG_RAW_FIN(tcph)
Definition decode-tcp.h:119
#define TCP_GET_RAW_X2(tcph)
Definition decode-tcp.h:73
#define TCP_GET_RAW_URG_POINTER(tcph)
Definition decode-tcp.h:84
#define TCP_ISSET_FLAG_RAW_RST(tcph)
Definition decode-tcp.h:121
#define TCP_ISSET_FLAG_RAW_SYN(tcph)
Definition decode-tcp.h:120
#define TCP_GET_RAW_WINDOW(tcph)
Definition decode-tcp.h:83
#define TCP_ISSET_FLAG_RAW_URG(tcph)
Definition decode-tcp.h:124
uint8_t proto
#define UDP_GET_RAW_LEN(udph)
Definition decode-udp.h:30
#define PKT_IS_TOCLIENT(p)
Definition decode.h:239
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
#define PKT_IS_TOSERVER(p)
Definition decode.h:238
#define FLOW_PKT_TOSERVER
Definition flow.h:233
#define FLOW_ACTION_DROP
Definition flow.h:70
#define FLOW_TOCLIENT_DROP_LOGGED
Definition flow.h:80
#define FLOW_TOSERVER_DROP_LOGGED
Definition flow.h:78
ThreadVars * tv
const char * PacketDropReasonToString(enum PacketDropReason r)
Definition decode.c:903
@ LOG_DIR_PACKET
void EveAddVerdict(SCJsonBuilder *jb, const Packet *p)
Build verdict object.
void AlertJsonHeader(const Packet *p, const PacketAlert *pa, SCJsonBuilder *js, uint16_t flags, JsonAddrInfo *addr, char *xff_buffer)
int OutputJsonLogFlush(ThreadVars *tv, void *thread_data, const Packet *p)
OutputJsonThreadCtx * CreateEveThreadCtx(ThreadVars *t, OutputJsonCtx *ctx)
void FreeEveThreadCtx(OutputJsonThreadCtx *ctx)
#define MODULE_NAME
struct JsonDropLogThread_ JsonDropLogThread
struct JsonDropOutputCtx_ JsonDropOutputCtx
#define LOG_DROP_ALERTS
#define LOG_DROP_VERDICT
void JsonDropLogRegister(void)
SCJsonBuilder * CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx)
void JsonAddrInfoInit(const Packet *p, enum SCOutputJsonLogDirection dir, JsonAddrInfo *addr)
const JsonAddrInfo json_addr_info_zero
Definition output-json.c:81
void OutputJsonBuilderBuffer(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
void OutputRegisterPacketSubModule(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, OutputPacketLoggerFunctions *output_logger_functions)
Register a packet output sub-module.
Definition output.c:234
int OutputDropLoggerEnable(void)
Definition output.c:672
void OutputDropLoggerDisable(void)
Definition output.c:680
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition packet.c:49
uint32_t flags
Definition flow.h:421
JsonDropOutputCtx * drop_ctx
OutputJsonThreadCtx * ctx
OutputJsonCtx * eve_ctx
void * data
Definition tm-modules.h:91
void(* DeInit)(struct OutputCtx_ *)
Definition tm-modules.h:94
OutputCtx * ctx
Definition output.h:47
const struct Signature_ * s
Definition decode.h:252
uint8_t action
Definition decode.h:250
uint16_t cnt
Definition decode.h:287
PacketAlert * alerts
Definition decode.h:290
PacketAlert drop
Definition decode.h:293
uint8_t flowflags
Definition decode.h:532
uint8_t drop_reason
Definition decode.h:647
PacketAlerts alerts
Definition decode.h:620
struct Flow_ * flow
Definition decode.h:546
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_JSON_DROP
#define str(s)
int EngineModeIsIPS(void)
Definition suricata.c:242
@ TM_ECODE_FAILED
@ TM_ECODE_OK
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)