vlib: refactor checksum offload 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
24 always_inline void *
25 vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum)
26 {
27   udp_header_t *uh;
28   u16 udp_len = sizeof (udp_header_t) + b->current_length;
29   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID))
30     udp_len += b->total_length_not_including_first_buffer;
31
32   uh = vlib_buffer_push_uninit (b, sizeof (udp_header_t));
33   uh->src_port = sp;
34   uh->dst_port = dp;
35   uh->checksum = 0;
36   uh->length = clib_host_to_net_u16 (udp_len);
37   if (offload_csum)
38     vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
39   vnet_buffer (b)->l4_hdr_offset = (u8 *) uh - b->data;
40   b->flags |= VNET_BUFFER_F_L4_HDR_OFFSET_VALID;
41   return uh;
42 }
43
44 always_inline void
45 ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4)
46 {
47   u16 new_l0;
48   udp_header_t *udp0;
49
50   if (is_ip4)
51     {
52       ip4_header_t *ip0;
53       ip_csum_t sum0;
54       u16 old_l0 = 0;
55
56       ip0 = vlib_buffer_get_current (b0);
57
58       /* fix the <bleep>ing outer-IP checksum */
59       sum0 = ip0->checksum;
60       /* old_l0 always 0, see the rewrite setup */
61       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
62
63       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
64                              length /* changed member */ );
65       ip0->checksum = ip_csum_fold (sum0);
66       ip0->length = new_l0;
67
68       /* Fix UDP length */
69       udp0 = (udp_header_t *) (ip0 + 1);
70       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
71                                      - sizeof (*ip0));
72       udp0->length = new_l0;
73     }
74   else
75     {
76       ip6_header_t *ip0;
77       int bogus0;
78
79       ip0 = vlib_buffer_get_current (b0);
80
81       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
82                                      - sizeof (*ip0));
83       ip0->payload_length = new_l0;
84
85       /* Fix UDP length */
86       udp0 = (udp_header_t *) (ip0 + 1);
87       udp0->length = new_l0;
88
89       udp0->checksum =
90         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
91       ASSERT (bogus0 == 0);
92
93       if (udp0->checksum == 0)
94         udp0->checksum = 0xffff;
95     }
96 }
97
98 always_inline void
99 ip_udp_encap_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 * ec0, word ec_len,
100                   u8 is_ip4)
101 {
102   vlib_buffer_advance (b0, -ec_len);
103
104   if (is_ip4)
105     {
106       ip4_header_t *ip0;
107
108       ip0 = vlib_buffer_get_current (b0);
109
110       /* Apply the encap string. */
111       clib_memcpy_fast (ip0, ec0, ec_len);
112       ip_udp_fixup_one (vm, b0, 1);
113     }
114   else
115     {
116       ip6_header_t *ip0;
117
118       ip0 = vlib_buffer_get_current (b0);
119
120       /* Apply the encap string. */
121       clib_memcpy_fast (ip0, ec0, ec_len);
122       ip_udp_fixup_one (vm, b0, 0);
123     }
124 }
125
126 always_inline void
127 ip_udp_encap_two (vlib_main_t * vm, vlib_buffer_t * b0, vlib_buffer_t * b1,
128                   u8 * ec0, u8 * ec1, word ec_len, u8 is_v4)
129 {
130   u16 new_l0, new_l1;
131   udp_header_t *udp0, *udp1;
132
133   ASSERT (_vec_len (ec0) == _vec_len (ec1));
134
135   vlib_buffer_advance (b0, -ec_len);
136   vlib_buffer_advance (b1, -ec_len);
137
138   if (is_v4)
139     {
140       ip4_header_t *ip0, *ip1;
141       ip_csum_t sum0, sum1;
142       u16 old_l0 = 0, old_l1 = 0;
143
144       ip0 = vlib_buffer_get_current (b0);
145       ip1 = vlib_buffer_get_current (b1);
146
147       /* Apply the encap string */
148       clib_memcpy_fast (ip0, ec0, ec_len);
149       clib_memcpy_fast (ip1, ec1, ec_len);
150
151       /* fix the <bleep>ing outer-IP checksum */
152       sum0 = ip0->checksum;
153       sum1 = ip1->checksum;
154
155       /* old_l0 always 0, see the rewrite setup */
156       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
157       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
158
159       sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
160                              length /* changed member */ );
161       sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
162                              length /* changed member */ );
163
164       ip0->checksum = ip_csum_fold (sum0);
165       ip1->checksum = ip_csum_fold (sum1);
166
167       ip0->length = new_l0;
168       ip1->length = new_l1;
169
170       /* Fix UDP length */
171       udp0 = (udp_header_t *) (ip0 + 1);
172       udp1 = (udp_header_t *) (ip1 + 1);
173
174       new_l0 =
175         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
176                               sizeof (*ip0));
177       new_l1 =
178         clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) -
179                               sizeof (*ip1));
180       udp0->length = new_l0;
181       udp1->length = new_l1;
182     }
183   else
184     {
185       ip6_header_t *ip0, *ip1;
186       int bogus0, bogus1;
187
188       ip0 = vlib_buffer_get_current (b0);
189       ip1 = vlib_buffer_get_current (b1);
190
191       /* Apply the encap string. */
192       clib_memcpy_fast (ip0, ec0, ec_len);
193       clib_memcpy_fast (ip1, ec1, ec_len);
194
195       new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
196                                      - sizeof (*ip0));
197       new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)
198                                      - sizeof (*ip1));
199       ip0->payload_length = new_l0;
200       ip1->payload_length = new_l1;
201
202       /* Fix UDP length */
203       udp0 = (udp_header_t *) (ip0 + 1);
204       udp1 = (udp_header_t *) (ip1 + 1);
205
206       udp0->length = new_l0;
207       udp1->length = new_l1;
208
209       udp0->checksum =
210         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0);
211       udp1->checksum =
212         ip6_tcp_udp_icmp_compute_checksum (vm, b1, ip1, &bogus1);
213       ASSERT (bogus0 == 0);
214       ASSERT (bogus1 == 0);
215
216       if (udp0->checksum == 0)
217         udp0->checksum = 0xffff;
218       if (udp1->checksum == 0)
219         udp1->checksum = 0xffff;
220     }
221 }
222
223 #endif /* SRC_VNET_UDP_UDP_INLINES_H_ */
224
225 /*
226  * fd.io coding-style-patch-verification: ON
227  *
228  * Local Variables:
229  * eval: (c-set-style "gnu")
230  * End:
231  */