blob: 1ac8b66a1bce28cbc0380998ea3cdcb20cd57a51 [file] [log] [blame]
Steven Luong05a68d62021-12-03 12:05:45 -08001/*
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>
Neale Rannsf7040f02022-02-15 09:02:27 +000026#include <vnet/tcp/tcp_packet.h>
Steven Luong05a68d62021-12-03 12:05:45 -080027#include <vppinfra/lb_hash_hash.h>
28#include <vnet/hash/hash.h>
29
30static_always_inline u16 *
31locate_ethertype (ethernet_header_t *eth)
32{
33 u16 *ethertype_p;
34 ethernet_vlan_header_t *vlan;
35
36 if (!ethernet_frame_is_tagged (clib_net_to_host_u16 (eth->type)))
37 {
38 ethertype_p = &eth->type;
39 }
40 else
41 {
42 vlan = (void *) (eth + 1);
43 ethertype_p = &vlan->type;
44 if (*ethertype_p == ntohs (ETHERNET_TYPE_VLAN))
45 {
46 vlan++;
47 ethertype_p = &vlan->type;
48 }
49 }
50 return ethertype_p;
51}
52
53static void
54hash_eth_l2 (void **p, u32 *hash, u32 n_packets)
55{
56 u32 n_left_from = n_packets;
57
58 while (n_left_from >= 8)
59 {
60 ethernet_header_t *eth = *p;
61 u64 *dst = (u64 *) &eth->dst_address[0];
62 u64 a = clib_mem_unaligned (dst, u64);
63 u32 *src = (u32 *) &eth->src_address[2];
64 u32 b = clib_mem_unaligned (src, u32);
65
66 clib_prefetch_load (p[4]);
67 clib_prefetch_load (p[5]);
68 clib_prefetch_load (p[6]);
69 clib_prefetch_load (p[7]);
70
71 hash[0] = lb_hash_hash_2_tuples (a, b);
72 hash[1] = lb_hash_hash_2_tuples (a, b);
73 hash[2] = lb_hash_hash_2_tuples (a, b);
74 hash[3] = lb_hash_hash_2_tuples (a, b);
75
76 hash += 4;
77 n_left_from -= 4;
78 p += 4;
79 }
80
81 while (n_left_from > 0)
82 {
83 ethernet_header_t *eth = *p;
84 u64 *dst = (u64 *) &eth->dst_address[0];
85 u64 a = clib_mem_unaligned (dst, u64);
86 u32 *src = (u32 *) &eth->src_address[2];
87 u32 b = clib_mem_unaligned (src, u32);
88
89 hash[0] = lb_hash_hash_2_tuples (a, b);
90
91 hash += 1;
92 n_left_from -= 1;
93 p += 1;
94 }
95}
96
97static_always_inline u32
98hash_eth_l23_inline (void **p)
99{
100 ethernet_header_t *eth = *p;
101 u8 ip_version;
102 ip4_header_t *ip4;
103 u16 ethertype, *ethertype_p;
104 u32 *mac1, *mac2, *mac3;
105 u32 hash;
106
107 ethertype_p = locate_ethertype (eth);
108 ethertype = clib_mem_unaligned (ethertype_p, u16);
109
110 if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
111 (ethertype != htons (ETHERNET_TYPE_IP6)))
112 {
113 hash_eth_l2 (p, &hash, 1);
114 return hash;
115 }
116
117 ip4 = (ip4_header_t *) (ethertype_p + 1);
118 ip_version = (ip4->ip_version_and_header_length >> 4);
119
120 if (ip_version == 0x4)
121 {
122 u32 a;
123
124 mac1 = (u32 *) &eth->dst_address[0];
125 mac2 = (u32 *) &eth->dst_address[4];
126 mac3 = (u32 *) &eth->src_address[2];
127
128 a = clib_mem_unaligned (mac1, u32) ^ clib_mem_unaligned (mac2, u32) ^
129 clib_mem_unaligned (mac3, u32);
130 hash = lb_hash_hash_2_tuples (
131 clib_mem_unaligned (&ip4->address_pair, u64), a);
132 return hash;
133 }
134
135 if (ip_version == 0x6)
136 {
137 u64 a;
138 ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
139
140 mac1 = (u32 *) &eth->dst_address[0];
141 mac2 = (u32 *) &eth->dst_address[4];
142 mac3 = (u32 *) &eth->src_address[2];
143
144 a = clib_mem_unaligned (mac1, u32) ^ clib_mem_unaligned (mac2, u32) ^
145 clib_mem_unaligned (mac3, u32);
146 hash = lb_hash_hash (
147 clib_mem_unaligned (&ip6->src_address.as_uword[0], uword),
148 clib_mem_unaligned (&ip6->src_address.as_uword[1], uword),
149 clib_mem_unaligned (&ip6->dst_address.as_uword[0], uword),
150 clib_mem_unaligned (&ip6->dst_address.as_uword[1], uword), a);
151 return hash;
152 }
153
154 hash_eth_l2 (p, &hash, 1);
155 return hash;
156}
157
158static void
159hash_eth_l23 (void **p, u32 *hash, u32 n_packets)
160{
161 u32 n_left_from = n_packets;
162
163 while (n_left_from >= 8)
164 {
165 clib_prefetch_load (p[4]);
166 clib_prefetch_load (p[5]);
167 clib_prefetch_load (p[6]);
168 clib_prefetch_load (p[7]);
169
170 hash[0] = hash_eth_l23_inline (&p[0]);
171 hash[1] = hash_eth_l23_inline (&p[1]);
172 hash[2] = hash_eth_l23_inline (&p[2]);
173 hash[3] = hash_eth_l23_inline (&p[3]);
174
175 hash += 4;
176 n_left_from -= 4;
177 p += 4;
178 }
179
180 while (n_left_from > 0)
181 {
182 hash[0] = hash_eth_l23_inline (&p[0]);
183
184 hash += 1;
185 n_left_from -= 1;
186 p += 1;
187 }
188}
189
190static_always_inline u32
191hash_eth_l34_inline (void **p)
192{
193 ethernet_header_t *eth = *p;
194 u8 ip_version;
195 uword is_tcp_udp;
196 ip4_header_t *ip4;
197 u16 ethertype, *ethertype_p;
198 u32 hash;
199
200 ethertype_p = locate_ethertype (eth);
201 ethertype = clib_mem_unaligned (ethertype_p, u16);
202
203 if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
204 (ethertype != htons (ETHERNET_TYPE_IP6)))
205 {
206 hash_eth_l2 (p, &hash, 1);
207 return hash;
208 }
209
210 ip4 = (ip4_header_t *) (ethertype_p + 1);
211 ip_version = (ip4->ip_version_and_header_length >> 4);
212
213 if (ip_version == 0x4)
214 {
215 u32 a, t1, t2;
216 tcp_header_t *tcp = (void *) (ip4 + 1);
217
218 is_tcp_udp = (ip4->protocol == IP_PROTOCOL_TCP) ||
219 (ip4->protocol == IP_PROTOCOL_UDP);
220 t1 = is_tcp_udp ? clib_mem_unaligned (&tcp->src, u16) : 0;
221 t2 = is_tcp_udp ? clib_mem_unaligned (&tcp->dst, u16) : 0;
222 a = t1 ^ t2;
223 hash = lb_hash_hash_2_tuples (
224 clib_mem_unaligned (&ip4->address_pair, u64), a);
225 return hash;
226 }
227
228 if (ip_version == 0x6)
229 {
230 u64 a;
231 u32 t1, t2;
232 ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
233 tcp_header_t *tcp = (void *) (ip6 + 1);
234
235 is_tcp_udp = 0;
236 if (PREDICT_TRUE ((ip6->protocol == IP_PROTOCOL_TCP) ||
237 (ip6->protocol == IP_PROTOCOL_UDP)))
238 {
239 is_tcp_udp = 1;
240 tcp = (void *) (ip6 + 1);
241 }
242 else if (ip6->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)
243 {
244 ip6_hop_by_hop_header_t *hbh = (ip6_hop_by_hop_header_t *) (ip6 + 1);
245 if ((hbh->protocol == IP_PROTOCOL_TCP) ||
246 (hbh->protocol == IP_PROTOCOL_UDP))
247 {
248 is_tcp_udp = 1;
249 tcp = (tcp_header_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
250 }
251 }
252 t1 = is_tcp_udp ? clib_mem_unaligned (&tcp->src, u16) : 0;
253 t2 = is_tcp_udp ? clib_mem_unaligned (&tcp->dst, u16) : 0;
254 a = t1 ^ t2;
255 hash = lb_hash_hash (
256 clib_mem_unaligned (&ip6->src_address.as_uword[0], uword),
257 clib_mem_unaligned (&ip6->src_address.as_uword[1], uword),
258 clib_mem_unaligned (&ip6->dst_address.as_uword[0], uword),
259 clib_mem_unaligned (&ip6->dst_address.as_uword[1], uword), a);
260 return hash;
261 }
262
263 hash_eth_l2 (p, &hash, 1);
264 return hash;
265}
266
267static void
268hash_eth_l34 (void **p, u32 *hash, u32 n_packets)
269{
270 u32 n_left_from = n_packets;
271
272 while (n_left_from >= 8)
273 {
274 clib_prefetch_load (p[4]);
275 clib_prefetch_load (p[5]);
276 clib_prefetch_load (p[6]);
277 clib_prefetch_load (p[7]);
278
279 hash[0] = hash_eth_l34_inline (&p[0]);
280 hash[1] = hash_eth_l34_inline (&p[1]);
281 hash[2] = hash_eth_l34_inline (&p[2]);
282 hash[3] = hash_eth_l34_inline (&p[3]);
283
284 hash += 4;
285 n_left_from -= 4;
286 p += 4;
287 }
288
289 while (n_left_from > 0)
290 {
291 hash[0] = hash_eth_l34_inline (&p[0]);
292
293 hash += 1;
294 n_left_from -= 1;
295 p += 1;
296 }
297}
298
299VNET_REGISTER_HASH_FUNCTION (hash_eth_l2, static) = {
300 .name = "hash-eth-l2",
301 .description = "Hash ethernet L2 headers",
302 .priority = 50,
303 .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l2,
304};
305
306VNET_REGISTER_HASH_FUNCTION (hash_eth_l23, static) = {
307 .name = "hash-eth-l23",
308 .description = "Hash ethernet L23 headers",
309 .priority = 50,
310 .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l23,
311};
312
313VNET_REGISTER_HASH_FUNCTION (hash_eth_l34, static) = {
314 .name = "hash-eth-l34",
315 .description = "Hash ethernet L34 headers",
316 .priority = 50,
317 .function[VNET_HASH_FN_TYPE_ETHERNET] = hash_eth_l34,
318};
319
320/*
321 * fd.io coding-style-patch-verification: ON
322 *
323 * Local Variables:
324 * eval: (c-set-style "gnu")
325 * End:
326 */