Skip to content

Commit f7af676

Browse files
sedmichaLukas955
authored andcommitted
IPFIX output plugin: Fixes and improvements
* Fixed incorrect man page filename. * Fixed incorrect naming in man page. * Removed leftover file in doc. * Renamed useUTCForFilenameTime to useLocalTime, also defaults to UTC now. * Changed default value notation in man page. * Fixed incorrect arg type in config. * Fixed problem with unitialized variable * Fixed first filename not being properly aligned to window size. * Fixed warnings about comparsion of unsigned and signed types. * Fixed SEGFAULT on quit when no data is received. * Added the option to split based on system or export time * Fixed infinite loop when template size exceeds max message sizes, the template will be skipped instead * Improved ODID collision message.
1 parent 36cfef2 commit f7af676

File tree

9 files changed

+93
-82
lines changed

9 files changed

+93
-82
lines changed

src/plugins/output/ipfix/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ install(
1414

1515
if (ENABLE_DOC_MANPAGE)
1616
# Build a manual page
17-
set(SRC_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doc/ipfixcol2-ipfix.7.rst")
18-
set(DST_FILE "${CMAKE_CURRENT_BINARY_DIR}/ipfixcol2-ipfix.7")
17+
set(SRC_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doc/ipfixcol2-ipfix-output.7.rst")
18+
set(DST_FILE "${CMAKE_CURRENT_BINARY_DIR}/ipfixcol2-ipfix-output.7")
1919

2020
add_custom_command(TARGET ipfix PRE_BUILD
2121
COMMAND ${RST2MAN_EXECUTABLE} --syntax-highlight=none ${SRC_FILE} ${DST_FILE}

src/plugins/output/ipfix/README.rst

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ Example configuration
1313
<plugin>ipfix</plugin>
1414
<params>
1515
<filename>/tmp/ipfix/%d-%m-%y/%H:%M:%S.ipfix</filename>
16-
<useUtcForFilenameTime>false</useUtcForFilenameTime>
16+
<useLocalTime>false</useLocalTime>
1717
<windowSize>60</windowSize>
1818
<alignWindows>true</alignWindows>
1919
<skipUnknownDataSets>true</skipUnknownDataSets>
20+
<splitOnExportTime>false</splitOnExportTime>
2021
</params>
2122
</output>
2223
@@ -26,14 +27,17 @@ Parameters
2627
:``filename``:
2728
The output filename, allows the use of strftime format values to add the export time into the file path.
2829

29-
:``useUtcForFilenameTime``:
30-
Specifies whether the time values in filenames should be in local time or UTC. (optional, local time by default)
30+
:``useLocalTime``:
31+
Specifies whether the time values in filenames should be in local time instead of UTC. [default: false]
3132

3233
:``windowSize``:
33-
The number of seconds before a new file is created. The value of 0 means the file will never split. (optional, 0 by default)
34+
The number of seconds before a new file is created. The value of 0 means the file will never split. [default: 0]
3435

3536
:``alignWindows``:
36-
Specifies whether the file should only be split on multiples of windowSize. (optional, true by default)
37+
Specifies whether the file should only be split on multiples of windowSize. [default: true]
3738

3839
:``skipUnknownDataSets``:
39-
Specifies whether data sets with missing template should be left out from the output file. Sequence numbers and message lengths will be adjusted accordingly. (optional, false by default)
40+
Specifies whether data sets with missing template should be left out from the output file. Sequence numbers and message lengths will be adjusted accordingly. [default: false]
41+
42+
:``splitOnExportTime``:
43+
Specifies whether files should be split based on export time instead of system time. [default: false]

src/plugins/output/ipfix/doc/ipfixcol2-dummy-output.7.rst

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/plugins/output/ipfix/doc/ipfixcol2-ipfix.7.rst renamed to src/plugins/output/ipfix/doc/ipfixcol2-ipfix-output.7.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
========================
2-
ipfixcol2-dummy-output
2+
ipfixcol2-ipfix-output
33
========================
44

55
---------------------
6-
Dummy (output plugin)
6+
IPFIX (output plugin)
77
---------------------
88

99
:Author: Michal Sedlák ([email protected])

src/plugins/output/ipfix/src/Config.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,32 @@
4545
#include <memory>
4646

4747
enum params_xml_nodes {
48-
PARAM_FILENAME,
49-
PARAM_USE_UTC_FOR_FILENAME_TIME,
50-
PARAM_WINDOW_SIZE,
48+
PARAM_FILENAME,
49+
PARAM_USE_LOCALTIME,
50+
PARAM_WINDOW_SIZE,
5151
PARAM_ALIGN_WINDOWS,
52-
PARAM_SKIP_UNKNOWN_DATASETS
52+
PARAM_SKIP_UNKNOWN_DATASETS,
53+
PARAM_SPLIT_ON_EXPORT_TIME
5354
};
5455

5556
static const struct fds_xml_args args_params[] = {
5657
FDS_OPTS_ROOT("params"),
5758
FDS_OPTS_ELEM(PARAM_FILENAME, "filename", FDS_OPTS_T_STRING, 0),
58-
FDS_OPTS_ELEM(PARAM_USE_UTC_FOR_FILENAME_TIME, "useUtcForFilenameTime", FDS_OPTS_T_STRING, FDS_OPTS_P_OPT),
59+
FDS_OPTS_ELEM(PARAM_USE_LOCALTIME, "useLocalTime", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
5960
FDS_OPTS_ELEM(PARAM_WINDOW_SIZE, "windowSize", FDS_OPTS_T_UINT, FDS_OPTS_P_OPT),
6061
FDS_OPTS_ELEM(PARAM_ALIGN_WINDOWS, "alignWindows", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
6162
FDS_OPTS_ELEM(PARAM_SKIP_UNKNOWN_DATASETS, "skipUnknownDataSets", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
63+
FDS_OPTS_ELEM(PARAM_SPLIT_ON_EXPORT_TIME, "splitOnExportTime", FDS_OPTS_T_BOOL, FDS_OPTS_P_OPT),
6264
FDS_OPTS_END
6365
};
6466

6567
void Config::set_defaults() {
6668
filename = "";
67-
use_utc_for_filename_time = false;
69+
use_localtime = false;
6870
window_size = 0;
6971
align_windows = true;
7072
skip_unknown_datasets = false;
73+
split_on_export_time = false;
7174
}
7275

7376
void Config::parse_params(fds_xml_ctx_t *params)
@@ -79,9 +82,9 @@ void Config::parse_params(fds_xml_ctx_t *params)
7982
assert(content->type == FDS_OPTS_T_STRING);
8083
filename = std::string(content->ptr_string);
8184
break;
82-
case PARAM_USE_UTC_FOR_FILENAME_TIME:
85+
case PARAM_USE_LOCALTIME:
8386
assert(content->type == FDS_OPTS_T_BOOL);
84-
use_utc_for_filename_time = content->val_bool;
87+
use_localtime = content->val_bool;
8588
break;
8689
case PARAM_WINDOW_SIZE:
8790
assert(content->type == FDS_OPTS_T_UINT);
@@ -95,6 +98,10 @@ void Config::parse_params(fds_xml_ctx_t *params)
9598
assert(content->type == FDS_OPTS_T_BOOL);
9699
skip_unknown_datasets = content->val_bool;
97100
break;
101+
case PARAM_SPLIT_ON_EXPORT_TIME:
102+
assert(content->type == FDS_OPTS_T_BOOL);
103+
split_on_export_time = content->val_bool;
104+
break;
98105
default:
99106
throw std::invalid_argument("Unexpected element within <params>!");
100107
}
@@ -104,9 +111,9 @@ void Config::parse_params(fds_xml_ctx_t *params)
104111
void
105112
Config::check_validity()
106113
{
107-
if (filename.empty()) {
108-
throw std::invalid_argument("Filename cannot be empty!");
109-
}
114+
if (filename.empty()) {
115+
throw std::invalid_argument("Filename cannot be empty!");
116+
}
110117
}
111118

112119
Config::Config(const char *params)

src/plugins/output/ipfix/src/Config.hpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,20 @@
5050
class Config {
5151

5252
private:
53-
void set_defaults();
54-
void parse_params(fds_xml_ctx_t *params);
55-
void check_validity();
53+
void set_defaults();
54+
void parse_params(fds_xml_ctx_t *params);
55+
void check_validity();
5656

5757
public:
58-
std::string filename;
59-
bool use_utc_for_filename_time;
60-
uint64_t window_size;
61-
bool align_windows;
62-
bool skip_unknown_datasets;
58+
std::string filename;
59+
bool use_localtime;
60+
uint64_t window_size;
61+
bool align_windows;
62+
bool skip_unknown_datasets;
63+
bool split_on_export_time;
6364

64-
Config(const char *params);
65-
~Config();
65+
Config(const char *params);
66+
~Config();
6667
};
6768

6869
#endif // CONFIG_HPP

src/plugins/output/ipfix/src/IPFIXOutput.cpp

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ make_directories(char *dir_name)
106106
* \return true or false
107107
*/
108108
bool
109-
IPFIXOutput::should_start_new_file(uint32_t export_time)
109+
IPFIXOutput::should_start_new_file(std::time_t current_time)
110110
{
111111
if (config->window_size == 0 && output_file != NULL) {
112112
return false;
113113
}
114114

115-
uint32_t time_difference = export_time - file_start_export_time;
115+
uint32_t time_difference = current_time - file_start_time;
116116
if (time_difference >= config->window_size) {
117117
return true;
118118
} else {
@@ -126,23 +126,19 @@ IPFIXOutput::should_start_new_file(uint32_t export_time)
126126
* \param[in] export_time Current export time
127127
*/
128128
void
129-
IPFIXOutput::new_file(uint32_t export_time)
129+
IPFIXOutput::new_file(std::time_t current_time)
130130
{
131-
file_start_export_time = export_time;
132131
if (config->align_windows) {
133132
// Round down to the nearest multiple of window size
134-
file_start_export_time = (export_time / config->window_size) * config->window_size;
133+
current_time = (current_time / config->window_size) * config->window_size;
135134
}
135+
file_start_time = current_time;
136136

137137
constexpr size_t max_filename_size = 4096;
138138
char filename[max_filename_size];
139139

140-
// Warning: assigning uint32_t to time_t
141-
std::time_t export_time_t = export_time;
142-
assert(export_time_t >= 0 && sizeof (export_time_t) >= sizeof (export_time));
143-
std::tm *export_time_tm = (config->use_utc_for_filename_time ? std::gmtime(&export_time_t) : std::localtime(&export_time_t));
144-
145-
if (std::strftime(filename, max_filename_size, config->filename.c_str(), export_time_tm) == 0) {
140+
std::tm *current_time_tm = (config->use_localtime ? std::localtime(&current_time) : std::gmtime(&current_time));
141+
if (std::strftime(filename, max_filename_size, config->filename.c_str(), current_time_tm) == 0) {
146142
throw std::runtime_error("Max filename size exceeded (" + std::to_string(max_filename_size) + " b)!");
147143
}
148144

@@ -182,6 +178,9 @@ IPFIXOutput::write_bytes(const void *bytes, size_t bytes_count)
182178
void
183179
IPFIXOutput::close_file()
184180
{
181+
if (!output_file) {
182+
return;
183+
}
185184
int return_code = std::fclose(output_file);
186185
if (return_code != 0) {
187186
IPX_CTX_WARNING(plugin_context, "Error closing output file", 0);
@@ -240,7 +239,7 @@ int
240239
IPFIXOutput::write_template_set(uint16_t set_id, const fds_tsnapshot_t *templates_snapshot,
241240
std::set<uint16_t>::iterator template_ids,
242241
std::set<uint16_t>::iterator template_ids_end,
243-
int size_limit, int *out_set_length)
242+
unsigned size_limit, unsigned *out_set_length)
244243
{
245244
assert(set_id == FDS_IPFIX_SET_TMPLT || set_id == FDS_IPFIX_SET_OPTS_TMPLT);
246245

@@ -258,7 +257,7 @@ IPFIXOutput::write_template_set(uint16_t set_id, const fds_tsnapshot_t *template
258257
return 0;
259258
}
260259

261-
unsigned int set_length = 0;
260+
unsigned set_length = 0;
262261

263262
// Set header
264263
fds_ipfix_set_hdr set_header;
@@ -314,7 +313,7 @@ IPFIXOutput::write_templates(odid_context_s *odid_context, const fds_tsnapshot_t
314313
uint32_t export_time, uint32_t sequence_number)
315314
{
316315
// TODO
317-
unsigned int message_size_limit = 512;
316+
unsigned message_size_limit = 512;
318317

319318
std::set<uint16_t>::iterator template_ids_iter = odid_context->templates_seen.begin();
320319
std::set<uint16_t>::iterator template_ids_end = odid_context->templates_seen.end();
@@ -328,7 +327,7 @@ IPFIXOutput::write_templates(odid_context_s *odid_context, const fds_tsnapshot_t
328327
}
329328

330329
while (template_ids_iter != template_ids_end) {
331-
unsigned int message_length = 0;
330+
unsigned message_length = 0;
332331

333332
// Message header
334333
fds_ipfix_msg_hdr message_header;
@@ -345,14 +344,25 @@ IPFIXOutput::write_templates(odid_context_s *odid_context, const fds_tsnapshot_t
345344

346345
// Write templates
347346
int templates_written;
348-
int set_length;
347+
int templates_written_total = 0;
348+
unsigned set_length;
349349
do {
350350
templates_written = write_template_set(set_id, templates_snapshot, template_ids_iter, template_ids_end,
351351
message_size_limit - message_length, &set_length);
352+
templates_written_total += templates_written;
353+
if (templates_written_total == 0) {
354+
// We can't even fit the first template into the message
355+
IPX_CTX_WARNING(plugin_context, "[ODID: %u] Template with ID %hu is bigger than the message size limit! Skipping...",
356+
odid_context->odid, *template_ids_iter);
357+
templates_written = 1;
358+
set_length = 0;
359+
}
360+
352361
message_length += set_length;
353362
for (int i = 0; i < templates_written; i++) {
354363
template_ids_iter++;
355-
}
364+
}
365+
356366
if (template_ids_iter == template_ids_end) {
357367
if (set_id == FDS_IPFIX_SET_TMPLT) {
358368
// Switch to options templates if all the regular templates are written
@@ -392,12 +402,19 @@ IPFIXOutput::on_ipfix_message(ipx_msg_ipfix *message)
392402
uint32_t export_time = ntohl(original_message_header->export_time);
393403
uint32_t odid = ntohl(original_message_header->odid);
394404

405+
std::time_t current_time;
406+
if (!config->split_on_export_time) {
407+
current_time = std::time(NULL);
408+
} else {
409+
// Warning: assigning uint32_t to time_t
410+
current_time = export_time;
411+
assert(current_time >= 0 && sizeof (current_time) >= sizeof (export_time));
412+
}
413+
395414
// Start a new file if needed
396-
if (should_start_new_file(export_time)) {
397-
if (output_file != NULL) {
398-
close_file();
399-
}
400-
new_file(export_time);
415+
if (should_start_new_file(current_time)) {
416+
close_file();
417+
new_file(current_time);
401418
}
402419

403420
// Find context corresponding to the ODID
@@ -415,7 +432,7 @@ IPFIXOutput::on_ipfix_message(ipx_msg_ipfix *message)
415432
// the messages from the first session using the ODID through and skip the others.
416433
if (odid_context->colliding_sessions.find(message_ipx_ctx->session) != odid_context->colliding_sessions.end()) {
417434
// We only want to print the warning message once
418-
IPX_CTX_WARNING(plugin_context, "ODID collision detected! Messages will be skipped.", '\0');
435+
IPX_CTX_WARNING(plugin_context, "[ODID: %u] ODID collision detected! Messages will be skipped.", odid_context->odid);
419436
odid_context->colliding_sessions.insert(message_ipx_ctx->session);
420437
}
421438
return;
@@ -445,7 +462,7 @@ IPFIXOutput::on_ipfix_message(ipx_msg_ipfix *message)
445462
}
446463

447464
// Message header, copy the original
448-
unsigned int message_length = 0;
465+
unsigned message_length = 0;
449466
fds_ipfix_msg_hdr message_header;
450467
std::memcpy(&message_header, original_message_header, sizeof (message_header));
451468
// ??? why does message_header = *original_message_header not work and memcpy does?
@@ -506,7 +523,7 @@ IPFIXOutput::on_ipfix_message(ipx_msg_ipfix *message)
506523
}
507524

508525
// Finish message header
509-
assert(message_length > sizeof (message_header) && message_length <= UINT_MAX);
526+
assert(message_length >= sizeof (message_header) && message_length <= UINT_MAX);
510527
assert(message_length <= ntohs(message_header.length));
511528
message_header.length = htons(message_length);
512529
if (config->skip_unknown_datasets) {

src/plugins/output/ipfix/src/IPFIXOutput.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <map>
4848
#include <vector>
4949
#include <cstdio>
50+
#include <ctime>
5051

5152
#include <ipfixcol2.h>
5253
#include <libfds.h>
@@ -68,12 +69,12 @@ class IPFIXOutput {
6869

6970
std::map<uint32_t, odid_context_s> odid_contexts;
7071
std::FILE *output_file = NULL;
71-
uint32_t file_start_export_time;
72+
std::time_t file_start_time = 0;
7273

7374
bool
74-
should_start_new_file(uint32_t export_time);
75+
should_start_new_file(std::time_t current_time);
7576
void
76-
new_file(uint32_t export_time);
77+
new_file(std::time_t current_time);
7778
void
7879
write_bytes(const void *bytes, size_t bytes_count);
7980
void
@@ -84,7 +85,7 @@ class IPFIXOutput {
8485
int
8586
write_template_set(uint16_t set_id, const fds_tsnapshot_t *templates_snapshot,
8687
std::set<uint16_t>::iterator template_ids, std::set<uint16_t>::iterator template_ids_end,
87-
int size_limit, int *out_set_length);
88+
unsigned size_limit, unsigned *out_set_length);
8889
void
8990
write_templates(odid_context_s *odid_context, const fds_tsnapshot_t *templates_snapshot,
9091
uint32_t export_time, uint32_t sequence_number);

0 commit comments

Comments
 (0)