1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2005-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation. You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //--------------------------------------------------------------------------
19 // flow_key.cc author Steven Sturges <ssturges@sourcefire.com>
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "flow/flow_key.h"
26
27 #include "hash/hash_key_operations.h"
28 #include "main/snort_config.h"
29 #include "protocols/icmp4.h"
30 #include "protocols/icmp6.h"
31 #include "utils/util.h"
32
33 using namespace snort;
34
35 //-------------------------------------------------------------------------
36 // icmp foo
37 //-------------------------------------------------------------------------
38
39 static const uint32_t empty_addr[4] = { 0, 0, 0, 0 };
40 static const SfIp empty_ip4 = { (void*) &empty_addr, AF_INET };
41 static const SfIp empty_ip6 = { (void*) &empty_addr, AF_INET6 };
42
update_icmp4(const SfIp * & srcIP,uint16_t & srcPort,const SfIp * & dstIP,uint16_t & dstPort)43 static inline void update_icmp4(const SfIp*& srcIP, uint16_t& srcPort,
44 const SfIp*& dstIP, uint16_t& dstPort)
45 {
46 if (srcPort == ICMP_ECHOREPLY)
47 {
48 /* Treat ICMP echo reply the same as request */
49 dstPort = ICMP_ECHO;
50 srcPort = 0;
51 }
52 else if (srcPort == ICMP_ROUTER_ADVERTISE)
53 {
54 dstPort = ICMP_ROUTER_SOLICIT; /* Treat ICMP router advertisement the same as solicitation */
55 srcPort = 0;
56 srcIP = &empty_ip4; /* Matching src address to solicit dest address */
57 }
58 else
59 {
60 /* otherwise, every ICMP type gets different key */
61 dstPort = 0;
62 if (srcPort == ICMP_ROUTER_SOLICIT)
63 dstIP = &empty_ip4; /* To get unique key, don't use multicast/broadcast addr (RFC 1256) */
64 }
65 }
66
update_icmp6(const SfIp * & srcIP,uint16_t & srcPort,const SfIp * & dstIP,uint16_t & dstPort)67 static inline void update_icmp6(const SfIp*& srcIP, uint16_t& srcPort,
68 const SfIp*& dstIP, uint16_t& dstPort)
69 {
70 if (srcPort == icmp::Icmp6Types::ECHO_REPLY)
71 {
72 /* Treat ICMPv6 echo reply the same as request */
73 dstPort = icmp::Icmp6Types::ECHO_REQUEST;
74 srcPort = 0;
75 }
76 else if (srcPort == icmp::Icmp6Types::ROUTER_ADVERTISEMENT)
77 {
78 dstPort = icmp::Icmp6Types::ROUTER_SOLICITATION; /* Treat ICMPv6 router advertisement the same as solicitation */
79 srcPort = 0;
80 srcIP = &empty_ip6; /* Matching src address to solicit dest address */
81 }
82 else
83 {
84 /* otherwise, every ICMP type gets different key */
85 dstPort = 0;
86 if (srcPort == icmp::Icmp6Types::ROUTER_SOLICITATION)
87 dstIP = &empty_ip6; /* To get unique key, don't use multicast addr (RFC 4861) */
88 }
89 }
90
91 //-------------------------------------------------------------------------
92 // init foo
93 //-------------------------------------------------------------------------
init4(IpProtocol ip_proto,const SfIp * srcIP,uint16_t srcPort,const SfIp * dstIP,uint16_t dstPort,bool order)94 inline bool FlowKey::init4(IpProtocol ip_proto, const SfIp *srcIP, uint16_t srcPort,
95 const SfIp *dstIP, uint16_t dstPort, bool order)
96 {
97 uint32_t src;
98 uint32_t dst;
99 bool reversed = false;
100
101 if (ip_proto == IpProtocol::ICMPV4)
102 update_icmp4(srcIP, srcPort, dstIP, dstPort);
103
104 src = srcIP->get_ip4_value();
105 dst = dstIP->get_ip4_value();
106
107 /* These comparisons are done in this fashion for performance reasons */
108 if ( !order || src < dst)
109 {
110 COPY4(ip_l, srcIP->get_ip6_ptr());
111 COPY4(ip_h, dstIP->get_ip6_ptr());
112 port_l = srcPort;
113 port_h = dstPort;
114 }
115 else if (src == dst)
116 {
117 COPY4(ip_l, srcIP->get_ip6_ptr());
118 COPY4(ip_h, dstIP->get_ip6_ptr());
119 if (srcPort < dstPort)
120 {
121 port_l = srcPort;
122 port_h = dstPort;
123 }
124 else
125 {
126 port_l = dstPort;
127 port_h = srcPort;
128 reversed = true;
129 }
130 }
131 else
132 {
133 COPY4(ip_l, dstIP->get_ip6_ptr());
134 port_l = dstPort;
135 COPY4(ip_h, srcIP->get_ip6_ptr());
136 port_h = srcPort;
137 reversed = true;
138 }
139
140 return reversed;
141 }
142
init6(IpProtocol ip_proto,const SfIp * srcIP,uint16_t srcPort,const SfIp * dstIP,uint16_t dstPort,bool order)143 inline bool FlowKey::init6(IpProtocol ip_proto, const SfIp *srcIP, uint16_t srcPort,
144 const SfIp *dstIP, uint16_t dstPort, bool order)
145 {
146 bool reversed = false;
147
148 if (ip_proto == IpProtocol::ICMPV6)
149 update_icmp6(srcIP, srcPort, dstIP, dstPort);
150 else if (ip_proto == IpProtocol::ICMPV4)
151 update_icmp4(srcIP, srcPort, dstIP, dstPort);
152
153 if ( !order || srcIP->fast_lt6(*dstIP))
154 {
155 COPY4(ip_l, srcIP->get_ip6_ptr());
156 port_l = srcPort;
157 COPY4(ip_h, dstIP->get_ip6_ptr());
158 port_h = dstPort;
159 }
160 else if (srcIP->fast_eq6(*dstIP))
161 {
162 COPY4(ip_l, srcIP->get_ip6_ptr());
163 COPY4(ip_h, dstIP->get_ip6_ptr());
164 if (srcPort < dstPort)
165 {
166 port_l = srcPort;
167 port_h = dstPort;
168 }
169 else
170 {
171 port_l = dstPort;
172 port_h = srcPort;
173 reversed = true;
174 }
175 }
176 else
177 {
178 COPY4(ip_l, dstIP->get_ip6_ptr());
179 port_l = dstPort;
180 COPY4(ip_h, srcIP->get_ip6_ptr());
181 port_h = srcPort;
182 reversed = true;
183 }
184
185 return reversed;
186 }
187
init_vlan(const SnortConfig * sc,uint16_t vlanId)188 void FlowKey::init_vlan(const SnortConfig* sc, uint16_t vlanId)
189 {
190 if (!sc->get_vlan_agnostic())
191 vlan_tag = vlanId;
192 else
193 vlan_tag = 0;
194 }
195
init_address_space(const SnortConfig * sc,uint16_t addrSpaceId)196 void FlowKey::init_address_space(const SnortConfig* sc, uint16_t addrSpaceId)
197 {
198 if (!sc->address_space_agnostic())
199 addressSpaceId = addrSpaceId;
200 else
201 addressSpaceId = 0;
202 }
203
init_groups(int16_t ingress_group,int16_t egress_group,bool rev)204 void FlowKey::init_groups(int16_t ingress_group, int16_t egress_group, bool rev)
205 {
206 if (flags.group_used)
207 {
208 if (rev)
209 {
210 group_l = egress_group;
211 group_h = ingress_group;
212 }
213 else
214 {
215 group_l = ingress_group;
216 group_h = egress_group;
217 }
218 }
219 else
220 group_l = group_h = DAQ_PKTHDR_UNKNOWN;
221 }
222
init_mpls(const SnortConfig * sc,uint32_t mplsId)223 void FlowKey::init_mpls(const SnortConfig* sc, uint32_t mplsId)
224 {
225 if (!sc->get_mpls_agnostic())
226 mplsLabel = mplsId;
227 else
228 mplsLabel = 0;
229 }
230
init(const SnortConfig * sc,PktType type,IpProtocol ip_proto,const SfIp * srcIP,uint16_t srcPort,const SfIp * dstIP,uint16_t dstPort,uint16_t vlanId,uint32_t mplsId,uint16_t addrSpaceId,int16_t ingress_group,int16_t egress_group)231 bool FlowKey::init(
232 const SnortConfig* sc,
233 PktType type, IpProtocol ip_proto,
234 const SfIp *srcIP, uint16_t srcPort,
235 const SfIp *dstIP, uint16_t dstPort,
236 uint16_t vlanId, uint32_t mplsId,
237 uint16_t addrSpaceId, int16_t ingress_group,
238 int16_t egress_group)
239 {
240 bool reversed;
241
242 /* Because the key is going to be used for hash lookups,
243 * the key fields will be normalized such that the lower
244 * of the IP addresses is stored in ip_l and the port for
245 * that IP is stored in port_l.
246 */
247
248 if (srcIP->is_ip4() && dstIP->is_ip4())
249 {
250 version = 4;
251 reversed = init4(ip_proto, srcIP, srcPort, dstIP, dstPort);
252 }
253 else
254 {
255 version = 6;
256 reversed = init6(ip_proto, srcIP, srcPort, dstIP, dstPort);
257 }
258
259 pkt_type = type;
260 ip_protocol = (uint8_t)ip_proto;
261
262 init_vlan(sc, vlanId);
263 init_address_space(sc, addrSpaceId);
264 init_mpls(sc, mplsId);
265
266 if (ingress_group == DAQ_PKTHDR_UNKNOWN or egress_group == DAQ_PKTHDR_UNKNOWN)
267 flags.group_used = 0;
268 else
269 flags.group_used = 1;
270
271 init_groups(ingress_group, egress_group, reversed);
272
273 flags.ubits = 0;
274 return reversed;
275 }
276
init(const SnortConfig * sc,PktType type,IpProtocol ip_proto,const SfIp * srcIP,uint16_t srcPort,const SfIp * dstIP,uint16_t dstPort,uint16_t vlanId,uint32_t mplsId,const DAQ_PktHdr_t & pkt_hdr)277 bool FlowKey::init(
278 const SnortConfig* sc,
279 PktType type, IpProtocol ip_proto,
280 const SfIp *srcIP, uint16_t srcPort,
281 const SfIp *dstIP, uint16_t dstPort,
282 uint16_t vlanId, uint32_t mplsId,
283 const DAQ_PktHdr_t& pkt_hdr)
284 {
285 bool reversed;
286
287 /* Because the key is going to be used for hash lookups,
288 * the key fields will be normalized such that the lower
289 * of the IP addresses is stored in ip_l and the port for
290 * that IP is stored in port_l.
291 */
292
293 if (srcIP->is_ip4() && dstIP->is_ip4())
294 {
295 version = 4;
296 reversed = init4(ip_proto, srcIP, srcPort, dstIP, dstPort);
297 }
298 else
299 {
300 version = 6;
301 reversed = init6(ip_proto, srcIP, srcPort, dstIP, dstPort);
302 }
303
304 pkt_type = type;
305 ip_protocol = (uint8_t)ip_proto;
306
307 init_vlan(sc, vlanId);
308 init_address_space(sc, pkt_hdr.address_space_id);
309 init_mpls(sc, mplsId);
310
311 flags.group_used = ((pkt_hdr.flags & DAQ_PKT_FLAG_SIGNIFICANT_GROUPS) != 0);
312 init_groups(pkt_hdr.ingress_group, pkt_hdr.egress_group, reversed);
313
314 flags.ubits = 0;
315
316 return reversed;
317 }
318
init(const SnortConfig * sc,PktType type,IpProtocol ip_proto,const SfIp * srcIP,const SfIp * dstIP,uint32_t id,uint16_t vlanId,uint32_t mplsId,uint16_t addrSpaceId,int16_t ingress_group,int16_t egress_group)319 bool FlowKey::init(
320 const SnortConfig* sc,
321 PktType type, IpProtocol ip_proto,
322 const SfIp *srcIP, const SfIp *dstIP,
323 uint32_t id, uint16_t vlanId,
324 uint32_t mplsId, uint16_t addrSpaceId,
325 int16_t ingress_group, int16_t egress_group)
326 {
327 // to avoid confusing 2 different datagrams or confusing a datagram
328 // with a session, we don't order the addresses and we set version
329
330 uint16_t srcPort = id & 0xFFFF;
331 uint16_t dstPort = id >> 16;
332 bool reversed;
333
334 if (srcIP->is_ip4() && dstIP->is_ip4())
335 {
336 version = 4;
337 reversed = init4(ip_proto, srcIP, srcPort, dstIP, dstPort, false);
338 ip_protocol = (uint8_t)ip_proto;
339 }
340 else
341 {
342 version = 6;
343 reversed = init6(ip_proto, srcIP, srcPort, dstIP, dstPort, false);
344 ip_protocol = 0;
345 }
346
347 pkt_type = type;
348
349 init_vlan(sc, vlanId);
350 init_address_space(sc, addrSpaceId);
351 init_mpls(sc, mplsId);
352
353 if (ingress_group == DAQ_PKTHDR_UNKNOWN or egress_group == DAQ_PKTHDR_UNKNOWN)
354 flags.group_used = 0;
355 else
356 flags.group_used = 1;
357
358 init_groups(ingress_group, egress_group, reversed);
359
360 flags.ubits = 0;
361
362 return false;
363 }
364
init(const SnortConfig * sc,PktType type,IpProtocol ip_proto,const SfIp * srcIP,const SfIp * dstIP,uint32_t id,uint16_t vlanId,uint32_t mplsId,const DAQ_PktHdr_t & pkt_hdr)365 bool FlowKey::init(
366 const SnortConfig* sc,
367 PktType type, IpProtocol ip_proto,
368 const SfIp *srcIP, const SfIp *dstIP,
369 uint32_t id, uint16_t vlanId,
370 uint32_t mplsId, const DAQ_PktHdr_t& pkt_hdr)
371 {
372 // to avoid confusing 2 different datagrams or confusing a datagram
373 // with a session, we don't order the addresses and we set version
374
375 uint16_t srcPort = id & 0xFFFF;
376 uint16_t dstPort = id >> 16;
377 bool reversed;
378
379 if (srcIP->is_ip4() && dstIP->is_ip4())
380 {
381 version = 4;
382 reversed = init4(ip_proto, srcIP, srcPort, dstIP, dstPort, false);
383 ip_protocol = (uint8_t)ip_proto;
384 }
385 else
386 {
387 version = 6;
388 reversed = init6(ip_proto, srcIP, srcPort, dstIP, dstPort, false);
389 ip_protocol = 0;
390 }
391
392 pkt_type = type;
393
394 init_vlan(sc, vlanId);
395 init_address_space(sc, pkt_hdr.address_space_id);
396 init_mpls(sc, mplsId);
397
398 flags.group_used = ((pkt_hdr.flags & DAQ_PKT_FLAG_SIGNIFICANT_GROUPS) != 0);
399 init_groups(pkt_hdr.ingress_group, pkt_hdr.egress_group, reversed);
400
401 flags.ubits = 0;
402
403 return false;
404 }
405
406 //-------------------------------------------------------------------------
407 //-------------------------------------------------------------------------
408 // hash foo
409 //-------------------------------------------------------------------------
410
is_equal(const void * s1,const void * s2,size_t)411 bool FlowKey::is_equal(const void* s1, const void* s2, size_t)
412 {
413 const uint64_t* a = (const uint64_t*)s1;
414 const uint64_t* b = (const uint64_t*)s2;
415 const uint32_t* c;
416 const uint32_t* d;
417
418 if (*a - *b)
419 return false; /* Compares IPv4 lo/hi
420 Compares IPv6 low[0,1] */
421
422 a++;
423 b++;
424 if (*a - *b)
425 return false; /* Compares port lo/hi, vlan, protocol, version
426 Compares IPv6 low[2,3] */
427
428 a++;
429 b++;
430 if (*a - *b)
431 return false; /* Compares IPv6 hi[0,1] */
432
433 a++;
434 b++;
435 if (*a - *b)
436 return false; /* Compares IPv6 hi[2,3] */
437
438 a++;
439 b++;
440 if (*a - *b)
441 return false; /* Compares MPLS label, port lo/hi */
442
443 a++;
444 b++;
445 if (*a - *b)
446 return false; /* Compares group lo/hi, addressSpaceId, vlan */
447
448 c = (const uint32_t*)(++a);
449 d = (const uint32_t*)(++b);
450 if (*c - *d)
451 return false; /* ip_proto, type, version, 8 bit pad */
452
453 return true;
454 }
455
do_hash(const unsigned char * k,int)456 unsigned FlowHashKeyOps::do_hash(const unsigned char* k, int)
457 {
458 uint32_t a, b, c;
459 a = b = c = hardener;
460
461 const uint32_t* d = (const uint32_t*)k;
462
463 a += d[0]; // IPv6 lo[0]
464 b += d[1]; // IPv6 lo[1]
465 c += d[2]; // IPv6 lo[2]
466
467 mix(a, b, c);
468
469 a += d[3]; // IPv6 lo[3]
470 b += d[4]; // IPv6 hi[0]
471 c += d[5]; // IPv6 hi[1]
472
473 mix(a, b, c);
474
475 a += d[6]; // IPv6 hi[2]
476 b += d[7]; // IPv6 hi[3]
477 c += d[8]; // mpls label
478
479 mix(a, b, c);
480
481 a += d[9]; // port lo & port hi
482 b += d[10]; // group lo & group hi
483 c += d[11]; // addressSpaceId, vlan
484
485 mix(a, b, c);
486
487 a += d[12]; // ip_proto, pkt_type, version, 8 bits of zeroed pad
488
489 finalize(a, b, c);
490
491 return c;
492 }
493
key_compare(const void * k1,const void * k2,size_t len)494 bool FlowHashKeyOps::key_compare(const void* k1, const void* k2, size_t len)
495 {
496 return FlowKey::is_equal(k1, k2, len);
497 }
498
499
500