suricata
output-json-dnp3.c
Go to the documentation of this file.
1 /* Copyright (C) 2015-2021 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 #include "suricata-common.h"
19 #include "detect.h"
20 #include "pkt-var.h"
21 #include "conf.h"
22 
23 #include "threads.h"
24 #include "threadvars.h"
25 #include "tm-threads.h"
26 
27 #include "util-print.h"
28 #include "util-buffer.h"
29 #include "util-debug.h"
30 
31 #include "app-layer.h"
32 #include "app-layer-parser.h"
33 #include "app-layer-dnp3.h"
34 #include "app-layer-dnp3-objects.h"
35 
36 #include "detect-dnp3.h"
37 
38 #include "output.h"
39 #include "output-json.h"
40 #include "output-json-dnp3.h"
42 
43 typedef struct LogDNP3FileCtx_ {
44  uint32_t flags;
48 
49 typedef struct LogDNP3LogThread_ {
53 
54 static void JsonDNP3LogLinkControl(SCJsonBuilder *js, uint8_t lc)
55 {
56  SCJbSetBool(js, "dir", DNP3_LINK_DIR(lc));
57  SCJbSetBool(js, "pri", DNP3_LINK_PRI(lc));
58  SCJbSetBool(js, "fcb", DNP3_LINK_FCB(lc));
59  SCJbSetBool(js, "fcv", DNP3_LINK_FCV(lc));
60  SCJbSetUint(js, "function_code", DNP3_LINK_FC(lc));
61 }
62 
63 static void JsonDNP3LogApplicationControl(SCJsonBuilder *js, uint8_t ac)
64 {
65  SCJbSetBool(js, "fir", DNP3_APP_FIR(ac));
66  SCJbSetBool(js, "fin", DNP3_APP_FIN(ac));
67  SCJbSetBool(js, "con", DNP3_APP_CON(ac));
68  SCJbSetBool(js, "uns", DNP3_APP_UNS(ac));
69  SCJbSetUint(js, "sequence", DNP3_APP_SEQ(ac));
70 }
71 
72 /**
73  * \brief Log the items (points) for an object.
74  *
75  * TODO: Autogenerate this function based on object definitions.
76  */
77 static void JsonDNP3LogObjectItems(SCJsonBuilder *js, DNP3Object *object)
78 {
79  DNP3Point *item;
80 
81  TAILQ_FOREACH(item, object->points, next) {
82  SCJbStartObject(js);
83 
84  SCJbSetUint(js, "prefix", item->prefix);
85  SCJbSetUint(js, "index", item->index);
86  if (DNP3PrefixIsSize(object->prefix_code)) {
87  SCJbSetUint(js, "size", item->size);
88  }
89 
90  OutputJsonDNP3SetItem(js, object, item);
91  SCJbClose(js);
92  }
93 }
94 
95 /**
96  * \brief Log the application layer objects.
97  *
98  * \param objects A list of DNP3 objects.
99  * \param jb A SCJsonBuilder instance with an open array.
100  */
101 static void JsonDNP3LogObjects(SCJsonBuilder *js, DNP3ObjectList *objects)
102 {
103  DNP3Object *object;
104 
105  TAILQ_FOREACH(object, objects, next) {
106  SCJbStartObject(js);
107  SCJbSetUint(js, "group", object->group);
108  SCJbSetUint(js, "variation", object->variation);
109  SCJbSetUint(js, "qualifier", object->qualifier);
110  SCJbSetUint(js, "prefix_code", object->prefix_code);
111  SCJbSetUint(js, "range_code", object->range_code);
112  SCJbSetUint(js, "start", object->start);
113  SCJbSetUint(js, "stop", object->stop);
114  SCJbSetUint(js, "count", object->count);
115 
116  if (object->points != NULL && !TAILQ_EMPTY(object->points)) {
117  SCJbOpenArray(js, "points");
118  JsonDNP3LogObjectItems(js, object);
119  SCJbClose(js);
120  }
121 
122  SCJbClose(js);
123  }
124 }
125 
126 static void JsonDNP3LogRequest(SCJsonBuilder *js, DNP3Transaction *dnp3tx)
127 {
128  JB_SET_STRING(js, "type", "request");
129 
130  SCJbOpenObject(js, "control");
131  JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
132  SCJbClose(js);
133 
134  SCJbSetUint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
135  SCJbSetUint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
136 
137  SCJbOpenObject(js, "application");
138 
139  SCJbOpenObject(js, "control");
140  JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
141  SCJbClose(js);
142 
143  SCJbSetUint(js, "function_code", dnp3tx->ah.function_code);
144 
145  if (!TAILQ_EMPTY(&dnp3tx->objects)) {
146  SCJbOpenArray(js, "objects");
147  JsonDNP3LogObjects(js, &dnp3tx->objects);
148  SCJbClose(js);
149  }
150 
151  SCJbSetBool(js, "complete", dnp3tx->complete);
152 
153  /* Close application. */
154  SCJbClose(js);
155 }
156 
157 static void JsonDNP3LogResponse(SCJsonBuilder *js, DNP3Transaction *dnp3tx)
158 {
159  if (dnp3tx->ah.function_code == DNP3_APP_FC_UNSOLICITED_RESP) {
160  JB_SET_STRING(js, "type", "unsolicited_response");
161  } else {
162  JB_SET_STRING(js, "type", "response");
163  }
164 
165  SCJbOpenObject(js, "control");
166  JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
167  SCJbClose(js);
168 
169  SCJbSetUint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
170  SCJbSetUint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
171 
172  SCJbOpenObject(js, "application");
173 
174  SCJbOpenObject(js, "control");
175  JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
176  SCJbClose(js);
177 
178  SCJbSetUint(js, "function_code", dnp3tx->ah.function_code);
179 
180  if (!TAILQ_EMPTY(&dnp3tx->objects)) {
181  SCJbOpenArray(js, "objects");
182  JsonDNP3LogObjects(js, &dnp3tx->objects);
183  SCJbClose(js);
184  }
185 
186  SCJbSetBool(js, "complete", dnp3tx->complete);
187 
188  /* Close application. */
189  SCJbClose(js);
190 
191  SCJbOpenObject(js, "iin");
192  SCJsonDNP3LogIin(js, (uint16_t)(dnp3tx->iin.iin1 << 8 | dnp3tx->iin.iin2));
193  SCJbClose(js);
194 }
195 
196 bool AlertJsonDnp3(void *vtx, SCJsonBuilder *js)
197 {
198  DNP3Transaction *tx = (DNP3Transaction *)vtx;
199  if (tx->done && tx->buffer == NULL) {
200  /* Carrier TX for app-layer event, don't log. */
201  return false;
202  }
203  bool logged = false;
204  SCJbOpenObject(js, "dnp3");
205  if (tx->is_request && tx->done) {
206  SCJbOpenObject(js, "request");
207  JsonDNP3LogRequest(js, tx);
208  SCJbClose(js);
209  logged = true;
210  }
211  if (!tx->is_request && tx->done) {
212  SCJbOpenObject(js, "response");
213  JsonDNP3LogResponse(js, tx);
214  SCJbClose(js);
215  logged = true;
216  }
217  SCJbClose(js);
218  return logged;
219 }
220 
221 static int JsonDNP3LoggerToServer(ThreadVars *tv, void *thread_data,
222  const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
223 {
224  SCEnter();
225  LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
226  DNP3Transaction *tx = vtx;
227 
228  SCJsonBuilder *js =
229  CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
230  if (unlikely(js == NULL)) {
231  return TM_ECODE_OK;
232  }
233 
234  SCJbOpenObject(js, "dnp3");
235  JsonDNP3LogRequest(js, tx);
236  SCJbClose(js);
237  OutputJsonBuilderBuffer(tv, p, p->flow, js, thread->ctx);
238  SCJbFree(js);
239 
241 }
242 
243 static int JsonDNP3LoggerToClient(ThreadVars *tv, void *thread_data,
244  const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
245 {
246  SCEnter();
247  LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
248  DNP3Transaction *tx = vtx;
249 
250  SCJsonBuilder *js =
251  CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
252  if (unlikely(js == NULL)) {
253  return TM_ECODE_OK;
254  }
255 
256  SCJbOpenObject(js, "dnp3");
257  JsonDNP3LogResponse(js, tx);
258  SCJbClose(js);
259  OutputJsonBuilderBuffer(tv, p, p->flow, js, thread->ctx);
260  SCJbFree(js);
261 
263 }
264 
265 static int JsonDNP3Logger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
266  void *vtx, uint64_t tx_id)
267 {
268  SCEnter();
269  DNP3Transaction *tx = vtx;
270  if (tx->done && tx->buffer == NULL) {
271  /* Carrier TX for app-layer event, don't log. */
273  }
274  if (tx->is_request && tx->done) {
275  JsonDNP3LoggerToServer(tv, thread_data, p, f, state, vtx, tx_id);
276  } else if (!tx->is_request && tx->done) {
277  JsonDNP3LoggerToClient(tv, thread_data, p, f, state, vtx, tx_id);
278  }
280 }
281 
282 static void OutputDNP3LogDeInitCtxSub(OutputCtx *output_ctx)
283 {
284  SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
285  LogDNP3FileCtx *dnp3log_ctx = (LogDNP3FileCtx *)output_ctx->data;
286  SCFree(dnp3log_ctx);
287  SCFree(output_ctx);
288 }
289 
290 static OutputInitResult OutputDNP3LogInitSub(SCConfNode *conf, OutputCtx *parent_ctx)
291 {
292  OutputInitResult result = { NULL, false };
293  OutputJsonCtx *json_ctx = parent_ctx->data;
294 
295  LogDNP3FileCtx *dnp3log_ctx = SCCalloc(1, sizeof(*dnp3log_ctx));
296  if (unlikely(dnp3log_ctx == NULL)) {
297  return result;
298  }
299  dnp3log_ctx->eve_ctx = json_ctx;
300 
301  OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
302  if (unlikely(output_ctx == NULL)) {
303  SCFree(dnp3log_ctx);
304  return result;
305  }
306  output_ctx->data = dnp3log_ctx;
307  output_ctx->DeInit = OutputDNP3LogDeInitCtxSub;
308 
309  SCLogInfo("DNP3 log sub-module initialized.");
310 
312 
313  result.ctx = output_ctx;
314  result.ok = true;
315  return result;
316 }
317 
318 
319 static TmEcode JsonDNP3LogThreadInit(ThreadVars *t, const void *initdata, void **data)
320 {
321  LogDNP3LogThread *thread = SCCalloc(1, sizeof(*thread));
322  if (unlikely(thread == NULL)) {
323  return TM_ECODE_FAILED;
324  }
325 
326  if (initdata == NULL) {
327  SCLogDebug("Error getting context for DNP3. \"initdata\" is NULL.");
328  goto error_exit;
329  }
330 
331  thread->dnp3log_ctx = ((OutputCtx *)initdata)->data;
332  thread->ctx = CreateEveThreadCtx(t, thread->dnp3log_ctx->eve_ctx);
333  if (thread->ctx == NULL) {
334  goto error_exit;
335  }
336 
337  *data = (void *)thread;
338 
339  return TM_ECODE_OK;
340 
341 error_exit:
342  SCFree(thread);
343  return TM_ECODE_FAILED;
344 }
345 
346 static TmEcode JsonDNP3LogThreadDeinit(ThreadVars *t, void *data)
347 {
348  LogDNP3LogThread *thread = (LogDNP3LogThread *)data;
349  if (thread == NULL) {
350  return TM_ECODE_OK;
351  }
352  FreeEveThreadCtx(thread->ctx);
353  SCFree(thread);
354  return TM_ECODE_OK;
355 }
356 
358 {
359  OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonDNP3Log", "eve-log.dnp3",
360  OutputDNP3LogInitSub, ALPROTO_DNP3, JsonDNP3Logger, JsonDNP3LogThreadInit,
361  JsonDNP3LogThreadDeinit);
362 }
SCJsonBuilder
struct SCJsonBuilder SCJsonBuilder
Definition: detect-engine-helper.h:83
LogDNP3LogThread_
Definition: output-json-dnp3.c:49
LogDNP3FileCtx_::flags
uint32_t flags
Definition: output-json-dnp3.c:44
tm-threads.h
DNP3Transaction_::complete
uint8_t complete
Definition: app-layer-dnp3.h:231
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
DNP3Transaction_::done
uint8_t done
Definition: app-layer-dnp3.h:230
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
FreeEveThreadCtx
void FreeEveThreadCtx(OutputJsonThreadCtx *ctx)
Definition: output-json-common.c:58
DNP3Object_
Struct to hold the list of decoded objects.
Definition: app-layer-dnp3.h:196
CreateEveHeader
SCJsonBuilder * CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx)
Definition: output-json.c:816
threads.h
OutputJsonCtx_
Definition: output-json.h:77
Flow_
Flow data structure.
Definition: flow.h:359
logged
int logged
Definition: app-layer-htp.h:1
JsonDNP3LogRegister
void JsonDNP3LogRegister(void)
Definition: output-json-dnp3.c:357
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:248
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
CreateEveThreadCtx
OutputJsonThreadCtx * CreateEveThreadCtx(ThreadVars *t, OutputJsonCtx *ctx)
Definition: output-json-common.c:29
OutputRegisterTxSubModule
void OutputRegisterTxSubModule(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, AppProto alproto, TxLogger TxLogFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit)
Definition: output.c:411
DNP3Object_::stop
uint32_t stop
Definition: app-layer-dnp3.h:203
DNP3Transaction_::objects
DNP3ObjectList objects
Definition: app-layer-dnp3.h:225
LogDNP3FileCtx_
Definition: output-json-dnp3.c:43
DNP3PrefixIsSize
int DNP3PrefixIsSize(uint8_t prefix_code)
Check if the prefix code is a size prefix.
Definition: app-layer-dnp3.c:1557
detect-dnp3.h
DNP3_APP_FC_UNSOLICITED_RESP
#define DNP3_APP_FC_UNSOLICITED_RESP
Definition: app-layer-dnp3.h:71
p
Packet * p
Definition: fuzz_dataset.c:30
OutputJsonBuilderBuffer
void OutputJsonBuilderBuffer(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
Definition: output-json.c:1018
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
OutputCtx_::data
void * data
Definition: tm-modules.h:91
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
OutputCtx_
Definition: tm-modules.h:88
DNP3_SWAP16
#define DNP3_SWAP16(x)
Definition: app-layer-dnp3.h:96
OutputJsonThreadCtx_
Definition: output-json.h:85
output-json-dnp3.h
DNP3Object_::points
DNP3PointList * points
Definition: app-layer-dnp3.h:205
LogDNP3LogThread_::dnp3log_ctx
LogDNP3FileCtx * dnp3log_ctx
Definition: output-json-dnp3.c:50
LogDNP3FileCtx_::include_object_data
uint8_t include_object_data
Definition: output-json-dnp3.c:45
util-debug.h
JB_SET_STRING
#define JB_SET_STRING(jb, key, val)
Definition: rust.h:36
DNP3_LINK_FCV
#define DNP3_LINK_FCV(control)
Definition: app-layer-dnp3.h:78
DNP3Transaction_::ah
DNP3ApplicationHeader ah
Definition: app-layer-dnp3.h:228
OutputInitResult_::ctx
OutputCtx * ctx
Definition: output.h:47
DNP3Point_::size
uint32_t size
Definition: app-layer-dnp3.h:185
SCAppLayerParserRegisterLogger
void SCAppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
Definition: app-layer-parser.c:535
DNP3Transaction_::iin
DNP3InternalInd iin
Definition: app-layer-dnp3.h:229
ALPROTO_DNP3
@ ALPROTO_DNP3
Definition: app-layer-protos.h:50
app-layer-dnp3.h
output-json.h
DNP3_APP_FIR
#define DNP3_APP_FIR(x)
Definition: app-layer-dnp3.h:87
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
pkt-var.h
OutputInitResult_::ok
bool ok
Definition: output.h:48
OutputJsonDNP3SetItem
void OutputJsonDNP3SetItem(SCJsonBuilder *js, DNP3Object *object, DNP3Point *point)
Definition: output-json-dnp3-objects.c:33
LOG_DIR_FLOW
@ LOG_DIR_FLOW
Definition: output-eve-bindgen.h:35
app-layer-parser.h
Packet_
Definition: decode.h:516
DNP3Object_::group
uint8_t group
Definition: app-layer-dnp3.h:197
DNP3_LINK_FCB
#define DNP3_LINK_FCB(control)
Definition: app-layer-dnp3.h:77
conf.h
DNP3_LINK_DIR
#define DNP3_LINK_DIR(control)
Definition: app-layer-dnp3.h:75
TmEcode
TmEcode
Definition: tm-threads-common.h:80
DNP3Object_::start
uint32_t start
Definition: app-layer-dnp3.h:202
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
DNP3Transaction_::is_request
bool is_request
Definition: app-layer-dnp3.h:219
DNP3Object_::count
uint32_t count
Definition: app-layer-dnp3.h:204
LogDNP3FileCtx_::eve_ctx
OutputJsonCtx * eve_ctx
Definition: output-json-dnp3.c:46
LogDNP3LogThread
struct LogDNP3LogThread_ LogDNP3LogThread
OutputInitResult_
Definition: output.h:46
Packet_::flow
struct Flow_ * flow
Definition: decode.h:564
LogDNP3LogThread_::ctx
OutputJsonThreadCtx * ctx
Definition: output-json-dnp3.c:51
suricata-common.h
OutputCtx_::DeInit
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:94
DNP3Object_::range_code
uint8_t range_code
Definition: app-layer-dnp3.h:201
DNP3_LINK_PRI
#define DNP3_LINK_PRI(control)
Definition: app-layer-dnp3.h:76
DNP3_APP_UNS
#define DNP3_APP_UNS(x)
Definition: app-layer-dnp3.h:90
DNP3_APP_CON
#define DNP3_APP_CON(x)
Definition: app-layer-dnp3.h:89
DNP3_APP_FIN
#define DNP3_APP_FIN(x)
Definition: app-layer-dnp3.h:88
DNP3Object_::prefix_code
uint8_t prefix_code
Definition: app-layer-dnp3.h:200
output-json-dnp3-objects.h
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:34
threadvars.h
LOGGER_JSON_TX
@ LOGGER_JSON_TX
Definition: suricata-common.h:489
LogDNP3FileCtx
struct LogDNP3FileCtx_ LogDNP3FileCtx
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DNP3Object_::variation
uint8_t variation
Definition: app-layer-dnp3.h:198
util-buffer.h
AlertJsonDnp3
bool AlertJsonDnp3(void *vtx, SCJsonBuilder *js)
Definition: output-json-dnp3.c:196
DNP3_APP_SEQ
#define DNP3_APP_SEQ(x)
Definition: app-layer-dnp3.h:91
DNP3Object_::qualifier
uint8_t qualifier
Definition: app-layer-dnp3.h:199
DNP3Point_::index
uint32_t index
Definition: app-layer-dnp3.h:181
DNP3_LINK_FC
#define DNP3_LINK_FC(control)
Definition: app-layer-dnp3.h:79
app-layer-dnp3-objects.h
DNP3Point_::prefix
uint32_t prefix
Definition: app-layer-dnp3.h:180
DNP3Transaction_::lh
DNP3LinkHeader lh
Definition: app-layer-dnp3.h:226
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
SCConfNode_
Definition: conf.h:37
DNP3Point_
DNP3 object point.
Definition: app-layer-dnp3.h:179
output.h
DNP3Transaction_
DNP3 transaction.
Definition: app-layer-dnp3.h:215
app-layer.h
f
Flow f
Definition: fuzz_dataset.c:32
DNP3Transaction_::buffer
uint8_t * buffer
Definition: app-layer-dnp3.h:223