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"
36
37#include "detect-dnp3.h"
38
39#include "output.h"
40#include "output-json.h"
41#include "output-json-dnp3.h"
43
49
54
55static void JsonDNP3LogLinkControl(SCJsonBuilder *js, uint8_t lc)
56{
57 SCJbSetBool(js, "dir", DNP3_LINK_DIR(lc));
58 SCJbSetBool(js, "pri", DNP3_LINK_PRI(lc));
59 SCJbSetBool(js, "fcb", DNP3_LINK_FCB(lc));
60 SCJbSetBool(js, "fcv", DNP3_LINK_FCV(lc));
61 SCJbSetUint(js, "function_code", DNP3_LINK_FC(lc));
62}
63
64static void JsonDNP3LogIin(SCJsonBuilder *js, uint16_t iin)
65{
66 if (iin) {
67 SCJbOpenArray(js, "indicators");
68
69 int mapping = 0;
70 do {
71 if (iin & DNP3IndicatorsMap[mapping].value) {
72 SCJbAppendString(js, DNP3IndicatorsMap[mapping].name);
73 }
74 mapping++;
75 } while (DNP3IndicatorsMap[mapping].name != NULL);
76 SCJbClose(js);
77 }
78}
79
80static void JsonDNP3LogApplicationControl(SCJsonBuilder *js, uint8_t ac)
81{
82 SCJbSetBool(js, "fir", DNP3_APP_FIR(ac));
83 SCJbSetBool(js, "fin", DNP3_APP_FIN(ac));
84 SCJbSetBool(js, "con", DNP3_APP_CON(ac));
85 SCJbSetBool(js, "uns", DNP3_APP_UNS(ac));
86 SCJbSetUint(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 */
94static void JsonDNP3LogObjectItems(SCJsonBuilder *js, DNP3Object *object)
95{
96 DNP3Point *item;
97
98 TAILQ_FOREACH(item, object->points, next) {
99 SCJbStartObject(js);
100
101 SCJbSetUint(js, "prefix", item->prefix);
102 SCJbSetUint(js, "index", item->index);
103 if (DNP3PrefixIsSize(object->prefix_code)) {
104 SCJbSetUint(js, "size", item->size);
105 }
106
107 OutputJsonDNP3SetItem(js, object, item);
108 SCJbClose(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 SCJsonBuilder instance with an open array.
117 */
118static void JsonDNP3LogObjects(SCJsonBuilder *js, DNP3ObjectList *objects)
119{
120 DNP3Object *object;
121
122 TAILQ_FOREACH(object, objects, next) {
123 SCJbStartObject(js);
124 SCJbSetUint(js, "group", object->group);
125 SCJbSetUint(js, "variation", object->variation);
126 SCJbSetUint(js, "qualifier", object->qualifier);
127 SCJbSetUint(js, "prefix_code", object->prefix_code);
128 SCJbSetUint(js, "range_code", object->range_code);
129 SCJbSetUint(js, "start", object->start);
130 SCJbSetUint(js, "stop", object->stop);
131 SCJbSetUint(js, "count", object->count);
132
133 if (object->points != NULL && !TAILQ_EMPTY(object->points)) {
134 SCJbOpenArray(js, "points");
135 JsonDNP3LogObjectItems(js, object);
136 SCJbClose(js);
137 }
138
139 SCJbClose(js);
140 }
141}
142
143static void JsonDNP3LogRequest(SCJsonBuilder *js, DNP3Transaction *dnp3tx)
144{
145 JB_SET_STRING(js, "type", "request");
146
147 SCJbOpenObject(js, "control");
148 JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
149 SCJbClose(js);
150
151 SCJbSetUint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
152 SCJbSetUint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
153
154 SCJbOpenObject(js, "application");
155
156 SCJbOpenObject(js, "control");
157 JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
158 SCJbClose(js);
159
160 SCJbSetUint(js, "function_code", dnp3tx->ah.function_code);
161
162 if (!TAILQ_EMPTY(&dnp3tx->objects)) {
163 SCJbOpenArray(js, "objects");
164 JsonDNP3LogObjects(js, &dnp3tx->objects);
165 SCJbClose(js);
166 }
167
168 SCJbSetBool(js, "complete", dnp3tx->complete);
169
170 /* Close application. */
171 SCJbClose(js);
172}
173
174static void JsonDNP3LogResponse(SCJsonBuilder *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 SCJbOpenObject(js, "control");
183 JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
184 SCJbClose(js);
185
186 SCJbSetUint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
187 SCJbSetUint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
188
189 SCJbOpenObject(js, "application");
190
191 SCJbOpenObject(js, "control");
192 JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
193 SCJbClose(js);
194
195 SCJbSetUint(js, "function_code", dnp3tx->ah.function_code);
196
197 if (!TAILQ_EMPTY(&dnp3tx->objects)) {
198 SCJbOpenArray(js, "objects");
199 JsonDNP3LogObjects(js, &dnp3tx->objects);
200 SCJbClose(js);
201 }
202
203 SCJbSetBool(js, "complete", dnp3tx->complete);
204
205 /* Close application. */
206 SCJbClose(js);
207
208 SCJbOpenObject(js, "iin");
209 JsonDNP3LogIin(js, (uint16_t)(dnp3tx->iin.iin1 << 8 | dnp3tx->iin.iin2));
210 SCJbClose(js);
211}
212
213bool AlertJsonDnp3(void *vtx, SCJsonBuilder *js)
214{
215 DNP3Transaction *tx = (DNP3Transaction *)vtx;
216 bool logged = false;
217 SCJbOpenObject(js, "dnp3");
218 if (tx->is_request && tx->done) {
219 SCJbOpenObject(js, "request");
220 JsonDNP3LogRequest(js, tx);
221 SCJbClose(js);
222 logged = true;
223 }
224 if (!tx->is_request && tx->done) {
225 SCJbOpenObject(js, "response");
226 JsonDNP3LogResponse(js, tx);
227 SCJbClose(js);
228 logged = true;
229 }
230 SCJbClose(js);
231 return logged;
232}
233
234static int JsonDNP3LoggerToServer(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 SCJsonBuilder *js =
242 CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
243 if (unlikely(js == NULL)) {
244 return TM_ECODE_OK;
245 }
246
247 SCJbOpenObject(js, "dnp3");
248 JsonDNP3LogRequest(js, tx);
249 SCJbClose(js);
250 OutputJsonBuilderBuffer(tv, p, p->flow, js, thread->ctx);
251 SCJbFree(js);
252
254}
255
256static int JsonDNP3LoggerToClient(ThreadVars *tv, void *thread_data,
257 const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
258{
259 SCEnter();
260 LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
261 DNP3Transaction *tx = vtx;
262
263 SCJsonBuilder *js =
264 CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
265 if (unlikely(js == NULL)) {
266 return TM_ECODE_OK;
267 }
268
269 SCJbOpenObject(js, "dnp3");
270 JsonDNP3LogResponse(js, tx);
271 SCJbClose(js);
272 OutputJsonBuilderBuffer(tv, p, p->flow, js, thread->ctx);
273 SCJbFree(js);
274
276}
277
278static int JsonDNP3Logger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
279 void *vtx, uint64_t tx_id)
280{
281 SCEnter();
282 DNP3Transaction *tx = vtx;
283 if (tx->is_request && tx->done) {
284 JsonDNP3LoggerToServer(tv, thread_data, p, f, state, vtx, tx_id);
285 } else if (!tx->is_request && tx->done) {
286 JsonDNP3LoggerToClient(tv, thread_data, p, f, state, vtx, tx_id);
287 }
289}
290
291static void OutputDNP3LogDeInitCtxSub(OutputCtx *output_ctx)
292{
293 SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
294 LogDNP3FileCtx *dnp3log_ctx = (LogDNP3FileCtx *)output_ctx->data;
295 SCFree(dnp3log_ctx);
296 SCFree(output_ctx);
297}
298
299static OutputInitResult OutputDNP3LogInitSub(SCConfNode *conf, OutputCtx *parent_ctx)
300{
301 OutputInitResult result = { NULL, false };
302 OutputJsonCtx *json_ctx = parent_ctx->data;
303
304 LogDNP3FileCtx *dnp3log_ctx = SCCalloc(1, sizeof(*dnp3log_ctx));
305 if (unlikely(dnp3log_ctx == NULL)) {
306 return result;
307 }
308 dnp3log_ctx->eve_ctx = json_ctx;
309
310 OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
311 if (unlikely(output_ctx == NULL)) {
312 SCFree(dnp3log_ctx);
313 return result;
314 }
315 output_ctx->data = dnp3log_ctx;
316 output_ctx->DeInit = OutputDNP3LogDeInitCtxSub;
317
318 SCLogInfo("DNP3 log sub-module initialized.");
319
321
322 result.ctx = output_ctx;
323 result.ok = true;
324 return result;
325}
326
327
328static TmEcode JsonDNP3LogThreadInit(ThreadVars *t, const void *initdata, void **data)
329{
330 LogDNP3LogThread *thread = SCCalloc(1, sizeof(*thread));
331 if (unlikely(thread == NULL)) {
332 return TM_ECODE_FAILED;
333 }
334
335 if (initdata == NULL) {
336 SCLogDebug("Error getting context for DNP3. \"initdata\" is NULL.");
337 goto error_exit;
338 }
339
340 thread->dnp3log_ctx = ((OutputCtx *)initdata)->data;
341 thread->ctx = CreateEveThreadCtx(t, thread->dnp3log_ctx->eve_ctx);
342 if (thread->ctx == NULL) {
343 goto error_exit;
344 }
345
346 *data = (void *)thread;
347
348 return TM_ECODE_OK;
349
350error_exit:
351 SCFree(thread);
352 return TM_ECODE_FAILED;
353}
354
355static TmEcode JsonDNP3LogThreadDeinit(ThreadVars *t, void *data)
356{
357 LogDNP3LogThread *thread = (LogDNP3LogThread *)data;
358 if (thread == NULL) {
359 return TM_ECODE_OK;
360 }
361 FreeEveThreadCtx(thread->ctx);
362 SCFree(thread);
363 return TM_ECODE_OK;
364}
365
367{
368 OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonDNP3Log", "eve-log.dnp3",
369 OutputDNP3LogInitSub, ALPROTO_DNP3, JsonDNP3Logger, JsonDNP3LogThreadInit,
370 JsonDNP3LogThreadDeinit);
371}
int DNP3PrefixIsSize(uint8_t prefix_code)
Check if the prefix code is a size prefix.
#define DNP3_LINK_FC(control)
#define DNP3_APP_UNS(x)
#define DNP3_APP_FC_UNSOLICITED_RESP
#define DNP3_APP_CON(x)
#define DNP3_APP_FIN(x)
#define DNP3_APP_FIR(x)
#define DNP3_LINK_FCB(control)
#define DNP3_LINK_PRI(control)
#define DNP3_LINK_DIR(control)
#define DNP3_APP_SEQ(x)
#define DNP3_LINK_FCV(control)
#define DNP3_SWAP16(x)
struct HtpBodyChunk_ * next
int logged
void SCAppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
@ ALPROTO_DNP3
DNP3Mapping DNP3IndicatorsMap[]
Definition detect-dnp3.c:61
ThreadVars * tv
@ LOG_DIR_FLOW
OutputJsonThreadCtx * CreateEveThreadCtx(ThreadVars *t, OutputJsonCtx *ctx)
void FreeEveThreadCtx(OutputJsonThreadCtx *ctx)
void OutputJsonDNP3SetItem(SCJsonBuilder *js, DNP3Object *object, DNP3Point *point)
struct LogDNP3FileCtx_ LogDNP3FileCtx
struct LogDNP3LogThread_ LogDNP3LogThread
bool AlertJsonDnp3(void *vtx, SCJsonBuilder *js)
void JsonDNP3LogRegister(void)
SCJsonBuilder * CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection dir, const char *event_type, JsonAddrInfo *addr, OutputJsonCtx *eve_ctx)
void OutputJsonBuilderBuffer(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *js, OutputJsonThreadCtx *ctx)
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:406
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:252
#define TAILQ_EMPTY(head)
Definition queue.h:248
#define JB_SET_STRING(jb, key, val)
Definition rust.h:26
Struct to hold the list of decoded objects.
uint8_t qualifier
DNP3PointList * points
uint8_t range_code
uint8_t prefix_code
uint8_t variation
DNP3 object point.
uint32_t prefix
uint32_t index
uint32_t size
DNP3 transaction.
DNP3LinkHeader lh
DNP3InternalInd iin
DNP3ObjectList objects
DNP3ApplicationHeader ah
Flow data structure.
Definition flow.h:356
OutputJsonCtx * eve_ctx
uint8_t include_object_data
LogDNP3FileCtx * dnp3log_ctx
OutputJsonThreadCtx * ctx
void * data
Definition tm-modules.h:91
void(* DeInit)(struct OutputCtx_ *)
Definition tm-modules.h:94
OutputCtx * ctx
Definition output.h:47
struct Flow_ * flow
Definition decode.h:546
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_JSON_TX
@ TM_ECODE_FAILED
@ TM_ECODE_OK
const char * name
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)