1 //--------------------------------------------------------------------------
2 // Copyright (C) 2015-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18
19 // flow_tracker.cc author Carter Waxman <cwaxman@cisco.com>
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "flow_tracker.h"
26
27 #include "protocols/icmp4.h"
28 #include "protocols/packet.h"
29 #include "protocols/tcp.h"
30 #include "protocols/udp.h"
31
32 #ifdef UNIT_TEST
33 #include "catch/snort_catch.h"
34 #endif
35
36 using namespace snort;
37
38 #define TRACKER_NAME PERF_NAME "_flow"
39
40 #define MAX_PKT_LEN 9000
41
FlowTracker(PerfConfig * perf)42 FlowTracker::FlowTracker(PerfConfig* perf) : PerfTracker(perf, TRACKER_NAME),
43 flow_max_port_to_track(perf->flow_max_port_to_track)
44 {
45 pkt_len_cnt.resize( MAX_PKT_LEN + 1 );
46 tcp.src.resize( flow_max_port_to_track + 1, 0 );
47 tcp.dst.resize( flow_max_port_to_track + 1, 0 );
48 udp.src.resize( flow_max_port_to_track + 1, 0 );
49 udp.dst.resize( flow_max_port_to_track + 1, 0 );
50 type_icmp.resize( UINT8_MAX + 1, 0 );
51
52 formatter->register_section("flow");
53 formatter->register_field("byte_total", &byte_total);
54 formatter->register_field("packets_by_bytes", &pkt_len_cnt);
55 formatter->register_field("oversized_packets", &pkt_len_oversize_cnt);
56
57 formatter->register_section("flow_tcp");
58 formatter->register_field("bytes_by_source", &tcp.src);
59 formatter->register_field("bytes_by_dest", &tcp.dst);
60 formatter->register_field("high_port_bytes", &tcp.high);
61
62 formatter->register_section("flow_udp");
63 formatter->register_field("bytes_by_source", &udp.src);
64 formatter->register_field("bytes_by_dest", &udp.dst);
65 formatter->register_field("high_port_bytes", &udp.high);
66
67 formatter->register_section("flow_icmp");
68 formatter->register_field("bytes_by_type", &type_icmp);
69
70 formatter->finalize_fields();
71 }
72
update(Packet * p)73 void FlowTracker::update(Packet* p)
74 {
75 if (!p->is_rebuilt())
76 {
77 auto len = p->pktlen;
78
79 if (p->ptrs.tcph)
80 update_transport_flows(p->ptrs.sp, p->ptrs.dp, tcp, len);
81
82 else if (p->ptrs.udph)
83 update_transport_flows(p->ptrs.sp, p->ptrs.dp, udp, len);
84
85 else if (p->ptrs.icmph)
86 type_icmp[p->ptrs.icmph->type] += len;
87
88 if (len <= MAX_PKT_LEN)
89 pkt_len_cnt[len]++;
90 else
91 pkt_len_oversize_cnt++;
92
93 byte_total += len;
94 }
95 }
96
process(bool)97 void FlowTracker::process(bool)
98 {
99 write();
100 clear();
101 }
102
clear()103 void FlowTracker::clear()
104 {
105 byte_total = 0;
106
107 memset(&pkt_len_cnt[0], 0, pkt_len_cnt.size() * sizeof(PegCount));
108 pkt_len_oversize_cnt = 0;
109
110 memset(&tcp.src[0], 0, tcp.src.size() * sizeof(PegCount));
111 memset(&tcp.dst[0], 0, tcp.dst.size() * sizeof(PegCount));
112 tcp.high = 0;
113
114 memset(&udp.src[0], 0, udp.src.size() * sizeof(PegCount));
115 memset(&udp.dst[0], 0, udp.dst.size() * sizeof(PegCount));
116 udp.high = 0;
117
118 memset(&type_icmp[0], 0, type_icmp.size() * sizeof(PegCount));
119 }
120
update_transport_flows(int sport,int dport,FlowProto & proto,int len)121 void FlowTracker::update_transport_flows(int sport, int dport,
122 FlowProto& proto, int len)
123 {
124 if (sport <= flow_max_port_to_track &&
125 dport > flow_max_port_to_track)
126 {
127 proto.src[sport] += len;
128 }
129
130 else if (dport <= flow_max_port_to_track &&
131 sport > flow_max_port_to_track)
132 {
133 proto.dst[dport] += len;
134 }
135
136 else if (sport <= flow_max_port_to_track &&
137 dport <= flow_max_port_to_track)
138 {
139 proto.src[sport] += len;
140 proto.dst[dport] += len;
141 }
142
143 else
144 {
145 proto.high += len;
146 }
147 }
148
149 #ifdef UNIT_TEST
150 class MockFlowTracker : public FlowTracker
151 {
152 public:
153 PerfFormatter* output;
154
MockFlowTracker(PerfConfig * config)155 MockFlowTracker(PerfConfig* config) : FlowTracker(config)
156 { output = formatter; }
157
clear()158 void clear() override {}
159
real_clear()160 void real_clear() { FlowTracker::clear(); }
161 };
162
163 TEST_CASE("no protocol", "[FlowTracker]")
164 {
165 Packet p;
166 uint32_t* len_ptr = &p.pktlen;
167
168 PerfConfig config;
169 config.format = PerfFormat::MOCK;
170 config.flow_max_port_to_track = 1024;
171
172 MockFlowTracker tracker(&config);
173 MockFormatter *f = (MockFormatter*)tracker.output;
174
175 tracker.reset();
176
177 p.packet_flags = 0;
178 p.ptrs.tcph = nullptr;
179 p.ptrs.udph = nullptr;
180 p.ptrs.icmph = nullptr;
181
182 *len_ptr = 127;
183 tracker.update(&p);
184
185 *len_ptr = 256;
186 tracker.update(&p);
187 tracker.update(&p);
188
189 *len_ptr = 32000;
190 tracker.update(&p);
191
192 tracker.process(false);
193 CHECK( (*f->public_values["flow.byte_total"].pc == 32639) );
194 CHECK( f->public_values["flow.packets_by_bytes"].ipc->at(123) == 0 );
195 CHECK( f->public_values["flow.packets_by_bytes"].ipc->at(127) == 1 );
196 CHECK( (f->public_values["flow.packets_by_bytes"].ipc->at(256) == 2) );
197 CHECK( *f->public_values["flow.oversized_packets"].pc == 1 );
198
199 tracker.real_clear();
200 CHECK( *f->public_values["flow.byte_total"].pc == 0);
201 CHECK( f->public_values["flow.packets_by_bytes"].ipc->at(123) == 0 );
202 CHECK( f->public_values["flow.packets_by_bytes"].ipc->at(127) == 0 );
203 CHECK( f->public_values["flow.packets_by_bytes"].ipc->at(256) == 0 );
204 CHECK( *f->public_values["flow.oversized_packets"].pc == 0 );
205 }
206
207 TEST_CASE("icmp", "[FlowTracker]")
208 {
209 Packet p;
210 icmp::ICMPHdr icmp;
211 uint32_t* len_ptr = &p.pktlen;
212 uint8_t* type_ptr = (uint8_t*) &icmp.type;
213
214 PerfConfig config;
215 config.format = PerfFormat::MOCK;
216 config.flow_max_port_to_track = 1024;
217
218 MockFlowTracker tracker(&config);
219 MockFormatter *f = (MockFormatter*)tracker.output;
220
221 tracker.reset();
222
223 p.packet_flags = 0;
224 p.ptrs.tcph = nullptr;
225 p.ptrs.udph = nullptr;
226 p.ptrs.icmph = &icmp;
227
228 *len_ptr = 127;
229 *type_ptr = 3;
230 tracker.update(&p);
231
232 *len_ptr = 256;
233 *type_ptr = 9;
234 tracker.update(&p);
235 tracker.update(&p);
236
237 *len_ptr = 32000;
238 *type_ptr = 127;
239 tracker.update(&p);
240
241 tracker.process(false);
242 CHECK( (f->public_values["flow_icmp.bytes_by_type"].ipc->at(3) == 127) );
243 CHECK( (f->public_values["flow_icmp.bytes_by_type"].ipc->at(9) == 512) );
244 CHECK( (f->public_values["flow_icmp.bytes_by_type"].ipc->at(127) == 32000) );
245
246 tracker.real_clear();
247 CHECK( f->public_values["flow_icmp.bytes_by_type"].ipc->at(3) == 0 );
248 CHECK( f->public_values["flow_icmp.bytes_by_type"].ipc->at(9) == 0 );
249 CHECK( f->public_values["flow_icmp.bytes_by_type"].ipc->at(127) == 0 );
250 }
251
252 TEST_CASE("tcp", "[FlowTracker]")
253 {
254 Packet p;
255 tcp::TCPHdr tcp;
256 uint32_t* len_ptr = &p.pktlen;
257
258 PerfConfig config;
259 config.format = PerfFormat::MOCK;
260 config.flow_max_port_to_track = 1024;
261
262 MockFlowTracker tracker(&config);
263 MockFormatter *f = (MockFormatter*)tracker.output;
264
265 p.packet_flags = 0;
266 p.ptrs.tcph = &tcp;
267 p.ptrs.udph = nullptr;
268 p.ptrs.icmph = nullptr;
269
270 tracker.reset();
271
272 *len_ptr = 127;
273 p.ptrs.sp = 1024;
274 p.ptrs.dp = 1025;
275 tracker.update(&p);
276
277 *len_ptr = 256;
278 p.ptrs.dp = 1024;
279 p.ptrs.sp = 1025;
280 tracker.update(&p);
281 tracker.update(&p);
282
283 *len_ptr = 512;
284 p.ptrs.dp = 1024;
285 p.ptrs.sp = 1024;
286 tracker.update(&p);
287 tracker.update(&p);
288 tracker.update(&p);
289
290 *len_ptr = 32000;
291 p.ptrs.dp = 1025;
292 p.ptrs.sp = 1025;
293 tracker.update(&p);
294
295 tracker.process(false);
296 CHECK( (f->public_values["flow_tcp.bytes_by_source"].ipc->at(1024) == 1663) );
297 CHECK( (f->public_values["flow_tcp.bytes_by_dest"].ipc->at(1024) == 2048) );
298 CHECK( (*f->public_values["flow_tcp.high_port_bytes"].pc == 32000) );
299
300 tracker.real_clear();
301 CHECK( f->public_values["flow_tcp.bytes_by_source"].ipc->at(1024) == 0 );
302 CHECK( f->public_values["flow_tcp.bytes_by_dest"].ipc->at(1024) == 0 );
303 CHECK( *f->public_values["flow_tcp.high_port_bytes"].pc == 0 );
304 }
305
306 TEST_CASE("udp", "[FlowTracker]")
307 {
308 Packet p;
309 udp::UDPHdr udp;
310 uint32_t* len_ptr = &p.pktlen;
311
312 PerfConfig config;
313 config.format = PerfFormat::MOCK;
314 config.flow_max_port_to_track = 1024;
315
316 MockFlowTracker tracker(&config);
317 MockFormatter *f = (MockFormatter*)tracker.output;
318
319 p.packet_flags = 0;
320 p.ptrs.tcph = nullptr;
321 p.ptrs.udph = &udp;
322 p.ptrs.icmph = nullptr;
323
324 tracker.reset();
325
326 *len_ptr = 127;
327 p.ptrs.sp = 1024;
328 p.ptrs.dp = 1025;
329 tracker.update(&p);
330
331 *len_ptr = 256;
332 p.ptrs.dp = 1024;
333 p.ptrs.sp = 1025;
334 tracker.update(&p);
335 tracker.update(&p);
336
337 *len_ptr = 512;
338 p.ptrs.dp = 1024;
339 p.ptrs.sp = 1024;
340 tracker.update(&p);
341 tracker.update(&p);
342 tracker.update(&p);
343
344 *len_ptr = 32000;
345 p.ptrs.dp = 1025;
346 p.ptrs.sp = 1025;
347 tracker.update(&p);
348
349 tracker.process(false);
350 CHECK( (f->public_values["flow_udp.bytes_by_source"].ipc->at(1024) == 1663) );
351 CHECK( (f->public_values["flow_udp.bytes_by_dest"].ipc->at(1024) == 2048) );
352 CHECK( (*f->public_values["flow_udp.high_port_bytes"].pc == 32000) );
353
354 tracker.real_clear();
355 CHECK( f->public_values["flow_udp.bytes_by_source"].ipc->at(1024) == 0 );
356 CHECK( f->public_values["flow_udp.bytes_by_dest"].ipc->at(1024) == 0 );
357 CHECK( *f->public_values["flow_udp.high_port_bytes"].pc == 0 );
358 }
359 #endif
360