misc: Initial 24.10-rc0 commit
[vpp.git] / src / vnet / tcp / tcp_pg.c
1 /*
2  * Copyright (c) 2016-2019 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/tcp_pg: TCP 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 /* TCP flags bit 0 first. */
44 #define foreach_tcp_flag                        \
45   _ (FIN)                                       \
46   _ (SYN)                                       \
47   _ (RST)                                       \
48   _ (PSH)                                       \
49   _ (ACK)                                       \
50   _ (URG)                                       \
51   _ (ECE)                                       \
52   _ (CWR)
53
54 #define foreach_tcp_options                                                   \
55   _ (mss, TCP_OPTION_MSS, TCP_OPTION_LEN_MSS, 1)                              \
56   _ (timestamp, TCP_OPTION_TIMESTAMP, TCP_OPTION_LEN_TIMESTAMP, 2)            \
57   _ (winscale, TCP_OPTION_WINDOW_SCALE, TCP_OPTION_LEN_WINDOW_SCALE, 1)       \
58   _ (sackperm, TCP_OPTION_SACK_PERMITTED, TCP_OPTION_LEN_SACK_PERMITTED, 0)   \
59   _ (sack, TCP_OPTION_SACK_BLOCK, TCP_OPTION_LEN_SACK_BLOCK, 0)
60
61 static void
62 tcp_pg_edit_function (pg_main_t * pg,
63                       pg_stream_t * s,
64                       pg_edit_group_t * g, u32 * packets, u32 n_packets)
65 {
66   vlib_main_t *vm = vlib_get_main ();
67   u32 ip_offset, tcp_offset;
68
69   tcp_offset = g->start_byte_offset;
70   ip_offset = (g - 1)->start_byte_offset;
71
72   while (n_packets >= 1)
73     {
74       vlib_buffer_t *p0;
75       ip4_header_t *ip0;
76       tcp_header_t *tcp0;
77       ip_csum_t sum0;
78       u32 tcp_len0;
79
80       p0 = vlib_get_buffer (vm, packets[0]);
81       n_packets -= 1;
82       packets += 1;
83
84       ASSERT (p0->current_data == 0);
85       ip0 = (void *) (p0->data + ip_offset);
86       tcp0 = (void *) (p0->data + tcp_offset);
87       /* if IP length has been specified, then calculate the length based on buffer */
88       if (ip0->length == 0)
89         tcp_len0 = vlib_buffer_length_in_chain (vm, p0) - tcp_offset;
90       else
91         tcp_len0 = clib_net_to_host_u16 (ip0->length) - tcp_offset;
92
93       /* Initialize checksum with header. */
94       if (BITS (sum0) == 32)
95         {
96           sum0 = clib_mem_unaligned (&ip0->src_address, u32);
97           sum0 =
98             ip_csum_with_carry (sum0,
99                                 clib_mem_unaligned (&ip0->dst_address, u32));
100         }
101       else
102         sum0 = clib_mem_unaligned (&ip0->src_address, u64);
103
104       sum0 = ip_csum_with_carry
105         (sum0, clib_host_to_net_u32 (tcp_len0 + (ip0->protocol << 16)));
106
107       /* Invalidate possibly old checksum. */
108       tcp0->checksum = 0;
109
110       sum0 =
111         ip_incremental_checksum_buffer (vm, p0, tcp_offset, tcp_len0, sum0);
112
113       tcp0->checksum = ~ip_csum_fold (sum0);
114     }
115 }
116
117 typedef struct
118 {
119   pg_edit_t src, dst;
120   pg_edit_t seq_number, ack_number;
121   pg_edit_t data_offset_and_reserved;
122 #define _(f) pg_edit_t f##_flag;
123     foreach_tcp_flag
124 #undef _
125     pg_edit_t window;
126   pg_edit_t checksum;
127   pg_edit_t urgent_pointer;
128 } pg_tcp_header_t;
129
130 static inline void
131 pg_tcp_header_init (pg_tcp_header_t * p)
132 {
133   /* Initialize fields that are not bit fields in the IP header. */
134 #define _(f) pg_edit_init (&p->f, tcp_header_t, f);
135   _(src);
136   _(dst);
137   _(seq_number);
138   _(ack_number);
139   _(window);
140   _(checksum);
141   _(urgent_pointer);
142 #undef _
143
144   /* Initialize bit fields. */
145 #define _(f)                                            \
146   pg_edit_init_bitfield (&p->f##_flag, tcp_header_t,    \
147                          flags,                         \
148                          TCP_FLAG_BIT_##f, 1);
149
150   foreach_tcp_flag
151 #undef _
152     pg_edit_init_bitfield (&p->data_offset_and_reserved, tcp_header_t,
153                            data_offset_and_reserved, 4, 4);
154 }
155
156 uword
157 unformat_pg_tcp_header (unformat_input_t * input, va_list * args)
158 {
159   pg_stream_t *s = va_arg (*args, pg_stream_t *);
160   pg_tcp_header_t *pth;
161   u32 header_group_index, opt_group_index = ~0, noop_len, opts_len = 0;
162
163   pth = pg_create_edit_group (s, sizeof (pth[0]), sizeof (tcp_header_t),
164                               &header_group_index);
165   pg_tcp_header_init (pth);
166
167   /* Defaults. */
168   pg_edit_set_fixed (&pth->seq_number, 0);
169   pg_edit_set_fixed (&pth->ack_number, 0);
170
171   pg_edit_set_fixed (&pth->window, 4096);
172   pg_edit_set_fixed (&pth->urgent_pointer, 0);
173
174 #define _(f) pg_edit_set_fixed (&pth->f##_flag, 0);
175   foreach_tcp_flag
176 #undef _
177     pth->checksum.type = PG_EDIT_UNSPECIFIED;
178
179   if (!unformat (input, "TCP: %U -> %U", unformat_pg_edit,
180                  unformat_tcp_udp_port, &pth->src, unformat_pg_edit,
181                  unformat_tcp_udp_port, &pth->dst))
182     goto error;
183
184   /* Parse options. */
185   while (1)
186     {
187       if (unformat (input, "window %U", unformat_pg_edit, unformat_pg_number,
188                     &pth->window))
189         ;
190
191       else if (unformat (input, "checksum %U", unformat_pg_edit,
192                          unformat_pg_number, &pth->checksum))
193         ;
194
195       else if (unformat (input, "seqnum %U", unformat_pg_edit,
196                          unformat_pg_number, &pth->seq_number))
197         ;
198       else if (unformat (input, "acknum %U", unformat_pg_edit,
199                          unformat_pg_number, &pth->ack_number))
200         ;
201       /* Flags. */
202 #define _(f)                                                                  \
203   else if (unformat (input, #f)) pg_edit_set_fixed (&pth->f##_flag, 1);
204       foreach_tcp_flag
205 #undef _
206         /* Can't parse input: try TCP options and next protocol level. */
207         else break;
208     }
209
210   while (unformat (input, "opt"))
211     {
212       int i;
213       pg_edit_t *opt_header, *opt_values;
214       u8 type, opt_len, n_values;
215
216       /* first allocate a new edit group for options */
217       if (opt_group_index == ~0)
218         (void) pg_create_edit_group (s, 0, 0, &opt_group_index);
219
220       if (false)
221         {
222         }
223 #define _(n, t, l, k)                                                         \
224   else if (unformat (input, #n))                                              \
225   {                                                                           \
226     type = (t);                                                               \
227     opt_len = (l);                                                            \
228     n_values = (k);                                                           \
229   }
230       foreach_tcp_options
231 #undef _
232         else
233       {
234         /* unknown TCP option */
235         break;
236       }
237
238 #define pg_tcp_option_init(e, o, b)                                           \
239   do                                                                          \
240     {                                                                         \
241       *(o) += (b);                                                            \
242       (e)->lsb_bit_offset = *(o) > 0 ? (*(o) -1) * BITS (u8) : 0;             \
243       (e)->n_bits = (b) *BITS (u8);                                           \
244     }                                                                         \
245   while (0);
246
247       /* if we don't know how many values to read, just ask */
248       if (n_values == 0 &&
249           unformat (input, "nvalues %D", sizeof (n_values), &n_values))
250         {
251           switch (type)
252             {
253             case TCP_OPTION_SACK_BLOCK:
254               /* each sack block is composed of 2 32-bits values */
255               n_values *= 2;
256               /*
257                 opt_len contains the length of a single sack block,
258                 it needs to be updated to contains the final number of bytes
259                 for the sack options
260               */
261               opt_len = 2 + 2 * opt_len;
262               break;
263             default:
264               /* unknown variable options */
265               continue;
266             }
267         }
268
269       opt_header = pg_add_edits (s, sizeof (pg_edit_t) * (2 + n_values),
270                                  opt_len, opt_group_index);
271       pg_tcp_option_init (opt_header, &opts_len, 1);
272       pg_tcp_option_init (opt_header + 1, &opts_len, 1);
273       pg_edit_set_fixed (opt_header, type);
274       pg_edit_set_fixed (opt_header + 1, opt_len);
275       opt_values = opt_header + 2;
276
277       switch (type)
278         {
279         case TCP_OPTION_MSS:
280           pg_tcp_option_init (opt_values, &opts_len, 2);
281           break;
282         case TCP_OPTION_WINDOW_SCALE:
283           pg_tcp_option_init (opt_values, &opts_len, 1);
284           break;
285         case TCP_OPTION_TIMESTAMP:
286         case TCP_OPTION_SACK_BLOCK:
287           for (i = 0; i < n_values; ++i)
288             pg_tcp_option_init (opt_values + i, &opts_len, 4);
289           break;
290         default:
291           break;
292         }
293
294       for (i = 0; i < n_values; ++i)
295         {
296           if (!unformat (input, "%U", unformat_pg_edit, unformat_pg_number,
297                          opt_values + i))
298             goto error;
299         }
300     }
301
302   /* add TCP NO-OP options to fill options up to a 4-bytes boundary */
303   noop_len = (TCP_OPTS_ALIGN - opts_len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN;
304   if (noop_len > 0)
305     {
306       pg_edit_t *noop_edit;
307       u8 *noops = 0;
308
309       vec_validate (noops, noop_len - 1);
310       clib_memset (noops, 1, noop_len);
311
312       noop_edit =
313         pg_add_edits (s, sizeof (noop_edit[0]), noop_len, opt_group_index);
314       pg_tcp_option_init (noop_edit, &opts_len, noop_len);
315       noop_edit->type = PG_EDIT_FIXED;
316       noop_edit->values[PG_EDIT_LO] = noops;
317     }
318 #undef pg_tcp_option_init
319
320   /* set the data offset according to options */
321   pg_edit_set_fixed (&pth->data_offset_and_reserved,
322                      (sizeof (tcp_header_t) + opts_len) / sizeof (u32));
323
324   {
325     ip_main_t *im = &ip_main;
326     u16 dst_port;
327     tcp_udp_port_info_t *pi;
328
329     pi = 0;
330     if (pth->dst.type == PG_EDIT_FIXED)
331       {
332         dst_port = pg_edit_get_value (&pth->dst, PG_EDIT_LO);
333         pi = ip_get_tcp_udp_port_info (im, dst_port);
334       }
335
336     if (pi && pi->unformat_pg_edit &&
337         unformat_user (input, pi->unformat_pg_edit, s))
338       ;
339
340     else if (!unformat_user (input, unformat_pg_payload, s))
341       goto error;
342
343     if (pth->checksum.type == PG_EDIT_UNSPECIFIED)
344       {
345         pg_edit_group_t *g = pg_stream_get_group (s, header_group_index);
346         g->edit_function = tcp_pg_edit_function;
347         g->edit_function_opaque = 0;
348       }
349
350     return 1;
351   }
352
353 error:
354   /* Free up any edits we may have added. */
355   pg_free_edit_group (s);
356   return 0;
357 }
358
359 /*
360  * fd.io coding-style-patch-verification: ON
361  *
362  * Local Variables:
363  * eval: (c-set-style "gnu")
364  * End:
365  */