Skip to content

Commit 01f3982

Browse files
committed
FDS Output: enable selection of fields saved in output based on <outputSelection> configuration
1 parent a98ff16 commit 01f3982

File tree

2 files changed

+223
-19
lines changed

2 files changed

+223
-19
lines changed

src/plugins/output/fds/src/Storage.cpp

Lines changed: 216 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* \file src/plugins/output/fds/src/Storage.cpp
33
* \author Lukas Hutak <[email protected]>
4+
* \author Michal Sedlak <[email protected]>
45
* \brief FDS file storage (source file)
56
* \date June 2019
67
*
@@ -13,13 +14,176 @@
1314
#include <cstdio>
1415
#include <cstring>
1516
#include <sys/stat.h>
17+
#include <netinet/in.h>
1618
#include <ipfixcol2.h>
1719
#include <libgen.h>
1820
#include "Storage.hpp"
1921

2022
const std::string TMP_SUFFIX = ".tmp";
2123

22-
Storage::Storage(ipx_ctx_t *ctx, const Config &cfg) : m_ctx(ctx), m_path(cfg.m_path)
24+
static void
25+
buf_write(std::vector<uint8_t> &buffer, const uint8_t *data, std::size_t data_len)
26+
{
27+
if (data_len == 0) {
28+
return;
29+
}
30+
std::size_t idx = buffer.size();
31+
buffer.resize(idx + data_len);
32+
std::memcpy(&buffer[idx], data, data_len);
33+
}
34+
35+
template <typename T>
36+
static void
37+
buf_write(std::vector<uint8_t> &buffer, T &&value)
38+
{
39+
buf_write(buffer, reinterpret_cast<const uint8_t *>(&value), sizeof(value));
40+
}
41+
42+
static bool
43+
contains_element(const std::vector<Config::element> &elements, const fds_tfield &field)
44+
{
45+
auto it = std::find_if(elements.begin(), elements.end(),
46+
[&](const Config::element &elem) { return elem.id == field.id && elem.pen == field.en; });
47+
return it != elements.end();
48+
}
49+
50+
void
51+
create_modified_template(const fds_template *tmplt,
52+
const std::vector<Config::element> &selected_elements,
53+
std::vector<uint8_t> &out_buffer)
54+
{
55+
out_buffer.clear();
56+
57+
// Collect the fields we want in the resulting template
58+
std::vector<const fds_tfield *> fields;
59+
for (uint16_t i = 0; i < tmplt->fields_cnt_total; i++) {
60+
const fds_tfield &field = tmplt->fields[i];
61+
if (contains_element(selected_elements, field)) {
62+
fields.push_back(&field);
63+
}
64+
}
65+
66+
67+
/**
68+
* Build the template definition...
69+
*
70+
* Template:
71+
* [Template Record Header]
72+
* [Field Specifier]
73+
* [Field Specifier]
74+
* ...
75+
* [Field Specifier]
76+
*
77+
* Template Record Header:
78+
* [Template ID : 16 bit] [Field Count : 16 bit]
79+
*
80+
* Example:
81+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82+
* | Template ID = 256 | Field Count = N |
83+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84+
* |1| Information Element id. 1.1 | Field Length 1.1 |
85+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86+
* | Enterprise Number 1.1 |
87+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88+
* |0| Information Element id. 1.2 | Field Length 1.2 |
89+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90+
* | ... | ... |
91+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92+
* |1| Information Element id. 1.N | Field Length 1.N |
93+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
94+
* | Enterprise Number 1.N |
95+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96+
* | Template ID = 257 | Field Count = M |
97+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98+
* |0| Information Element id. 2.1 | Field Length 2.1 |
99+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100+
* |1| Information Element id. 2.2 | Field Length 2.2 |
101+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102+
* | Enterprise Number 2.2 |
103+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104+
* | ... | ... |
105+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
106+
* |1| Information Element id. 2.M | Field Length 2.M |
107+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
108+
* | Enterprise Number 2.M |
109+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
110+
* | Padding (opt) |
111+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
112+
*/
113+
114+
// Write the header
115+
buf_write(out_buffer, htons(tmplt->id));
116+
buf_write(out_buffer, htons(fields.size()));
117+
118+
// Write the template fields
119+
for (const fds_tfield *field : fields) {
120+
if (field->en == 0) {
121+
buf_write(out_buffer, htons(field->id));
122+
buf_write(out_buffer, htons(field->length));
123+
} else {
124+
// If PEN is specified, MSB of element ID is set to 1
125+
buf_write(out_buffer, htons(field->id | 0x8000));
126+
buf_write(out_buffer, htons(field->length));
127+
buf_write(out_buffer, htonl(field->en));
128+
}
129+
}
130+
}
131+
132+
static void
133+
create_modified_data_record(fds_drec &drec,
134+
const std::vector<Config::element> &selected_elements,
135+
std::vector<uint8_t> &out_buffer)
136+
{
137+
out_buffer.clear();
138+
139+
/**
140+
* Data record consists of a sequence of field values.
141+
*
142+
* Data Record:
143+
* +--------------------------------------------------+
144+
* | Field Value |
145+
* +--------------------------------------------------+
146+
* | Field Value |
147+
* +--------------------------------------------------+
148+
* ...
149+
* +--------------------------------------------------+
150+
* | Field Value |
151+
* +--------------------------------------------------+
152+
*/
153+
154+
fds_drec_iter iter;
155+
fds_drec_iter_init(&iter, &drec, 0);
156+
while (fds_drec_iter_next(&iter) != FDS_EOC) {
157+
if (contains_element(selected_elements, *iter.field.info)) {
158+
if (iter.field.info->length == FDS_IPFIX_VAR_IE_LEN) {
159+
/**
160+
* Variable length fields are specified with 65535 in their
161+
* template field length. In this case, the first octet of the
162+
* field value in the data record specifies its length (of the
163+
* data only, the extra octet is not included in this length).
164+
*
165+
* If the data were to be longer than 254, the octet is 255 and the
166+
* next 2 octets are uint16 of the length.
167+
*/
168+
if (iter.field.size < 255) {
169+
buf_write(out_buffer, uint8_t(iter.field.size));
170+
} else {
171+
buf_write(out_buffer, uint8_t(255));
172+
buf_write(out_buffer, htons(uint16_t(iter.field.size)));
173+
}
174+
buf_write(out_buffer, iter.field.data, iter.field.size);
175+
} else {
176+
buf_write(out_buffer, iter.field.data, iter.field.size);
177+
}
178+
}
179+
}
180+
}
181+
182+
Storage::Storage(ipx_ctx_t *ctx, const Config &cfg) :
183+
m_ctx(ctx),
184+
m_path(cfg.m_path),
185+
m_selection_used(cfg.m_selection_used),
186+
m_selection(cfg.m_selection)
23187
{
24188
// Check if the directory exists
25189
struct stat file_info;
@@ -142,6 +306,16 @@ Storage::process_msg(ipx_msg_ipfix_t *msg)
142306
uint16_t rec_size = rec_ptr->rec.size;
143307
uint16_t tmplt_id = rec_ptr->rec.tmplt->id;
144308

309+
if (m_selection_used) {
310+
create_modified_data_record(rec_ptr->rec, m_selection, m_buffer);
311+
rec_data = m_buffer.data();
312+
rec_size = m_buffer.size();
313+
if (rec_size == 0) {
314+
// No record fields were selected -> the record is empty, skip
315+
continue;
316+
}
317+
}
318+
145319
if (fds_file_write_rec(m_file.get(), tmplt_id, rec_data, rec_size) != FDS_OK) {
146320
const char *err_msg = fds_file_error(m_file.get());
147321
throw FDS_exception("Failed to add a Data Record: " + std::string(err_msg));
@@ -160,6 +334,12 @@ struct tmplt_update_data {
160334
fds_file_t *file;
161335
/// Set of processed Templates in the snapshot
162336
std::set<uint16_t> ids;
337+
/// Whether selection is used
338+
bool selection_used;
339+
/// The selection
340+
std::vector<Config::element> *selection;
341+
/// Buffer for building modified templates
342+
std::vector<uint8_t> *buffer;
163343
};
164344

165345
/**
@@ -175,21 +355,40 @@ struct tmplt_update_data {
175355
static bool
176356
tmplt_update_cb(const struct fds_template *tmplt, void *data)
177357
{
178-
// Template type, raw data and size
179-
enum fds_template_type t_type;
180-
const uint8_t *t_data;
181-
uint16_t t_size;
182-
183358
auto info = reinterpret_cast<tmplt_update_data *>(data);
184359

185360
// No exceptions can be thrown in the C callback!
186361
try {
187-
uint16_t t_id = tmplt->id;
188-
info->ids.emplace(t_id);
362+
if (info->selection_used && tmplt->type != FDS_TYPE_TEMPLATE) {
363+
// Skip Option Templates in case field selection is used
364+
return info->is_ok;
365+
}
366+
367+
// Get definition of the template we want to store
368+
fds_template_type new_t_type = tmplt->type;
369+
const uint8_t *new_t_data = tmplt->raw.data;
370+
uint16_t new_t_size = tmplt->raw.length;
371+
372+
if (info->selection_used) {
373+
create_modified_template(tmplt, *info->selection, *info->buffer);
374+
new_t_data = info->buffer->data();
375+
new_t_size = info->buffer->size();
376+
377+
if (new_t_size == 0) {
378+
// None of the template fields have been selected -> skip this template
379+
return info->is_ok;
380+
}
381+
}
382+
383+
// Only now store the template ID as we're now sure that this is an active template
384+
info->ids.emplace(tmplt->id);
189385

190386
// Get definition of the Template specified in the file
191-
int res = fds_file_write_tmplt_get(info->file, t_id, &t_type, &t_data, &t_size);
387+
fds_template_type old_t_type;
388+
const uint8_t *old_t_data;
389+
uint16_t old_t_size;
192390

391+
int res = fds_file_write_tmplt_get(info->file, tmplt->id, &old_t_type, &old_t_data, &old_t_size);
193392
if (res != FDS_OK && res != FDS_ERR_NOTFOUND) {
194393
// Something bad happened
195394
const char *err_msg = fds_file_error(info->file);
@@ -198,21 +397,17 @@ tmplt_update_cb(const struct fds_template *tmplt, void *data)
198397

199398
// Should we add/redefine the definition of the Template
200399
if (res == FDS_OK
201-
&& tmplt->type == t_type
202-
&& tmplt->raw.length == t_size
203-
&& memcmp(tmplt->raw.data, t_data, t_size) == 0) {
400+
&& old_t_type == new_t_type
401+
&& old_t_size == new_t_size
402+
&& memcmp(old_t_data, new_t_data, new_t_size) == 0) {
204403
// The same -> nothing to do
205404
return info->is_ok;
206405
}
207406

208407
// Add the definition (i.e. templates are different or the template hasn't been defined)
209-
IPX_CTX_DEBUG(info->ctx, "Adding/updating definition of Template ID %" PRIu16, t_id);
408+
IPX_CTX_DEBUG(info->ctx, "Adding/updating definition of Template ID %" PRIu16, tmplt->id);
210409

211-
t_type = tmplt->type;
212-
t_data = tmplt->raw.data;
213-
t_size = tmplt->raw.length;
214-
215-
if (fds_file_write_tmplt_add(info->file, t_type, t_data, t_size) != FDS_OK) {
410+
if (fds_file_write_tmplt_add(info->file, new_t_type, new_t_data, new_t_size) != FDS_OK) {
216411
const char *err_msg = fds_file_error(info->file);
217412
throw FDS_exception("fds_file_write_tmplt_add() failed: " + std::string(err_msg));
218413
}
@@ -258,6 +453,9 @@ Storage::tmplts_update(struct snap_info &info, const fds_tsnapshot_t *snap)
258453
data.ctx = m_ctx;
259454
data.file = m_file.get();
260455
data.ids.clear();
456+
data.selection_used = m_selection_used;
457+
data.selection = &m_selection;
458+
data.buffer = &m_buffer;
261459

262460
// Update templates
263461
fds_tsnapshot_for(snap, &tmplt_update_cb, &data);
@@ -436,4 +634,3 @@ Storage::session_ipx2fds(const struct ipx_session *ipx_desc, struct fds_file_ses
436634
memcpy(&fds_desc->ip_dst, &net_desc->addr_dst.ipv6, sizeof fds_desc->ip_dst);
437635
}
438636
}
439-

src/plugins/output/fds/src/Storage.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ class Storage {
110110
/// Mapping of Transport Sessions to FDS specific parameters
111111
std::map<const struct ipx_session *, struct session_ctx> m_session2params;
112112

113+
/// Whether field selection is used
114+
bool m_selection_used;
115+
/// The fields to select
116+
std::vector<Config::element> m_selection;
117+
/// Data buffer for preparing modified template/data records if necessary
118+
std::vector<uint8_t> m_buffer;
119+
113120
std::string
114121
filename_gen(const time_t &ts);
115122
static void

0 commit comments

Comments
 (0)