Skip to content

Commit ceb1559

Browse files
Simona BennarovaLukas955
authored andcommitted
JSON converter: add (Options) templates records
1 parent 5dc7558 commit ceb1559

File tree

6 files changed

+232
-22
lines changed

6 files changed

+232
-22
lines changed

doc/data/configs/tcp2anon2json.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
<ignoreUnknown>true</ignoreUnknown>
4141
<ignoreOptions>false</ignoreOptions>
4242
<nonPrintableChar>true</nonPrintableChar>
43-
<detailedInfo>true</detailedInfo>
43+
<detailedInfo>false</detailedInfo>
44+
<templateInfo>false</templateInfo>
4445

4546
<!-- Output methods -->
4647
<outputs>

src/plugins/output/json/README.rst

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ Don't forget to remove (or comment) outputs that you don't want to use!
7070
<nonPrintableChar>true</nonPrintableChar>
7171
<numericNames>false</numericNames>
7272
<splitBiflow>false</splitBiflow>
73-
<detailedInfo>true</detailedInfo>
73+
<detailedInfo>false</detailedInfo>
74+
<templateInfo>false</templateInfo>
7475
7576
<outputs>
7677
<server>
@@ -147,6 +148,10 @@ Formatting parameters:
147148
Add detailed info about the IPFIX message (export time, sequence number, ...) to each record
148149
under "ipfix:" prefix. [values: true/false, default: false]
149150

151+
:``templateInfo``:
152+
Add Template and Options Template records to the top of the exported JSON file with prefix
153+
"ipfix.template" and "ipfix.optionsTemplate". [values: true/false, default: false]
154+
150155
----
151156

152157
Output types: At least one of the following output must be configured. Multiple server/send/file
@@ -280,4 +285,49 @@ among the nested arrays.
280285
}
281286
}
282287
283-
Keep on mind that all structures can be nested in each other.
288+
Keep on mind that all structures can be nested in each other.
289+
290+
Template and Options Template records
291+
-------------------------------------
292+
293+
Template and Options Template records are special records.
294+
295+
*Template record* describes structure of flow records, and its type is "ipfix.template".
296+
Converted *Template record* contains "ipfix:templateId" field, which is unique to the Transport Session
297+
and Observation Domain and "ipfix:fields" which is an array of JSON objects specifing fields of flow records.
298+
299+
.. code-block:: json
300+
{
301+
"@type": "ipfix.template",
302+
"ipfix:templateId": "49171",
303+
"ipfix:fields": [{
304+
"ipfix:elementId": "16399",
305+
"ipfix:enterpriseId": "6871",
306+
"ipfix:fieldLength": "1"
307+
}, {
308+
"ipfix:elementId": "184",
309+
"ipfix:enterpriseId": "29305",
310+
"ipfix:fieldLength": "4"
311+
}]
312+
}
313+
314+
*Options Template record* describes structure of additional information for the collector.
315+
These additional information are converted to record with type "ipfix.optionsEntry".
316+
Converted *Options Template record* is similar to previous type, however it contains also
317+
"ipfix:scopeCount" field, which specifies number of scope fields in the record.
318+
319+
.. code-block:: json
320+
{
321+
"@type": "ipfix.optionsTemplate",
322+
"ipfix:templateId": "53252",
323+
"ipfix:scopeCount": "3",
324+
"ipfix:fields": [{
325+
"ipfix:elementId": "322",
326+
"ipfix:enterpriseId": "0",
327+
"ipfix:fieldLength": "4"
328+
}, {
329+
"ipfix:elementId": "554",
330+
"ipfix:enterpriseId": "6871",
331+
"ipfix:fieldLength": "65535"
332+
}]
333+
}

src/plugins/output/json/src/Config.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ enum params_xml_nodes {
6060
FMT_NUMERIC, /**< Use numeric names */
6161
FMT_BFSPLIT, /**< Split biflow */
6262
FMT_DETAILEDINFO, /**< Detailed information */
63+
FMT_TMPLTINFO, /**< Template records */
6364
// Common output
6465
OUTPUT_LIST, /**< List of output types */
6566
OUTPUT_PRINT, /**< Print to standard output */
@@ -141,6 +142,7 @@ static const struct fds_xml_args args_params[] = {
141142
FDS_OPTS_ELEM(FMT_NUMERIC, "numericNames", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
142143
FDS_OPTS_ELEM(FMT_BFSPLIT, "splitBiflow", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
143144
FDS_OPTS_ELEM(FMT_DETAILEDINFO, "detailedInfo", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
145+
FDS_OPTS_ELEM(FMT_TMPLTINFO, "templateInfo", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
144146
FDS_OPTS_NESTED(OUTPUT_LIST, "outputs", args_outputs, 0),
145147
FDS_OPTS_END
146148
};
@@ -459,14 +461,18 @@ Config::parse_params(fds_xml_ctx_t *params)
459461
assert(content->type == FDS_OPTS_T_BOOL);
460462
format.split_biflow = content->val_bool;
461463
break;
462-
case FMT_DETAILEDINFO: //Add detailed information about each record
464+
case FMT_DETAILEDINFO: // Add detailed information about each record
463465
assert(content->type == FDS_OPTS_T_BOOL);
464466
format.detailed_info = content->val_bool;
465467
break;
466468
case OUTPUT_LIST: // List of output plugin
467469
assert(content->type == FDS_OPTS_T_CONTEXT);
468470
parse_outputs(content->ptr_ctx);
469471
break;
472+
case FMT_TMPLTINFO: // Add template records
473+
assert(content->type == FDS_OPTS_T_BOOL);
474+
format.template_info = content->val_bool;
475+
break;
470476
default:
471477
throw std::invalid_argument("Unexpected element within <params>!");
472478
}
@@ -488,6 +494,7 @@ Config::default_set()
488494
format.numeric_names = false;
489495
format.split_biflow = false;
490496
format.detailed_info = false;
497+
format.template_info = false;
491498

492499
outputs.prints.clear();
493500
outputs.files.clear();

src/plugins/output/json/src/Config.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ struct cfg_format {
6666
bool numeric_names;
6767
/** Split biflow records */
6868
bool split_biflow;
69+
/** Add template records */
70+
bool template_info;
6971
};
7072

7173
/** Output configuration base structure */

src/plugins/output/json/src/Storage.cpp

Lines changed: 162 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,147 @@ Storage::output_add(Output *output)
144144
m_outputs.push_back(output);
145145
}
146146

147+
/**
148+
* \brief Convert template record to JSON string
149+
*
150+
* \param[in] tset_iter (Options) Template set structure to convert
151+
* \param[in] set_id Id of the set
152+
* \param[in] hdr Message header of IPFIX record
153+
* \throw runtime_error if template parser failed
154+
*/
155+
156+
void
157+
Storage::convert_tmplt_rec(struct fds_tset_iter *tset_iter, uint16_t set_id, fds_ipfix_msg_hdr* hdr)
158+
{
159+
enum fds_template_type type;
160+
void *ptr;
161+
if (set_id == FDS_IPFIX_SET_TMPLT) {
162+
buffer_append("{\"@type\":\"ipfix.template\",");
163+
type = FDS_TYPE_TEMPLATE;
164+
ptr = tset_iter->ptr.trec;
165+
} else {
166+
assert(set_id == FDS_IPFIX_SET_OPTS_TMPLT);
167+
buffer_append("{\"@type\":\"ipfix.optionsTemplate\",");
168+
type = FDS_TYPE_TEMPLATE_OPTS;
169+
ptr = tset_iter->ptr.opts_trec;
170+
}
171+
172+
// Filling the template structure with data from raw packet
173+
uint16_t tmplt_size = tset_iter->size;
174+
struct fds_template *tmplt;
175+
int rc;
176+
rc = fds_template_parse(type, ptr, &tmplt_size, &tmplt);
177+
if (rc != FDS_OK) {
178+
throw std::runtime_error("Parsing failed due to memory allocation error or the format of template is invalid!");
179+
}
180+
181+
// Printing out the header
182+
char field[64];
183+
snprintf(field, 64, "\"ipfix:templateId\":\"%" PRIu16 "\"", tmplt->id);
184+
buffer_append(field);
185+
if (set_id == FDS_IPFIX_SET_OPTS_TMPLT) {
186+
snprintf(field, 64, ",\"ipfix:scopeCount\":\"%" PRIu16 "\"", tmplt->fields_cnt_scope);
187+
buffer_append(field);
188+
}
189+
190+
// Add detailed info to record
191+
if (m_format.detailed_info) {
192+
addDetailedInfo(hdr);
193+
}
194+
195+
buffer_append(",\"ipfix:fields\":[");
196+
197+
// Iteration through the fields and converting them to JSON string
198+
for (uint16_t i = 0; i < tmplt->fields_cnt_total; i++) {
199+
struct fds_tfield current = tmplt->fields[i];
200+
if (i != 0) { // Not first field
201+
buffer_append(",");
202+
}
203+
buffer_append("{");
204+
snprintf(field, 64, "\"ipfix:elementId\":\"%" PRIu16 "\"", current.id);
205+
buffer_append(field);
206+
snprintf(field, 64, ",\"ipfix:enterpriseId\":\"%" PRIu32 "\"", current.en);
207+
buffer_append(field);
208+
snprintf(field, 64, ",\"ipfix:fieldLength\":\"%" PRIu16 "\"", current.length);
209+
buffer_append(field);
210+
buffer_append("}");
211+
}
212+
buffer_append("]}\n");
213+
214+
// Free allocated memory
215+
fds_template_destroy(tmplt);
216+
}
217+
218+
/**
219+
* \brief Convert Template sets and Options Template sets
220+
*
221+
* From all sets in the Message, try to convert just Template and Options template sets.
222+
* \param[in] set All sets in the Message
223+
* \param[in] hdr Message header of IPFIX record
224+
*/
225+
void
226+
Storage::convert_set(struct ipx_ipfix_set *set, fds_ipfix_msg_hdr* hdr)
227+
{
228+
229+
bool flush = false;
230+
int ret = IPX_OK;
231+
uint16_t set_id = ntohs(set->ptr->flowset_id);
232+
if (set_id == FDS_IPFIX_SET_TMPLT || set_id == FDS_IPFIX_SET_OPTS_TMPLT) {
233+
234+
// Template set
235+
struct fds_tset_iter tset_iter;
236+
fds_tset_iter_init(&tset_iter, set->ptr);
237+
238+
// Iteration through all templates in the set
239+
while (fds_tset_iter_next(&tset_iter) == FDS_OK) {
240+
flush = true;
241+
242+
// Read and print single template
243+
convert_tmplt_rec(&tset_iter, set_id, hdr);
244+
245+
// Store it
246+
for (Output *output : m_outputs) {
247+
if (output->process(m_record.buffer, m_record.size_used) != IPX_OK) {
248+
ret = IPX_ERR_DENIED;
249+
goto endloop;
250+
}
251+
}
252+
253+
// Buffer is empty
254+
m_record.size_used = 0;
255+
}
256+
}
257+
endloop:
258+
if (flush) {
259+
for (Output *output : m_outputs) {
260+
output->flush();
261+
}
262+
}
263+
}
264+
147265
int
148266
Storage::records_store(ipx_msg_ipfix_t *msg, const fds_iemgr_t *iemgr)
149267
{
268+
// Message header
269+
auto hdr = (fds_ipfix_msg_hdr*) ipx_msg_ipfix_get_packet(msg);
270+
271+
// Process template records if enabled
272+
if (m_format.template_info) {
273+
struct ipx_ipfix_set *sets;
274+
size_t set_cnt;
275+
ipx_msg_ipfix_get_sets(msg, &sets, &set_cnt);
276+
277+
// Iteration through all sets
278+
for (uint32_t i = 0; i < set_cnt; i++) {
279+
convert_set(&sets[i], hdr);
280+
}
281+
}
282+
150283
// Process all data records
151284
const uint32_t rec_cnt = ipx_msg_ipfix_get_drec_cnt(msg);
152285
bool flush = false;
153286
int ret = IPX_OK;
154287

155-
// Message header
156-
auto hdr = (fds_ipfix_msg_hdr*) ipx_msg_ipfix_get_packet(msg);
157-
158288
for (uint32_t i = 0; i < rec_cnt; ++i) {
159289
ipx_ipfix_record *ipfix_rec = ipx_msg_ipfix_get_drec(msg, i);
160290

@@ -204,6 +334,30 @@ Storage::records_store(ipx_msg_ipfix_t *msg, const fds_iemgr_t *iemgr)
204334
return ret;
205335
}
206336

337+
/**
338+
* \brief Add fields with detailed info (export time, sequence number, ODID, message length) to record
339+
*
340+
* For each record, add detailed information if detailedInfo is enabled.
341+
* @param[in] hdr Message header of IPFIX record
342+
*/
343+
void
344+
Storage::addDetailedInfo(fds_ipfix_msg_hdr *hdr)
345+
{
346+
// Array for formatting detailed info fields
347+
char field[64];
348+
snprintf(field, 64, ",\"ipfix:exportTime\":\"%" PRIu32 "\"", ntohl(hdr->export_time));
349+
buffer_append(field);
350+
351+
snprintf(field, 64, ",\"ipfix:seqNumber\":\"%" PRIu32 "\"", ntohl(hdr->seq_num));
352+
buffer_append(field);
353+
354+
snprintf(field, 64, ",\"ipfix:odid\":\"%" PRIu32 "\"", ntohl(hdr->odid));
355+
buffer_append(field);
356+
357+
snprintf(field, 32, ",\"ipfix:msgLength\":\"%" PRIu16 "\"", ntohs(hdr->length));
358+
buffer_append(field);
359+
}
360+
207361
/**
208362
* \brief Convert an IPFIX record to JSON string
209363
*
@@ -228,25 +382,15 @@ Storage::convert(struct fds_drec &rec, const fds_iemgr_t *iemgr, fds_ipfix_msg_h
228382

229383
m_record.size_used = size_t(rc);
230384

231-
if (m_format.detailed_info) {
232-
385+
if (m_format.detailed_info && !m_format.template_info) {
233386
// Remove '}' parenthesis at the end of the record
234387
m_record.size_used--;
235388

236-
// Array for formatting detailed info fields
237-
char field[64];
238-
snprintf(field, 64, ",\"ipfix:exportTime\":\"%" PRIu32 "\"", ntohl(hdr->export_time));
239-
buffer_append(field);
240-
241-
snprintf(field, 64, ",\"ipfix:seqNumber\":\"%" PRIu32 "\"", ntohl(hdr->seq_num));
242-
buffer_append(field);
243-
244-
snprintf(field, 64, ",\"ipfix:odid\":\"%" PRIu32 "\"", ntohl(hdr->odid));
245-
buffer_append(field);
246-
247-
snprintf(field, 32, ",\"ipfix:msgLength\":\"%" PRIu16 "\"", ntohs(hdr->length));
248-
buffer_append(field);
389+
// Add detailed info to JSON string
390+
addDetailedInfo(hdr);
249391

392+
// Add template ID to JSON string
393+
char field[64];
250394
snprintf(field, 32, ",\"ipfix:templateId\":\"%" PRIu16 "\"", rec.tmplt->id);
251395
buffer_append(field);
252396

src/plugins/output/json/src/Storage.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ class Storage {
110110
void buffer_append(const char *str);
111111
// Reserve memory for a JSON string
112112
void buffer_reserve(size_t n);
113+
// Convert set to JSON string
114+
void convert_set(struct ipx_ipfix_set *set, fds_ipfix_msg_hdr* hdr);
115+
// Convert template record to a JSON string
116+
void convert_tmplt_rec(struct fds_tset_iter *tset_iter, uint16_t set_id, fds_ipfix_msg_hdr* hdr);
117+
// Add detailed info (templateId, ODID, seqNum, exportTime) to JSON string
118+
void addDetailedInfo(fds_ipfix_msg_hdr *hdr);
113119

114120
public:
115121
/**

0 commit comments

Comments
 (0)