hash: add support for hashing infra
[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/ip/ip46_address.h>
11 #include <vnet/udp/udp_packet.h>
12 #include <vnet/hash/hash.h>
13 #include <vppinfra/crc32.h>
14
15 typedef union
16 {
17   struct
18   {
19     ip46_address_t src_address;
20     ip46_address_t dst_address;
21     union
22     {
23       struct
24       {
25         u16 src_port;
26         u16 dst_port;
27       };
28       u32 l4_hdr;
29     };
30   };
31   u8 as_u8[36];
32 } crc32c_5tuple_key_t;
33
34 static const u8 l4_mask_bits[256] = {
35   [IP_PROTOCOL_ICMP] = 16,      [IP_PROTOCOL_IGMP] = 8,
36   [IP_PROTOCOL_TCP] = 32,       [IP_PROTOCOL_UDP] = 32,
37   [IP_PROTOCOL_IPSEC_ESP] = 32, [IP_PROTOCOL_IPSEC_AH] = 32,
38   [IP_PROTOCOL_ICMP6] = 16,
39 };
40
41 static_always_inline void
42 compute_ip6_key (ip6_header_t *ip, crc32c_5tuple_key_t *k)
43 {
44   u8 pr;
45
46   /* copy 32 bytes of ip6 src and dst addresses into hash_key_t */
47   clib_memcpy_fast ((u8 *) k, (u8 *) ip + 8, sizeof (ip6_address_t) * 2);
48   pr = ip->protocol;
49   /* write l4 header */
50   k->l4_hdr = *(u32 *) ip6_next_header (ip) & pow2_mask (l4_mask_bits[pr]);
51 }
52
53 static_always_inline void
54 compute_ip4_key (ip4_header_t *ip, crc32c_5tuple_key_t *k)
55 {
56   u8 pr;
57   u64 *key = (u64 *) k;
58   /* copy 8 bytes of ip src and dst addresses into hash_key_t */
59   key[0] = 0;
60   key[1] = 0;
61   key[2] = 0;
62   key[3] = *(u64 *) ((u8 *) ip + 12);
63   pr = ip->protocol;
64   /* write l4 header */
65   k->l4_hdr = *(u32 *) ip4_next_header (ip) & pow2_mask (l4_mask_bits[pr]);
66 }
67 static_always_inline void
68 compute_ip_key (void *p, crc32c_5tuple_key_t *key)
69 {
70   if ((((u8 *) p)[0] & 0xf0) == 0x40)
71     compute_ip4_key (p, key);
72   else if ((((u8 *) p)[0] & 0xf0) == 0x60)
73     compute_ip6_key (p, key);
74 }
75
76 void
77 vnet_crc32c_5tuple_ip_func (void **p, u32 *hash, u32 n_packets)
78 {
79   u32 n_left_from = n_packets;
80
81   while (n_left_from >= 8)
82     {
83       crc32c_5tuple_key_t key[4];
84
85       clib_prefetch_load (p[4]);
86       clib_prefetch_load (p[5]);
87       clib_prefetch_load (p[6]);
88       clib_prefetch_load (p[7]);
89
90       compute_ip_key (p[0], &key[0]);
91       compute_ip_key (p[1], &key[1]);
92       compute_ip_key (p[2], &key[2]);
93       compute_ip_key (p[3], &key[3]);
94
95       hash[0] = clib_crc32c (key[0].as_u8, sizeof (key[0]));
96       hash[1] = clib_crc32c (key[1].as_u8, sizeof (key[1]));
97       hash[2] = clib_crc32c (key[2].as_u8, sizeof (key[2]));
98       hash[3] = clib_crc32c (key[3].as_u8, sizeof (key[3]));
99
100       hash += 4;
101       n_left_from -= 4;
102       p += 4;
103     }
104
105   while (n_left_from > 0)
106     {
107       crc32c_5tuple_key_t key;
108
109       compute_ip_key (p[0], &key);
110
111       hash[0] = clib_crc32c (key.as_u8, sizeof (key));
112
113       hash += 1;
114       n_left_from -= 1;
115       p += 1;
116     }
117 }
118
119 static_always_inline void
120 compute_ethernet_key (void *p, crc32c_5tuple_key_t *key)
121 {
122   u16 ethertype = 0, l2hdr_sz = 0;
123
124   ethernet_header_t *eh = (ethernet_header_t *) p;
125   ethertype = clib_net_to_host_u16 (eh->type);
126   l2hdr_sz = sizeof (ethernet_header_t);
127
128   if (ethernet_frame_is_tagged (ethertype))
129     {
130       ethernet_vlan_header_t *vlan = (ethernet_vlan_header_t *) (eh + 1);
131
132       ethertype = clib_net_to_host_u16 (vlan->type);
133       l2hdr_sz += sizeof (*vlan);
134       while (ethernet_frame_is_tagged (ethertype))
135         {
136           vlan++;
137           ethertype = clib_net_to_host_u16 (vlan->type);
138           l2hdr_sz += sizeof (*vlan);
139         }
140     }
141
142   if (ethertype == ETHERNET_TYPE_IP4)
143     {
144       ip4_header_t *ip4 = (ip4_header_t *) (p + l2hdr_sz);
145       compute_ip4_key (ip4, key);
146     }
147   else if (ethertype == ETHERNET_TYPE_IP6)
148     {
149       ip6_header_t *ip6 = (ip6_header_t *) (p + l2hdr_sz);
150       compute_ip6_key (ip6, key);
151     }
152 }
153
154 void
155 vnet_crc32c_5tuple_ethernet_func (void **p, u32 *hash, u32 n_packets)
156 {
157   u32 n_left_from = n_packets;
158
159   while (n_left_from >= 8)
160     {
161       crc32c_5tuple_key_t key[4];
162
163       clib_prefetch_load (p[4]);
164       clib_prefetch_load (p[5]);
165       clib_prefetch_load (p[6]);
166       clib_prefetch_load (p[7]);
167
168       compute_ethernet_key (p[0], &key[0]);
169       compute_ethernet_key (p[1], &key[1]);
170       compute_ethernet_key (p[2], &key[2]);
171       compute_ethernet_key (p[3], &key[3]);
172
173       hash[0] = clib_crc32c (key[0].as_u8, sizeof (key[0]));
174       hash[1] = clib_crc32c (key[1].as_u8, sizeof (key[1]));
175       hash[2] = clib_crc32c (key[2].as_u8, sizeof (key[2]));
176       hash[3] = clib_crc32c (key[3].as_u8, sizeof (key[3]));
177
178       hash += 4;
179       n_left_from -= 4;
180       p += 4;
181     }
182
183   while (n_left_from > 0)
184     {
185       crc32c_5tuple_key_t key;
186
187       compute_ethernet_key (p[0], &key);
188
189       hash[0] = clib_crc32c (key.as_u8, sizeof (key));
190
191       hash += 1;
192       n_left_from -= 1;
193       p += 1;
194     }
195 }
196
197 VNET_REGISTER_HASH_FUNCTION (crc32c_5tuple, static) = {
198   .name = "crc32c-5tuple",
199   .description = "IPv4/IPv6 header and TCP/UDP ports",
200   .priority = 50,
201   .function[VNET_HASH_FN_TYPE_ETHERNET] = vnet_crc32c_5tuple_ethernet_func,
202   .function[VNET_HASH_FN_TYPE_IP] = vnet_crc32c_5tuple_ip_func,
203 };