Skip to content

Commit 897371d

Browse files
committed
fdsdump: add extra fields (flow count, direction, subnet, timewindow)
1 parent 8319294 commit 897371d

File tree

2 files changed

+360
-0
lines changed

2 files changed

+360
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief Extra fields
5+
*
6+
* Copyright: (C) 2024 CESNET, z.s.p.o.
7+
* SPDX-License-Identifier: BSD-3-Clause
8+
*/
9+
10+
#include <aggregator/extraFields.hpp>
11+
12+
namespace fdsdump {
13+
namespace aggregator {
14+
15+
FlowCountField::FlowCountField()
16+
{
17+
set_data_type(DataType::Unsigned64);
18+
set_name("flowcount");
19+
}
20+
21+
bool
22+
FlowCountField::load(FlowContext &ctx, Value &value) const
23+
{
24+
(void) ctx;
25+
value.u64 = 1;
26+
return true;
27+
}
28+
29+
bool
30+
FlowCountField::operator==(const Field &other) const
31+
{
32+
const auto *other_flowcount = dynamic_cast<const FlowCountField *>(&other);
33+
if (!other_flowcount) {
34+
return false;
35+
}
36+
return true;
37+
}
38+
39+
std::string
40+
FlowCountField::repr() const
41+
{
42+
return std::string("FlowCountField(") +
43+
+ "name=" + name() + ", data_type=" + data_type_to_str(data_type())
44+
+ ", size= " + std::to_string(size()) + ", offset=" + std::to_string(offset())
45+
+ ")";
46+
}
47+
48+
DirectionField::DirectionField()
49+
{
50+
set_data_type(DataType::Unsigned8);
51+
set_name("direction");
52+
}
53+
54+
bool
55+
DirectionField::load(FlowContext &ctx, Value &value) const
56+
{
57+
value.u8 = (ctx.flow_dir == FlowDirection::Reverse ? REV_VALUE : FWD_VALUE);
58+
return true;
59+
}
60+
61+
bool
62+
DirectionField::operator==(const Field &other) const
63+
{
64+
const auto *other_dirfield = dynamic_cast<const DirectionField *>(&other);
65+
if (!other_dirfield) {
66+
return false;
67+
}
68+
return true;
69+
}
70+
71+
std::string
72+
DirectionField::repr() const
73+
{
74+
return std::string("DirectionField(") +
75+
+ "name=" + name() + ", data_type=" + data_type_to_str(data_type())
76+
+ ", size= " + std::to_string(size()) + ", offset=" + std::to_string(offset())
77+
+ ")";
78+
}
79+
80+
TimeWindowField::TimeWindowField(std::unique_ptr<Field> source_field, uint64_t window_millisec) :
81+
m_window_millisec(window_millisec)
82+
{
83+
if (source_field->data_type() != DataType::DateTime) {
84+
throw std::invalid_argument("source field of timewindow field is not datetime");
85+
}
86+
87+
if (window_millisec == 0) {
88+
throw std::invalid_argument("time window duration cannot be 0");
89+
}
90+
91+
m_source_field = std::move(source_field);
92+
set_name("timewindow(" + m_source_field->name() + ", " + std::to_string(window_millisec) + "ms)");
93+
set_data_type(m_source_field->data_type());
94+
}
95+
96+
bool
97+
TimeWindowField::load(FlowContext &ctx, Value &value) const
98+
{
99+
if (!m_source_field->load(ctx, value)) {
100+
return false;
101+
}
102+
103+
value.ts_millisecs = (value.ts_millisecs / m_window_millisec) * m_window_millisec;
104+
return true;
105+
}
106+
107+
bool
108+
TimeWindowField::operator==(const Field &other) const
109+
{
110+
const auto *other_timewindow = dynamic_cast<const TimeWindowField *>(&other);
111+
if (!other_timewindow) {
112+
return false;
113+
}
114+
115+
return m_source_field == other_timewindow->m_source_field
116+
&& m_window_millisec == other_timewindow->m_window_millisec;
117+
}
118+
119+
std::string
120+
TimeWindowField::repr() const
121+
{
122+
return std::string("TimeWindowField(") +
123+
+ "name=" + name() + ", data_type=" + data_type_to_str(data_type())
124+
+ ", size= " + std::to_string(size()) + ", offset=" + std::to_string(offset())
125+
+ ", source=" + m_source_field->repr() + ", window_millisec=" + std::to_string(m_window_millisec)
126+
+ ")";
127+
}
128+
129+
SubnetField::SubnetField(std::unique_ptr<Field> source_field, uint8_t prefix_len)
130+
{
131+
uint8_t addr_bytes;
132+
if (source_field->data_type() == DataType::IPv4Address) {
133+
addr_bytes = 4;
134+
} else if (source_field->data_type() == DataType::IPv6Address) {
135+
addr_bytes = 16;
136+
} else {
137+
throw std::invalid_argument("invalid data type of source field"); //FIXME
138+
}
139+
140+
if (prefix_len > addr_bytes * 8) {
141+
throw std::invalid_argument("invalid prefix len"); //FIXME
142+
}
143+
144+
m_source_field = std::move(source_field);
145+
set_data_type(m_source_field->data_type());
146+
set_name(m_source_field->name() + "/" + std::to_string(prefix_len));
147+
148+
m_startbyte = prefix_len / 8;
149+
m_zerobytes = addr_bytes - m_startbyte;
150+
m_startmask = ~(uint8_t(0xFF) >> (prefix_len % 8));
151+
m_prefix_len = prefix_len;
152+
}
153+
154+
bool
155+
SubnetField::load(FlowContext &ctx, Value &value) const
156+
{
157+
if (!m_source_field->load(ctx, value)) {
158+
return false;
159+
}
160+
161+
if (m_zerobytes > 0) {
162+
// value.str to access raw bytes
163+
uint8_t tmp = value.str[m_startbyte] & m_startmask;
164+
memset(&value.str[m_startbyte], 0, m_zerobytes);
165+
value.str[m_startbyte] = tmp;
166+
}
167+
return true;
168+
}
169+
170+
bool
171+
SubnetField::operator==(const Field &other) const
172+
{
173+
const auto *other_subnet = dynamic_cast<const SubnetField *>(&other);
174+
if (!other_subnet) {
175+
return false;
176+
}
177+
178+
return m_source_field == other_subnet->m_source_field
179+
&& m_prefix_len == other_subnet->m_prefix_len;
180+
}
181+
182+
std::string
183+
SubnetField::repr() const
184+
{
185+
return std::string("SubnetField(") +
186+
+ "name=" + name() + ", data_type=" + data_type_to_str(data_type())
187+
+ ", size= " + std::to_string(size()) + ", offset=" + std::to_string(offset())
188+
+ ", source=" + m_source_field->repr() + ", prefix_len=" + std::to_string(m_prefix_len)
189+
+ ")";
190+
}
191+
192+
} // aggregator
193+
} // fdsdump
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief Extra fields
5+
*
6+
* Copyright: (C) 2024 CESNET, z.s.p.o.
7+
* SPDX-License-Identifier: BSD-3-Clause
8+
*/
9+
10+
#pragma once
11+
12+
#include <aggregator/field.hpp>
13+
14+
#include <string>
15+
16+
namespace fdsdump {
17+
namespace aggregator {
18+
19+
/**
20+
* @brief A special field providing the number of flows
21+
*/
22+
class FlowCountField : public Field {
23+
public:
24+
/**
25+
* @brief Create a new instance of a FlowCountField
26+
*/
27+
FlowCountField();
28+
29+
/**
30+
* @brief Load the value of the field flow record
31+
*
32+
* @param[in] ctx The flow record
33+
* @param[out] value The value
34+
*/
35+
bool
36+
load(FlowContext &ctx, Value &value) const override;
37+
38+
/**
39+
* @brief Check if the fields are equal
40+
*/
41+
bool
42+
operator==(const Field &other) const override;
43+
44+
/**
45+
* @brief Get a string representation of the field
46+
*/
47+
std::string
48+
repr() const override;
49+
};
50+
51+
/**
52+
* @brief A special field providing the direction of the flow
53+
*/
54+
class DirectionField : public Field {
55+
public:
56+
static constexpr uint8_t FWD_VALUE = 1;
57+
static constexpr uint8_t REV_VALUE = 2;
58+
59+
DirectionField();
60+
61+
/**
62+
* @brief Load the value of the field flow record
63+
*
64+
* @param[in] ctx The flow record
65+
* @param[out] value The value
66+
*/
67+
bool
68+
load(FlowContext &ctx, Value &value) const override;
69+
70+
/**
71+
* @brief Check if the fields are equal
72+
*/
73+
bool
74+
operator==(const Field &other) const override;
75+
76+
/**
77+
* @brief Get a string representation of the field
78+
*/
79+
std::string
80+
repr() const override;
81+
};
82+
83+
/**
84+
* @brief A special field allowing to group datetime fields into time windows of a specified duration
85+
*/
86+
class TimeWindowField : public Field {
87+
public:
88+
/**
89+
* @brief Create a new instance of a TimeWindowField
90+
*
91+
* @param source_field The source field
92+
* @param window_millisec The length of the time window in milliseconds
93+
*/
94+
TimeWindowField(std::unique_ptr<Field> source_field, uint64_t window_millisec);
95+
96+
/**
97+
* @brief Load the value of the field flow record
98+
*
99+
* @param[in] ctx The flow record
100+
* @param[out] value The value
101+
*/
102+
bool
103+
load(FlowContext &ctx, Value &value) const override;
104+
105+
/**
106+
* @brief Check if the fields are equal
107+
*/
108+
bool
109+
operator==(const Field &other) const override;
110+
111+
/**
112+
* @brief Get a string representation of the field
113+
*/
114+
std::string
115+
repr() const override;
116+
117+
private:
118+
uint64_t m_window_millisec;
119+
std::unique_ptr<Field> m_source_field;
120+
};
121+
122+
/**
123+
* @brief A special field allowing to group IP fields based on their subnet
124+
*/
125+
class SubnetField : public Field {
126+
friend class InOutKeyField;
127+
128+
public:
129+
/**
130+
* @brief Create a new instance of a SubnetField
131+
*
132+
* @param source_field The source field
133+
* @param prefix_len The prefix length of the subnet
134+
*/
135+
SubnetField(std::unique_ptr<Field> source_field, uint8_t prefix_len);
136+
137+
/**
138+
* @brief Load the value of the field flow record
139+
*
140+
* @param[in] ctx The flow record
141+
* @param[out] value The value
142+
*/
143+
bool
144+
load(FlowContext &ctx, Value &value) const override;
145+
146+
/**
147+
* @brief Check if the fields are equal
148+
*/
149+
bool
150+
operator==(const Field &other) const override;
151+
152+
/**
153+
* @brief Get a string representation of the field
154+
*/
155+
std::string
156+
repr() const override;
157+
158+
private:
159+
std::unique_ptr<Field> m_source_field;
160+
uint8_t m_startbyte;
161+
uint8_t m_startmask;
162+
uint8_t m_zerobytes;
163+
uint8_t m_prefix_len;
164+
};
165+
166+
} // aggregator
167+
} // fdsdump

0 commit comments

Comments
 (0)