Skip to content

Commit 0c41275

Browse files
committed
fdsdump: add view factory component for creating views/fields
1 parent ed41aae commit 0c41275

File tree

4 files changed

+405
-0
lines changed

4 files changed

+405
-0
lines changed

src/tools/fdsdump/src/aggregator/field.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ enum class CmpResult {
3535
* @brief The base class for all aggregator fields
3636
*/
3737
class Field {
38+
friend class ViewFactory;
39+
3840
public:
3941
/**
4042
* @brief Get the size of the field in bytes

src/tools/fdsdump/src/aggregator/view.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace aggregator {
2121
* @brief A view object for accessing fields and their values in an aggregation record
2222
*/
2323
class View {
24+
friend class ViewFactory;
25+
2426
public:
2527
/**
2628
* @brief The ordering/sorting direction
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief View factory
5+
*
6+
* Copyright: (C) 2024 CESNET, z.s.p.o.
7+
* SPDX-License-Identifier: BSD-3-Clause
8+
*/
9+
10+
#include <aggregator/aggregatedField.hpp>
11+
#include <aggregator/aliasField.hpp>
12+
#include <aggregator/extraFields.hpp>
13+
#include <aggregator/inOutField.hpp>
14+
#include <aggregator/ipfixField.hpp>
15+
#include <aggregator/viewFactory.hpp>
16+
#include <common/common.hpp>
17+
#include <common/ieMgr.hpp>
18+
19+
#include <limits>
20+
#include <stdexcept>
21+
22+
namespace fdsdump {
23+
namespace aggregator {
24+
25+
static bool
26+
check_strip_prefix(std::string& str, const std::string &prefix)
27+
{
28+
if (string_to_lower(str.substr(0, prefix.size())) == string_to_lower(prefix)) {
29+
str = str.substr(prefix.size());
30+
return true;
31+
}
32+
return false;
33+
}
34+
35+
static bool
36+
check_strip_suffix(std::string& str, const std::string &suffix)
37+
{
38+
if (str.size() < suffix.size()) {
39+
return false;
40+
}
41+
42+
const std::string &tail_lower = string_to_lower(
43+
str.substr(str.size() - suffix.size(), suffix.size()));
44+
const std::string &suffix_lower = string_to_lower(suffix);
45+
if (tail_lower == suffix_lower) {
46+
str = str.substr(0, str.size() - suffix.size());
47+
return true;
48+
}
49+
return false;
50+
}
51+
52+
static Optional<int>
53+
parse_number(const std::string &str)
54+
{
55+
char *end;
56+
errno = 0;
57+
long value = std::strtol(str.c_str(), &end, 10);
58+
if (errno != 0
59+
|| *end != '\0'
60+
|| value < std::numeric_limits<int>::min()
61+
|| value > std::numeric_limits<int>::max()) {
62+
return {};
63+
}
64+
return value;
65+
}
66+
67+
static Optional<int>
68+
parse_time_unit_to_millisecs(std::string str)
69+
{
70+
int multiplier = 1;
71+
if (check_strip_suffix(str, "s")) {
72+
multiplier = 1;
73+
} else if (check_strip_suffix(str, "m")) {
74+
multiplier = 60;
75+
} else if (check_strip_suffix(str, "h")) {
76+
multiplier = 3600;
77+
}
78+
79+
auto secs = parse_number(str);
80+
if (!secs) {
81+
return {};
82+
}
83+
84+
return *secs * multiplier * 1000;
85+
}
86+
87+
static std::string
88+
parse_func_name(std::string &def)
89+
{
90+
if (def.empty()) {
91+
return "";
92+
}
93+
if (def[def.size() - 1] != ')') {
94+
return "";
95+
}
96+
97+
const auto &pieces = string_split(std::string(def.begin(), def.end() - 1), "(", 2);
98+
if (pieces.size() != 2) {
99+
return "";
100+
}
101+
102+
def = pieces[1];
103+
return string_to_lower(pieces[0]);
104+
}
105+
106+
std::unique_ptr<Field>
107+
ViewFactory::create_elem_or_alias(const std::string &def)
108+
{
109+
const fds_iemgr_alias *alias =
110+
fds_iemgr_alias_find(IEMgr::instance().ptr(), def.c_str());
111+
if (alias) {
112+
std::unique_ptr<Field> field(new AliasField(*alias));
113+
field->set_name(def);
114+
return field;
115+
}
116+
117+
const fds_iemgr_elem *elem =
118+
fds_iemgr_elem_find_name(IEMgr::instance().ptr(), def.c_str());
119+
if (elem) {
120+
std::unique_ptr<Field> field(new IpfixField(*elem));
121+
field->set_name(def);
122+
return field;
123+
}
124+
125+
throw std::invalid_argument("cannot find field \"" + def + "\"");
126+
}
127+
128+
std::unique_ptr<Field>
129+
ViewFactory::parse_timewindow_func(const std::string &def)
130+
{
131+
std::string def_copy = def;
132+
auto func = parse_func_name(def_copy);
133+
if (func != "timewindow") {
134+
return {};
135+
}
136+
137+
auto args = string_split(def_copy, ",");
138+
if (args.size() != 2) {
139+
throw std::runtime_error("timewindow field bad args"); //FIXME
140+
}
141+
142+
for (auto &arg : args) {
143+
string_trim(arg);
144+
}
145+
146+
auto inner = create_elem_or_alias(args[0]);
147+
auto millis = parse_time_unit_to_millisecs(args[1]);
148+
if (!millis) {
149+
throw std::runtime_error("timewindow field bad time unit"); //FIXME
150+
}
151+
152+
std::unique_ptr<Field> field(new TimeWindowField(std::move(inner), *millis));
153+
field->set_name(def);
154+
return field;
155+
}
156+
157+
std::unique_ptr<Field>
158+
ViewFactory::parse_prefixlen_field(const std::string &def)
159+
{
160+
auto pieces = string_split(def, "/", 2);
161+
if (pieces.size() != 2) {
162+
return {};
163+
}
164+
165+
auto prefix_len = parse_number(pieces[1]);
166+
if (!prefix_len) {
167+
throw std::runtime_error("invalid ip prefix len"); //FIXME
168+
}
169+
170+
auto inner = create_elem_or_alias(pieces[0]);
171+
172+
std::unique_ptr<Field> field(new SubnetField(std::move(inner), *prefix_len));
173+
field->set_name(def);
174+
return field;
175+
}
176+
177+
std::unique_ptr<Field>
178+
ViewFactory::parse_dir_field(const std::string &def)
179+
{
180+
std::string def_lower = string_to_lower(def);
181+
if (def_lower == "direction" || def_lower == "dir" || def_lower == "biflowdir") {
182+
std::unique_ptr<Field> field(new DirectionField);
183+
field->set_name(def);
184+
return field;
185+
} else {
186+
return {};
187+
}
188+
}
189+
190+
std::unique_ptr<Field>
191+
ViewFactory::create_key_field(const std::string &def)
192+
{
193+
std::unique_ptr<Field> field;
194+
195+
if ((field = parse_timewindow_func(def))) {
196+
return field;
197+
}
198+
199+
if ((field = parse_prefixlen_field(def))) {
200+
return field;
201+
}
202+
203+
if ((field = parse_dir_field(def))) {
204+
return field;
205+
}
206+
207+
field = create_elem_or_alias(def);
208+
return field;
209+
}
210+
211+
std::unique_ptr<Field>
212+
ViewFactory::create_value_field(const std::string& def)
213+
{
214+
std::string rem_def = def;
215+
216+
std::unique_ptr<Field> field;
217+
218+
std::string func = parse_func_name(rem_def);
219+
std::string prefix = parse_inout_prefix(rem_def);
220+
221+
std::string rem_def_lower = string_to_lower(rem_def);
222+
if (rem_def_lower == "flows" || rem_def_lower == "flowcount" || rem_def_lower == "count") {
223+
field.reset(new FlowCountField);
224+
field->set_name(rem_def);
225+
} else {
226+
field = create_elem_or_alias(rem_def);
227+
}
228+
229+
if (func == "min") {
230+
field.reset(new MinAggregatedField(std::move(field)));
231+
} else if (func == "max") {
232+
field.reset(new MaxAggregatedField(std::move(field)));
233+
} else if (func == "sum") {
234+
field.reset(new SumAggregatedField(std::move(field)));
235+
} else if (func == "" && field->is_number()) {
236+
field.reset(new SumAggregatedField(std::move(field)));
237+
} else {
238+
throw std::invalid_argument("invalid aggregation function " + func);
239+
}
240+
241+
field->set_name(def);
242+
return field;
243+
}
244+
245+
static std::vector<std::string>
246+
split_args(const std::string &str)
247+
{
248+
std::vector<std::string> pieces;
249+
std::string tmp;
250+
int depth = 0;
251+
for (char c : str) {
252+
if (c == '(') {
253+
depth++;
254+
tmp.push_back(c);
255+
} else if (c == ')') {
256+
depth--;
257+
tmp.push_back(c);
258+
} else if (c == ',' && depth == 0) {
259+
pieces.push_back(tmp);
260+
tmp.clear();
261+
} else {
262+
tmp.push_back(c);
263+
}
264+
}
265+
pieces.push_back(tmp);
266+
return pieces;
267+
}
268+
269+
std::shared_ptr<View>
270+
ViewFactory::create_view(
271+
const std::string &key_def,
272+
const std::string &value_def,
273+
const std::string &order_def)
274+
{
275+
std::shared_ptr<View> view(new View);
276+
create_view(*view.get(), key_def, value_def, order_def);
277+
return view;
278+
}
279+
280+
void
281+
ViewFactory::create_view(
282+
View &view,
283+
const std::string &key_def,
284+
const std::string &value_def,
285+
const std::string &order_def)
286+
{
287+
for (auto def : split_args(key_def)) {
288+
string_trim(def);
289+
auto field = create_key_field(def);
290+
field->set_offset(view.m_key_size);
291+
view.m_key_size += field->size();
292+
view.m_key_count++;
293+
view.m_fields.push_back(std::move(field));
294+
}
295+
296+
for (auto def : split_args(value_def)) {
297+
string_trim(def);
298+
299+
auto field = create_value_field(def);
300+
field->set_offset(view.m_key_size + view.m_value_size);
301+
view.m_value_size += field->size();
302+
view.m_value_count++;
303+
304+
view.m_fields.push_back(std::move(field));
305+
}
306+
307+
if (!order_def.empty()) {
308+
for (auto def : split_args(order_def)) {
309+
string_trim(def);
310+
311+
const auto &pieces = string_split(def, "/", 2);
312+
313+
const auto *field = view.find_field(pieces[0]);
314+
if (!field) {
315+
throw std::invalid_argument("cannot find compare field \"" + def + "\"");
316+
}
317+
318+
View::OrderDirection dir;
319+
if (pieces.size() == 2) {
320+
if (pieces[1] == "asc") {
321+
dir = View::OrderDirection::Ascending;
322+
} else if (pieces[1] == "desc") {
323+
dir = View::OrderDirection::Descending;
324+
} else {
325+
throw std::invalid_argument("invalid compare field dir \"" + def + "\"");
326+
}
327+
} else {
328+
// Default
329+
dir = View::OrderDirection::Ascending;
330+
}
331+
332+
view.m_order_fields.push_back({field, dir});
333+
}
334+
}
335+
}
336+
337+
} // namespace aggregator
338+
} // namespace fdsdump

0 commit comments

Comments
 (0)