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