Skip to content

Commit bb30bc1

Browse files
author
Pavel Siska
committed
WIP - introduce new process plugin API
1 parent a24b8e2 commit bb30bc1

21 files changed

+1488
-0
lines changed

process-plugin-api/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CXX := g++
2+
CXXFLAGS := -std=c++20 -Wall -Wextra -pedantic -Wunused -Wconversion -Wsign-conversion -g -fsanitize=address
3+
TARGET := ipx
4+
SRC := main.cpp
5+
6+
.PHONY: all clean
7+
8+
all: $(TARGET)
9+
10+
$(TARGET): $(SRC)
11+
$(CXX) $(CXXFLAGS) $^ -o $@
12+
13+
clean:
14+
rm -f $(TARGET)

process-plugin-api/biflowPair.hpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <utility>
5+
6+
/**
7+
* @brief Represents a pair of field names that are semantically linked in a bidirectional flow.
8+
*
9+
* The order of the fields does not matter; (A, B) is considered equal to (B, A).
10+
*/
11+
struct BiflowPair {
12+
std::string forwardField; ///< Field name representing the forward direction.
13+
std::string reverseField; ///< Field name representing the reverse direction.
14+
15+
/**
16+
* @brief Constructs a BiflowPair with the given field names.
17+
*
18+
* @param forward Name of the forward-direction field.
19+
* @param reverse Name of the reverse-direction field.
20+
*/
21+
BiflowPair(std::string forward, std::string reverse)
22+
: forwardField(std::move(forward))
23+
, reverseField(std::move(reverse))
24+
{
25+
}
26+
27+
/**
28+
* @brief Compares two BiflowPairs for equality.
29+
*
30+
* The comparison is order-independent: (A, B) == (B, A).
31+
*
32+
* @param other The BiflowPair to compare with.
33+
* @return true if the pairs are semantically equal.
34+
*/
35+
bool operator==(const BiflowPair& other) const
36+
{
37+
return (forwardField == other.forwardField && reverseField == other.reverseField)
38+
|| (forwardField == other.reverseField && reverseField == other.forwardField);
39+
}
40+
41+
/**
42+
* @brief Compares two BiflowPairs for inequality.
43+
*
44+
* @param other The BiflowPair to compare with.
45+
* @return true if the pairs are not equal.
46+
*/
47+
bool operator!=(const BiflowPair& other) const { return !(*this == other); }
48+
};
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#pragma once
2+
3+
#include "fieldHandler.hpp"
4+
#include "fieldManager.hpp"
5+
#include "fieldSchema.hpp"
6+
#include "processPlugin.hpp"
7+
#include "flowRecord.hpp"
8+
9+
enum Direction : std::size_t { Forward = 0, Reverse = 1 };
10+
11+
template<typename T>
12+
struct DirectionalField {
13+
T values[2];
14+
15+
T& operator[](Direction d) { return values[static_cast<std::size_t>(d)]; }
16+
const T& operator[](Direction d) const { return values[static_cast<std::size_t>(d)]; }
17+
};
18+
19+
/*
20+
struct DummyExport {
21+
uint8_t ipTtl;
22+
uint8_t ipTtlRev;
23+
uint8_t ipFlag;
24+
uint8_t ipFlagRev;
25+
uint16_t tcpWindow = 100;
26+
uint16_t tcpWindowRev;
27+
uint64_t tcpOption;
28+
uint64_t tcpOptionRev;
29+
uint32_t tcpMss;
30+
uint32_t tcpMssRev;
31+
uint16_t tcpSynSize;
32+
std::vector<uint64_t> packets;
33+
std::vector<uint64_t> packetsRev;
34+
};
35+
*/
36+
37+
struct DummyExport {
38+
DirectionalField<uint8_t> ipTtl;
39+
DirectionalField<uint8_t> ipFlag;
40+
DirectionalField<uint16_t> tcpWindow;
41+
DirectionalField<uint64_t> tcpOption;
42+
DirectionalField<uint32_t> tcpMss;
43+
uint16_t tcpSynSize;
44+
DirectionalField<std::vector<uint64_t>> packets;
45+
};
46+
47+
enum class DummyFields : std::size_t {
48+
IP_TTL = 0,
49+
IP_TTL_REV,
50+
IP_FLG,
51+
IP_FLG_REV,
52+
TCP_WIN,
53+
TCP_WIN_REV,
54+
TCP_OPT,
55+
TCP_OPT_REV,
56+
TCP_MSS,
57+
TCP_MSS_REV,
58+
TCP_SYN_SIZE,
59+
PACKETS,
60+
PACKETS_REV,
61+
FIELDS_SIZE,
62+
};
63+
64+
static FieldSchema createDummySchema()
65+
{
66+
FieldSchema schema("basicplus");
67+
68+
schema.addScalarField<uint8_t>(
69+
"IP_TTL",
70+
FieldDirection::Forward,
71+
offsetof(DummyExport, ipTtl.values[Direction::Forward]));
72+
schema.addScalarField<uint8_t>(
73+
"IP_TTL_REV",
74+
FieldDirection::Reverse,
75+
offsetof(DummyExport, ipTtl.values[Direction::Reverse]));
76+
schema.addScalarField<uint8_t>(
77+
"IP_FLG",
78+
FieldDirection::Forward,
79+
offsetof(DummyExport, ipFlag.values[Direction::Forward]));
80+
schema.addScalarField<uint8_t>(
81+
"IP_FLG_REV",
82+
FieldDirection::Reverse,
83+
offsetof(DummyExport, ipFlag.values[Direction::Reverse]));
84+
schema.addScalarField<uint16_t>(
85+
"TCP_WIN",
86+
FieldDirection::Forward,
87+
offsetof(DummyExport, tcpWindow.values[Direction::Forward]));
88+
schema.addScalarField<uint16_t>(
89+
"TCP_WIN_REV",
90+
FieldDirection::Reverse,
91+
offsetof(DummyExport, tcpWindow.values[Direction::Reverse]));
92+
schema.addScalarField<uint64_t>(
93+
"TCP_OPT",
94+
FieldDirection::Forward,
95+
offsetof(DummyExport, tcpOption.values[Direction::Forward]));
96+
schema.addScalarField<uint64_t>(
97+
"TCP_OPT_REV",
98+
FieldDirection::Reverse,
99+
offsetof(DummyExport, tcpOption.values[Direction::Reverse]));
100+
schema.addScalarField<uint32_t>(
101+
"TCP_MSS",
102+
FieldDirection::Forward,
103+
offsetof(DummyExport, tcpMss.values[Direction::Forward]));
104+
schema.addScalarField<uint32_t>(
105+
"TCP_MSS_REV",
106+
FieldDirection::Reverse,
107+
offsetof(DummyExport, tcpMss.values[Direction::Reverse]));
108+
schema.addScalarField<uint16_t>(
109+
"TCP_SYN_SIZE",
110+
FieldDirection::DirectionalIndifferent,
111+
offsetof(DummyExport, tcpSynSize));
112+
113+
schema.addVectorField<uint64_t>(
114+
"PACKETS",
115+
FieldDirection::Forward,
116+
[](const void* thisPtr) -> std::span<const uint64_t> {
117+
return reinterpret_cast<const DummyExport*>(thisPtr)
118+
->packets.values[Direction::Forward];
119+
});
120+
121+
schema.addVectorField<uint64_t>(
122+
"PACKETS_REV",
123+
FieldDirection::Forward,
124+
[](const void* thisPtr) -> std::span<const uint64_t> {
125+
return reinterpret_cast<const DummyExport*>(thisPtr)
126+
->packets.values[Direction::Reverse];
127+
});
128+
129+
schema.addBiflowPair("IP_TTL", "IP_TTL_REV");
130+
schema.addBiflowPair("IP_FLG", "IP_FLG_REV");
131+
schema.addBiflowPair("TCP_WIN", "TCP_WIN_REV");
132+
schema.addBiflowPair("TCP_OPT", "TCP_OPT_REV");
133+
schema.addBiflowPair("TCP_MSS", "TCP_MSS_REV");
134+
schema.addBiflowPair("PACKETS", "PACKETS_REV");
135+
136+
return schema;
137+
}
138+
139+
class DummyPlugin
140+
: private FieldHandlers<DummyFields>
141+
, public ProcessPlugin {
142+
public:
143+
DummyPlugin(const std::string& params, FieldManager& manager)
144+
{
145+
const FieldSchema schema = createDummySchema();
146+
const FieldSchemaHandler schemaHandler = manager.registerSchema(schema);
147+
148+
(void) params;
149+
150+
m_fieldHandlers[DummyFields::TCP_WIN] = schemaHandler.getFieldHandler("TCP_WIN");
151+
m_fieldHandlers[DummyFields::TCP_OPT] = schemaHandler.getFieldHandler("TCP_OPT");
152+
m_fieldHandlers[DummyFields::TCP_OPT_REV] = schemaHandler.getFieldHandler("TCP_OPT_REV");
153+
m_fieldHandlers[DummyFields::PACKETS] = schemaHandler.getFieldHandler("PACKETS");
154+
m_fieldHandlers[DummyFields::PACKETS_REV] = schemaHandler.getFieldHandler("PACKETS_REV");
155+
// je potreba inicializovat vsechny fieldy
156+
}
157+
158+
FlowAction onFlowCreate(FlowRecord& flowRecord, const Packet& packet) override
159+
{
160+
(void) packet;
161+
162+
m_exportData.tcpOption[Direction::Forward] = 156;
163+
m_exportData.tcpOption[Direction::Reverse] = 1689;
164+
165+
// kdyz field dostane hodnotu, je potreba ho oznacit jako dostupny
166+
167+
m_fieldHandlers[DummyFields::TCP_OPT].setAsAvailable(flowRecord);
168+
m_fieldHandlers[DummyFields::TCP_OPT_REV].setAsAvailable(flowRecord);
169+
170+
return FlowAction::RequestNoData;
171+
}
172+
173+
FlowAction onFlowUpdate(FlowRecord& flowRecord, const Packet& packet) override
174+
{
175+
(void) flowRecord;
176+
(void) packet;
177+
178+
m_exportData.packets[Direction::Forward] = {1, 2, 3, 4, 5, 6};
179+
m_fieldHandlers[DummyFields::PACKETS].setAsAvailable(flowRecord);
180+
181+
return FlowAction::RequestNoData;
182+
}
183+
184+
void onFlowExport() override {}
185+
186+
const void* getExportData() const noexcept override { return &m_exportData; }
187+
188+
ProcessPlugin* clone(std::byte* constructAtAddress) const override
189+
{
190+
return std::construct_at(reinterpret_cast<DummyPlugin*>(constructAtAddress), *this);
191+
}
192+
193+
std::string getName() const override { return "DummyPlugin"; }
194+
195+
~DummyPlugin() override {}
196+
197+
DummyPlugin(const DummyPlugin& other) = default;
198+
DummyPlugin(DummyPlugin&& other) = delete;
199+
200+
private:
201+
DummyExport m_exportData;
202+
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <functional>
5+
#include <span>
6+
#include <string>
7+
#include <variant>
8+
9+
/**
10+
* @brief Accessor for reading a scalar field of type T from a binary structure using offset.
11+
*
12+
* Typically used when the field is stored at a known offset in a flat structure.
13+
*
14+
* @tparam T Type of the field (e.g. uint32_t, float, etc.).
15+
*/
16+
template<typename T>
17+
struct ScalarAccessor {
18+
/// Construct accessor using byte offset from the base pointer.
19+
explicit constexpr ScalarAccessor(std::size_t offset) noexcept
20+
: offset(offset)
21+
{
22+
}
23+
24+
/// Read the scalar value from the provided memory.
25+
T operator()(const void* data) const
26+
{
27+
return *reinterpret_cast<const T*>(static_cast<const char*>(data) + offset);
28+
}
29+
30+
/// Byte offset of the field in the source structure.
31+
std::size_t offset;
32+
};
33+
34+
/**
35+
* @brief Accessor for reading a read-only vector-like field using a lambda function.
36+
*
37+
* This accessor supports any container that can be viewed as `std::span<const T>`,
38+
* such as:
39+
* - `std::vector<T>`
40+
* - `std::array<T, N>`
41+
* - C-style arrays `T[N]` (when the size is known)
42+
* - any custom container or accessor function returning `std::span<const T>`
43+
*
44+
*
45+
* @tparam T Element type of the vector (e.g. `uint64_t`)
46+
*/
47+
template<typename T>
48+
struct VectorAccessor {
49+
/// Type of the getter function that extracts a span from the given structure pointer.
50+
using GetterFunc = std::function<std::span<const T>(const void*)>;
51+
52+
/// Construct the accessor with a user-supplied lambda or function.
53+
explicit VectorAccessor(GetterFunc func)
54+
: func(std::move(func))
55+
{
56+
}
57+
58+
/// Call the accessor to get a span of values from the provided structure pointer.
59+
std::span<const T> operator()(const void* data) const { return func(data); }
60+
61+
private:
62+
GetterFunc func;
63+
};
64+
65+
// Type-erased scalar getter for supported field types
66+
using ScalarValueGetter = std::variant<
67+
ScalarAccessor<uint8_t>,
68+
ScalarAccessor<uint16_t>,
69+
ScalarAccessor<uint32_t>,
70+
ScalarAccessor<uint64_t>,
71+
ScalarAccessor<int8_t>,
72+
ScalarAccessor<int16_t>,
73+
ScalarAccessor<int32_t>,
74+
ScalarAccessor<int64_t>,
75+
ScalarAccessor<float>,
76+
ScalarAccessor<double>>;
77+
78+
// Type-erased vector getter for supported field types
79+
using VectorValueGetter = std::variant<
80+
VectorAccessor<uint8_t>,
81+
VectorAccessor<uint16_t>,
82+
VectorAccessor<uint32_t>,
83+
VectorAccessor<uint64_t>,
84+
VectorAccessor<int8_t>,
85+
VectorAccessor<int16_t>,
86+
VectorAccessor<int32_t>,
87+
VectorAccessor<int64_t>,
88+
VectorAccessor<float>,
89+
VectorAccessor<double>,
90+
VectorAccessor<std::string>>;
91+
92+
/**
93+
* @brief Generic (type-erased) value getter that supports both scalar and vector types.
94+
*
95+
* Used in output fields to provide unified access to underlying data.
96+
*/
97+
using GenericValueGetter = std::variant<ScalarValueGetter, VectorValueGetter>;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include "fieldAccessor.hpp"
4+
#include "fieldDirection.hpp"
5+
6+
#include <cstdint>
7+
#include <string>
8+
#include <variant>
9+
10+
/**
11+
* @brief Describes a field in a flow record schema.
12+
*
13+
* Contains metadata necessary to identify and access a field value,
14+
* such as its group (e.g., "http", "tcp"), name (e.g., "IP_TTL"), direction
15+
* (forward, reverse, or indifferent), and value accessor.
16+
*/
17+
struct FieldDescription {
18+
/// Logical group to which this field belongs (e.g., "tls", "dns").
19+
std::string group;
20+
21+
/// Name of the field (e.g., "SRC_PORT", "IP_TTL").
22+
std::string name;
23+
24+
/// Direction of the field (Forward, Reverse, DirectionalIndifferent).
25+
FieldDirection direction;
26+
27+
/// Value getter that allows access to the field's value in a record.
28+
GenericValueGetter getter;
29+
};

0 commit comments

Comments
 (0)