udp: add udp encap source port entropy support
[vpp.git] / src / vnet / udp / udp_inlines.h
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #ifndef SRC_VNET_UDP_UDP_INLINES_H_
17 #define SRC_VNET_UDP_UDP_INLINES_H_
18
19 #include <vnet/vnet.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/ip/ip6.h>
22 #include <vnet/udp/udp_packet.h>
23 #include <vnet/interface_output.h>
24 #include <vnet/ip/ip4_inlines.h>
25 #include <vnet/ip/ip6_inlines.h>
26 #include <vnet/udp/udp_encap.h>
27
28 always_inline void *
29 vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum)
30 {
31   udp_header_t *uh;
32   u16 udp_len = sizeof (udp_header_t) + b->current_length;
33   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID))
34     udp_len += b->total_length_not_including_first_buffer;
35
36   uh = vlib_buffer_push_uninit (b, sizeof (udp_header_t));
37   uh->src_port = sp;
38   uh->dst_port = dp;
39   uh->checksum = 0;
40   uh->length = clib_host_to_net_u16 (udp_len);
41   if (offload_csum)
42     vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
43   vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data;
44   b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
45   return uh;
46 }
47
48 /*
49  * Encode udp source port entropy value per
50  * https://datatracker.ietf.org/doc/html/rfc7510#section-3
51  */
52 always_inline u16
53 ip_udp_sport_entropy (vlib_buffer_t *b0)
54 {
55   u16 port = clib_host_to_net_u16 (0x03 << 14);
56   port |= vnet_buffer (b0)->ip.flow_hash & 0xffff;
57   return port;
58 }
59
60 always_inline u32
61 ip_udp_compute_flow_hash (vlib_buffer_t *b0, u8 is_ip4)
62 {
63   ip4_header_t *ip4;
64   ip6_header_t *ip6;
65
66   if (is_ip4)
67     {
68       ip4 = (ip4_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
69       return ip4_compute_flow_hash (ip4, IP_FLOW_HASH_DEFAULT);
70     }
71   else
72     {
73       ip6 = (ip6_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
74       return ip6_compute_flow_hash (ip6, IP_FLOW_HASH_DEFAULT);
75     }
76 }
77
78 always_inline void
79 ip_udp_fixup_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 is_ip4,
80                   u8 sport_entropy)
81 {
82   u16 new_l0;
83   udp_header_t *udp0;
84
85   if (is_ip4)
86     {
87       ip4_header_t *ip0;
88       ip_csum_t sum0;
89       u16 old_l0 = 0;
90
91       ip0 = vlib_buffer_get_current (b0);
92
93       /* fix the <bleep>ing outer-IP checksum */
94       sum0 = ip0->checksum;
95       /* old_l0 always 0, see the rewrite setup */
96       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
97
98       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
99                              length /* changed member */ );
100       ip0->checksum = ip_csum_fold (sum0);
101       ip0->length = new_l0;
102
103       /* Fix UDP length */
104       udp0 = (udp_header_t *) (ip0 + 1);
105       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
106                                      - sizeof (*ip0));
107       udp0->length = new_l0;
108
109       if (sport_entropy)
110         udp0->src_port = ip_udp_sport_entropy (b0);
111     }
112   else
113     {
114       ip6_header_t *ip0;
115       int bogus0;
116
117       ip0 = vlib_buffer_get_current (b0);
118
119       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
120                                      - sizeof (*ip0));
121       ip0->payload_length = new_l0;
122
123       /* Fix UDP length */
124       udp0 = (udp_header_t *) (ip0 + 1);
125       udp0->length = new_l0;
126
127       if (sport_entropy)
128         udp0->src_port = ip_udp_sport_entropy (b0);
129
130       udp0->checksum =
131         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
132       ASSERT (bogus0 == 0);
133
134       if (udp0->checksum == 0)
135         udp0->checksum = 0xffff;
136     }
137 }
138
139 always_inline void
140 ip_udp_encap_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 *ec0, word ec_len,
141                   ip_address_family_t encap_family,
142                   ip_address_family_t payload_family,
143                   udp_encap_fixup_flags_t flags)
144 {
145   u8 sport_entropy = (flags & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
146
147   if (payload_family < N_AF)
148     {
149       vnet_calc_checksums_inline (vm, b0, payload_family == AF_IP4,
150                                   payload_family == AF_IP6);
151
152       /* Сalculate flow hash to be used for entropy */
153       if (sport_entropy && 0 == vnet_buffer (b0)->ip.flow_hash)
154         vnet_buffer (b0)->ip.flow_hash =
155           ip_udp_compute_flow_hash (b0, payload_family == AF_IP4);
156     }
157
158   vlib_buffer_advance (b0, -ec_len);
159
160   if (encap_family == AF_IP4)
161     {
162       ip4_header_t *ip0;
163
164       ip0 = vlib_buffer_get_current (b0);
165
166       /* Apply the encap string. */
167       clib_memcpy_fast (ip0, ec0, ec_len);
168       ip_udp_fixup_one (vm, b0, 1, sport_entropy);
169     }
170   else
171     {
172       ip6_header_t *ip0;
173
174       ip0 = vlib_buffer_get_current (b0);
175
176       /* Apply the encap string. */
177       clib_memcpy_fast (ip0, ec0, ec_len);
178       ip_udp_fixup_one (vm, b0, 0, sport_entropy);
179     }
180 }
181
182 always_inline void
183 ip_udp_encap_two (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1,
184                   u8 *ec0, u8 *ec1, word ec_len,
185                   ip_address_family_t encap_family,
186                   ip_address_family_t payload_family,
187                   udp_encap_fixup_flags_t flags0,
188                   udp_encap_fixup_flags_t flags1)
189 {
190   u16 new_l0, new_l1;
191   udp_header_t *udp0, *udp1;
192   int payload_ip4 = (payload_family == AF_IP4);
193   int sport_entropy0 = (flags0 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
194   int sport_entropy1 = (flags1 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
195
196   if (payload_family < N_AF)
197     {
198       vnet_calc_checksums_inline (vm, b0, payload_ip4, !payload_ip4);
199       vnet_calc_checksums_inline (vm, b1, payload_ip4, !payload_ip4);
200
201       /* Сalculate flow hash to be used for entropy */
202       if (sport_entropy0 && 0 == vnet_buffer (b0)->ip.flow_hash)
203         vnet_buffer (b0)->ip.flow_hash =
204           ip_udp_compute_flow_hash (b0, payload_ip4);
205       if (sport_entropy1 && 0 == vnet_buffer (b1)->ip.flow_hash)
206         vnet_buffer (b1)->ip.flow_hash =
207           ip_udp_compute_flow_hash (b1, payload_ip4);
208     }
209
210   vlib_buffer_advance (b0, -ec_len);
211   vlib_buffer_advance (b1, -ec_len);
212
213   if (encap_family == AF_IP4)
214     {
215       ip4_header_t *ip0, *ip1;
216       ip_csum_t sum0, sum1;
217       u16 old_l0 = 0, old_l1 = 0;
218
219       ip0 = vlib_buffer_get_current (b0);
220       ip1 = vlib_buffer_get_current (b1);
221
222       /* Apply the encap string */
223       clib_memcpy_fast (ip0, ec0, ec_len);
224       clib_memcpy_fast (ip1, ec1, ec_len);
225
226       /* fix the <bleep>ing outer-IP checksum */
227       sum0 = ip0->checksum;
228       sum1 = ip1->checksum;
229
230       /* old_l0 always 0, see the rewrite setup */
231       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
232       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
233
234       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
235                              length /* changed member */ );
236       sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
237                              length /* changed member */ );
238
239       ip0->checksum = ip_csum_fold (sum0);
240       ip1->checksum = ip_csum_fold (sum1);
241
242       ip0->length = new_l0;
243       ip1->length = new_l1;
244
245       /* Fix UDP length */
246       udp0 = (udp_header_t *) (ip0 + 1);
247       udp1 = (udp_header_t *) (ip1 + 1);
248
249       new_l0 =
250         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
251                               sizeof (*ip0));
252       new_l1 =
253         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) -
254                               sizeof (*ip1));
255       udp0->length = new_l0;
256       udp1->length = new_l1;
257
258       if (sport_entropy0)
259         udp0->src_port = ip_udp_sport_entropy (b0);
260       if (sport_entropy1)
261         udp1->src_port = ip_udp_sport_entropy (b1);
262     }
263   else
264     {
265       ip6_header_t *ip0, *ip1;
266       int bogus0, bogus1;
267
268       ip0 = vlib_buffer_get_current (b0);
269       ip1 = vlib_buffer_get_current (b1);
270
271       /* Apply the encap string. */
272       clib_memcpy_fast (ip0, ec0, ec_len);
273       clib_memcpy_fast (ip1, ec1, ec_len);
274
275       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
276                                      - sizeof (*ip0));
277       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
278                                      - sizeof (*ip1));
279       ip0->payload_length = new_l0;
280       ip1->payload_length = new_l1;
281
282       /* Fix UDP length */
283       udp0 = (udp_header_t *) (ip0 + 1);
284       udp1 = (udp_header_t *) (ip1 + 1);
285
286       udp0->length = new_l0;
287       udp1->length = new_l1;
288
289       if (sport_entropy0)
290         udp0->src_port = ip_udp_sport_entropy (b0);
291       if (sport_entropy1)
292         udp1->src_port = ip_udp_sport_entropy (b1);
293
294       udp0->checksum =
295         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
296       udp1->checksum =
297         ip6_tcp_udp_icmp_compute_checksum (vm, b1, ip1, &bogus1);
298       ASSERT (bogus0 == 0);
299       ASSERT (bogus1 == 0);
300
301       if (udp0->checksum == 0)
302         udp0->checksum = 0xffff;
303       if (udp1->checksum == 0)
304         udp1->checksum = 0xffff;
305     }
306 }
307
308 #endif /* SRC_VNET_UDP_UDP_INLINES_H_ */
309
310 /*
311  * fd.io coding-style-patch-verification: ON
312  *
313  * Local Variables:
314  * eval: (c-set-style "gnu")
315  * End:
316  */