Line data Source code
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 0 : 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 0 : vlib_main_t *vm = vlib_get_main ();
67 : u32 ip_offset, tcp_offset;
68 :
69 0 : tcp_offset = g->start_byte_offset;
70 0 : ip_offset = (g - 1)->start_byte_offset;
71 :
72 0 : 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 0 : p0 = vlib_get_buffer (vm, packets[0]);
81 0 : n_packets -= 1;
82 0 : packets += 1;
83 :
84 0 : ASSERT (p0->current_data == 0);
85 0 : ip0 = (void *) (p0->data + ip_offset);
86 0 : tcp0 = (void *) (p0->data + tcp_offset);
87 : /* if IP length has been specified, then calculate the length based on buffer */
88 0 : if (ip0->length == 0)
89 0 : tcp_len0 = vlib_buffer_length_in_chain (vm, p0) - tcp_offset;
90 : else
91 0 : 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 0 : sum0 = clib_mem_unaligned (&ip0->src_address, u64);
103 :
104 0 : sum0 = ip_csum_with_carry
105 0 : (sum0, clib_host_to_net_u32 (tcp_len0 + (ip0->protocol << 16)));
106 :
107 : /* Invalidate possibly old checksum. */
108 0 : tcp0->checksum = 0;
109 :
110 : sum0 =
111 0 : ip_incremental_checksum_buffer (vm, p0, tcp_offset, tcp_len0, sum0);
112 :
113 0 : tcp0->checksum = ~ip_csum_fold (sum0);
114 : }
115 0 : }
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 0 : 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 0 : _(src);
136 0 : _(dst);
137 0 : _(seq_number);
138 0 : _(ack_number);
139 0 : _(window);
140 0 : _(checksum);
141 0 : _(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 0 : foreach_tcp_flag
151 : #undef _
152 0 : pg_edit_init_bitfield (&p->data_offset_and_reserved, tcp_header_t,
153 : data_offset_and_reserved, 4, 4);
154 0 : }
155 :
156 : uword
157 0 : unformat_pg_tcp_header (unformat_input_t * input, va_list * args)
158 : {
159 0 : pg_stream_t *s = va_arg (*args, pg_stream_t *);
160 : pg_tcp_header_t *pth;
161 0 : u32 header_group_index, opt_group_index = ~0, noop_len, opts_len = 0;
162 :
163 0 : pth = pg_create_edit_group (s, sizeof (pth[0]), sizeof (tcp_header_t),
164 : &header_group_index);
165 0 : pg_tcp_header_init (pth);
166 :
167 : /* Defaults. */
168 0 : pg_edit_set_fixed (&pth->seq_number, 0);
169 0 : pg_edit_set_fixed (&pth->ack_number, 0);
170 :
171 0 : pg_edit_set_fixed (&pth->window, 4096);
172 0 : pg_edit_set_fixed (&pth->urgent_pointer, 0);
173 :
174 : #define _(f) pg_edit_set_fixed (&pth->f##_flag, 0);
175 0 : foreach_tcp_flag
176 : #undef _
177 0 : pth->checksum.type = PG_EDIT_UNSPECIFIED;
178 :
179 0 : 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 0 : goto error;
183 :
184 : /* Parse options. */
185 : while (1)
186 : {
187 0 : if (unformat (input, "window %U", unformat_pg_edit, unformat_pg_number,
188 : &pth->window))
189 : ;
190 :
191 0 : else if (unformat (input, "checksum %U", unformat_pg_edit,
192 : unformat_pg_number, &pth->checksum))
193 : ;
194 :
195 0 : else if (unformat (input, "seqnum %U", unformat_pg_edit,
196 : unformat_pg_number, &pth->seq_number))
197 : ;
198 0 : 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 0 : foreach_tcp_flag
205 : #undef _
206 : /* Can't parse input: try TCP options and next protocol level. */
207 0 : else break;
208 : }
209 :
210 0 : 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 0 : if (opt_group_index == ~0)
218 0 : (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 0 : foreach_tcp_options
231 : #undef _
232 : else
233 : {
234 : /* unknown TCP option */
235 0 : 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 0 : if (n_values == 0 &&
249 0 : unformat (input, "nvalues %D", sizeof (n_values), &n_values))
250 : {
251 0 : switch (type)
252 : {
253 0 : case TCP_OPTION_SACK_BLOCK:
254 : /* each sack block is composed of 2 32-bits values */
255 0 : 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 0 : opt_len = 2 + 2 * opt_len;
262 0 : break;
263 0 : default:
264 : /* unknown variable options */
265 0 : continue;
266 : }
267 0 : }
268 :
269 0 : opt_header = pg_add_edits (s, sizeof (pg_edit_t) * (2 + n_values),
270 : opt_len, opt_group_index);
271 0 : pg_tcp_option_init (opt_header, &opts_len, 1);
272 0 : pg_tcp_option_init (opt_header + 1, &opts_len, 1);
273 0 : pg_edit_set_fixed (opt_header, type);
274 0 : pg_edit_set_fixed (opt_header + 1, opt_len);
275 0 : opt_values = opt_header + 2;
276 :
277 0 : switch (type)
278 : {
279 0 : case TCP_OPTION_MSS:
280 0 : pg_tcp_option_init (opt_values, &opts_len, 2);
281 0 : break;
282 0 : case TCP_OPTION_WINDOW_SCALE:
283 0 : pg_tcp_option_init (opt_values, &opts_len, 1);
284 0 : break;
285 0 : case TCP_OPTION_TIMESTAMP:
286 : case TCP_OPTION_SACK_BLOCK:
287 0 : for (i = 0; i < n_values; ++i)
288 0 : pg_tcp_option_init (opt_values + i, &opts_len, 4);
289 0 : break;
290 0 : default:
291 0 : break;
292 : }
293 :
294 0 : for (i = 0; i < n_values; ++i)
295 : {
296 0 : if (!unformat (input, "%U", unformat_pg_edit, unformat_pg_number,
297 0 : opt_values + i))
298 0 : goto error;
299 : }
300 : }
301 :
302 : /* add TCP NO-OP options to fill options up to a 4-bytes boundary */
303 0 : noop_len = (TCP_OPTS_ALIGN - opts_len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN;
304 0 : if (noop_len > 0)
305 : {
306 : pg_edit_t *noop_edit;
307 0 : u8 *noops = 0;
308 :
309 0 : vec_validate (noops, noop_len - 1);
310 0 : clib_memset (noops, 1, noop_len);
311 :
312 : noop_edit =
313 0 : pg_add_edits (s, sizeof (noop_edit[0]), noop_len, opt_group_index);
314 0 : pg_tcp_option_init (noop_edit, &opts_len, noop_len);
315 0 : noop_edit->type = PG_EDIT_FIXED;
316 0 : noop_edit->values[PG_EDIT_LO] = noops;
317 : }
318 : #undef pg_tcp_option_init
319 :
320 : /* set the data offset according to options */
321 0 : pg_edit_set_fixed (&pth->data_offset_and_reserved,
322 0 : (sizeof (tcp_header_t) + opts_len) / sizeof (u32));
323 :
324 : {
325 0 : ip_main_t *im = &ip_main;
326 : u16 dst_port;
327 : tcp_udp_port_info_t *pi;
328 :
329 0 : pi = 0;
330 0 : if (pth->dst.type == PG_EDIT_FIXED)
331 : {
332 0 : dst_port = pg_edit_get_value (&pth->dst, PG_EDIT_LO);
333 0 : pi = ip_get_tcp_udp_port_info (im, dst_port);
334 : }
335 :
336 0 : if (pi && pi->unformat_pg_edit &&
337 0 : unformat_user (input, pi->unformat_pg_edit, s))
338 : ;
339 :
340 0 : else if (!unformat_user (input, unformat_pg_payload, s))
341 0 : goto error;
342 :
343 0 : if (pth->checksum.type == PG_EDIT_UNSPECIFIED)
344 : {
345 0 : pg_edit_group_t *g = pg_stream_get_group (s, header_group_index);
346 0 : g->edit_function = tcp_pg_edit_function;
347 0 : g->edit_function_opaque = 0;
348 : }
349 :
350 0 : return 1;
351 : }
352 :
353 0 : error:
354 : /* Free up any edits we may have added. */
355 0 : pg_free_edit_group (s);
356 0 : 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 : */
|