blob: 55bc5764df2e48a436648192bd69e60626808df0 [file] [log] [blame]
Florin Coras999840c2020-03-18 20:31:34 +00001/*
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#include <vnet/tcp/tcp.h>
17#include <vnet/tcp/tcp_inlines.h>
18#include <vnet/dpo/receive_dpo.h>
19#include <vnet/ip-neighbor/ip_neighbor.h>
20
21const char *tcp_fsm_states[] = {
22#define _(sym, str) str,
23 foreach_tcp_fsm_state
24#undef _
25};
26
27u8 *
28format_tcp_state (u8 * s, va_list * args)
29{
30 u32 state = va_arg (*args, u32);
31
32 if (state < TCP_N_STATES)
33 s = format (s, "%s", tcp_fsm_states[state]);
34 else
35 s = format (s, "UNKNOWN (%d (0x%x))", state, state);
36 return s;
37}
38
39const char *tcp_cfg_flags_str[] = {
40#define _(sym, str) str,
41 foreach_tcp_cfg_flag
42#undef _
43};
44
45static u8 *
46format_tcp_cfg_flags (u8 * s, va_list * args)
47{
48 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
49 int i, last = -1;
50
51 for (i = 0; i < TCP_CFG_N_FLAG_BITS; i++)
52 if (tc->cfg_flags & (1 << i))
53 last = i;
54 for (i = 0; i < last; i++)
55 {
56 if (tc->cfg_flags & (1 << i))
57 s = format (s, "%s, ", tcp_cfg_flags_str[i]);
58 }
59 if (last >= 0)
60 s = format (s, "%s", tcp_cfg_flags_str[last]);
61 return s;
62}
63
64const char *tcp_connection_flags_str[] = {
65#define _(sym, str) str,
66 foreach_tcp_connection_flag
67#undef _
68};
69
70static u8 *
71format_tcp_connection_flags (u8 * s, va_list * args)
72{
73 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
74 int i, last = -1;
75
76 for (i = 0; i < TCP_CONN_N_FLAG_BITS; i++)
77 if (tc->flags & (1 << i))
78 last = i;
79 for (i = 0; i < last; i++)
80 {
81 if (tc->flags & (1 << i))
82 s = format (s, "%s, ", tcp_connection_flags_str[i]);
83 }
84 if (last >= 0)
85 s = format (s, "%s", tcp_connection_flags_str[last]);
86 return s;
87}
88
89const char *tcp_conn_timers[] = {
90#define _(sym, str) str,
91 foreach_tcp_timer
92#undef _
93};
94
95static u8 *
96format_tcp_timers (u8 * s, va_list * args)
97{
98 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
99 int i, last = -1;
100
101 for (i = 0; i < TCP_N_TIMERS; i++)
102 if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
103 last = i;
104
105 for (i = 0; i < last; i++)
106 {
107 if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
108 s = format (s, "%s,", tcp_conn_timers[i]);
109 }
110
111 if (last >= 0)
112 s = format (s, "%s", tcp_conn_timers[i]);
113
114 return s;
115}
116
117static u8 *
118format_tcp_congestion_status (u8 * s, va_list * args)
119{
120 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
121 if (tcp_in_recovery (tc))
122 s = format (s, "recovery");
123 else if (tcp_in_fastrecovery (tc))
124 s = format (s, "fastrecovery");
125 else
126 s = format (s, "none");
127 return s;
128}
129
130static i32
131tcp_rcv_wnd_available (tcp_connection_t * tc)
132{
133 return (i32) tc->rcv_wnd - (tc->rcv_nxt - tc->rcv_las);
134}
135
136static u8 *
137format_tcp_congestion (u8 * s, va_list * args)
138{
139 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
140 u32 indent = format_get_indent (s), prr_space = 0;
141
142 s = format (s, "%U ", format_tcp_congestion_status, tc);
143 s = format (s, "algo %s cwnd %u ssthresh %u bytes_acked %u\n",
144 tc->cc_algo->name, tc->cwnd, tc->ssthresh, tc->bytes_acked);
145 s = format (s, "%Ucc space %u prev_cwnd %u prev_ssthresh %u\n",
146 format_white_space, indent, tcp_available_cc_snd_space (tc),
147 tc->prev_cwnd, tc->prev_ssthresh);
148 s = format (s, "%Usnd_cong %u dupack %u limited_tx %u\n",
149 format_white_space, indent, tc->snd_congestion - tc->iss,
150 tc->rcv_dupacks, tc->limited_transmit - tc->iss);
151 s = format (s, "%Urxt_bytes %u rxt_delivered %u rxt_head %u rxt_ts %u\n",
Florin Coras8f10b902021-04-02 18:32:00 -0700152 format_white_space, indent, tc->snd_rxt_bytes, tc->rxt_delivered,
153 tc->rxt_head - tc->iss, tcp_tstamp (tc) - tc->snd_rxt_ts);
Florin Coras999840c2020-03-18 20:31:34 +0000154 if (tcp_in_fastrecovery (tc))
155 prr_space = tcp_fastrecovery_prr_snd_space (tc);
156 s = format (s, "%Uprr_start %u prr_delivered %u prr space %u\n",
157 format_white_space, indent, tc->prr_start - tc->iss,
158 tc->prr_delivered, prr_space);
159 return s;
160}
161
162static u8 *
163format_tcp_stats (u8 * s, va_list * args)
164{
165 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
166 u32 indent = format_get_indent (s);
167 s = format (s, "in segs %lu dsegs %lu bytes %lu dupacks %u\n",
168 tc->segs_in, tc->data_segs_in, tc->bytes_in, tc->dupacks_in);
169 s = format (s, "%Uout segs %lu dsegs %lu bytes %lu dupacks %u\n",
170 format_white_space, indent, tc->segs_out,
171 tc->data_segs_out, tc->bytes_out, tc->dupacks_out);
172 s = format (s, "%Ufr %u tr %u rxt segs %lu bytes %lu duration %.3f\n",
173 format_white_space, indent, tc->fr_occurences,
174 tc->tr_occurences, tc->segs_retrans, tc->bytes_retrans,
175 tcp_time_now_us (tc->c_thread_index) - tc->start_ts);
176 s = format (s, "%Uerr wnd data below %u above %u ack below %u above %u",
177 format_white_space, indent, tc->errors.below_data_wnd,
178 tc->errors.above_data_wnd, tc->errors.below_ack_wnd,
179 tc->errors.above_ack_wnd);
180 return s;
181}
182
183static u8 *
184format_tcp_vars (u8 * s, va_list * args)
185{
186 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
187 s = format (s, " index: %u cfg: %U flags: %U timers: %U\n", tc->c_c_index,
188 format_tcp_cfg_flags, tc, format_tcp_connection_flags, tc,
189 format_tcp_timers, tc);
Florin Coras55e556c2020-10-23 10:45:48 -0700190 s = format (s, " snd_una %u snd_nxt %u", tc->snd_una - tc->iss,
191 tc->snd_nxt - tc->iss);
Florin Coras999840c2020-03-18 20:31:34 +0000192 s = format (s, " rcv_nxt %u rcv_las %u\n",
193 tc->rcv_nxt - tc->irs, tc->rcv_las - tc->irs);
194 s = format (s, " snd_wnd %u rcv_wnd %u rcv_wscale %u ",
195 tc->snd_wnd, tc->rcv_wnd, tc->rcv_wscale);
196 s = format (s, "snd_wl1 %u snd_wl2 %u\n", tc->snd_wl1 - tc->irs,
197 tc->snd_wl2 - tc->iss);
198 s = format (s, " flight size %u out space %u rcv_wnd_av %u",
199 tcp_flight_size (tc), tcp_available_output_snd_space (tc),
200 tcp_rcv_wnd_available (tc));
201 s = format (s, " tsval_recent %u\n", tc->tsval_recent);
202 s = format (s, " tsecr %u tsecr_last_ack %u tsval_recent_age %u",
203 tc->rcv_opts.tsecr, tc->tsecr_last_ack,
Florin Coras8f10b902021-04-02 18:32:00 -0700204 tcp_time_tstamp (tc->c_thread_index) - tc->tsval_recent_age);
Florin Coras999840c2020-03-18 20:31:34 +0000205 s = format (s, " snd_mss %u\n", tc->snd_mss);
Florin Coraseedc74b2020-07-31 12:32:40 -0700206 s = format (s, " rto %u rto_boff %u srtt %.1f us %.3f rttvar %.1f",
207 tc->rto / 1000, tc->rto_boff, tc->srtt / 1000.0,
208 tc->mrtt_us * 1e3, tc->rttvar / 1000.0);
209 s = format (s, " rtt_ts %.4f rtt_seq %u\n", tc->rtt_ts,
210 tc->rtt_seq - tc->iss);
Florin Coras596c45b2021-09-09 12:04:17 -0700211 s = format (s, " next_node %u opaque 0x%x fib_index %u sw_if_index %d\n",
212 tc->next_node_index, tc->next_node_opaque, tc->c_fib_index,
213 tc->sw_if_index);
Florin Coras999840c2020-03-18 20:31:34 +0000214 s = format (s, " cong: %U", format_tcp_congestion, tc);
215
216 if (tc->state >= TCP_STATE_ESTABLISHED)
217 {
218 s = format (s, " sboard: %U\n", format_tcp_scoreboard, &tc->sack_sb,
219 tc);
220 s = format (s, " stats: %U\n", format_tcp_stats, tc);
221 }
222 if (vec_len (tc->snd_sacks))
223 s = format (s, " sacks tx: %U\n", format_tcp_sacks, tc);
224
225 return s;
226}
227
228u8 *
229format_tcp_connection_id (u8 * s, va_list * args)
230{
231 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
232 if (!tc)
233 return s;
234 if (tc->c_is_ip4)
235 {
236 s = format (s, "[%d:%d][%s] %U:%d->%U:%d", tc->c_thread_index,
237 tc->c_s_index, "T", format_ip4_address, &tc->c_lcl_ip4,
238 clib_net_to_host_u16 (tc->c_lcl_port), format_ip4_address,
239 &tc->c_rmt_ip4, clib_net_to_host_u16 (tc->c_rmt_port));
240 }
241 else
242 {
243 s = format (s, "[%d:%d][%s] %U:%d->%U:%d", tc->c_thread_index,
244 tc->c_s_index, "T", format_ip6_address, &tc->c_lcl_ip6,
245 clib_net_to_host_u16 (tc->c_lcl_port), format_ip6_address,
246 &tc->c_rmt_ip6, clib_net_to_host_u16 (tc->c_rmt_port));
247 }
248
249 return s;
250}
251
252u8 *
Aritra Basue30f7122024-07-26 09:36:55 -0700253format_tcp_listener_connection (u8 *s, va_list *args)
254{
255 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
256
257 s = format (s, " index: %u cfg_flags: %U cong_algo: %s snd_mss: %u\n",
258 tc->c_c_index, format_tcp_cfg_flags, tc, tc->cc_algo->name,
259 tc->snd_mss);
260 s = format (s, " next_node %u opaque 0x%x fib_index %u sw_if_index %d",
261 tc->next_node_index, tc->next_node_opaque, tc->c_fib_index,
262 tc->sw_if_index);
263
264 return s;
265}
266
267u8 *
Florin Coras999840c2020-03-18 20:31:34 +0000268format_tcp_connection (u8 * s, va_list * args)
269{
270 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
271 u32 verbose = va_arg (*args, u32);
272
273 if (!tc)
274 return s;
Florin Coras7bf6ed62020-09-23 12:02:08 -0700275 s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_tcp_connection_id, tc);
Florin Coras999840c2020-03-18 20:31:34 +0000276 if (verbose)
277 {
Florin Coras7bf6ed62020-09-23 12:02:08 -0700278 s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_tcp_state,
279 tc->state);
Florin Coras999840c2020-03-18 20:31:34 +0000280 if (verbose > 1)
281 s = format (s, "\n%U", format_tcp_vars, tc);
282 }
283
284 return s;
285}
286
287u8 *
288format_tcp_sacks (u8 * s, va_list * args)
289{
290 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
291 sack_block_t *sacks = tc->snd_sacks;
292 sack_block_t *block;
293 int i, len = 0;
294
295 len = vec_len (sacks);
296 for (i = 0; i < len - 1; i++)
297 {
298 block = &sacks[i];
299 s = format (s, " start %u end %u\n", block->start - tc->irs,
300 block->end - tc->irs);
301 }
302 if (len)
303 {
304 block = &sacks[len - 1];
305 s = format (s, " start %u end %u", block->start - tc->irs,
306 block->end - tc->irs);
307 }
308 return s;
309}
310
311u8 *
312format_tcp_rcv_sacks (u8 * s, va_list * args)
313{
314 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
315 sack_block_t *sacks = tc->rcv_opts.sacks;
316 sack_block_t *block;
317 int i, len = 0;
318
319 len = vec_len (sacks);
320 for (i = 0; i < len - 1; i++)
321 {
322 block = &sacks[i];
323 s = format (s, " start %u end %u\n", block->start - tc->iss,
324 block->end - tc->iss);
325 }
326 if (len)
327 {
328 block = &sacks[len - 1];
329 s = format (s, " start %u end %u", block->start - tc->iss,
330 block->end - tc->iss);
331 }
332 return s;
333}
334
335static u8 *
336format_tcp_sack_hole (u8 * s, va_list * args)
337{
338 sack_scoreboard_hole_t *hole = va_arg (*args, sack_scoreboard_hole_t *);
339 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
340 if (tc)
341 s = format (s, " [%u, %u]", hole->start - tc->iss, hole->end - tc->iss);
342 else
343 s = format (s, " [%u, %u]", hole->start, hole->end);
344 return s;
345}
346
347u8 *
348format_tcp_scoreboard (u8 * s, va_list * args)
349{
350 sack_scoreboard_t *sb = va_arg (*args, sack_scoreboard_t *);
351 tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
352 sack_scoreboard_hole_t *hole;
353 u32 indent = format_get_indent (s);
354
355 s = format (s, "sacked %u last_sacked %u lost %u last_lost %u"
356 " rxt_sacked %u\n",
357 sb->sacked_bytes, sb->last_sacked_bytes, sb->lost_bytes,
358 sb->last_lost_bytes, sb->rxt_sacked);
Florin Corascc4d6d02020-07-29 23:03:39 -0700359 s = format (s, "%Ulast_delivered %u high_sacked %u is_reneging %u",
Florin Coras999840c2020-03-18 20:31:34 +0000360 format_white_space, indent, sb->last_bytes_delivered,
361 sb->high_sacked - tc->iss, sb->is_reneging);
Florin Corascc4d6d02020-07-29 23:03:39 -0700362 s = format (s, " reorder %u\n", sb->reorder);
Florin Coras999840c2020-03-18 20:31:34 +0000363 s = format (s, "%Ucur_rxt_hole %u high_rxt %u rescue_rxt %u",
364 format_white_space, indent, sb->cur_rxt_hole,
365 sb->high_rxt - tc->iss, sb->rescue_rxt - tc->iss);
366
367 hole = scoreboard_first_hole (sb);
368 if (hole)
369 s = format (s, "\n%Uhead %u tail %u %u holes:\n%U", format_white_space,
370 indent, sb->head, sb->tail, pool_elts (sb->holes),
371 format_white_space, indent);
372
373 while (hole)
374 {
375 s = format (s, "%U", format_tcp_sack_hole, hole, tc);
376 hole = scoreboard_next_hole (sb, hole);
377 }
378
379 return s;
380}
381
382/**
383 * \brief Configure an ipv4 source address range
384 * @param vm vlib_main_t pointer
385 * @param start first ipv4 address in the source address range
386 * @param end last ipv4 address in the source address range
387 * @param table_id VRF / table ID, 0 for the default FIB
388 * @return 0 if all OK, else an error indication from api_errno.h
389 */
390
391int
392tcp_configure_v4_source_address_range (vlib_main_t * vm,
393 ip4_address_t * start,
394 ip4_address_t * end, u32 table_id)
395{
396 u32 start_host_byte_order, end_host_byte_order;
397 fib_prefix_t prefix;
398 fib_node_index_t fei;
399 u32 fib_index = 0;
400 u32 sw_if_index;
401 int rv;
402
403 clib_memset (&prefix, 0, sizeof (prefix));
404
405 fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
406
407 if (fib_index == ~0)
408 return VNET_API_ERROR_NO_SUCH_FIB;
409
410 start_host_byte_order = clib_net_to_host_u32 (start->as_u32);
411 end_host_byte_order = clib_net_to_host_u32 (end->as_u32);
412
413 /* sanity check for reversed args or some such */
414 if ((end_host_byte_order - start_host_byte_order) > (10 << 10))
415 return VNET_API_ERROR_INVALID_ARGUMENT;
416
417 /* Lookup the last address, to identify the interface involved */
418 prefix.fp_len = 32;
419 prefix.fp_proto = FIB_PROTOCOL_IP4;
420 memcpy (&prefix.fp_addr.ip4, end, sizeof (ip4_address_t));
421
422 fei = fib_table_lookup (fib_index, &prefix);
423
424 /* Couldn't find route to destination. Bail out. */
425 if (fei == FIB_NODE_INDEX_INVALID)
426 return VNET_API_ERROR_NEXT_HOP_NOT_IN_FIB;
427
428 sw_if_index = fib_entry_get_resolving_interface (fei);
Mercury8ade4a52021-12-11 15:58:32 +0800429 if (sw_if_index == (u32) ~0)
430 return VNET_API_ERROR_NO_MATCHING_INTERFACE;
Florin Coras999840c2020-03-18 20:31:34 +0000431
432 /* Configure proxy arp across the range */
433 rv = ip4_neighbor_proxy_add (fib_index, start, end);
434
435 if (rv)
436 return rv;
437
438 rv = ip4_neighbor_proxy_enable (sw_if_index);
439
440 if (rv)
441 return rv;
442
443 do
444 {
445 dpo_id_t dpo = DPO_INVALID;
446
447 vec_add1 (tcp_cfg.ip4_src_addrs, start[0]);
448
449 /* Add local adjacencies for the range */
450
Mercury89618da2021-12-24 11:58:27 +0800451 receive_dpo_add_or_lock (DPO_PROTO_IP4, sw_if_index /* sw_if_index */,
Florin Coras999840c2020-03-18 20:31:34 +0000452 NULL, &dpo);
453 prefix.fp_len = 32;
454 prefix.fp_proto = FIB_PROTOCOL_IP4;
455 prefix.fp_addr.ip4.as_u32 = start->as_u32;
456
457 fib_table_entry_special_dpo_update (fib_index,
458 &prefix,
459 FIB_SOURCE_API,
460 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
461 dpo_reset (&dpo);
462
463 start_host_byte_order++;
464 start->as_u32 = clib_host_to_net_u32 (start_host_byte_order);
465 }
466 while (start_host_byte_order <= end_host_byte_order);
467
468 return 0;
469}
470
471/**
472 * \brief Configure an ipv6 source address range
473 * @param vm vlib_main_t pointer
474 * @param start first ipv6 address in the source address range
475 * @param end last ipv6 address in the source address range
476 * @param table_id VRF / table ID, 0 for the default FIB
477 * @return 0 if all OK, else an error indication from api_errno.h
478 */
479
480int
481tcp_configure_v6_source_address_range (vlib_main_t * vm,
482 ip6_address_t * start,
483 ip6_address_t * end, u32 table_id)
484{
485 fib_prefix_t prefix;
486 u32 fib_index = 0;
487 fib_node_index_t fei;
488 u32 sw_if_index;
489
490 clib_memset (&prefix, 0, sizeof (prefix));
491
492 fib_index = fib_table_find (FIB_PROTOCOL_IP6, table_id);
493
494 if (fib_index == ~0)
495 return VNET_API_ERROR_NO_SUCH_FIB;
496
497 while (1)
498 {
499 int i;
500 ip6_address_t tmp;
501 dpo_id_t dpo = DPO_INVALID;
502
503 /* Remember this address */
504 vec_add1 (tcp_cfg.ip6_src_addrs, start[0]);
505
506 /* Lookup the prefix, to identify the interface involved */
507 prefix.fp_len = 128;
508 prefix.fp_proto = FIB_PROTOCOL_IP6;
509 memcpy (&prefix.fp_addr.ip6, start, sizeof (ip6_address_t));
510
511 fei = fib_table_lookup (fib_index, &prefix);
512
513 /* Couldn't find route to destination. Bail out. */
514 if (fei == FIB_NODE_INDEX_INVALID)
515 return VNET_API_ERROR_NEXT_HOP_NOT_IN_FIB;
516
517 sw_if_index = fib_entry_get_resolving_interface (fei);
518
519 if (sw_if_index == (u32) ~ 0)
520 return VNET_API_ERROR_NO_MATCHING_INTERFACE;
521
522 /* Add a proxy neighbor discovery entry for this address */
523 ip6_neighbor_proxy_add (sw_if_index, start);
524
525 /* Add a receive adjacency for this address */
Mercury89618da2021-12-24 11:58:27 +0800526 receive_dpo_add_or_lock (DPO_PROTO_IP6, sw_if_index /* sw_if_index */,
Florin Coras999840c2020-03-18 20:31:34 +0000527 NULL, &dpo);
528
529 fib_table_entry_special_dpo_update (fib_index,
530 &prefix,
531 FIB_SOURCE_API,
532 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
533 dpo_reset (&dpo);
534
535 /* Done with the entire range? */
536 if (!memcmp (start, end, sizeof (start[0])))
537 break;
538
539 /* Increment the address. DGMS. */
540 tmp = start[0];
541 for (i = 15; i >= 0; i--)
542 {
543 tmp.as_u8[i] += 1;
544 if (tmp.as_u8[i] != 0)
545 break;
546 }
547 start[0] = tmp;
548 }
549 return 0;
550}
551
552static clib_error_t *
553tcp_src_address_fn (vlib_main_t * vm,
554 unformat_input_t * input, vlib_cli_command_t * cmd_arg)
555{
556 ip4_address_t v4start, v4end;
557 ip6_address_t v6start, v6end;
558 u32 table_id = 0;
559 int v4set = 0;
560 int v6set = 0;
561 int rv;
562
563 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
564 {
565 if (unformat (input, "%U - %U", unformat_ip4_address, &v4start,
566 unformat_ip4_address, &v4end))
567 v4set = 1;
568 else if (unformat (input, "%U", unformat_ip4_address, &v4start))
569 {
570 memcpy (&v4end, &v4start, sizeof (v4start));
571 v4set = 1;
572 }
573 else if (unformat (input, "%U - %U", unformat_ip6_address, &v6start,
574 unformat_ip6_address, &v6end))
575 v6set = 1;
576 else if (unformat (input, "%U", unformat_ip6_address, &v6start))
577 {
578 memcpy (&v6end, &v6start, sizeof (v6start));
579 v6set = 1;
580 }
581 else if (unformat (input, "fib-table %d", &table_id))
582 ;
583 else
584 break;
585 }
586
587 if (!v4set && !v6set)
588 return clib_error_return (0, "at least one v4 or v6 address required");
589
590 if (v4set)
591 {
592 rv = tcp_configure_v4_source_address_range (vm, &v4start, &v4end,
593 table_id);
594 switch (rv)
595 {
596 case 0:
597 break;
598
599 case VNET_API_ERROR_NO_SUCH_FIB:
600 return clib_error_return (0, "Invalid table-id %d", table_id);
601
602 case VNET_API_ERROR_INVALID_ARGUMENT:
603 return clib_error_return (0, "Invalid address range %U - %U",
604 format_ip4_address, &v4start,
605 format_ip4_address, &v4end);
606 default:
607 return clib_error_return (0, "error %d", rv);
608 break;
609 }
610 }
611 if (v6set)
612 {
613 rv = tcp_configure_v6_source_address_range (vm, &v6start, &v6end,
614 table_id);
615 switch (rv)
616 {
617 case 0:
618 break;
619
620 case VNET_API_ERROR_NO_SUCH_FIB:
621 return clib_error_return (0, "Invalid table-id %d", table_id);
622
623 default:
624 return clib_error_return (0, "error %d", rv);
625 break;
626 }
627 }
628 return 0;
629}
630
Florin Coras999840c2020-03-18 20:31:34 +0000631VLIB_CLI_COMMAND (tcp_src_address_command, static) =
632{
633 .path = "tcp src-address",
634 .short_help = "tcp src-address <ip-addr> [- <ip-addr>] add src address range",
635 .function = tcp_src_address_fn,
636};
Florin Coras999840c2020-03-18 20:31:34 +0000637
638static u8 *
639tcp_scoreboard_dump_trace (u8 * s, sack_scoreboard_t * sb)
640{
641#if TCP_SCOREBOARD_TRACE
642
643 scoreboard_trace_elt_t *block;
644 int i = 0;
645
646 if (!sb->trace)
647 return s;
648
649 s = format (s, "scoreboard trace:");
650 vec_foreach (block, sb->trace)
651 {
652 s = format (s, "{%u, %u, %u, %u, %u}, ", block->start, block->end,
653 block->ack, block->snd_una_max, block->group);
654 if ((++i % 3) == 0)
655 s = format (s, "\n");
656 }
657 return s;
658#else
659 return 0;
660#endif
661}
662
663static clib_error_t *
664tcp_show_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
665 vlib_cli_command_t * cmd_arg)
666{
667 transport_connection_t *tconn = 0;
668 tcp_connection_t *tc;
669 u8 *s = 0;
670 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
671 {
672 if (unformat (input, "%U", unformat_transport_connection, &tconn,
673 TRANSPORT_PROTO_TCP))
674 ;
675 else
676 return clib_error_return (0, "unknown input `%U'",
677 format_unformat_error, input);
678 }
679
680 if (!TCP_SCOREBOARD_TRACE)
681 {
682 vlib_cli_output (vm, "scoreboard tracing not enabled");
683 return 0;
684 }
685
686 tc = tcp_get_connection_from_transport (tconn);
687 s = tcp_scoreboard_dump_trace (s, &tc->sack_sb);
688 vlib_cli_output (vm, "%v", s);
689 return 0;
690}
691
Florin Coras999840c2020-03-18 20:31:34 +0000692VLIB_CLI_COMMAND (tcp_show_scoreboard_trace_command, static) =
693{
694 .path = "show tcp scoreboard trace",
695 .short_help = "show tcp scoreboard trace <connection>",
696 .function = tcp_show_scoreboard_trace_fn,
697};
Florin Coras999840c2020-03-18 20:31:34 +0000698
699u8 *
700tcp_scoreboard_replay (u8 * s, tcp_connection_t * tc, u8 verbose)
701{
702 int i, trace_len;
703 scoreboard_trace_elt_t *trace;
704 u32 next_ack, left, group, has_new_ack = 0;
Dave Barach11fb09e2020-08-06 12:10:09 -0400705 tcp_connection_t _placeholder_tc, *placeholder_tc = &_placeholder_tc;
Florin Coras999840c2020-03-18 20:31:34 +0000706 sack_block_t *block;
707
708 if (!TCP_SCOREBOARD_TRACE)
709 {
710 s = format (s, "scoreboard tracing not enabled");
711 return s;
712 }
713
714 if (!tc)
715 return s;
716
Dave Barach11fb09e2020-08-06 12:10:09 -0400717 clib_memset (placeholder_tc, 0, sizeof (*placeholder_tc));
718 tcp_connection_timers_init (placeholder_tc);
719 scoreboard_init (&placeholder_tc->sack_sb);
720 placeholder_tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK;
Florin Coras999840c2020-03-18 20:31:34 +0000721
722#if TCP_SCOREBOARD_TRACE
723 trace = tc->sack_sb.trace;
724 trace_len = vec_len (tc->sack_sb.trace);
725#endif
726
727 for (i = 0; i < trace_len; i++)
728 {
729 if (trace[i].ack != 0)
730 {
Dave Barach11fb09e2020-08-06 12:10:09 -0400731 placeholder_tc->snd_una = trace[i].ack - 1448;
Florin Coras55e556c2020-10-23 10:45:48 -0700732 placeholder_tc->snd_nxt = trace[i].ack;
Florin Coras999840c2020-03-18 20:31:34 +0000733 }
734 }
735
736 left = 0;
737 while (left < trace_len)
738 {
739 group = trace[left].group;
Dave Barach11fb09e2020-08-06 12:10:09 -0400740 vec_reset_length (placeholder_tc->rcv_opts.sacks);
Florin Coras999840c2020-03-18 20:31:34 +0000741 has_new_ack = 0;
742 while (trace[left].group == group)
743 {
744 if (trace[left].ack != 0)
745 {
746 if (verbose)
747 s = format (s, "Adding ack %u, snd_una_max %u, segs: ",
Florin Coras55e556c2020-10-23 10:45:48 -0700748 trace[left].ack, trace[left].snd_nxt);
749 placeholder_tc->snd_nxt = trace[left].snd_nxt;
Florin Coras999840c2020-03-18 20:31:34 +0000750 next_ack = trace[left].ack;
751 has_new_ack = 1;
752 }
753 else
754 {
755 if (verbose)
756 s = format (s, "[%u, %u], ", trace[left].start,
757 trace[left].end);
Dave Barach11fb09e2020-08-06 12:10:09 -0400758 vec_add2 (placeholder_tc->rcv_opts.sacks, block, 1);
Florin Coras999840c2020-03-18 20:31:34 +0000759 block->start = trace[left].start;
760 block->end = trace[left].end;
761 }
762 left++;
763 }
764
765 /* Push segments */
Dave Barach11fb09e2020-08-06 12:10:09 -0400766 tcp_rcv_sacks (placeholder_tc, next_ack);
Florin Coras999840c2020-03-18 20:31:34 +0000767 if (has_new_ack)
Dave Barach11fb09e2020-08-06 12:10:09 -0400768 placeholder_tc->snd_una = next_ack;
Florin Coras999840c2020-03-18 20:31:34 +0000769
770 if (verbose)
771 s = format (s, "result: %U", format_tcp_scoreboard,
Dave Barach11fb09e2020-08-06 12:10:09 -0400772 &placeholder_tc->sack_sb);
Florin Coras999840c2020-03-18 20:31:34 +0000773
774 }
Dave Barach11fb09e2020-08-06 12:10:09 -0400775 s =
776 format (s, "result: %U", format_tcp_scoreboard, &placeholder_tc->sack_sb);
Florin Coras999840c2020-03-18 20:31:34 +0000777
778 return s;
779}
780
781static clib_error_t *
782tcp_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
783 vlib_cli_command_t * cmd_arg)
784{
785 transport_connection_t *tconn = 0;
786 tcp_connection_t *tc = 0;
787 u8 *str = 0;
788 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
789 {
790 if (unformat (input, "%U", unformat_transport_connection, &tconn,
791 TRANSPORT_PROTO_TCP))
792 ;
793 else
794 return clib_error_return (0, "unknown input `%U'",
795 format_unformat_error, input);
796 }
797
798 if (!TCP_SCOREBOARD_TRACE)
799 {
800 vlib_cli_output (vm, "scoreboard tracing not enabled");
801 return 0;
802 }
803
804 tc = tcp_get_connection_from_transport (tconn);
805 if (!tc)
806 {
807 vlib_cli_output (vm, "connection not found");
808 return 0;
809 }
810 str = tcp_scoreboard_replay (str, tc, 1);
811 vlib_cli_output (vm, "%v", str);
812 return 0;
813}
814
Florin Coras999840c2020-03-18 20:31:34 +0000815VLIB_CLI_COMMAND (tcp_replay_scoreboard_command, static) =
816{
817 .path = "tcp replay scoreboard",
818 .short_help = "tcp replay scoreboard <connection>",
819 .function = tcp_scoreboard_trace_fn,
820};
Florin Coras999840c2020-03-18 20:31:34 +0000821
822static clib_error_t *
823show_tcp_punt_fn (vlib_main_t * vm, unformat_input_t * input,
824 vlib_cli_command_t * cmd_arg)
825{
826 tcp_main_t *tm = vnet_get_tcp_main ();
827 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
828 return clib_error_return (0, "unknown input `%U'", format_unformat_error,
829 input);
830 vlib_cli_output (vm, "IPv4 TCP punt: %s",
831 tm->punt_unknown4 ? "enabled" : "disabled");
832 vlib_cli_output (vm, "IPv6 TCP punt: %s",
833 tm->punt_unknown6 ? "enabled" : "disabled");
834 return 0;
835}
Florin Coras999840c2020-03-18 20:31:34 +0000836VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
837{
838 .path = "show tcp punt",
839 .short_help = "show tcp punt",
840 .function = show_tcp_punt_fn,
841};
Florin Coras999840c2020-03-18 20:31:34 +0000842
Aritra Basudd4356d2024-07-24 10:11:00 -0700843static u8 *
844format_tcp_cfg (u8 *s, va_list *args)
845{
846 tcp_configuration_t tm_cfg = va_arg (*args, tcp_configuration_t);
847
848 s = format (s, "max rx fifo size: %U\n", format_memory_size,
849 tm_cfg.max_rx_fifo);
850 s = format (s, "min rx fifo size: %U\n", format_memory_size,
851 tm_cfg.min_rx_fifo);
852 s = format (s, "default mtu: %u\n", tm_cfg.default_mtu);
853 s = format (s, "initial cwnd multiplier: %u\n",
854 tm_cfg.initial_cwnd_multiplier);
Aritra Basu4a65e012024-07-29 10:57:25 -0700855 s = format (s, "tx pacing: %s\n",
856 tm_cfg.enable_tx_pacing ? "enabled" : "disabled");
Aritra Basudd4356d2024-07-24 10:11:00 -0700857 s = format (s, "tso: %s\n", tm_cfg.allow_tso ? "allowed" : "disallowed");
858 s = format (s, "checksum offload: %s\n",
859 tm_cfg.csum_offload ? "enabled" : "disabled");
860 s = format (s, "congestion control algorithm: %s\n",
861 tcp_cc_algo_get (tm_cfg.cc_algo)->name);
862 s = format (s, "min rwnd update ack: %u\n", tm_cfg.rwnd_min_update_ack);
863 s = format (s, "max gso packet size: %U\n", format_memory_size,
864 tm_cfg.max_gso_size);
865 s = format (s, "close_wait time: %u sec\n",
866 (u32) (tm_cfg.closewait_time * TCP_TIMER_TICK));
867 s = format (s, "time_wait time: %u sec\n",
868 (u32) (tm_cfg.timewait_time * TCP_TIMER_TICK));
869 s = format (s, "fin_wait1 time: %u sec\n",
870 (u32) (tm_cfg.finwait1_time * TCP_TIMER_TICK));
871 s = format (s, "fin_wait2 time: %u sec\n",
872 (u32) (tm_cfg.finwait2_time * TCP_TIMER_TICK));
873 s = format (s, "last_ack time: %u sec\n",
874 (u32) (tm_cfg.lastack_time * TCP_TIMER_TICK));
875 s = format (s, "fin_ack time: %u sec\n",
876 (u32) (tm_cfg.closing_time * TCP_TIMER_TICK));
877 s = format (s, "syn_rcvd time: %u sec\n",
878 (u32) (tm_cfg.syn_rcvd_time * TCP_TICK));
879 s = format (s, "tcp allocation error cleanup time: %0.2f sec\n",
880 (f32) (tm_cfg.alloc_err_timeout * TCP_TIMER_TICK));
881 s = format (s, "connection cleanup time: %.2f sec\n", tm_cfg.cleanup_time);
882 s = format (s, "tcp preallocated connections: %u",
883 tm_cfg.preallocated_connections);
884
885 return s;
886}
887
888static clib_error_t *
889show_tcp_cfg_fn (vlib_main_t *vm, unformat_input_t *input,
890 vlib_cli_command_t *cmd)
891{
892 tcp_main_t *tm = vnet_get_tcp_main ();
893
894 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
895 return clib_error_return (0, "unknown input `%U'", format_unformat_error,
896 input);
897 vlib_cli_output (vm, "-----------");
898 vlib_cli_output (vm, "tcp config");
899 vlib_cli_output (vm, "-----------");
900 vlib_cli_output (vm, "%U\n", format_tcp_cfg, tm->cfg);
901
902 return 0;
903}
904
905VLIB_CLI_COMMAND (show_tcp_cfg_command, static) = {
906 .path = "show tcp config",
907 .short_help = "show tcp config",
908 .function = show_tcp_cfg_fn,
909};
910
Florin Coras999840c2020-03-18 20:31:34 +0000911static clib_error_t *
912show_tcp_stats_fn (vlib_main_t * vm, unformat_input_t * input,
913 vlib_cli_command_t * cmd)
914{
915 tcp_main_t *tm = vnet_get_tcp_main ();
916 tcp_worker_ctx_t *wrk;
917 u32 thread;
918
919 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
920 return clib_error_return (0, "unknown input `%U'", format_unformat_error,
921 input);
922 for (thread = 0; thread < vec_len (tm->wrk_ctx); thread++)
923 {
924 wrk = tcp_get_worker (thread);
925 vlib_cli_output (vm, "Thread %u:\n", thread);
926
927 if (clib_fifo_elts (wrk->pending_timers))
928 vlib_cli_output (vm, " %lu pending timers",
929 clib_fifo_elts (wrk->pending_timers));
930
931#define _(name,type,str) \
932 if (wrk->stats.name) \
933 vlib_cli_output (vm, " %lu %s", wrk->stats.name, str);
934 foreach_tcp_wrk_stat
935#undef _
936 }
937
938 return 0;
939}
940
Florin Coras999840c2020-03-18 20:31:34 +0000941VLIB_CLI_COMMAND (show_tcp_stats_command, static) =
942{
943 .path = "show tcp stats",
944 .short_help = "show tcp stats",
945 .function = show_tcp_stats_fn,
946};
Florin Coras999840c2020-03-18 20:31:34 +0000947
948static clib_error_t *
949clear_tcp_stats_fn (vlib_main_t * vm, unformat_input_t * input,
950 vlib_cli_command_t * cmd)
951{
952 tcp_main_t *tm = vnet_get_tcp_main ();
953 tcp_worker_ctx_t *wrk;
954 u32 thread;
955
956 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
957 return clib_error_return (0, "unknown input `%U'", format_unformat_error,
958 input);
959
960 for (thread = 0; thread < vec_len (tm->wrk_ctx); thread++)
961 {
962 wrk = tcp_get_worker (thread);
963 clib_memset (&wrk->stats, 0, sizeof (wrk->stats));
964 }
965
966 return 0;
967}
968
Florin Coras999840c2020-03-18 20:31:34 +0000969VLIB_CLI_COMMAND (clear_tcp_stats_command, static) =
970{
971 .path = "clear tcp stats",
972 .short_help = "clear tcp stats",
973 .function = clear_tcp_stats_fn,
974};
Florin Coras999840c2020-03-18 20:31:34 +0000975
976uword
977unformat_tcp_cc_algo (unformat_input_t * input, va_list * va)
978{
979 tcp_cc_algorithm_type_e *result = va_arg (*va, tcp_cc_algorithm_type_e *);
980 tcp_main_t *tm = &tcp_main;
981 char *cc_algo_name;
982 u8 found = 0;
983 uword *p;
984
985 if (unformat (input, "%s", &cc_algo_name)
986 && ((p = hash_get_mem (tm->cc_algo_by_name, cc_algo_name))))
987 {
988 *result = *p;
989 found = 1;
990 }
991
992 vec_free (cc_algo_name);
993 return found;
994}
995
996uword
997unformat_tcp_cc_algo_cfg (unformat_input_t * input, va_list * va)
998{
999 tcp_main_t *tm = vnet_get_tcp_main ();
1000 tcp_cc_algorithm_t *cc_alg;
1001 unformat_input_t sub_input;
1002 int found = 0;
1003
1004 vec_foreach (cc_alg, tm->cc_algos)
1005 {
1006 if (!unformat (input, cc_alg->name))
1007 continue;
1008
1009 if (cc_alg->unformat_cfg
1010 && unformat (input, "%U", unformat_vlib_cli_sub_input, &sub_input))
1011 {
1012 if (cc_alg->unformat_cfg (&sub_input))
1013 found = 1;
1014 }
1015 }
1016 return found;
1017}
1018
1019static clib_error_t *
1020tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
1021{
Florin Coras57b2e4a2021-07-06 08:25:36 -07001022 u32 cwnd_multiplier, tmp_time, mtu, max_gso_size, tmp;
Florin Coras999840c2020-03-18 20:31:34 +00001023 uword memory_size;
1024
1025 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1026 {
1027 if (unformat (input, "preallocated-connections %d",
1028 &tcp_cfg.preallocated_connections))
1029 ;
Florin Coras57b2e4a2021-07-06 08:25:36 -07001030 /* Config deprecated. Will be removed in a later release */
1031 else if (unformat (input, "preallocated-half-open-connections %d", &tmp))
Florin Coras999840c2020-03-18 20:31:34 +00001032 ;
1033 else if (unformat (input, "buffer-fail-fraction %f",
1034 &tcp_cfg.buffer_fail_fraction))
1035 ;
1036 else if (unformat (input, "max-rx-fifo %U", unformat_memory_size,
1037 &memory_size))
1038 {
1039 if (memory_size >= 0x100000000)
1040 {
1041 return clib_error_return
1042 (0, "max-rx-fifo %llu (0x%llx) too large", memory_size,
1043 memory_size);
1044 }
1045 tcp_cfg.max_rx_fifo = memory_size;
1046 }
1047 else if (unformat (input, "min-rx-fifo %U", unformat_memory_size,
1048 &memory_size))
1049 {
1050 if (memory_size >= 0x100000000)
1051 {
1052 return clib_error_return
1053 (0, "min-rx-fifo %llu (0x%llx) too large", memory_size,
1054 memory_size);
1055 }
1056 tcp_cfg.min_rx_fifo = memory_size;
1057 }
Florin Corase10d1672020-04-07 04:14:45 +00001058 else if (unformat (input, "mtu %u", &mtu))
1059 tcp_cfg.default_mtu = mtu;
Florin Coras999840c2020-03-18 20:31:34 +00001060 else if (unformat (input, "rwnd-min-update-ack %d",
1061 &tcp_cfg.rwnd_min_update_ack))
1062 ;
1063 else if (unformat (input, "initial-cwnd-multiplier %u",
1064 &cwnd_multiplier))
1065 tcp_cfg.initial_cwnd_multiplier = cwnd_multiplier;
1066 else if (unformat (input, "no-tx-pacing"))
1067 tcp_cfg.enable_tx_pacing = 0;
1068 else if (unformat (input, "tso"))
1069 tcp_cfg.allow_tso = 1;
1070 else if (unformat (input, "no-csum-offload"))
1071 tcp_cfg.csum_offload = 0;
Simon Zhang23c3d342020-09-15 23:40:28 +08001072 else if (unformat (input, "max-gso-size %u", &max_gso_size))
1073 tcp_cfg.max_gso_size = clib_min (max_gso_size, TCP_MAX_GSO_SZ);
Florin Coras999840c2020-03-18 20:31:34 +00001074 else if (unformat (input, "cc-algo %U", unformat_tcp_cc_algo,
1075 &tcp_cfg.cc_algo))
1076 ;
1077 else if (unformat (input, "%U", unformat_tcp_cc_algo_cfg))
1078 ;
1079 else if (unformat (input, "closewait-time %u", &tmp_time))
1080 tcp_cfg.closewait_time = tmp_time / TCP_TIMER_TICK;
1081 else if (unformat (input, "timewait-time %u", &tmp_time))
1082 tcp_cfg.timewait_time = tmp_time / TCP_TIMER_TICK;
1083 else if (unformat (input, "finwait1-time %u", &tmp_time))
1084 tcp_cfg.finwait1_time = tmp_time / TCP_TIMER_TICK;
1085 else if (unformat (input, "finwait2-time %u", &tmp_time))
1086 tcp_cfg.finwait2_time = tmp_time / TCP_TIMER_TICK;
1087 else if (unformat (input, "lastack-time %u", &tmp_time))
1088 tcp_cfg.lastack_time = tmp_time / TCP_TIMER_TICK;
1089 else if (unformat (input, "closing-time %u", &tmp_time))
1090 tcp_cfg.closing_time = tmp_time / TCP_TIMER_TICK;
liuyacan7e781192021-06-14 18:09:01 +08001091 else if (unformat (input, "alloc-err-timeout %u", &tmp_time))
1092 tcp_cfg.alloc_err_timeout = tmp_time / TCP_TIMER_TICK;
Florin Coras999840c2020-03-18 20:31:34 +00001093 else if (unformat (input, "cleanup-time %u", &tmp_time))
1094 tcp_cfg.cleanup_time = tmp_time / 1000.0;
Florin Coras1c30d2d2024-06-10 13:12:40 -07001095 else if (unformat (input, "syn-rcvd-time %u", &tmp_time))
1096 tcp_cfg.syn_rcvd_time = tmp_time * THZ;
Florin Coras999840c2020-03-18 20:31:34 +00001097 else
1098 return clib_error_return (0, "unknown input `%U'",
1099 format_unformat_error, input);
1100 }
1101 return 0;
1102}
1103
1104VLIB_CONFIG_FUNCTION (tcp_config_fn, "tcp");
1105
1106/*
1107 * fd.io coding-style-patch-verification: ON
1108 *
1109 * Local Variables:
1110 * eval: (c-set-style "gnu")
1111 * End:
1112 */