Skip to content

Commit 17c753c

Browse files
Zainullin DamirZainullin Damir
authored andcommitted
Process plugins - Introduce NetBIOS process plugin
1 parent 420f466 commit 17c753c

File tree

7 files changed

+244
-185
lines changed

7 files changed

+244
-185
lines changed

src/plugins/process/netbios/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@ project(ipfixprobe-process-netbios VERSION 1.0.0 DESCRIPTION "ipfixprobe-process
33
add_library(ipfixprobe-process-netbios MODULE
44
src/netbios.cpp
55
src/netbios.hpp
6+
src/netbiosContext.hpp
7+
src/netbiosFields.hpp
68
)
79

810
set_target_properties(ipfixprobe-process-netbios PROPERTIES
911
CXX_VISIBILITY_PRESET hidden
1012
VISIBILITY_INLINES_HIDDEN YES
1113
)
1214

13-
target_include_directories(ipfixprobe-process-netbios PRIVATE
15+
target_include_directories(ipfixprobe-process-netbios PRIVATE
1416
${CMAKE_SOURCE_DIR}/include/
17+
${CMAKE_SOURCE_DIR}/include/ipfixprobe/processPlugin
18+
${CMAKE_SOURCE_DIR}/include/ipfixprobe/pluginFactory
1519
${CMAKE_SOURCE_DIR}/src/plugins/process/common
20+
${adaptmon_SOURCE_DIR}/lib/include/public/
1621
)
1722

1823
if(ENABLE_NEMEA)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# NetBIOS Plugin
2+
3+
This plugin provides in-depth analysis of NetBIOS traffic by capturing and exporting fields from NetBIOS packets.
4+
5+
## Features
6+
7+
- Extracts and exports NetBIOS name and suffix fields from NetBIOS packets.
8+
- Expects traffic to be on port 137.
9+
10+
## Output Fields
11+
12+
| Field Name | Data Type | Description |
13+
| ----------- | --------- | ---------------------------------------- |
14+
| `NB_NAME` | `string` | NetBIOS name extracted from the packet |
15+
| `NB_SUFFIX` | `char` | NetBIOS suffix extracted from the packet |
16+
17+
## Usage
18+
19+
### YAML Configuration
20+
21+
Add the plugin to your ipfixprobe YAML configuration:
22+
23+
```yaml
24+
process_plugins:
25+
- netbios
26+
```
27+
28+
### CLI Usage
29+
30+
You can also enable the plugin directly from the command line:
31+
32+
`ipfixprobe -p netbios ...`

src/plugins/process/netbios/src/netbios.cpp

Lines changed: 71 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,31 @@
55
* @author Pavel Siska <[email protected]>
66
* @date 2025
77
*
8-
* Copyright (c) 2025 CESNET
8+
* Provides a plugin that extracts NetBIOS suffix and name from packets,
9+
* stores them in per-flow plugin data, and exposes fields via FieldManager.
910
*
10-
* SPDX-License-Identifier: BSD-3-Clause
11+
* @copyright Copyright (c) 2025 CESNET, z.s.p.o.
1112
*/
1213

1314
#include "netbios.hpp"
1415

15-
#ifdef WITH_NEMEA
16-
#include <unirec/unirec.h>
17-
#endif
16+
#include "netbiosGetters.hpp"
1817

18+
#include <cmath>
1919
#include <iostream>
2020

21-
#include <ipfixprobe/pluginFactory/pluginManifest.hpp>
22-
#include <ipfixprobe/pluginFactory/pluginRegistrar.hpp>
21+
#include <dns-utils.hpp>
22+
#include <fieldGroup.hpp>
23+
#include <fieldManager.hpp>
24+
#include <flowRecord.hpp>
25+
#include <ipfixprobe/options.hpp>
26+
#include <pluginFactory.hpp>
27+
#include <pluginManifest.hpp>
28+
#include <pluginRegistrar.hpp>
29+
#include <utils/spanUtils.hpp>
30+
#include <utils/stringViewUtils.hpp>
2331

24-
namespace ipxp {
32+
namespace ipxp::process::netbios {
2533

2634
static const PluginManifest netbiosPluginManifest = {
2735
.name = "netbios",
@@ -34,121 +42,95 @@ static const PluginManifest netbiosPluginManifest = {
3442
parser.usage(std::cout);
3543
},
3644
};
37-
NETBIOSPlugin::NETBIOSPlugin(const std::string& params, int pluginID)
38-
: ProcessPlugin(pluginID)
39-
, total_netbios_packets(0)
40-
{
41-
init(params.c_str());
42-
}
4345

44-
NETBIOSPlugin::~NETBIOSPlugin()
46+
static FieldGroup
47+
createNetBIOSSchema(FieldManager& fieldManager, FieldHandlers<NetBIOSFields>& fieldHandlers)
4548
{
46-
close();
47-
}
49+
FieldGroup schema = fieldManager.createFieldGroup("netbios");
4850

49-
void NETBIOSPlugin::init(const char* params)
50-
{
51-
(void) params;
52-
}
51+
fieldHandlers.insert(
52+
NetBIOSFields::NB_SUFFIX,
53+
schema.addScalarField("NB_SUFFIX", getNBSuffixField));
5354

54-
void NETBIOSPlugin::close() {}
55+
fieldHandlers.insert(NetBIOSFields::NB_NAME, schema.addScalarField("NB_NAME", getNBNameField));
5556

56-
ProcessPlugin* NETBIOSPlugin::copy()
57-
{
58-
return new NETBIOSPlugin(*this);
57+
return schema;
5958
}
6059

61-
int NETBIOSPlugin::post_create(Flow& rec, const Packet& pkt)
60+
NetBIOSPlugin::NetBIOSPlugin([[maybe_unused]] const std::string& params, FieldManager& manager)
6261
{
63-
if (pkt.dst_port == 137 || pkt.src_port == 137) {
64-
return add_netbios_ext(rec, pkt);
65-
}
66-
67-
return 0;
62+
createNetBIOSSchema(manager, m_fieldHandlers);
6863
}
6964

70-
int NETBIOSPlugin::post_update(Flow& rec, const Packet& pkt)
65+
OnInitResult NetBIOSPlugin::onInit(const FlowContext& flowContext, void* pluginContext)
7166
{
72-
if (pkt.dst_port == 137 || pkt.src_port == 137) {
73-
return add_netbios_ext(rec, pkt);
67+
constexpr uint8_t NETBIOS_PORT = 137;
68+
if (flowContext.flowRecord.flowKey.srcPort == NETBIOS_PORT
69+
|| flowContext.flowRecord.flowKey.dstPort == NETBIOS_PORT) {
70+
auto& netbiosContext = *std::construct_at(reinterpret_cast<NetBIOSContext*>(pluginContext));
71+
parseNetBIOS(
72+
flowContext.flowRecord,
73+
getPayload(*flowContext.packetContext.packet),
74+
netbiosContext);
75+
return OnInitResult::ConstructedFinal;
7476
}
7577

76-
return 0;
78+
return OnInitResult::Irrelevant;
7779
}
7880

79-
int NETBIOSPlugin::add_netbios_ext(Flow& rec, const Packet& pkt)
81+
constexpr static char compressCharPair(const char first, const char second)
8082
{
81-
RecordExtNETBIOS* ext = new RecordExtNETBIOS(m_pluginID);
82-
if (parse_nbns(ext, pkt)) {
83-
total_netbios_packets++;
84-
rec.add_extension(ext);
85-
} else {
86-
delete ext;
87-
}
88-
89-
return 0;
83+
return static_cast<char>(((first - 'A') << 4) | (second - 'A'));
9084
}
9185

92-
bool NETBIOSPlugin::parse_nbns(RecordExtNETBIOS* rec, const Packet& pkt)
86+
void NetBIOSPlugin::parseNetBIOS(
87+
FlowRecord& flowRecord,
88+
std::span<const std::byte> payload,
89+
NetBIOSContext& netbiosContext) noexcept
9390
{
94-
const char* payload = reinterpret_cast<const char*>(pkt.payload);
95-
96-
int qry_cnt = get_query_count(payload, pkt.payload_len);
97-
payload += sizeof(struct dns_hdr);
98-
if (qry_cnt < 1) {
99-
return false;
91+
if (payload.size() < sizeof(dns_hdr) || !netbiosContext.name.empty()) {
92+
return;
10093
}
10194

102-
return store_first_query(payload, rec);
103-
}
104-
105-
int NETBIOSPlugin::get_query_count(const char* payload, uint16_t payload_length)
106-
{
107-
if (payload_length < sizeof(struct dns_hdr)) {
108-
return -1;
95+
const std::size_t queryCount
96+
= reinterpret_cast<const dns_hdr*>(payload.data())->question_rec_cnt;
97+
if (queryCount == 0) {
98+
return;
10999
}
110100

111-
struct dns_hdr* hdr = (struct dns_hdr*) payload;
112-
return ntohs(hdr->question_rec_cnt);
113-
}
114-
115-
bool NETBIOSPlugin::store_first_query(const char* payload, RecordExtNETBIOS* rec)
116-
{
117-
uint8_t nb_name_length = *payload++;
118-
if (nb_name_length != 32) {
119-
return false;
101+
const uint8_t nameLength = *reinterpret_cast<const uint8_t*>(payload.data() + sizeof(dns_hdr));
102+
constexpr std::size_t VALID_NB_NAME_LENGTH = 32;
103+
if (nameLength != VALID_NB_NAME_LENGTH) {
104+
return;
120105
}
121106

122-
rec->netbios_name = "";
123-
for (int i = 0; i < nb_name_length; i += 2, payload += 2) {
124-
if (i != 30) {
125-
rec->netbios_name += compress_nbns_name_char(payload);
126-
} else {
127-
rec->netbios_suffix = get_nbns_suffix(payload);
128-
}
107+
auto nameIt = reinterpret_cast<const std::pair<char, char>*>(payload.data());
108+
for (; reinterpret_cast<const std::byte*>(nameIt) != payload.data() + payload.size() - 2;
109+
nameIt++) {
110+
netbiosContext.name.push_back(compressCharPair(nameIt->first, nameIt->second));
129111
}
130-
return true;
131-
}
112+
m_fieldHandlers[NetBIOSFields::NB_NAME].setAsAvailable(flowRecord);
132113

133-
char NETBIOSPlugin::compress_nbns_name_char(const char* uncompressed)
134-
{
135-
return (((uncompressed[0] - 'A') << 4) | (uncompressed[1] - 'A'));
114+
netbiosContext.suffix = compressCharPair(nameIt->first, nameIt->second);
115+
m_fieldHandlers[NetBIOSFields::NB_SUFFIX].setAsAvailable(flowRecord);
136116
}
137117

138-
uint8_t NETBIOSPlugin::get_nbns_suffix(const char* uncompressed)
118+
void NetBIOSPlugin::onDestroy(void* pluginContext) noexcept
139119
{
140-
return compress_nbns_name_char(uncompressed);
120+
std::destroy_at(reinterpret_cast<NetBIOSContext*>(pluginContext));
141121
}
142122

143-
void NETBIOSPlugin::finish(bool print_stats)
123+
PluginDataMemoryLayout NetBIOSPlugin::getDataMemoryLayout() const noexcept
144124
{
145-
if (print_stats) {
146-
std::cout << "NETBIOS plugin stats:" << std::endl;
147-
std::cout << " Parsed NBNS packets in total: " << total_netbios_packets << std::endl;
148-
}
125+
return {
126+
.size = sizeof(NetBIOSContext),
127+
.alignment = alignof(NetBIOSContext),
128+
};
149129
}
150130

151-
static const PluginRegistrar<NETBIOSPlugin, ProcessPluginFactory>
131+
static const PluginRegistrar<
132+
NetBIOSPlugin,
133+
PluginFactory<ProcessPlugin, const std::string&, FieldManager&>>
152134
netbiosRegistrar(netbiosPluginManifest);
153135

154-
} // namespace ipxp
136+
} // namespace ipxp::process::netbios

0 commit comments

Comments
 (0)