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