udp: fix csum computation when offload disabled
[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)
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   vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data;
42   b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
43   return uh;
44 }
45
46 /*
47  * Encode udp source port entropy value per
48  * https://datatracker.ietf.org/doc/html/rfc7510#section-3
49  */
50 always_inline u16
51 ip_udp_sport_entropy (vlib_buffer_t *b0)
52 {
53   u16 port = clib_host_to_net_u16 (0x03 << 14);
54   port |= vnet_buffer (b0)->ip.flow_hash & 0xffff;
55   return port;
56 }
57
58 always_inline u32
59 ip_udp_compute_flow_hash (vlib_buffer_t *b0, u8 is_ip4)
60 {
61   ip4_header_t *ip4;
62   ip6_header_t *ip6;
63
64   if (is_ip4)
65     {
66       ip4 = (ip4_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
67       return ip4_compute_flow_hash (ip4, IP_FLOW_HASH_DEFAULT);
68     }
69   else
70     {
71       ip6 = (ip6_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset);
72       return ip6_compute_flow_hash (ip6, IP_FLOW_HASH_DEFAULT);
73     }
74 }
75
76 always_inline void
77 ip_udp_fixup_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 is_ip4,
78                   u8 sport_entropy)
79 {
80   u16 new_l0;
81   udp_header_t *udp0;
82
83   if (is_ip4)
84     {
85       ip4_header_t *ip0;
86       ip_csum_t sum0;
87       u16 old_l0 = 0;
88
89       ip0 = vlib_buffer_get_current (b0);
90
91       /* fix the <bleep>ing outer-IP checksum */
92       sum0 = ip0->checksum;
93       /* old_l0 always 0, see the rewrite setup */
94       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
95
96       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
97                              length /* changed member */ );
98       ip0->checksum = ip_csum_fold (sum0);
99       ip0->length = new_l0;
100
101       /* Fix UDP length */
102       udp0 = (udp_header_t *) (ip0 + 1);
103       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
104                                      - sizeof (*ip0));
105       udp0->length = new_l0;
106
107       if (sport_entropy)
108         udp0->src_port = ip_udp_sport_entropy (b0);
109     }
110   else
111     {
112       ip6_header_t *ip0;
113       int bogus0;
114
115       ip0 = vlib_buffer_get_current (b0);
116
117       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
118                                      - sizeof (*ip0));
119       ip0->payload_length = new_l0;
120
121       /* Fix UDP length */
122       udp0 = (udp_header_t *) (ip0 + 1);
123       udp0->length = new_l0;
124
125       if (sport_entropy)
126         udp0->src_port = ip_udp_sport_entropy (b0);
127
128       udp0->checksum =
129         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
130       ASSERT (bogus0 == 0);
131
132       if (udp0->checksum == 0)
133         udp0->checksum = 0xffff;
134     }
135 }
136
137 always_inline void
138 ip_udp_encap_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 *ec0, word ec_len,
139                   ip_address_family_t encap_family,
140                   ip_address_family_t payload_family,
141                   udp_encap_fixup_flags_t flags)
142 {
143   u8 sport_entropy = (flags & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
144
145   if (payload_family < N_AF)
146     {
147       vnet_calc_checksums_inline (vm, b0, payload_family == AF_IP4,
148                                   payload_family == AF_IP6);
149
150       /* Сalculate flow hash to be used for entropy */
151       if (sport_entropy && 0 == vnet_buffer (b0)->ip.flow_hash)
152         vnet_buffer (b0)->ip.flow_hash =
153           ip_udp_compute_flow_hash (b0, payload_family == AF_IP4);
154     }
155
156   vlib_buffer_advance (b0, -ec_len);
157
158   if (encap_family == AF_IP4)
159     {
160       ip4_header_t *ip0;
161
162       ip0 = vlib_buffer_get_current (b0);
163
164       /* Apply the encap string. */
165       clib_memcpy_fast (ip0, ec0, ec_len);
166       ip_udp_fixup_one (vm, b0, 1, sport_entropy);
167     }
168   else
169     {
170       ip6_header_t *ip0;
171
172       ip0 = vlib_buffer_get_current (b0);
173
174       /* Apply the encap string. */
175       clib_memcpy_fast (ip0, ec0, ec_len);
176       ip_udp_fixup_one (vm, b0, 0, sport_entropy);
177     }
178 }
179
180 always_inline void
181 ip_udp_encap_two (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1,
182                   u8 *ec0, u8 *ec1, word ec_len,
183                   ip_address_family_t encap_family,
184                   ip_address_family_t payload_family,
185                   udp_encap_fixup_flags_t flags0,
186                   udp_encap_fixup_flags_t flags1)
187 {
188   u16 new_l0, new_l1;
189   udp_header_t *udp0, *udp1;
190   int payload_ip4 = (payload_family == AF_IP4);
191   int sport_entropy0 = (flags0 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
192   int sport_entropy1 = (flags1 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0;
193
194   if (payload_family < N_AF)
195     {
196       vnet_calc_checksums_inline (vm, b0, payload_ip4, !payload_ip4);
197       vnet_calc_checksums_inline (vm, b1, payload_ip4, !payload_ip4);
198
199       /* Сalculate flow hash to be used for entropy */
200       if (sport_entropy0 && 0 == vnet_buffer (b0)->ip.flow_hash)
201         vnet_buffer (b0)->ip.flow_hash =
202           ip_udp_compute_flow_hash (b0, payload_ip4);
203       if (sport_entropy1 && 0 == vnet_buffer (b1)->ip.flow_hash)
204         vnet_buffer (b1)->ip.flow_hash =
205           ip_udp_compute_flow_hash (b1, payload_ip4);
206     }
207
208   vlib_buffer_advance (b0, -ec_len);
209   vlib_buffer_advance (b1, -ec_len);
210
211   if (encap_family == AF_IP4)
212     {
213       ip4_header_t *ip0, *ip1;
214       ip_csum_t sum0, sum1;
215       u16 old_l0 = 0, old_l1 = 0;
216
217       ip0 = vlib_buffer_get_current (b0);
218       ip1 = vlib_buffer_get_current (b1);
219
220       /* Apply the encap string */
221       clib_memcpy_fast (ip0, ec0, ec_len);
222       clib_memcpy_fast (ip1, ec1, ec_len);
223
224       /* fix the <bleep>ing outer-IP checksum */
225       sum0 = ip0->checksum;
226       sum1 = ip1->checksum;
227
228       /* old_l0 always 0, see the rewrite setup */
229       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
230       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
231
232       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
233                              length /* changed member */ );
234       sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
235                              length /* changed member */ );
236
237       ip0->checksum = ip_csum_fold (sum0);
238       ip1->checksum = ip_csum_fold (sum1);
239
240       ip0->length = new_l0;
241       ip1->length = new_l1;
242
243       /* Fix UDP length */
244       udp0 = (udp_header_t *) (ip0 + 1);
245       udp1 = (udp_header_t *) (ip1 + 1);
246
247       new_l0 =
248         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
249                               sizeof (*ip0));
250       new_l1 =
251         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) -
252                               sizeof (*ip1));
253       udp0->length = new_l0;
254       udp1->length = new_l1;
255
256       if (sport_entropy0)
257         udp0->src_port = ip_udp_sport_entropy (b0);
258       if (sport_entropy1)
259         udp1->src_port = ip_udp_sport_entropy (b1);
260     }
261   else
262     {
263       ip6_header_t *ip0, *ip1;
264       int bogus0, bogus1;
265
266       ip0 = vlib_buffer_get_current (b0);
267       ip1 = vlib_buffer_get_current (b1);
268
269       /* Apply the encap string. */
270       clib_memcpy_fast (ip0, ec0, ec_len);
271       clib_memcpy_fast (ip1, ec1, ec_len);
272
273       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
274                                      - sizeof (*ip0));
275       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
276                                      - sizeof (*ip1));
277       ip0->payload_length = new_l0;
278       ip1->payload_length = new_l1;
279
280       /* Fix UDP length */
281       udp0 = (udp_header_t *) (ip0 + 1);
282       udp1 = (udp_header_t *) (ip1 + 1);
283
284       udp0->length = new_l0;
285       udp1->length = new_l1;
286
287       if (sport_entropy0)
288         udp0->src_port = ip_udp_sport_entropy (b0);
289       if (sport_entropy1)
290         udp1->src_port = ip_udp_sport_entropy (b1);
291
292       udp0->checksum =
293         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
294       udp1->checksum =
295         ip6_tcp_udp_icmp_compute_checksum (vm, b1, ip1, &bogus1);
296       ASSERT (bogus0 == 0);
297       ASSERT (bogus1 == 0);
298
299       if (udp0->checksum == 0)
300         udp0->checksum = 0xffff;
301       if (udp1->checksum == 0)
302         udp1->checksum = 0xffff;
303     }
304 }
305
306 #endif /* SRC_VNET_UDP_UDP_INLINES_H_ */
307
308 /*
309  * fd.io coding-style-patch-verification: ON
310  *
311  * Local Variables:
312  * eval: (c-set-style "gnu")
313  * End:
314  */