Skip to content

Commit c2020d4

Browse files
committed
fdsdump: add basic ipfix field
1 parent 048b629 commit c2020d4

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief IPFIX field
5+
*
6+
* Copyright: (C) 2024 CESNET, z.s.p.o.
7+
* SPDX-License-Identifier: BSD-3-Clause
8+
*/
9+
10+
#include <aggregator/ipfixField.hpp>
11+
#include <common/fieldView.hpp>
12+
13+
#include <cstring>
14+
15+
namespace fdsdump {
16+
namespace aggregator {
17+
18+
IpfixField::IpfixField(const fds_iemgr_elem &elem)
19+
{
20+
m_pen = elem.scope->pen;
21+
m_id = elem.id;
22+
23+
DataType data_type = DataType::Unassigned;
24+
switch (elem.data_type) {
25+
case FDS_ET_UNSIGNED_8:
26+
data_type = DataType::Unsigned8;
27+
break;
28+
case FDS_ET_UNSIGNED_16:
29+
data_type = DataType::Unsigned16;
30+
break;
31+
case FDS_ET_UNSIGNED_32:
32+
data_type = DataType::Unsigned32;
33+
break;
34+
case FDS_ET_UNSIGNED_64:
35+
data_type = DataType::Unsigned64;
36+
break;
37+
case FDS_ET_SIGNED_8:
38+
data_type = DataType::Signed8;
39+
break;
40+
case FDS_ET_SIGNED_16:
41+
data_type = DataType::Signed16;
42+
break;
43+
case FDS_ET_SIGNED_32:
44+
data_type = DataType::Signed32;
45+
break;
46+
case FDS_ET_SIGNED_64:
47+
data_type = DataType::Signed64;
48+
break;
49+
case FDS_ET_IPV4_ADDRESS:
50+
data_type = DataType::IPv4Address;
51+
break;
52+
case FDS_ET_IPV6_ADDRESS:
53+
data_type = DataType::IPv6Address;
54+
break;
55+
case FDS_ET_STRING:
56+
data_type = DataType::String128B;
57+
break;
58+
case FDS_ET_OCTET_ARRAY:
59+
data_type = DataType::Octets128B;
60+
break;
61+
case FDS_ET_DATE_TIME_MILLISECONDS:
62+
case FDS_ET_DATE_TIME_MICROSECONDS:
63+
case FDS_ET_DATE_TIME_NANOSECONDS:
64+
case FDS_ET_DATE_TIME_SECONDS:
65+
data_type = DataType::DateTime;
66+
break;
67+
case FDS_ET_MAC_ADDRESS:
68+
data_type = DataType::MacAddress;
69+
break;
70+
default:
71+
throw std::invalid_argument("unsupported IPFIX field data type");
72+
}
73+
74+
set_data_type(data_type);
75+
76+
set_name(elem.name);
77+
}
78+
79+
bool
80+
IpfixField::load(FlowContext &ctx, Value &value) const
81+
{
82+
fds_drec_field drec_field;
83+
if (!ctx.find_field(m_pen, m_id, drec_field)) {
84+
return false;
85+
}
86+
87+
switch (data_type()) {
88+
case DataType::Unsigned8:
89+
value.u8 = FieldView(drec_field).as_uint();
90+
break;
91+
case DataType::Unsigned16:
92+
value.u16 = FieldView(drec_field).as_uint();
93+
break;
94+
case DataType::Unsigned32:
95+
value.u32 = FieldView(drec_field).as_uint();
96+
break;
97+
case DataType::Unsigned64:
98+
value.u64 = FieldView(drec_field).as_uint();
99+
break;
100+
case DataType::Signed8:
101+
value.i8 = FieldView(drec_field).as_int();
102+
break;
103+
case DataType::Signed16:
104+
value.i16 = FieldView(drec_field).as_int();
105+
break;
106+
case DataType::Signed32:
107+
value.i32 = FieldView(drec_field).as_int();
108+
break;
109+
case DataType::Signed64:
110+
value.i64 = FieldView(drec_field).as_int();
111+
break;
112+
case DataType::String128B:
113+
case DataType::Octets128B:
114+
memset(value.str, 0, 128);
115+
memcpy(value.str, drec_field.data, std::min<int>(drec_field.size, 128));
116+
break;
117+
case DataType::DateTime:
118+
value.ts_millisecs = FieldView(drec_field).as_datetime_ms();
119+
break;
120+
case DataType::IPv4Address:
121+
if (drec_field.size != sizeof(value.ipv4)) {
122+
throw std::runtime_error(
123+
"Unexpected IPFIX field size when attempting to read IPv4 address:"
124+
" expected " + std::to_string(sizeof(value.ipv4)) +
125+
" got: " + std::to_string(drec_field.size)
126+
);
127+
}
128+
memcpy(&value.ipv4, drec_field.data, sizeof(value.ipv4));
129+
break;
130+
case DataType::IPv6Address:
131+
if (drec_field.size != sizeof(value.ipv6)) {
132+
throw std::runtime_error(
133+
"Unexpected IPFIX field size when attempting to read IPv6 address:"
134+
" expected " + std::to_string(sizeof(value.ipv6)) +
135+
" got: " + std::to_string(drec_field.size)
136+
);
137+
}
138+
memcpy(&value.ipv6, drec_field.data, sizeof(value.ipv6));
139+
break;
140+
case DataType::MacAddress:
141+
if (drec_field.size != sizeof(value.mac)) {
142+
throw std::runtime_error(
143+
"Unexpected IPFIX field size when attempting to read MAC address:"
144+
" expected " + std::to_string(sizeof(value.mac)) +
145+
" got: " + std::to_string(drec_field.size)
146+
);
147+
}
148+
memcpy(value.mac, drec_field.data, sizeof(value.mac));
149+
break;
150+
default:
151+
throw std::logic_error("unexpected IPFIX field data type");
152+
}
153+
154+
return true;
155+
}
156+
157+
std::string
158+
IpfixField::repr() const
159+
{
160+
return std::string("IpfixField(") +
161+
+ "name=" + name() + ", data_type=" + data_type_to_str(data_type())
162+
+ ", size=" + std::to_string(size()) + ", offset=" + std::to_string(offset())
163+
+ ", pen=" + std::to_string(m_pen) + ", id=" + std::to_string(m_id) + ")";
164+
}
165+
166+
uint32_t
167+
IpfixField::pen() const
168+
{
169+
return m_pen;
170+
}
171+
172+
uint16_t
173+
IpfixField::id() const
174+
{
175+
return m_id;
176+
}
177+
178+
bool
179+
IpfixField::operator==(const IpfixField &other) const
180+
{
181+
return pen() == other.pen() && id() == other.id();
182+
}
183+
184+
bool
185+
IpfixField::operator==(const Field &other) const
186+
{
187+
if (const auto *other_ipfix = dynamic_cast<const IpfixField *>(&other)) {
188+
return *this == *other_ipfix;
189+
}
190+
return false;
191+
}
192+
193+
} // aggregator
194+
} // fdsdump
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief IPFIX field
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 <libfds.h>
15+
16+
namespace fdsdump {
17+
namespace aggregator {
18+
19+
/**
20+
* @brief A field mapping directly to an IPFIX element
21+
*/
22+
class IpfixField : public Field {
23+
public:
24+
/**
25+
* @brief Construct a new IPFIX field
26+
*
27+
* @param elem The information element describing the IPFIX field
28+
*/
29+
IpfixField(const fds_iemgr_elem &elem);
30+
31+
/**
32+
* @brief Load the value of the IPFIX field from the flow record
33+
*
34+
* @param[in] ctx The flow record
35+
* @param[out] value The value
36+
*/
37+
bool load(FlowContext &ctx, Value &value) const override;
38+
39+
/**
40+
* @brief Get a string representation of the field
41+
*/
42+
std::string repr() const override;
43+
44+
/**
45+
* @brief Get the Private Enterprise Number of the IPFIX element
46+
*/
47+
uint32_t pen() const;
48+
49+
/**
50+
* @brief Get the ID number of the IPFIX element
51+
*/
52+
uint16_t id() const;
53+
54+
/**
55+
* @brief Compare two IPFIX fields for equality
56+
*/
57+
bool operator==(const IpfixField &other) const;
58+
59+
/**
60+
* @brief Compare two fields for equality
61+
*/
62+
bool operator==(const Field &other) const override;
63+
64+
private:
65+
uint32_t m_pen;
66+
uint16_t m_id;
67+
};
68+
69+
} // aggregator
70+
} // fdsdump

0 commit comments

Comments
 (0)