blob: 84a21b059be3d01e7a95e1303f7eb5ed4b4c1f6f [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>
26#include <vppinfra/lb_hash_hash.h>
27#include <vnet/hash/hash.h>
28
29static_always_inline u16 *
30locate_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
52static void
53hash_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
96static_always_inline u32
97hash_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
157static void
158hash_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
189static_always_inline u32
190hash_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
266static void
267hash_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
298VNET_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
305VNET_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
312VNET_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 */