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