Skip to content

Commit 0868e0a

Browse files
authored
Merge pull request #146 from CESNET/ssadetector_plugin
SSADetector plugin
2 parents 0ae4c19 + 8b030d1 commit 0868e0a

File tree

7 files changed

+526
-4
lines changed

7 files changed

+526
-4
lines changed

Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ ipfixprobe_process_src=\
124124
process/stats.hpp \
125125
process/md5.hpp \
126126
process/md5.cpp \
127-
process/common.hpp
127+
process/common.hpp \
128+
process/ssadetector.hpp \
129+
process/ssadetector.cpp
128130

129131
if WITH_QUIC
130132
ipfixprobe_process_src+=\

include/ipfixprobe/ipfix-elements.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ namespace ipxp {
219219
#define DNSSD_RESPONSES(F) F(8057, 827, -1, nullptr)
220220

221221
#define OVPN_CONF_LEVEL(F) F(8057, 828, 1, nullptr)
222+
#define SSA_CONF_LEVEL(F) F(8057, 903, 1, nullptr)
222223

223224
#define NB_NAME(F) F(8057, 831, -1, nullptr)
224225
#define NB_SUFFIX(F) F(8057, 832, 1, nullptr)
@@ -423,6 +424,9 @@ namespace ipxp {
423424
#define IPFIX_OVPN_TEMPLATE(F) \
424425
F(OVPN_CONF_LEVEL)
425426

427+
#define IPFIX_SSADETECTOR_TEMPLATE(F) \
428+
F(SSA_CONF_LEVEL)
429+
426430
#define IPFIX_SSDP_TEMPLATE(F) \
427431
F(SSDP_LOCATION_PORT) \
428432
F(SSDP_NT) \
@@ -539,7 +543,8 @@ namespace ipxp {
539543
IPFIX_OSQUERY_TEMPLATE(F) \
540544
IPFIX_FLEXPROBE_DATA_TEMPLATE(F) \
541545
IPFIX_FLEXPROBE_TCP_TEMPLATE(F) \
542-
IPFIX_FLEXPROBE_ENCR_TEMPLATE(F)
546+
IPFIX_FLEXPROBE_ENCR_TEMPLATE(F) \
547+
IPFIX_SSADETECTOR_TEMPLATE(F)
543548

544549
/**
545550
* Helper macro, convert FIELD into its name as a C literal.

process/ssadetector.cpp

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/**
2+
* \file ssadetector.cpp
3+
* \brief Plugin for detecting ssa sequence.
4+
* \author Jan Jirák [email protected]
5+
* \author Karel Hynek [email protected]
6+
* \date 2023
7+
*/
8+
/*
9+
* Copyright (C) 2023 CESNET
10+
*
11+
* LICENSE TERMS
12+
*
13+
* Redistribution and use in source and binary forms, with or without
14+
* modification, are permitted provided that the following conditions
15+
* are met:
16+
* 1. Redistributions of source code must retain the above copyright
17+
* notice, this list of conditions and the following disclaimer.
18+
* 2. Redistributions in binary form must reproduce the above copyright
19+
* notice, this list of conditions and the following disclaimer in
20+
* the documentation and/or other materials provided with the
21+
* distribution.
22+
* 3. Neither the name of the Company nor the names of its contributors
23+
* may be used to endorse or promote products derived from this
24+
* software without specific prior written permission.
25+
*
26+
* ALTERNATIVELY, provided that this notice is retained in full, this
27+
* product may be distributed under the terms of the GNU General Public
28+
* License (GPL) version 2 or later, in which case the provisions
29+
* of the GPL apply INSTEAD OF those given above.
30+
*
31+
* This software is provided as is'', and any express or implied
32+
* warranties, including, but not limited to, the implied warranties of
33+
* merchantability and fitness for a particular purpose are disclaimed.
34+
* In no event shall the company or contributors be liable for any
35+
* direct, indirect, incidental, special, exemplary, or consequential
36+
* damages (including, but not limited to, procurement of substitute
37+
* goods or services; loss of use, data, or profits; or business
38+
* interruption) however caused and on any theory of liability, whether
39+
* in contract, strict liability, or tort (including negligence or
40+
* otherwise) arising in any way out of the use of this software, even
41+
* if advised of the possibility of such damage.
42+
*
43+
*/
44+
45+
#include <iostream>
46+
47+
#include "ssadetector.hpp"
48+
49+
namespace ipxp {
50+
51+
int RecordExtSSADetector::REGISTERED_ID = -1;
52+
53+
__attribute__((constructor)) static void register_this_plugin()
54+
{
55+
static PluginRecord rec = PluginRecord("ssadetector", []() { return new SSADetectorPlugin(); });
56+
register_plugin(&rec);
57+
RecordExtSSADetector::REGISTERED_ID = register_extension();
58+
}
59+
60+
SSADetectorPlugin::SSADetectorPlugin()
61+
{
62+
close();
63+
}
64+
65+
SSADetectorPlugin::~SSADetectorPlugin() {}
66+
67+
void SSADetectorPlugin::init(const char* params) {}
68+
69+
void SSADetectorPlugin::close() {}
70+
71+
ProcessPlugin* SSADetectorPlugin::copy()
72+
{
73+
return new SSADetectorPlugin(*this);
74+
}
75+
76+
inline void SSADetectorPlugin::transition_from_init(
77+
RecordExtSSADetector* record,
78+
uint16_t len,
79+
const timeval& ts,
80+
uint8_t dir)
81+
{
82+
record->syn_table.update_entry(len, dir, ts);
83+
}
84+
85+
inline void SSADetectorPlugin::transition_from_syn(
86+
RecordExtSSADetector* record,
87+
uint16_t len,
88+
const timeval& ts,
89+
uint8_t dir)
90+
{
91+
bool can_transit = record->syn_table.check_range_for_presence(len, SYN_LOOKUP_WINDOW, !dir, ts);
92+
if (can_transit) {
93+
record->syn_ack_table.update_entry(len, dir, ts);
94+
}
95+
}
96+
97+
inline bool SSADetectorPlugin::transition_from_syn_ack(
98+
RecordExtSSADetector* record,
99+
uint16_t len,
100+
const timeval& ts,
101+
uint8_t dir)
102+
{
103+
return record->syn_table.check_range_for_presence(len, SYN_ACK_LOOKUP_WINDOW, !dir, ts);
104+
}
105+
106+
void SSADetectorPlugin::update_record(RecordExtSSADetector* record, const Packet& pkt)
107+
{
108+
/**
109+
* 0 - client -> server
110+
* 1 - server -> client
111+
*/
112+
uint8_t dir = pkt.source_pkt ? 0 : 1;
113+
uint16_t len = pkt.payload_len;
114+
timeval ts = pkt.ts;
115+
116+
if (!(MIN_PKT_SIZE <= len && len <= MAX_PKT_SIZE)) {
117+
return;
118+
}
119+
120+
bool reached_end_state = transition_from_syn_ack(record, len, ts, dir);
121+
122+
if (reached_end_state) {
123+
record->reset();
124+
if (record->syn_pkts_idx < SYN_RECORDS_NUM) {
125+
record->syn_pkts[record->syn_pkts_idx] = len;
126+
record->syn_pkts_idx += 1;
127+
}
128+
record->suspects += 1;
129+
return;
130+
}
131+
132+
transition_from_syn(record, len, ts, dir);
133+
transition_from_init(record, len, ts, dir);
134+
}
135+
136+
int SSADetectorPlugin::post_create(Flow& rec, const Packet& pkt)
137+
{
138+
RecordExtSSADetector* record = new RecordExtSSADetector();
139+
rec.add_extension(record);
140+
141+
update_record(record, pkt);
142+
return 0;
143+
}
144+
145+
int SSADetectorPlugin::post_update(Flow& rec, const Packet& pkt)
146+
{
147+
RecordExtSSADetector* record
148+
= (RecordExtSSADetector*) rec.get_extension(RecordExtSSADetector::REGISTERED_ID);
149+
update_record(record, pkt);
150+
return 0;
151+
}
152+
153+
double classes_ratio(uint8_t* syn_pkts, uint8_t size)
154+
{
155+
uint8_t unique_members = 0;
156+
bool marked[size];
157+
for (uint8_t i = 0; i < size; ++i)
158+
marked[i] = false;
159+
for (uint8_t i = 0; i < size; ++i) {
160+
if (marked[i]) {
161+
continue;
162+
}
163+
uint8_t akt_pkt_size = syn_pkts[i];
164+
unique_members++;
165+
marked[i] = true;
166+
for (uint8_t j = i + 1; j < size; ++j) {
167+
if (marked[j]) {
168+
continue;
169+
}
170+
if (syn_pkts[j] == akt_pkt_size) {
171+
marked[j] = true;
172+
}
173+
}
174+
}
175+
176+
return double(unique_members) / double(size);
177+
}
178+
179+
void SSADetectorPlugin::pre_export(Flow& rec)
180+
{
181+
// do not export for small packets flows
182+
uint32_t packets = rec.src_packets + rec.dst_packets;
183+
if (packets <= MIN_PKT_IN_FLOW) {
184+
rec.remove_extension(RecordExtSSADetector::REGISTERED_ID);
185+
return;
186+
}
187+
188+
RecordExtSSADetector* record
189+
= (RecordExtSSADetector*) rec.get_extension(RecordExtSSADetector::REGISTERED_ID);
190+
const auto& suspects = record->suspects;
191+
if (suspects < MIN_NUM_SUSPECTS) {
192+
return;
193+
}
194+
if (double(packets) / double(suspects) > MIN_SUSPECTS_RATIO) {
195+
return;
196+
}
197+
if (suspects < LOW_NUM_SUSPECTS_THRESHOLD) {
198+
if (classes_ratio(record->syn_pkts, record->syn_pkts_idx) > LOW_NUM_SUSPECTS_MAX_RATIO) {
199+
return;
200+
}
201+
} else if (suspects < MID_NUM_SUSPECTS_THRESHOLD) {
202+
if (classes_ratio(record->syn_pkts, record->syn_pkts_idx) > MID_NUM_SUSPECTS_MAX_RATIO) {
203+
return;
204+
}
205+
} else {
206+
if (classes_ratio(record->syn_pkts, record->syn_pkts_idx) > HIGH_NUM_SUSPECTS_MAX_RATIO) {
207+
return;
208+
}
209+
}
210+
211+
record->possible_vpn = 1;
212+
}
213+
214+
//--------------------RecordExtSSADetector::pkt_entry-------------------------------
215+
void RecordExtSSADetector::pkt_entry::reset()
216+
{
217+
ts_dir1.tv_sec = 0;
218+
ts_dir1.tv_usec = 0;
219+
ts_dir2.tv_sec = 0;
220+
ts_dir2.tv_usec = 0;
221+
}
222+
223+
timeval& RecordExtSSADetector::pkt_entry::get_time(dir_t dir)
224+
{
225+
return (dir == 1) ? ts_dir1 : ts_dir2;
226+
}
227+
228+
RecordExtSSADetector::pkt_entry::pkt_entry()
229+
{
230+
reset();
231+
}
232+
233+
//--------------------RecordExtSSADetector::pkt_table-------------------------------
234+
void RecordExtSSADetector::pkt_table::reset()
235+
{
236+
for (int i = 0; i < PKT_TABLE_SIZE; ++i) {
237+
table_[i].reset();
238+
}
239+
}
240+
241+
bool RecordExtSSADetector::pkt_table::check_range_for_presence(
242+
uint16_t len,
243+
uint8_t down_by,
244+
dir_t dir,
245+
const timeval& ts_to_compare)
246+
{
247+
int8_t idx = get_idx_from_len(len);
248+
for (int8_t i = std::max(idx - down_by, 0); i <= idx; ++i) {
249+
if (entry_is_present(i, dir, ts_to_compare)) {
250+
return true;
251+
}
252+
}
253+
return false;
254+
}
255+
256+
void RecordExtSSADetector::pkt_table::update_entry(uint16_t len, dir_t dir, timeval ts)
257+
{
258+
int8_t idx = get_idx_from_len(len);
259+
if (dir == 1) {
260+
table_[idx].ts_dir1 = ts;
261+
} else {
262+
table_[idx].ts_dir2 = ts;
263+
}
264+
}
265+
266+
bool RecordExtSSADetector::pkt_table::time_in_window(const timeval& ts_now, const timeval& ts_old)
267+
{
268+
long diff_secs = ts_now.tv_sec - ts_old.tv_sec;
269+
long diff_micro_secs = ts_now.tv_usec - ts_old.tv_usec;
270+
271+
diff_micro_secs += diff_secs * 1000000;
272+
if (diff_micro_secs > MAX_TIME_WINDOW) {
273+
return false;
274+
}
275+
return true;
276+
}
277+
278+
bool RecordExtSSADetector::pkt_table::entry_is_present(
279+
int8_t idx,
280+
dir_t dir,
281+
const timeval& ts_to_compare)
282+
{
283+
timeval& ts = table_[idx].get_time(dir);
284+
if (time_in_window(ts_to_compare, ts)) {
285+
return true;
286+
}
287+
return false;
288+
}
289+
290+
int8_t RecordExtSSADetector::pkt_table::get_idx_from_len(uint16_t len)
291+
{
292+
return std::max(int(len) - MIN_PKT_SIZE, 0);
293+
}
294+
295+
} // namespace ipxp

0 commit comments

Comments
 (0)