hash: refactor crc32_5tuple
[vpp.git] / src / vnet / hash / crc32_5tuple.c
1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  * Copyright(c) 2021 Cisco Systems, Inc.
4  */
5
6 #include <vnet/vnet.h>
7 #include <vnet/ethernet/ethernet.h>
8 #include <vnet/ip/ip4_packet.h>
9 #include <vnet/ip/ip6_packet.h>
10 #include <vnet/hash/hash.h>
11 #include <vppinfra/crc32.h>
12
13 #ifdef clib_crc32c_uses_intrinsics
14
15 static const u8 l4_mask_bits[256] = {
16   [IP_PROTOCOL_ICMP] = 16,      [IP_PROTOCOL_IGMP] = 8,
17   [IP_PROTOCOL_TCP] = 32,       [IP_PROTOCOL_UDP] = 32,
18   [IP_PROTOCOL_IPSEC_ESP] = 32, [IP_PROTOCOL_IPSEC_AH] = 32,
19   [IP_PROTOCOL_ICMP6] = 16,
20 };
21
22 static_always_inline u32
23 compute_ip6_key (ip6_header_t *ip)
24 {
25   u32 hash = 0, l4hdr;
26   u8 pr;
27   /* dst + src ip as u64 */
28   hash = clib_crc32c_u64 (hash, *(u64u *) ((u8 *) ip + 8));
29   hash = clib_crc32c_u64 (hash, *(u64u *) ((u8 *) ip + 16));
30   hash = clib_crc32c_u64 (hash, *(u64u *) ((u8 *) ip + 24));
31   hash = clib_crc32c_u64 (hash, *(u64u *) ((u8 *) ip + 32));
32   pr = ip->protocol;
33   l4hdr = *(u32 *) ip6_next_header (ip) & pow2_mask (l4_mask_bits[pr]);
34   /* protocol + l4 hdr */
35   return clib_crc32c_u64 (hash, ((u64) pr << 32) | l4hdr);
36 }
37
38 static_always_inline u32
39 compute_ip4_key (ip4_header_t *ip)
40 {
41   u32 hash = 0, l4hdr;
42   u8 pr;
43   /* dst + src ip as u64 */
44   hash = clib_crc32c_u64 (0, *(u64 *) ((u8 *) ip + 12));
45   pr = ip->protocol;
46   l4hdr = *(u32 *) ip4_next_header (ip) & pow2_mask (l4_mask_bits[pr]);
47   /* protocol + l4 hdr */
48   return clib_crc32c_u64 (hash, ((u64) pr << 32) | l4hdr);
49 }
50 static_always_inline u32
51 compute_ip_key (void *p)
52 {
53   if ((((u8 *) p)[0] & 0xf0) == 0x40)
54     return compute_ip4_key (p);
55   else if ((((u8 *) p)[0] & 0xf0) == 0x60)
56     return compute_ip6_key (p);
57   return 0;
58 }
59
60 void
61 vnet_crc32c_5tuple_ip_func (void **p, u32 *hash, u32 n_packets)
62 {
63   u32 n_left_from = n_packets;
64
65   while (n_left_from >= 8)
66     {
67       clib_prefetch_load (p[4]);
68       clib_prefetch_load (p[5]);
69       clib_prefetch_load (p[6]);
70       clib_prefetch_load (p[7]);
71
72       hash[0] = compute_ip_key (p[0]);
73       hash[1] = compute_ip_key (p[1]);
74       hash[2] = compute_ip_key (p[2]);
75       hash[3] = compute_ip_key (p[3]);
76
77       hash += 4;
78       n_left_from -= 4;
79       p += 4;
80     }
81
82   while (n_left_from > 0)
83     {
84       hash[0] = compute_ip_key (p[0]);
85
86       hash += 1;
87       n_left_from -= 1;
88       p += 1;
89     }
90 }
91
92 static_always_inline u32
93 compute_ethernet_key (void *p)
94 {
95   u16 ethertype = 0, l2hdr_sz = 0;
96
97   ethernet_header_t *eh = (ethernet_header_t *) p;
98   ethertype = clib_net_to_host_u16 (eh->type);
99   l2hdr_sz = sizeof (ethernet_header_t);
100
101   if (ethernet_frame_is_tagged (ethertype))
102     {
103       ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1);
104
105       ethertype = clib_net_to_host_u16 (vlan->type);
106       l2hdr_sz += sizeof (*vlan);
107       while (ethernet_frame_is_tagged (ethertype))
108         {
109           vlan++;
110           ethertype = clib_net_to_host_u16 (vlan->type);
111           l2hdr_sz += sizeof (*vlan);
112         }
113     }
114
115   if (ethertype == ETHERNET_TYPE_IP4)
116     {
117       ip4_header_t *ip4 = (ip4_header_t *) (p + l2hdr_sz);
118       return compute_ip4_key (ip4);
119     }
120   else if (ethertype == ETHERNET_TYPE_IP6)
121     {
122       ip6_header_t *ip6 = (ip6_header_t *) (p + l2hdr_sz);
123       return compute_ip6_key (ip6);
124     }
125   return 0;
126 }
127
128 void
129 vnet_crc32c_5tuple_ethernet_func (void **p, u32 *hash, u32 n_packets)
130 {
131   u32 n_left_from = n_packets;
132
133   while (n_left_from >= 8)
134     {
135       clib_prefetch_load (p[4]);
136       clib_prefetch_load (p[5]);
137       clib_prefetch_load (p[6]);
138       clib_prefetch_load (p[7]);
139
140       hash[0] = compute_ethernet_key (p[0]);
141       hash[1] = compute_ethernet_key (p[1]);
142       hash[2] = compute_ethernet_key (p[2]);
143       hash[3] = compute_ethernet_key (p[3]);
144
145       hash += 4;
146       n_left_from -= 4;
147       p += 4;
148     }
149
150   while (n_left_from > 0)
151     {
152       hash[0] = compute_ethernet_key (p[0]);
153
154       hash += 1;
155       n_left_from -= 1;
156       p += 1;
157     }
158 }
159
160 VNET_REGISTER_HASH_FUNCTION (crc32c_5tuple, static) = {
161   .name = "crc32c-5tuple",
162   .description = "IPv4/IPv6 header and TCP/UDP ports",
163   .priority = 50,
164   .function[VNET_HASH_FN_TYPE_ETHERNET] = vnet_crc32c_5tuple_ethernet_func,
165   .function[VNET_HASH_FN_TYPE_IP] = vnet_crc32c_5tuple_ip_func,
166 };
167
168 #endif