blob: 2ccd2b45a6f193fce6d16b3a7aa079e572a0f8b6 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 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 * ip/ip4_pg: IP v4 packet-generator interface
17 *
18 * Copyright (c) 2008 Eliot Dresselhaus
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 */
39
40#include <vnet/ip/ip.h>
41#include <vnet/pg/pg.h>
42
43#define IP4_PG_EDIT_CHECKSUM (1 << 0)
44#define IP4_PG_EDIT_LENGTH (1 << 1)
45
46static_always_inline void
47compute_length_and_or_checksum (vlib_main_t * vm,
48 u32 * packets,
49 u32 n_packets,
Dave Barachd7cb1b52016-12-09 09:52:16 -050050 u32 ip_header_offset, u32 flags)
Ed Warnickecb9cada2015-12-08 15:45:58 -070051{
52 ASSERT (flags != 0);
53
54 while (n_packets >= 2)
55 {
56 u32 pi0, pi1;
Dave Barachd7cb1b52016-12-09 09:52:16 -050057 vlib_buffer_t *p0, *p1;
58 ip4_header_t *ip0, *ip1;
Ed Warnickecb9cada2015-12-08 15:45:58 -070059 ip_csum_t sum0, sum1;
60
61 pi0 = packets[0];
62 pi1 = packets[1];
63 p0 = vlib_get_buffer (vm, pi0);
64 p1 = vlib_get_buffer (vm, pi1);
65 n_packets -= 2;
66 packets += 2;
67
68 ip0 = (void *) (p0->data + ip_header_offset);
69 ip1 = (void *) (p1->data + ip_header_offset);
70
71 if (flags & IP4_PG_EDIT_LENGTH)
72 {
Dave Barachd7cb1b52016-12-09 09:52:16 -050073 ip0->length =
74 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p0) -
75 ip_header_offset);
76 ip1->length =
77 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p1) -
78 ip_header_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -070079 }
80
81 if (flags & IP4_PG_EDIT_CHECKSUM)
82 {
83 ASSERT (ip4_header_bytes (ip0) == sizeof (ip0[0]));
84 ASSERT (ip4_header_bytes (ip1) == sizeof (ip1[0]));
85
86 ip0->checksum = 0;
87 ip1->checksum = 0;
88
89 ip4_partial_header_checksum_x2 (ip0, ip1, sum0, sum1);
Dave Barachd7cb1b52016-12-09 09:52:16 -050090 ip0->checksum = ~ip_csum_fold (sum0);
91 ip1->checksum = ~ip_csum_fold (sum1);
Ed Warnickecb9cada2015-12-08 15:45:58 -070092
Benoît Ganne6e334e32020-08-31 18:59:34 +020093 ASSERT (ip4_header_checksum_is_valid (ip0));
94 ASSERT (ip4_header_checksum_is_valid (ip1));
Ed Warnickecb9cada2015-12-08 15:45:58 -070095 }
96 }
97
98 while (n_packets >= 1)
99 {
100 u32 pi0;
Dave Barachd7cb1b52016-12-09 09:52:16 -0500101 vlib_buffer_t *p0;
102 ip4_header_t *ip0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700103 ip_csum_t sum0;
104
105 pi0 = packets[0];
106 p0 = vlib_get_buffer (vm, pi0);
107 n_packets -= 1;
108 packets += 1;
109
110 ip0 = (void *) (p0->data + ip_header_offset);
111
112 if (flags & IP4_PG_EDIT_LENGTH)
Dave Barachd7cb1b52016-12-09 09:52:16 -0500113 ip0->length =
114 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, p0) -
115 ip_header_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700116
117 if (flags & IP4_PG_EDIT_CHECKSUM)
118 {
119 ASSERT (ip4_header_bytes (ip0) == sizeof (ip0[0]));
120
121 ip0->checksum = 0;
122
123 ip4_partial_header_checksum_x1 (ip0, sum0);
Dave Barachd7cb1b52016-12-09 09:52:16 -0500124 ip0->checksum = ~ip_csum_fold (sum0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700125
Benoît Ganne6e334e32020-08-31 18:59:34 +0200126 ASSERT (ip4_header_checksum_is_valid (ip0));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700127 }
128 }
129}
130
131static void
132ip4_pg_edit_function (pg_main_t * pg,
133 pg_stream_t * s,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500134 pg_edit_group_t * g, u32 * packets, u32 n_packets)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700135{
Dave Barachd7cb1b52016-12-09 09:52:16 -0500136 vlib_main_t *vm = vlib_get_main ();
Ed Warnickecb9cada2015-12-08 15:45:58 -0700137 u32 ip_offset;
138
139 ip_offset = g->start_byte_offset;
140
141 switch (g->edit_function_opaque)
142 {
143 case IP4_PG_EDIT_LENGTH:
144 compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
145 IP4_PG_EDIT_LENGTH);
146 break;
147
148 case IP4_PG_EDIT_CHECKSUM:
149 compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
150 IP4_PG_EDIT_CHECKSUM);
151 break;
152
153 case IP4_PG_EDIT_LENGTH | IP4_PG_EDIT_CHECKSUM:
154 compute_length_and_or_checksum (vm, packets, n_packets, ip_offset,
155 IP4_PG_EDIT_LENGTH
156 | IP4_PG_EDIT_CHECKSUM);
157 break;
158
159 default:
160 ASSERT (0);
161 break;
162 }
163}
164
Dave Barachd7cb1b52016-12-09 09:52:16 -0500165typedef struct
166{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700167 pg_edit_t ip_version, header_length;
168 pg_edit_t tos;
169 pg_edit_t length;
170
171 pg_edit_t fragment_id, fragment_offset;
172
173 /* Flags together with fragment offset. */
174 pg_edit_t mf_flag, df_flag, ce_flag;
175
176 pg_edit_t ttl;
177
178 pg_edit_t protocol;
179
180 pg_edit_t checksum;
181
182 pg_edit_t src_address, dst_address;
183} pg_ip4_header_t;
184
185static inline void
186pg_ip4_header_init (pg_ip4_header_t * p)
187{
188 /* Initialize fields that are not bit fields in the IP header. */
189#define _(f) pg_edit_init (&p->f, ip4_header_t, f);
Dave Barachd7cb1b52016-12-09 09:52:16 -0500190 _(tos);
191 _(length);
192 _(fragment_id);
193 _(ttl);
194 _(protocol);
195 _(checksum);
196 _(src_address);
197 _(dst_address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700198#undef _
199
200 /* Initialize bit fields. */
201 pg_edit_init_bitfield (&p->header_length, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500202 ip_version_and_header_length, 0, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700203 pg_edit_init_bitfield (&p->ip_version, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500204 ip_version_and_header_length, 4, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700205
206 pg_edit_init_bitfield (&p->fragment_offset, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500207 flags_and_fragment_offset, 0, 13);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700208 pg_edit_init_bitfield (&p->mf_flag, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500209 flags_and_fragment_offset, 13, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700210 pg_edit_init_bitfield (&p->df_flag, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500211 flags_and_fragment_offset, 14, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700212 pg_edit_init_bitfield (&p->ce_flag, ip4_header_t,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500213 flags_and_fragment_offset, 15, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700214}
215
216uword
217unformat_pg_ip4_header (unformat_input_t * input, va_list * args)
218{
Dave Barachd7cb1b52016-12-09 09:52:16 -0500219 pg_stream_t *s = va_arg (*args, pg_stream_t *);
220 pg_ip4_header_t *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700221 u32 group_index;
Dave Barachd7cb1b52016-12-09 09:52:16 -0500222
Ed Warnickecb9cada2015-12-08 15:45:58 -0700223 p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ip4_header_t),
224 &group_index);
225 pg_ip4_header_init (p);
226
227 /* Defaults. */
228 pg_edit_set_fixed (&p->ip_version, 4);
Dave Barachd7cb1b52016-12-09 09:52:16 -0500229 pg_edit_set_fixed (&p->header_length, sizeof (ip4_header_t) / sizeof (u32));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700230
231 pg_edit_set_fixed (&p->tos, 0);
232 pg_edit_set_fixed (&p->ttl, 64);
233
234 pg_edit_set_fixed (&p->fragment_id, 0);
235 pg_edit_set_fixed (&p->fragment_offset, 0);
236 pg_edit_set_fixed (&p->mf_flag, 0);
237 pg_edit_set_fixed (&p->df_flag, 0);
238 pg_edit_set_fixed (&p->ce_flag, 0);
239
240 p->length.type = PG_EDIT_UNSPECIFIED;
241 p->checksum.type = PG_EDIT_UNSPECIFIED;
242
243 if (unformat (input, "%U: %U -> %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500244 unformat_pg_edit,
245 unformat_ip_protocol, &p->protocol,
246 unformat_pg_edit,
247 unformat_ip4_address, &p->src_address,
248 unformat_pg_edit, unformat_ip4_address, &p->dst_address))
249 goto found;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700250
Dave Barachd7cb1b52016-12-09 09:52:16 -0500251 if (!unformat (input, "%U:",
252 unformat_pg_edit, unformat_ip_protocol, &p->protocol))
253 goto error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700254
255found:
256 /* Parse options. */
257 while (1)
258 {
259 if (unformat (input, "version %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500260 unformat_pg_edit, unformat_pg_number, &p->ip_version))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700261 ;
262
263 else if (unformat (input, "header-length %U",
264 unformat_pg_edit,
265 unformat_pg_number, &p->header_length))
266 ;
267
268 else if (unformat (input, "tos %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500269 unformat_pg_edit, unformat_pg_number, &p->tos))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700270 ;
271
272 else if (unformat (input, "length %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500273 unformat_pg_edit, unformat_pg_number, &p->length))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700274 ;
275
276 else if (unformat (input, "checksum %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500277 unformat_pg_edit, unformat_pg_number, &p->checksum))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700278 ;
279
280 else if (unformat (input, "ttl %U",
Dave Barachd7cb1b52016-12-09 09:52:16 -0500281 unformat_pg_edit, unformat_pg_number, &p->ttl))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700282 ;
283
284 else if (unformat (input, "fragment id %U offset %U",
285 unformat_pg_edit,
286 unformat_pg_number, &p->fragment_id,
287 unformat_pg_edit,
288 unformat_pg_number, &p->fragment_offset))
289 {
290 int i;
Dave Barachd7cb1b52016-12-09 09:52:16 -0500291 for (i = 0; i < ARRAY_LEN (p->fragment_offset.values); i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700292 pg_edit_set_value (&p->fragment_offset, i,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500293 pg_edit_get_value (&p->fragment_offset,
294 i) / 8);
295
Ed Warnickecb9cada2015-12-08 15:45:58 -0700296 }
297
298 /* Flags. */
299 else if (unformat (input, "mf") || unformat (input, "MF"))
300 pg_edit_set_fixed (&p->mf_flag, 1);
301
302 else if (unformat (input, "df") || unformat (input, "DF"))
303 pg_edit_set_fixed (&p->df_flag, 1);
304
305 else if (unformat (input, "ce") || unformat (input, "CE"))
306 pg_edit_set_fixed (&p->ce_flag, 1);
Dave Barachd7cb1b52016-12-09 09:52:16 -0500307
Ed Warnickecb9cada2015-12-08 15:45:58 -0700308 /* Can't parse input: try next protocol level. */
309 else
310 break;
311 }
312
313 {
Dave Barachd7cb1b52016-12-09 09:52:16 -0500314 ip_main_t *im = &ip_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700315 ip_protocol_t protocol;
Dave Barachd7cb1b52016-12-09 09:52:16 -0500316 ip_protocol_info_t *pi;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700317
318 pi = 0;
319 if (p->protocol.type == PG_EDIT_FIXED)
320 {
321 protocol = pg_edit_get_value (&p->protocol, PG_EDIT_LO);
322 pi = ip_get_protocol_info (im, protocol);
323 }
324
325 if (pi && pi->unformat_pg_edit
326 && unformat_user (input, pi->unformat_pg_edit, s))
327 ;
328
Dave Barachd7cb1b52016-12-09 09:52:16 -0500329 else if (!unformat_user (input, unformat_pg_payload, s))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700330 goto error;
331
332 if (p->length.type == PG_EDIT_UNSPECIFIED
333 && s->min_packet_bytes == s->max_packet_bytes
334 && group_index + 1 < vec_len (s->edit_groups))
335 {
336 pg_edit_set_fixed (&p->length,
337 pg_edit_group_n_bytes (s, group_index));
338 }
339
340 /* Compute IP header checksum if all edits are fixed. */
341 if (p->checksum.type == PG_EDIT_UNSPECIFIED)
342 {
343 ip4_header_t fixed_header, fixed_mask, cmp_mask;
Dave Barachd7cb1b52016-12-09 09:52:16 -0500344
Ed Warnickecb9cada2015-12-08 15:45:58 -0700345 /* See if header is all fixed and specified except for
346 checksum field. */
Dave Barachb7b92992018-10-17 10:38:51 -0400347 clib_memset (&cmp_mask, ~0, sizeof (cmp_mask));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700348 cmp_mask.checksum = 0;
349
350 pg_edit_group_get_fixed_packet_data (s, group_index,
351 &fixed_header, &fixed_mask);
Dave Barachd7cb1b52016-12-09 09:52:16 -0500352 if (!memcmp (&fixed_mask, &cmp_mask, sizeof (cmp_mask)))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700353 pg_edit_set_fixed (&p->checksum,
Dave Barachd7cb1b52016-12-09 09:52:16 -0500354 clib_net_to_host_u16 (ip4_header_checksum
355 (&fixed_header)));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700356 }
357
358 p = pg_get_edit_group (s, group_index);
359 if (p->length.type == PG_EDIT_UNSPECIFIED
360 || p->checksum.type == PG_EDIT_UNSPECIFIED)
361 {
Dave Barachd7cb1b52016-12-09 09:52:16 -0500362 pg_edit_group_t *g = pg_stream_get_group (s, group_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700363 g->edit_function = ip4_pg_edit_function;
364 g->edit_function_opaque = 0;
365 if (p->length.type == PG_EDIT_UNSPECIFIED)
366 g->edit_function_opaque |= IP4_PG_EDIT_LENGTH;
367 if (p->checksum.type == PG_EDIT_UNSPECIFIED)
368 g->edit_function_opaque |= IP4_PG_EDIT_CHECKSUM;
369 }
370
371 return 1;
372 }
373
Dave Barachd7cb1b52016-12-09 09:52:16 -0500374error:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700375 /* Free up any edits we may have added. */
376 pg_free_edit_group (s);
377 return 0;
378}
379
Dave Barachd7cb1b52016-12-09 09:52:16 -0500380
381/*
382 * fd.io coding-style-patch-verification: ON
383 *
384 * Local Variables:
385 * eval: (c-set-style "gnu")
386 * End:
387 */