bonding: refactor bonding hash functions to vnet/hash
[vpp.git] / src / vnet / hash / hash_eth.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2021 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #define _GNU_SOURCE
19 #include <stdint.h>
20 #include <vlib/vlib.h>
21 #include <vlib/unix/unix.h>
22 #include <vnet/ethernet/ethernet.h>
23 #include <vnet/ip/ip4_packet.h>
24 #include <vnet/ip/ip6_packet.h>
25 #include <vnet/ip/ip6_hop_by_hop_packet.h>
26 #include <vppinfra/lb_hash_hash.h>
27 #include <vnet/hash/hash.h>
28
29 static_always_inline u16 *
30 locate_ethertype (ethernet_header_t *eth)
31 {
32   u16 *ethertype_p;
33   ethernet_vlan_header_t *vlan;
34
35   if (!ethernet_frame_is_tagged (clib_net_to_host_u16 (eth->type)))
36     {
37       ethertype_p = &eth->type;
38     }
39   else
40     {
41       vlan = (void *) (eth + 1);
42       ethertype_p = &vlan->type;
43       if (*ethertype_p == ntohs (ETHERNET_TYPE_VLAN))
44         {
45           vlan++;
46           ethertype_p = &vlan->type;
47         }
48     }
49   return ethertype_p;
50 }
51
52 static void
53 hash_eth_l2 (void **p, u32 *hash, u32 n_packets)
54 {
55   u32 n_left_from = n_packets;
56
57   while (n_left_from >= 8)
58     {
59       ethernet_header_t *eth = *p;
60       u64 *dst = (u64 *) &eth->dst_address[0];
61       u64 a = clib_mem_unaligned (dst, u64);
62       u32 *src = (u32 *) &eth->src_address[2];
63       u32 b = clib_mem_unaligned (src, u32);
64
65       clib_prefetch_load (p[4]);
66       clib_prefetch_load (p[5]);
67       clib_prefetch_load (p[6]);
68       clib_prefetch_load (p[7]);
69
70       hash[0] = lb_hash_hash_2_tuples (a, b);
71       hash[1] = lb_hash_hash_2_tuples (a, b);
72       hash[2] = lb_hash_hash_2_tuples (a, b);
73       hash[3] = lb_hash_hash_2_tuples (a, b);
74
75       hash += 4;
76       n_left_from -= 4;
77       p += 4;
78     }
79
80   while (n_left_from > 0)
81     {
82       ethernet_header_t *eth = *p;
83       u64 *dst = (u64 *) &eth->dst_address[0];
84       u64 a = clib_mem_unaligned (dst, u64);
85       u32 *src = (u32 *) &eth->src_address[2];
86       u32 b = clib_mem_unaligned (src, u32);
87
88       hash[0] = lb_hash_hash_2_tuples (a, b);
89
90       hash += 1;
91       n_left_from -= 1;
92       p += 1;
93     }
94 }
95
96 static_always_inline u32
97 hash_eth_l23_inline (void **p)
98 {
99   ethernet_header_t *eth = *p;
100   u8 ip_version;
101   ip4_header_t *ip4;
102   u16 ethertype, *ethertype_p;
103   u32 *mac1, *mac2, *mac3;
104   u32 hash;
105
106   ethertype_p = locate_ethertype (eth);
107   ethertype = clib_mem_unaligned (ethertype_p, u16);
108
109   if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
110       (ethertype != htons (ETHERNET_TYPE_IP6)))
111     {
112       hash_eth_l2 (p, &hash, 1);
113       return hash;
114     }
115
116   ip4 = (ip4_header_t *) (ethertype_p + 1);
117   ip_version = (ip4->ip_version_and_header_length >> 4);
118
119   if (ip_version == 0x4)
120     {
121       u32 a;
122
123       mac1 = (u32 *) &eth->dst_address[0];
124       mac2 = (u32 *) &eth->dst_address[4];
125       mac3 = (u32 *) &eth->src_address[2];
126
127       a = clib_mem_unaligned (mac1, u32) ^ clib_mem_unaligned (mac2, u32) ^
128           clib_mem_unaligned (mac3, u32);
129       hash = lb_hash_hash_2_tuples (
130         clib_mem_unaligned (&ip4->address_pair, u64), a);
131       return hash;
132     }
133
134   if (ip_version == 0x6)
135     {
136       u64 a;
137       ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
138
139       mac1 = (u32 *) &eth->dst_address[0];
140       mac2 = (u32 *) &eth->dst_address[4];
141       mac3 = (u32 *) &eth->src_address[2];
142
143       a = clib_mem_unaligned (mac1, u32) ^ clib_mem_unaligned (mac2, u32) ^
144           clib_mem_unaligned (mac3, u32);
145       hash = lb_hash_hash (
146         clib_mem_unaligned (&ip6->src_address.as_uword[0], uword),
147         clib_mem_unaligned (&ip6->src_address.as_uword[1], uword),
148         clib_mem_unaligned (&ip6->dst_address.as_uword[0], uword),
149         clib_mem_unaligned (&ip6->dst_address.as_uword[1], uword), a);
150       return hash;
151     }
152
153   hash_eth_l2 (p, &hash, 1);
154   return hash;
155 }
156
157 static void
158 hash_eth_l23 (void **p, u32 *hash, u32 n_packets)
159 {
160   u32 n_left_from = n_packets;
161
162   while (n_left_from >= 8)
163     {
164       clib_prefetch_load (p[4]);
165       clib_prefetch_load (p[5]);
166       clib_prefetch_load (p[6]);
167       clib_prefetch_load (p[7]);
168
169       hash[0] = hash_eth_l23_inline (&p[0]);
170       hash[1] = hash_eth_l23_inline (&p[1]);
171       hash[2] = hash_eth_l23_inline (&p[2]);
172       hash[3] = hash_eth_l23_inline (&p[3]);
173
174       hash += 4;
175       n_left_from -= 4;
176       p += 4;
177     }
178
179   while (n_left_from > 0)
180     {
181       hash[0] = hash_eth_l23_inline (&p[0]);
182
183       hash += 1;
184       n_left_from -= 1;
185       p += 1;
186     }
187 }
188
189 static_always_inline u32
190 hash_eth_l34_inline (void **p)
191 {
192   ethernet_header_t *eth = *p;
193   u8 ip_version;
194   uword is_tcp_udp;
195   ip4_header_t *ip4;
196   u16 ethertype, *ethertype_p;
197   u32 hash;
198
199   ethertype_p = locate_ethertype (eth);
200   ethertype = clib_mem_unaligned (ethertype_p, u16);
201
202   if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
203       (ethertype != htons (ETHERNET_TYPE_IP6)))
204     {
205       hash_eth_l2 (p, &hash, 1);
206       return hash;
207     }
208
209   ip4 = (ip4_header_t *) (ethertype_p + 1);
210   ip_version = (ip4->ip_version_and_header_length >> 4);
211
212   if (ip_version == 0x4)
213     {
214       u32 a, t1, t2;
215       tcp_header_t *tcp = (void *) (ip4 + 1);
216
217       is_tcp_udp = (ip4->protocol == IP_PROTOCOL_TCP) ||
218                    (ip4->protocol == IP_PROTOCOL_UDP);
219       t1 = is_tcp_udp ? clib_mem_unaligned (&tcp->src, u16) : 0;
220       t2 = is_tcp_udp ? clib_mem_unaligned (&tcp->dst, u16) : 0;
221       a = t1 ^ t2;
222       hash = lb_hash_hash_2_tuples (
223         clib_mem_unaligned (&ip4->address_pair, u64), a);
224       return hash;
225     }
226
227   if (ip_version == 0x6)
228     {
229       u64 a;
230       u32 t1, t2;
231       ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
232       tcp_header_t *tcp = (void *) (ip6 + 1);
233
234       is_tcp_udp = 0;
235       if (PREDICT_TRUE ((ip6->protocol == IP_PROTOCOL_TCP) ||
236                         (ip6->protocol == IP_PROTOCOL_UDP)))
237         {
238           is_tcp_udp = 1;
239           tcp = (void *) (ip6 + 1);
240         }
241       else if (ip6->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)
242         {
243           ip6_hop_by_hop_header_t *hbh = (ip6_hop_by_hop_header_t *) (ip6 + 1);
244           if ((hbh->protocol == IP_PROTOCOL_TCP) ||
245               (hbh->protocol == IP_PROTOCOL_UDP))
246             {
247               is_tcp_udp = 1;
248               tcp = (tcp_header_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
249             }
250         }
251       t1 = is_tcp_udp ? clib_mem_unaligned (&tcp->src, u16) : 0;
252       t2 = is_tcp_udp ? clib_mem_unaligned (&tcp->dst, u16) : 0;
253       a = t1 ^ t2;
254       hash = lb_hash_hash (
255         clib_mem_unaligned (&ip6->src_address.as_uword[0], uword),
256         clib_mem_unaligned (&ip6->src_address.as_uword[1], uword),
257         clib_mem_unaligned (&ip6->dst_address.as_uword[0], uword),
258         clib_mem_unaligned (&ip6->dst_address.as_uword[1], uword), a);
259       return hash;
260     }
261
262   hash_eth_l2 (p, &hash, 1);
263   return hash;
264 }
265
266 static void
267 hash_eth_l34 (void **p, u32 *hash, u32 n_packets)
268 {
269   u32 n_left_from = n_packets;
270
271   while (n_left_from >= 8)
272     {
273       clib_prefetch_load (p[4]);
274       clib_prefetch_load (p[5]);
275       clib_prefetch_load (p[6]);
276       clib_prefetch_load (p[7]);
277
278       hash[0] = hash_eth_l34_inline (&p[0]);
279       hash[1] = hash_eth_l34_inline (&p[1]);
280       hash[2] = hash_eth_l34_inline (&p[2]);
281       hash[3] = hash_eth_l34_inline (&p[3]);
282
283       hash += 4;
284       n_left_from -= 4;
285       p += 4;
286     }
287
288   while (n_left_from > 0)
289     {
290       hash[0] = hash_eth_l34_inline (&p[0]);
291
292       hash += 1;
293       n_left_from -= 1;
294       p += 1;
295     }
296 }
297
298 VNET_REGISTER_HASH_FUNCTION (hash_eth_l2, static) = {
299   .name = "hash-eth-l2",
300   .description = "Hash ethernet L2 headers",
301   .priority = 50,
302   .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l2,
303 };
304
305 VNET_REGISTER_HASH_FUNCTION (hash_eth_l23, static) = {
306   .name = "hash-eth-l23",
307   .description = "Hash ethernet L23 headers",
308   .priority = 50,
309   .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l23,
310 };
311
312 VNET_REGISTER_HASH_FUNCTION (hash_eth_l34, static) = {
313   .name = "hash-eth-l34",
314   .description = "Hash ethernet L34 headers",
315   .priority = 50,
316   .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l34,
317 };
318
319 /*
320  * fd.io coding-style-patch-verification: ON
321  *
322  * Local Variables:
323  * eval: (c-set-style "gnu")
324  * End:
325  */