Fixes and improved tcp/session debugging
[vpp.git] / src / vnet / tcp / tcp.c
1 /*
2  * Copyright (c) 2016 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/session/session.h>
18 #include <vnet/fib/fib.h>
19 #include <vnet/dpo/load_balance.h>
20 #include <math.h>
21
22 tcp_main_t tcp_main;
23
24 static u32
25 tcp_connection_bind (u32 session_index, ip46_address_t * ip,
26                      u16 port_host_byte_order, u8 is_ip4)
27 {
28   tcp_main_t *tm = &tcp_main;
29   tcp_connection_t *listener;
30
31   pool_get (tm->listener_pool, listener);
32   memset (listener, 0, sizeof (*listener));
33
34   listener->c_c_index = listener - tm->listener_pool;
35   listener->c_lcl_port = clib_host_to_net_u16 (port_host_byte_order);
36
37   if (is_ip4)
38     {
39       listener->c_lcl_ip4.as_u32 = ip->ip4.as_u32;
40       listener->c_is_ip4 = 1;
41       listener->c_proto = SESSION_TYPE_IP4_TCP;
42     }
43   else
44     {
45       clib_memcpy (&listener->c_lcl_ip6, &ip->ip6, sizeof (ip6_address_t));
46       listener->c_proto = SESSION_TYPE_IP6_TCP;
47     }
48
49   listener->c_s_index = session_index;
50   listener->state = TCP_STATE_LISTEN;
51
52   tcp_connection_timers_init (listener);
53
54   TCP_EVT_DBG (TCP_EVT_BIND, listener);
55
56   return listener->c_c_index;
57 }
58
59 u32
60 tcp_session_bind_ip4 (u32 session_index, ip46_address_t * ip,
61                       u16 port_host_byte_order)
62 {
63   return tcp_connection_bind (session_index, ip, port_host_byte_order, 1);
64 }
65
66 u32
67 tcp_session_bind_ip6 (u32 session_index, ip46_address_t * ip,
68                       u16 port_host_byte_order)
69 {
70   return tcp_connection_bind (session_index, ip, port_host_byte_order, 0);
71 }
72
73 static void
74 tcp_connection_unbind (u32 listener_index)
75 {
76   tcp_main_t *tm = vnet_get_tcp_main ();
77   tcp_connection_t *tc;
78
79   tc = pool_elt_at_index (tm->listener_pool, listener_index);
80
81   TCP_EVT_DBG (TCP_EVT_UNBIND, tc);
82
83   /* Poison the entry */
84   if (CLIB_DEBUG > 0)
85     memset (tc, 0xFA, sizeof (*tc));
86
87   pool_put_index (tm->listener_pool, listener_index);
88 }
89
90 u32
91 tcp_session_unbind (u32 listener_index)
92 {
93   tcp_connection_unbind (listener_index);
94   return 0;
95 }
96
97 transport_connection_t *
98 tcp_session_get_listener (u32 listener_index)
99 {
100   tcp_main_t *tm = vnet_get_tcp_main ();
101   tcp_connection_t *tc;
102   tc = pool_elt_at_index (tm->listener_pool, listener_index);
103   return &tc->connection;
104 }
105
106 /**
107  * Cleans up connection state.
108  *
109  * No notifications.
110  */
111 void
112 tcp_connection_cleanup (tcp_connection_t * tc)
113 {
114   tcp_main_t *tm = &tcp_main;
115   u32 tepi;
116   transport_endpoint_t *tep;
117
118   /* Cleanup local endpoint if this was an active connect */
119   tepi = transport_endpoint_lookup (&tm->local_endpoints_table, &tc->c_lcl_ip,
120                                     tc->c_lcl_port);
121
122   /*XXX lock */
123   if (tepi != TRANSPORT_ENDPOINT_INVALID_INDEX)
124     {
125       tep = pool_elt_at_index (tm->local_endpoints, tepi);
126       transport_endpoint_table_del (&tm->local_endpoints_table, tep);
127       pool_put (tm->local_endpoints, tep);
128     }
129
130   /* Make sure all timers are cleared */
131   tcp_connection_timers_reset (tc);
132
133   /* Check if half-open */
134   if (tc->state == TCP_STATE_SYN_SENT)
135     {
136       /* Poison the entry */
137       if (CLIB_DEBUG > 0)
138         memset (tc, 0xFA, sizeof (*tc));
139       pool_put (tm->half_open_connections, tc);
140     }
141   else
142     {
143       int thread_index = tc->c_thread_index;
144       /* Poison the entry */
145       if (CLIB_DEBUG > 0)
146         memset (tc, 0xFA, sizeof (*tc));
147       pool_put (tm->connections[thread_index], tc);
148     }
149 }
150
151 /**
152  * Connection removal.
153  *
154  * This should be called only once connection enters CLOSED state. Note
155  * that it notifies the session of the removal event, so if the goal is to
156  * just remove the connection, call tcp_connection_cleanup instead.
157  */
158 void
159 tcp_connection_del (tcp_connection_t * tc)
160 {
161   TCP_EVT_DBG (TCP_EVT_DELETE, tc);
162   stream_session_delete_notify (&tc->connection);
163   tcp_connection_cleanup (tc);
164 }
165
166 /** Notify session that connection has been reset.
167  *
168  * Switch state to closed and wait for session to call cleanup.
169  */
170 void
171 tcp_connection_reset (tcp_connection_t * tc)
172 {
173   switch (tc->state)
174     {
175     case TCP_STATE_SYN_RCVD:
176       /* Cleanup everything. App wasn't notified yet */
177       stream_session_delete_notify (&tc->connection);
178       tcp_connection_cleanup (tc);
179       break;
180     case TCP_STATE_SYN_SENT:
181     case TCP_STATE_ESTABLISHED:
182     case TCP_STATE_CLOSE_WAIT:
183     case TCP_STATE_FIN_WAIT_1:
184     case TCP_STATE_FIN_WAIT_2:
185     case TCP_STATE_CLOSING:
186       tc->state = TCP_STATE_CLOSED;
187
188       /* Make sure all timers are cleared */
189       tcp_connection_timers_reset (tc);
190       stream_session_reset_notify (&tc->connection);
191
192       /* Wait for cleanup from session layer but not forever */
193       tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
194       break;
195     case TCP_STATE_CLOSED:
196       return;
197     }
198 }
199
200 /**
201  * Begin connection closing procedure.
202  *
203  * If at the end the connection is not in CLOSED state, it is not removed.
204  * Instead, we rely on on TCP to advance through state machine to either
205  * 1) LAST_ACK (passive close) whereby when the last ACK is received
206  * tcp_connection_del is called. This notifies session of the delete and
207  * calls cleanup.
208  * 2) TIME_WAIT (active close) whereby after 2MSL the 2MSL timer triggers
209  * and cleanup is called.
210  *
211  * N.B. Half-close connections are not supported
212  */
213 void
214 tcp_connection_close (tcp_connection_t * tc)
215 {
216   TCP_EVT_DBG (TCP_EVT_CLOSE, tc);
217
218   /* Send FIN if needed */
219   if (tc->state == TCP_STATE_ESTABLISHED
220       || tc->state == TCP_STATE_SYN_RCVD || tc->state == TCP_STATE_CLOSE_WAIT)
221     tcp_send_fin (tc);
222
223   /* Switch state */
224   if (tc->state == TCP_STATE_ESTABLISHED || tc->state == TCP_STATE_SYN_RCVD)
225     tc->state = TCP_STATE_FIN_WAIT_1;
226   else if (tc->state == TCP_STATE_SYN_SENT)
227     tc->state = TCP_STATE_CLOSED;
228   else if (tc->state == TCP_STATE_CLOSE_WAIT)
229     tc->state = TCP_STATE_LAST_ACK;
230
231   /* If in CLOSED and WAITCLOSE timer is not set, delete connection now */
232   if (tc->timers[TCP_TIMER_WAITCLOSE] == TCP_TIMER_HANDLE_INVALID
233       && tc->state == TCP_STATE_CLOSED)
234     tcp_connection_del (tc);
235 }
236
237 void
238 tcp_session_close (u32 conn_index, u32 thread_index)
239 {
240   tcp_connection_t *tc;
241   tc = tcp_connection_get (conn_index, thread_index);
242   tcp_connection_close (tc);
243 }
244
245 void
246 tcp_session_cleanup (u32 conn_index, u32 thread_index)
247 {
248   tcp_connection_t *tc;
249   tc = tcp_connection_get (conn_index, thread_index);
250
251   /* Wait for the session tx events to clear */
252   tc->state = TCP_STATE_CLOSED;
253   tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
254 }
255
256 void *
257 ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4)
258 {
259   ip_lookup_main_t *lm4 = &ip4_main.lookup_main;
260   ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
261   ip_interface_address_t *ia = 0;
262
263   if (is_ip4)
264     {
265       /* *INDENT-OFF* */
266       foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ ,
267       ({
268         return ip_interface_address_get_address (lm4, ia);
269       }));
270       /* *INDENT-ON* */
271     }
272   else
273     {
274       /* *INDENT-OFF* */
275       foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ ,
276       ({
277         return ip_interface_address_get_address (lm6, ia);
278       }));
279       /* *INDENT-ON* */
280     }
281
282   return 0;
283 }
284
285 #define PORT_MASK ((1 << 16)- 1)
286 /**
287  * Allocate local port and add if successful add entry to local endpoint
288  * table to mark the pair as used.
289  */
290 u16
291 tcp_allocate_local_port (tcp_main_t * tm, ip46_address_t * ip)
292 {
293   transport_endpoint_t *tep;
294   u32 time_now, tei;
295   u16 min = 1024, max = 65535;  /* XXX configurable ? */
296   int tries;
297
298   tries = max - min;
299   time_now = tcp_time_now ();
300
301   /* Only support active opens from thread 0 */
302   ASSERT (vlib_get_thread_index () == 0);
303
304   /* Start at random point or max */
305   pool_get (tm->local_endpoints, tep);
306   clib_memcpy (&tep->ip, ip, sizeof (*ip));
307
308   /* Search for first free slot */
309   for (; tries >= 0; tries--)
310     {
311       u16 port = 0;
312
313       /* Find a port in the specified range */
314       while (1)
315         {
316           port = random_u32 (&time_now) & PORT_MASK;
317           if (PREDICT_TRUE (port >= min && port < max))
318             break;
319         }
320
321       tep->port = port;
322
323       /* Look it up */
324       tei = transport_endpoint_lookup (&tm->local_endpoints_table, &tep->ip,
325                                        tep->port);
326       /* If not found, we're done */
327       if (tei == TRANSPORT_ENDPOINT_INVALID_INDEX)
328         {
329           transport_endpoint_table_add (&tm->local_endpoints_table, tep,
330                                         tep - tm->local_endpoints);
331           return tep->port;
332         }
333     }
334   /* No free ports */
335   pool_put (tm->local_endpoints, tep);
336   return -1;
337 }
338
339 /**
340  * Initialize all connection timers as invalid
341  */
342 void
343 tcp_connection_timers_init (tcp_connection_t * tc)
344 {
345   int i;
346
347   /* Set all to invalid */
348   for (i = 0; i < TCP_N_TIMERS; i++)
349     {
350       tc->timers[i] = TCP_TIMER_HANDLE_INVALID;
351     }
352
353   tc->rto = TCP_RTO_INIT;
354 }
355
356 /**
357  * Stop all connection timers
358  */
359 void
360 tcp_connection_timers_reset (tcp_connection_t * tc)
361 {
362   int i;
363   for (i = 0; i < TCP_N_TIMERS; i++)
364     {
365       tcp_timer_reset (tc, i);
366     }
367 }
368
369 #if 0
370 typedef struct ip4_tcp_hdr
371 {
372   ip4_header_t ip;
373   tcp_header_t tcp;
374 } ip4_tcp_hdr_t;
375
376 typedef struct ip6_tcp_hdr
377 {
378   ip6_header_t ip;
379   tcp_header_t tcp;
380 } ip6_tcp_hdr_t;
381
382 static void
383 tcp_connection_select_lb_bucket (tcp_connection_t * tc, const dpo_id_t * dpo,
384                                  dpo_id_t * result)
385 {
386   const dpo_id_t *choice;
387   load_balance_t *lb;
388   int hash;
389
390   lb = load_balance_get (dpo->dpoi_index);
391   if (tc->c_is_ip4)
392     {
393       ip4_tcp_hdr_t hdr;
394       memset (&hdr, 0, sizeof (hdr));
395       hdr.ip.protocol = IP_PROTOCOL_TCP;
396       hdr.ip.address_pair.src.as_u32 = tc->c_lcl_ip.ip4.as_u32;
397       hdr.ip.address_pair.dst.as_u32 = tc->c_rmt_ip.ip4.as_u32;
398       hdr.tcp.src_port = tc->c_lcl_port;
399       hdr.tcp.dst_port = tc->c_rmt_port;
400       hash = ip4_compute_flow_hash (&hdr.ip, lb->lb_hash_config);
401     }
402   else
403     {
404       ip6_tcp_hdr_t hdr;
405       memset (&hdr, 0, sizeof (hdr));
406       hdr.ip.protocol = IP_PROTOCOL_TCP;
407       clib_memcpy (&hdr.ip.src_address, &tc->c_lcl_ip.ip6,
408                    sizeof (ip6_address_t));
409       clib_memcpy (&hdr.ip.dst_address, &tc->c_rmt_ip.ip6,
410                    sizeof (ip6_address_t));
411       hdr.tcp.src_port = tc->c_lcl_port;
412       hdr.tcp.dst_port = tc->c_rmt_port;
413       hash = ip6_compute_flow_hash (&hdr.ip, lb->lb_hash_config);
414     }
415   choice = load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
416   dpo_copy (result, choice);
417 }
418
419 fib_node_index_t
420 tcp_lookup_rmt_in_fib (tcp_connection_t * tc)
421 {
422   fib_prefix_t prefix;
423
424   clib_memcpy (&prefix.fp_addr, &tc->c_rmt_ip, sizeof (prefix.fp_addr));
425   prefix.fp_proto = tc->c_is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
426   prefix.fp_len = tc->c_is_ip4 ? 32 : 128;
427   return fib_table_lookup (0, &prefix);
428 }
429
430 static int
431 tcp_connection_stack_on_fib_entry (tcp_connection_t * tc)
432 {
433   dpo_id_t choice = DPO_INVALID;
434   u32 output_node_index;
435   fib_entry_t *fe;
436
437   fe = fib_entry_get (tc->c_rmt_fei);
438   if (fe->fe_lb.dpoi_type != DPO_LOAD_BALANCE)
439     return -1;
440
441   tcp_connection_select_lb_bucket (tc, &fe->fe_lb, &choice);
442
443   output_node_index =
444     tc->c_is_ip4 ? tcp4_output_node.index : tcp6_output_node.index;
445   dpo_stack_from_node (output_node_index, &tc->c_rmt_dpo, &choice);
446   return 0;
447 }
448
449 /** Stack tcp connection on peer's fib entry.
450  *
451  * This ultimately populates the dpo the connection will use to send packets.
452  */
453 static void
454 tcp_connection_fib_attach (tcp_connection_t * tc)
455 {
456   tc->c_rmt_fei = tcp_lookup_rmt_in_fib (tc);
457
458   ASSERT (tc->c_rmt_fei != FIB_NODE_INDEX_INVALID);
459
460   tcp_connection_stack_on_fib_entry (tc);
461 }
462 #endif /* 0 */
463
464 /** Initialize tcp connection variables
465  *
466  * Should be called after having received a msg from the peer, i.e., a SYN or
467  * a SYNACK, such that connection options have already been exchanged. */
468 void
469 tcp_connection_init_vars (tcp_connection_t * tc)
470 {
471   tcp_connection_timers_init (tc);
472   tcp_init_mss (tc);
473   scoreboard_init (&tc->sack_sb);
474   tcp_cc_init (tc);
475   //  tcp_connection_fib_attach (tc);
476 }
477
478 int
479 tcp_connection_open (ip46_address_t * rmt_addr, u16 rmt_port, u8 is_ip4)
480 {
481   tcp_main_t *tm = vnet_get_tcp_main ();
482   tcp_connection_t *tc;
483   fib_prefix_t prefix;
484   fib_node_index_t fei;
485   u32 sw_if_index;
486   ip46_address_t lcl_addr;
487   u16 lcl_port;
488
489   /*
490    * Find the local address and allocate port
491    */
492   memset (&lcl_addr, 0, sizeof (lcl_addr));
493
494   /* Find a FIB path to the destination */
495   clib_memcpy (&prefix.fp_addr, rmt_addr, sizeof (*rmt_addr));
496   prefix.fp_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
497   prefix.fp_len = is_ip4 ? 32 : 128;
498
499   fei = fib_table_lookup (0, &prefix);
500
501   /* Couldn't find route to destination. Bail out. */
502   if (fei == FIB_NODE_INDEX_INVALID)
503     return -1;
504
505   sw_if_index = fib_entry_get_resolving_interface (fei);
506
507   if (sw_if_index == (u32) ~ 0)
508     return -1;
509
510   if (is_ip4)
511     {
512       ip4_address_t *ip4;
513       int index;
514       if (vec_len (tm->ip4_src_addresses))
515         {
516           index = tm->last_v4_address_rotor++;
517           if (tm->last_v4_address_rotor >= vec_len (tm->ip4_src_addresses))
518             tm->last_v4_address_rotor = 0;
519           lcl_addr.ip4.as_u32 = tm->ip4_src_addresses[index].as_u32;
520         }
521       else
522         {
523           ip4 = ip_interface_get_first_ip (sw_if_index, 1);
524           lcl_addr.ip4.as_u32 = ip4->as_u32;
525         }
526     }
527   else
528     {
529       ip6_address_t *ip6;
530       int index;
531
532       if (vec_len (tm->ip6_src_addresses))
533         {
534           index = tm->last_v6_address_rotor++;
535           if (tm->last_v6_address_rotor >= vec_len (tm->ip6_src_addresses))
536             tm->last_v6_address_rotor = 0;
537           clib_memcpy (&lcl_addr.ip6, &tm->ip6_src_addresses[index],
538                        sizeof (*ip6));
539         }
540       else
541         {
542           ip6 = ip_interface_get_first_ip (sw_if_index, 0);
543           clib_memcpy (&lcl_addr.ip6, ip6, sizeof (*ip6));
544         }
545     }
546
547   /* Allocate source port */
548   lcl_port = tcp_allocate_local_port (tm, &lcl_addr);
549   if (lcl_port < 1)
550     {
551       clib_warning ("Failed to allocate src port");
552       return -1;
553     }
554
555   /*
556    * Create connection and send SYN
557    */
558
559   pool_get (tm->half_open_connections, tc);
560   memset (tc, 0, sizeof (*tc));
561
562   clib_memcpy (&tc->c_rmt_ip, rmt_addr, sizeof (ip46_address_t));
563   clib_memcpy (&tc->c_lcl_ip, &lcl_addr, sizeof (ip46_address_t));
564   tc->c_rmt_port = clib_host_to_net_u16 (rmt_port);
565   tc->c_lcl_port = clib_host_to_net_u16 (lcl_port);
566   tc->c_c_index = tc - tm->half_open_connections;
567   tc->c_is_ip4 = is_ip4;
568   tc->c_proto = is_ip4 ? SESSION_TYPE_IP4_TCP : SESSION_TYPE_IP6_TCP;
569
570   /* The other connection vars will be initialized after SYN ACK */
571   tcp_connection_timers_init (tc);
572
573   tcp_send_syn (tc);
574
575   tc->state = TCP_STATE_SYN_SENT;
576
577   TCP_EVT_DBG (TCP_EVT_OPEN, tc);
578
579   return tc->c_c_index;
580 }
581
582 int
583 tcp_session_open_ip4 (ip46_address_t * addr, u16 port)
584 {
585   return tcp_connection_open (addr, port, 1);
586 }
587
588 int
589 tcp_session_open_ip6 (ip46_address_t * addr, u16 port)
590 {
591   return tcp_connection_open (addr, port, 0);
592 }
593
594 const char *tcp_dbg_evt_str[] = {
595 #define _(sym, str) str,
596   foreach_tcp_dbg_evt
597 #undef _
598 };
599
600 const char *tcp_fsm_states[] = {
601 #define _(sym, str) str,
602   foreach_tcp_fsm_state
603 #undef _
604 };
605
606 u8 *
607 format_tcp_state (u8 * s, va_list * args)
608 {
609   u32 state = va_arg (*args, u32);
610
611   if (state < TCP_N_STATES)
612     s = format (s, "%s", tcp_fsm_states[state]);
613   else
614     s = format (s, "UNKNOWN (%d (0x%x))", state, state);
615   return s;
616 }
617
618 const char *tcp_conn_timers[] = {
619 #define _(sym, str) str,
620   foreach_tcp_timer
621 #undef _
622 };
623
624 u8 *
625 format_tcp_timers (u8 * s, va_list * args)
626 {
627   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
628   int i, last = -1;
629
630   for (i = 0; i < TCP_N_TIMERS; i++)
631     if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
632       last = i;
633
634   s = format (s, "[");
635   for (i = 0; i < last; i++)
636     {
637       if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
638         s = format (s, "%s,", tcp_conn_timers[i]);
639     }
640
641   if (last >= 0)
642     s = format (s, "%s]", tcp_conn_timers[i]);
643   else
644     s = format (s, "]");
645
646   return s;
647 }
648
649 u8 *
650 format_tcp_congestion_status (u8 * s, va_list * args)
651 {
652   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
653   if (tcp_in_recovery (tc))
654     s = format (s, "recovery");
655   else if (tcp_in_fastrecovery (tc))
656     s = format (s, "fastrecovery");
657   else
658     s = format (s, "none");
659   return s;
660 }
661
662 u8 *
663 format_tcp_vars (u8 * s, va_list * args)
664 {
665   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
666   s = format (s, " snd_una %u snd_nxt %u snd_una_max %u",
667               tc->snd_una - tc->iss, tc->snd_nxt - tc->iss,
668               tc->snd_una_max - tc->iss);
669   s = format (s, " rcv_nxt %u rcv_las %u\n",
670               tc->rcv_nxt - tc->irs, tc->rcv_las - tc->irs);
671   s = format (s, " snd_wnd %u rcv_wnd %u snd_wl1 %u snd_wl2 %u\n",
672               tc->snd_wnd, tc->rcv_wnd, tc->snd_wl1 - tc->irs,
673               tc->snd_wl2 - tc->iss);
674   s = format (s, " flight size %u send space %u rcv_wnd_av %d\n",
675               tcp_flight_size (tc), tcp_available_snd_space (tc),
676               tcp_rcv_wnd_available (tc));
677   s = format (s, " cong %U ", format_tcp_congestion_status, tc);
678   s = format (s, "cwnd %u ssthresh %u rtx_bytes %u bytes_acked %u\n",
679               tc->cwnd, tc->ssthresh, tc->snd_rxt_bytes, tc->bytes_acked);
680   s = format (s, " prev_ssthresh %u snd_congestion %u dupack %u",
681               tc->prev_ssthresh, tc->snd_congestion - tc->iss,
682               tc->rcv_dupacks);
683   s = format (s, " limited_transmit %u\n", tc->limited_transmit - tc->iss);
684   s = format (s, " tsecr %u tsecr_last_ack %u\n", tc->rcv_opts.tsecr,
685               tc->tsecr_last_ack);
686   s = format (s, " rto %u rto_boff %u srtt %u rttvar %u rtt_ts %u ", tc->rto,
687               tc->rto_boff, tc->srtt, tc->rttvar, tc->rtt_ts);
688   s = format (s, "rtt_seq %u\n", tc->rtt_seq);
689   s = format (s, " tsval_recent %u tsval_recent_age %u\n", tc->tsval_recent,
690               tcp_time_now () - tc->tsval_recent_age);
691   s = format (s, " scoreboard: %U\n", format_tcp_scoreboard, &tc->sack_sb);
692   if (vec_len (tc->snd_sacks))
693     s = format (s, " sacks tx: %U\n", format_tcp_sacks, tc);
694
695   return s;
696 }
697
698 u8 *
699 format_tcp_connection_id (u8 * s, va_list * args)
700 {
701   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
702   if (!tc)
703     return s;
704   if (tc->c_is_ip4)
705     {
706       s = format (s, "[#%d][%s] %U:%d->%U:%d", tc->c_thread_index, "T",
707                   format_ip4_address, &tc->c_lcl_ip4,
708                   clib_net_to_host_u16 (tc->c_lcl_port), format_ip4_address,
709                   &tc->c_rmt_ip4, clib_net_to_host_u16 (tc->c_rmt_port));
710     }
711   else
712     {
713       s = format (s, "[#%d][%s] %U:%d->%U:%d", tc->c_thread_index, "T",
714                   format_ip6_address, &tc->c_lcl_ip6,
715                   clib_net_to_host_u16 (tc->c_lcl_port), format_ip6_address,
716                   &tc->c_rmt_ip6, clib_net_to_host_u16 (tc->c_rmt_port));
717     }
718
719   return s;
720 }
721
722 u8 *
723 format_tcp_connection (u8 * s, va_list * args)
724 {
725   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
726   u32 verbose = va_arg (*args, u32);
727
728   s = format (s, "%-50U", format_tcp_connection_id, tc);
729   if (verbose)
730     {
731       s = format (s, "%-15U", format_tcp_state, tc->state);
732       if (verbose > 1)
733         s = format (s, " %U\n%U", format_tcp_timers, tc, format_tcp_vars, tc);
734     }
735
736   return s;
737 }
738
739 u8 *
740 format_tcp_session (u8 * s, va_list * args)
741 {
742   u32 tci = va_arg (*args, u32);
743   u32 thread_index = va_arg (*args, u32);
744   u32 verbose = va_arg (*args, u32);
745   tcp_connection_t *tc;
746
747   tc = tcp_connection_get (tci, thread_index);
748   if (tc)
749     s = format (s, "%U", format_tcp_connection, tc, verbose);
750   else
751     s = format (s, "empty");
752   return s;
753 }
754
755 u8 *
756 format_tcp_listener_session (u8 * s, va_list * args)
757 {
758   u32 tci = va_arg (*args, u32);
759   tcp_connection_t *tc = tcp_listener_get (tci);
760   return format (s, "%U", format_tcp_connection_id, tc);
761 }
762
763 u8 *
764 format_tcp_half_open_session (u8 * s, va_list * args)
765 {
766   u32 tci = va_arg (*args, u32);
767   tcp_connection_t *tc = tcp_half_open_connection_get (tci);
768   return format (s, "%U", format_tcp_connection_id, tc);
769 }
770
771 u8 *
772 format_tcp_sacks (u8 * s, va_list * args)
773 {
774   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
775   sack_block_t *sacks = tc->snd_sacks;
776   sack_block_t *block;
777   int i, len = 0;
778
779   len = vec_len (sacks);
780   for (i = 0; i < len - 1; i++)
781     {
782       block = &sacks[i];
783       s = format (s, " start %u end %u\n", block->start - tc->irs,
784                   block->end - tc->irs);
785     }
786   if (len)
787     {
788       block = &sacks[len - 1];
789       s = format (s, " start %u end %u", block->start - tc->irs,
790                   block->end - tc->irs);
791     }
792   return s;
793 }
794
795 u8 *
796 format_tcp_rcv_sacks (u8 * s, va_list * args)
797 {
798   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
799   sack_block_t *sacks = tc->rcv_opts.sacks;
800   sack_block_t *block;
801   int i, len = 0;
802
803   len = vec_len (sacks);
804   for (i = 0; i < len - 1; i++)
805     {
806       block = &sacks[i];
807       s = format (s, " start %u end %u\n", block->start - tc->iss,
808                   block->end - tc->iss);
809     }
810   if (len)
811     {
812       block = &sacks[len - 1];
813       s = format (s, " start %u end %u", block->start - tc->iss,
814                   block->end - tc->iss);
815     }
816   return s;
817 }
818
819 u8 *
820 format_tcp_sack_hole (u8 * s, va_list * args)
821 {
822   sack_scoreboard_hole_t *hole = va_arg (*args, sack_scoreboard_hole_t *);
823   s = format (s, "[%u, %u]", hole->start, hole->end);
824   return s;
825 }
826
827 u8 *
828 format_tcp_scoreboard (u8 * s, va_list * args)
829 {
830   sack_scoreboard_t *sb = va_arg (*args, sack_scoreboard_t *);
831   sack_scoreboard_hole_t *hole;
832   s = format (s, "sacked_bytes %u last_sacked_bytes %u lost_bytes %u\n",
833               sb->sacked_bytes, sb->last_sacked_bytes, sb->lost_bytes);
834   s = format (s, " last_bytes_delivered %u high_sacked %u snd_una_adv %u\n",
835               sb->last_bytes_delivered, sb->high_sacked, sb->snd_una_adv);
836   s = format (s, " cur_rxt_hole %u high_rxt %u rescue_rxt %u",
837               sb->cur_rxt_hole, sb->high_rxt, sb->rescue_rxt);
838
839   hole = scoreboard_first_hole (sb);
840   if (hole)
841     s = format (s, "\n head %u tail %u holes:\n", sb->head, sb->tail);
842
843   while (hole)
844     {
845       s = format (s, "%U", format_tcp_sack_hole, hole);
846       hole = scoreboard_next_hole (sb, hole);
847     }
848
849   return s;
850 }
851
852 transport_connection_t *
853 tcp_session_get_transport (u32 conn_index, u32 thread_index)
854 {
855   tcp_connection_t *tc = tcp_connection_get (conn_index, thread_index);
856   return &tc->connection;
857 }
858
859 transport_connection_t *
860 tcp_half_open_session_get_transport (u32 conn_index)
861 {
862   tcp_connection_t *tc = tcp_half_open_connection_get (conn_index);
863   return &tc->connection;
864 }
865
866 /**
867  * Compute maximum segment size for session layer.
868  *
869  * Since the result needs to be the actual data length, it first computes
870  * the tcp options to be used in the next burst and subtracts their
871  * length from the connection's snd_mss.
872  */
873 u16
874 tcp_session_send_mss (transport_connection_t * trans_conn)
875 {
876   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
877
878   /* Ensure snd_mss does accurately reflect the amount of data we can push
879    * in a segment. This also makes sure that options are updated according to
880    * the current state of the connection. */
881   tcp_update_snd_mss (tc);
882
883   return tc->snd_mss;
884 }
885
886 always_inline u32
887 tcp_round_snd_space (tcp_connection_t * tc, u32 snd_space)
888 {
889   if (PREDICT_FALSE (tc->snd_wnd < tc->snd_mss))
890     {
891       return tc->snd_wnd <= snd_space ? tc->snd_wnd : 0;
892     }
893
894   /* If we can't write at least a segment, don't try at all */
895   if (PREDICT_FALSE (snd_space < tc->snd_mss))
896     {
897       if (snd_space > clib_min (tc->mss, tc->rcv_opts.mss) - TCP_HDR_LEN_MAX)
898         return snd_space;
899       return 0;
900     }
901
902   /* round down to mss multiple */
903   return snd_space - (snd_space % tc->snd_mss);
904 }
905
906 /**
907  * Compute tx window session is allowed to fill.
908  *
909  * Takes into account available send space, snd_mss and the congestion
910  * state of the connection. If possible, the value returned is a multiple
911  * of snd_mss.
912  *
913  * @param tc tcp connection
914  * @return number of bytes session is allowed to write
915  */
916 u32
917 tcp_snd_space (tcp_connection_t * tc)
918 {
919   int snd_space, snt_limited;
920
921   if (PREDICT_TRUE (tcp_in_cong_recovery (tc) == 0))
922     {
923       snd_space = tcp_available_snd_space (tc);
924
925       /* If we haven't gotten dupacks or if we did and have gotten sacked
926        * bytes then we can still send as per Limited Transmit (RFC3042) */
927       if (PREDICT_FALSE (tc->rcv_dupacks != 0
928                          && (tcp_opts_sack_permitted (tc)
929                              && tc->sack_sb.last_sacked_bytes == 0)))
930         {
931           if (tc->rcv_dupacks == 1 && tc->limited_transmit != tc->snd_nxt)
932             tc->limited_transmit = tc->snd_nxt;
933           ASSERT (seq_leq (tc->limited_transmit, tc->snd_nxt));
934
935           snt_limited = tc->snd_nxt - tc->limited_transmit;
936           snd_space = clib_max (2 * tc->snd_mss - snt_limited, 0);
937         }
938       return tcp_round_snd_space (tc, snd_space);
939     }
940
941   if (tcp_in_recovery (tc))
942     {
943       tc->snd_nxt = tc->snd_una_max;
944       snd_space = tcp_available_wnd (tc) - tc->snd_rxt_bytes
945         - (tc->snd_una_max - tc->snd_congestion);
946       if (snd_space <= 0 || (tc->snd_una_max - tc->snd_una) >= tc->snd_wnd)
947         return 0;
948       return tcp_round_snd_space (tc, snd_space);
949     }
950
951   /* If in fast recovery, send 1 SMSS if wnd allows */
952   if (tcp_in_fastrecovery (tc)
953       && tcp_available_snd_space (tc) && !tcp_fastrecovery_sent_1_smss (tc))
954     {
955       tcp_fastrecovery_1_smss_on (tc);
956       return tc->snd_mss;
957     }
958
959   return 0;
960 }
961
962 u32
963 tcp_session_send_space (transport_connection_t * trans_conn)
964 {
965   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
966   return tcp_snd_space (tc);
967 }
968
969 i32
970 tcp_rcv_wnd_available (tcp_connection_t * tc)
971 {
972   return (i32) tc->rcv_wnd - (tc->rcv_nxt - tc->rcv_las);
973 }
974
975 u32
976 tcp_session_tx_fifo_offset (transport_connection_t * trans_conn)
977 {
978   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
979
980   ASSERT (seq_geq (tc->snd_nxt, tc->snd_una));
981
982   /* This still works if fast retransmit is on */
983   return (tc->snd_nxt - tc->snd_una);
984 }
985
986 /* *INDENT-OFF* */
987 const static transport_proto_vft_t tcp4_proto = {
988   .bind = tcp_session_bind_ip4,
989   .unbind = tcp_session_unbind,
990   .push_header = tcp_push_header,
991   .get_connection = tcp_session_get_transport,
992   .get_listener = tcp_session_get_listener,
993   .get_half_open = tcp_half_open_session_get_transport,
994   .open = tcp_session_open_ip4,
995   .close = tcp_session_close,
996   .cleanup = tcp_session_cleanup,
997   .send_mss = tcp_session_send_mss,
998   .send_space = tcp_session_send_space,
999   .tx_fifo_offset = tcp_session_tx_fifo_offset,
1000   .format_connection = format_tcp_session,
1001   .format_listener = format_tcp_listener_session,
1002   .format_half_open = format_tcp_half_open_session,
1003 };
1004
1005 const static transport_proto_vft_t tcp6_proto = {
1006   .bind = tcp_session_bind_ip6,
1007   .unbind = tcp_session_unbind,
1008   .push_header = tcp_push_header,
1009   .get_connection = tcp_session_get_transport,
1010   .get_listener = tcp_session_get_listener,
1011   .get_half_open = tcp_half_open_session_get_transport,
1012   .open = tcp_session_open_ip6,
1013   .close = tcp_session_close,
1014   .cleanup = tcp_session_cleanup,
1015   .send_mss = tcp_session_send_mss,
1016   .send_space = tcp_session_send_space,
1017   .tx_fifo_offset = tcp_session_tx_fifo_offset,
1018   .format_connection = format_tcp_session,
1019   .format_listener = format_tcp_listener_session,
1020   .format_half_open = format_tcp_half_open_session,
1021 };
1022 /* *INDENT-ON* */
1023
1024 void
1025 tcp_timer_keep_handler (u32 conn_index)
1026 {
1027   u32 thread_index = vlib_get_thread_index ();
1028   tcp_connection_t *tc;
1029
1030   tc = tcp_connection_get (conn_index, thread_index);
1031   tc->timers[TCP_TIMER_KEEP] = TCP_TIMER_HANDLE_INVALID;
1032
1033   tcp_connection_close (tc);
1034 }
1035
1036 void
1037 tcp_timer_establish_handler (u32 conn_index)
1038 {
1039   tcp_connection_t *tc;
1040   u8 sst;
1041
1042   tc = tcp_half_open_connection_get (conn_index);
1043   tc->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
1044
1045   ASSERT (tc->state == TCP_STATE_SYN_SENT);
1046
1047   sst = tc->c_is_ip4 ? SESSION_TYPE_IP4_TCP : SESSION_TYPE_IP6_TCP;
1048   stream_session_connect_notify (&tc->connection, sst, 1 /* fail */ );
1049
1050   tcp_connection_cleanup (tc);
1051 }
1052
1053 void
1054 tcp_timer_waitclose_handler (u32 conn_index)
1055 {
1056   u32 thread_index = vlib_get_thread_index ();
1057   tcp_connection_t *tc;
1058
1059   tc = tcp_connection_get (conn_index, thread_index);
1060   tc->timers[TCP_TIMER_WAITCLOSE] = TCP_TIMER_HANDLE_INVALID;
1061
1062   /* Session didn't come back with a close(). Send FIN either way
1063    * and switch to LAST_ACK. */
1064   if (tc->state == TCP_STATE_CLOSE_WAIT)
1065     {
1066       if (tc->flags & TCP_CONN_FINSNT)
1067         {
1068           clib_warning ("FIN was sent and still in CLOSE WAIT. Weird!");
1069         }
1070
1071       tcp_send_fin (tc);
1072       tc->state = TCP_STATE_LAST_ACK;
1073
1074       /* Make sure we don't wait in LAST ACK forever */
1075       tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
1076
1077       /* Don't delete the connection yet */
1078       return;
1079     }
1080
1081   tcp_connection_del (tc);
1082 }
1083
1084 /* *INDENT-OFF* */
1085 static timer_expiration_handler *timer_expiration_handlers[TCP_N_TIMERS] =
1086 {
1087     tcp_timer_retransmit_handler,
1088     tcp_timer_delack_handler,
1089     tcp_timer_persist_handler,
1090     tcp_timer_keep_handler,
1091     tcp_timer_waitclose_handler,
1092     tcp_timer_retransmit_syn_handler,
1093     tcp_timer_establish_handler
1094 };
1095 /* *INDENT-ON* */
1096
1097 static void
1098 tcp_expired_timers_dispatch (u32 * expired_timers)
1099 {
1100   int i;
1101   u32 connection_index, timer_id;
1102
1103   for (i = 0; i < vec_len (expired_timers); i++)
1104     {
1105       /* Get session index and timer id */
1106       connection_index = expired_timers[i] & 0x0FFFFFFF;
1107       timer_id = expired_timers[i] >> 28;
1108
1109       TCP_EVT_DBG (TCP_EVT_TIMER_POP, connection_index, timer_id);
1110
1111       /* Handle expiration */
1112       (*timer_expiration_handlers[timer_id]) (connection_index);
1113     }
1114 }
1115
1116 void
1117 tcp_initialize_timer_wheels (tcp_main_t * tm)
1118 {
1119   tw_timer_wheel_16t_2w_512sl_t *tw;
1120   /* *INDENT-OFF* */
1121   foreach_vlib_main (({
1122     tw = &tm->timer_wheels[ii];
1123     tw_timer_wheel_init_16t_2w_512sl (tw, tcp_expired_timers_dispatch,
1124                                       100e-3 /* timer period 100ms */ , ~0);
1125     tw->last_run_time = vlib_time_now (this_vlib_main);
1126   }));
1127   /* *INDENT-ON* */
1128 }
1129
1130 clib_error_t *
1131 tcp_main_enable (vlib_main_t * vm)
1132 {
1133   tcp_main_t *tm = vnet_get_tcp_main ();
1134   ip_protocol_info_t *pi;
1135   ip_main_t *im = &ip_main;
1136   vlib_thread_main_t *vtm = vlib_get_thread_main ();
1137   clib_error_t *error = 0;
1138   u32 num_threads;
1139   int thread, i;
1140   tcp_connection_t *tc __attribute__ ((unused));
1141
1142   if ((error = vlib_call_init_function (vm, ip_main_init)))
1143     return error;
1144   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
1145     return error;
1146   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
1147     return error;
1148
1149   /*
1150    * Registrations
1151    */
1152
1153   /* Register with IP */
1154   pi = ip_get_protocol_info (im, IP_PROTOCOL_TCP);
1155   if (pi == 0)
1156     return clib_error_return (0, "TCP protocol info AWOL");
1157   pi->format_header = format_tcp_header;
1158   pi->unformat_pg_edit = unformat_pg_tcp_header;
1159
1160   ip4_register_protocol (IP_PROTOCOL_TCP, tcp4_input_node.index);
1161
1162   /* Register as transport with URI */
1163   session_register_transport (SESSION_TYPE_IP4_TCP, &tcp4_proto);
1164   session_register_transport (SESSION_TYPE_IP6_TCP, &tcp6_proto);
1165
1166   /*
1167    * Initialize data structures
1168    */
1169
1170   num_threads = 1 /* main thread */  + vtm->n_threads;
1171   vec_validate (tm->connections, num_threads - 1);
1172
1173   /*
1174    * Preallocate connections
1175    */
1176   for (thread = 0; thread < num_threads; thread++)
1177     {
1178       for (i = 0; i < tm->preallocated_connections; i++)
1179         pool_get (tm->connections[thread], tc);
1180
1181       for (i = 0; i < tm->preallocated_connections; i++)
1182         pool_put_index (tm->connections[thread], i);
1183     }
1184
1185   /*
1186    * Preallocate half-open connections
1187    */
1188   for (i = 0; i < tm->preallocated_half_open_connections; i++)
1189     pool_get (tm->half_open_connections, tc);
1190
1191   for (i = 0; i < tm->preallocated_half_open_connections; i++)
1192     pool_put_index (tm->half_open_connections, i);
1193
1194   /* Initialize per worker thread tx buffers (used for control messages) */
1195   vec_validate (tm->tx_buffers, num_threads - 1);
1196
1197   /* Initialize timer wheels */
1198   vec_validate (tm->timer_wheels, num_threads - 1);
1199   tcp_initialize_timer_wheels (tm);
1200
1201   /* Initialize clocks per tick for TCP timestamp. Used to compute
1202    * monotonically increasing timestamps. */
1203   tm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
1204     / TCP_TSTAMP_RESOLUTION;
1205
1206   clib_bihash_init_24_8 (&tm->local_endpoints_table, "local endpoint table",
1207                          200000 /* $$$$ config parameter nbuckets */ ,
1208                          (64 << 20) /*$$$ config parameter table size */ );
1209
1210   return error;
1211 }
1212
1213 clib_error_t *
1214 vnet_tcp_enable_disable (vlib_main_t * vm, u8 is_en)
1215 {
1216   if (is_en)
1217     {
1218       if (tcp_main.is_enabled)
1219         return 0;
1220
1221       return tcp_main_enable (vm);
1222     }
1223   else
1224     {
1225       tcp_main.is_enabled = 0;
1226     }
1227
1228   return 0;
1229 }
1230
1231 clib_error_t *
1232 tcp_init (vlib_main_t * vm)
1233 {
1234   tcp_main_t *tm = vnet_get_tcp_main ();
1235
1236   tm->vnet_main = vnet_get_main ();
1237   tm->is_enabled = 0;
1238
1239   return 0;
1240 }
1241
1242 VLIB_INIT_FUNCTION (tcp_init);
1243
1244
1245 static clib_error_t *
1246 tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
1247 {
1248   tcp_main_t *tm = vnet_get_tcp_main ();
1249
1250   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1251     {
1252       if (unformat
1253           (input, "preallocated-connections %d",
1254            &tm->preallocated_connections))
1255         ;
1256       else if (unformat (input, "preallocated-half-open-connections %d",
1257                          &tm->preallocated_half_open_connections))
1258         ;
1259       else
1260         return clib_error_return (0, "unknown input `%U'",
1261                                   format_unformat_error, input);
1262     }
1263   return 0;
1264 }
1265
1266 VLIB_CONFIG_FUNCTION (tcp_config_fn, "tcp");
1267
1268 static clib_error_t *
1269 tcp_src_address (vlib_main_t * vm,
1270                  unformat_input_t * input, vlib_cli_command_t * cmd_arg)
1271 {
1272   tcp_main_t *tm = vnet_get_tcp_main ();
1273   ip4_address_t v4start, v4end;
1274   ip6_address_t v6start, v6end;
1275   int v4set = 0;
1276   int v6set = 0;
1277
1278   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1279     {
1280       if (unformat (input, "%U - %U", unformat_ip4_address, &v4start,
1281                     unformat_ip4_address, &v4end))
1282         v4set = 1;
1283       else if (unformat (input, "%U", unformat_ip4_address, &v4start))
1284         {
1285           memcpy (&v4end, &v4start, sizeof (v4start));
1286           v4set = 1;
1287         }
1288       else if (unformat (input, "%U - %U", unformat_ip6_address, &v6start,
1289                          unformat_ip4_address, &v6end))
1290         v6set = 1;
1291       else if (unformat (input, "%U", unformat_ip6_address, &v6start))
1292         {
1293           memcpy (&v6end, &v6start, sizeof (v4start));
1294           v6set = 1;
1295         }
1296       else
1297         break;
1298     }
1299
1300   if (!v4set && !v6set)
1301     return clib_error_return (0, "at least one v4 or v6 address required");
1302
1303   if (v4set)
1304     {
1305       u32 tmp;
1306
1307       do
1308         {
1309           vec_add1 (tm->ip4_src_addresses, v4start);
1310           tmp = clib_net_to_host_u32 (v4start.as_u32);
1311           tmp++;
1312           v4start.as_u32 = clib_host_to_net_u32 (tmp);
1313         }
1314       while (clib_host_to_net_u32 (v4start.as_u32) <=
1315              clib_host_to_net_u32 (v4end.as_u32));
1316     }
1317   if (v6set)
1318     {
1319       clib_warning ("v6 src address list unimplemented...");
1320     }
1321   return 0;
1322 }
1323
1324 /* *INDENT-OFF* */
1325 VLIB_CLI_COMMAND (tcp_src_address_command, static) =
1326 {
1327   .path = "tcp src-address",
1328   .short_help = "tcp src-address <ip-addr> [- <ip-addr>] add src address range",
1329   .function = tcp_src_address,
1330 };
1331 /* *INDENT-ON* */
1332
1333 static u8 *
1334 tcp_scoreboard_dump_trace (u8 * s, sack_scoreboard_t * sb)
1335 {
1336 #if TCP_SCOREBOARD_TRACE
1337
1338   scoreboard_trace_elt_t *block;
1339   int i = 0;
1340
1341   if (!sb->trace)
1342     return s;
1343
1344   s = format (s, "scoreboard trace:");
1345   vec_foreach (block, sb->trace)
1346   {
1347     s = format (s, "{%u, %u, %u, %u, %u}, ", block->start, block->end,
1348                 block->ack, block->snd_una_max, block->group);
1349     if ((++i % 3) == 0)
1350       s = format (s, "\n");
1351   }
1352   return s;
1353 #else
1354   return 0;
1355 #endif
1356 }
1357
1358 static clib_error_t *
1359 tcp_show_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
1360                               vlib_cli_command_t * cmd_arg)
1361 {
1362   transport_connection_t *tconn = 0;
1363   tcp_connection_t *tc;
1364   u8 *s = 0;
1365   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1366     {
1367       if (unformat (input, "%U", unformat_transport_connection, &tconn,
1368                     TRANSPORT_PROTO_TCP))
1369         ;
1370       else
1371         return clib_error_return (0, "unknown input `%U'",
1372                                   format_unformat_error, input);
1373     }
1374
1375   if (!TCP_SCOREBOARD_TRACE)
1376     {
1377       vlib_cli_output (vm, "scoreboard tracing not enabled");
1378       return 0;
1379     }
1380
1381   tc = tcp_get_connection_from_transport (tconn);
1382   s = tcp_scoreboard_dump_trace (s, &tc->sack_sb);
1383   vlib_cli_output (vm, "%v", s);
1384   return 0;
1385 }
1386
1387 /* *INDENT-OFF* */
1388 VLIB_CLI_COMMAND (tcp_show_scoreboard_trace_command, static) =
1389 {
1390   .path = "show tcp scoreboard trace",
1391   .short_help = "show tcp scoreboard trace <connection>",
1392   .function = tcp_show_scoreboard_trace_fn,
1393 };
1394 /* *INDENT-ON* */
1395
1396 u8 *
1397 tcp_scoreboard_replay (u8 * s, tcp_connection_t * tc, u8 verbose)
1398 {
1399   int i, trace_len;
1400   scoreboard_trace_elt_t *trace;
1401   u32 next_ack, left, group, has_new_ack = 0;
1402   tcp_connection_t _dummy_tc, *dummy_tc = &_dummy_tc;
1403   sack_block_t *block;
1404
1405   if (!tc)
1406     return s;
1407
1408   memset (dummy_tc, 0, sizeof (*dummy_tc));
1409   tcp_connection_timers_init (dummy_tc);
1410   scoreboard_init (&dummy_tc->sack_sb);
1411   dummy_tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK;
1412
1413 #if TCP_SCOREBOARD_TRACE
1414   trace = tc->sack_sb.trace;
1415   trace_len = vec_len (tc->sack_sb.trace);
1416 #else
1417   trace = 0;
1418   trace_len = 0;
1419 #endif
1420
1421   for (i = 0; i < trace_len; i++)
1422     {
1423       if (trace[i].ack != 0)
1424         {
1425           dummy_tc->snd_una = trace[i].ack - 1448;
1426           dummy_tc->snd_una_max = trace[i].ack;
1427         }
1428     }
1429
1430   left = 0;
1431   while (left < trace_len)
1432     {
1433       group = trace[left].group;
1434       vec_reset_length (dummy_tc->rcv_opts.sacks);
1435       has_new_ack = 0;
1436       while (trace[left].group == group)
1437         {
1438           if (trace[left].ack != 0)
1439             {
1440               if (verbose)
1441                 s = format (s, "Adding ack %u, snd_una_max %u, segs: ",
1442                             trace[left].ack, trace[left].snd_una_max);
1443               dummy_tc->snd_una_max = trace[left].snd_una_max;
1444               next_ack = trace[left].ack;
1445               has_new_ack = 1;
1446             }
1447           else
1448             {
1449               if (verbose)
1450                 s = format (s, "[%u, %u], ", trace[left].start,
1451                             trace[left].end);
1452               vec_add2 (dummy_tc->rcv_opts.sacks, block, 1);
1453               block->start = trace[left].start;
1454               block->end = trace[left].end;
1455             }
1456           left++;
1457         }
1458
1459       /* Push segments */
1460       tcp_rcv_sacks (dummy_tc, next_ack);
1461       if (has_new_ack)
1462         dummy_tc->snd_una = next_ack + dummy_tc->sack_sb.snd_una_adv;
1463
1464       if (verbose)
1465         s = format (s, "result: %U", format_tcp_scoreboard,
1466                     &dummy_tc->sack_sb);
1467
1468     }
1469   s = format (s, "result: %U", format_tcp_scoreboard, &dummy_tc->sack_sb);
1470
1471   return s;
1472 }
1473
1474 static clib_error_t *
1475 tcp_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
1476                          vlib_cli_command_t * cmd_arg)
1477 {
1478   transport_connection_t *tconn = 0;
1479   tcp_connection_t *tc = 0;
1480   u8 *str = 0;
1481   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1482     {
1483       if (unformat (input, "%U", unformat_transport_connection, &tconn,
1484                     TRANSPORT_PROTO_TCP))
1485         ;
1486       else
1487         return clib_error_return (0, "unknown input `%U'",
1488                                   format_unformat_error, input);
1489     }
1490
1491   if (!TCP_SCOREBOARD_TRACE)
1492     {
1493       vlib_cli_output (vm, "scoreboard tracing not enabled");
1494       return 0;
1495     }
1496
1497   tc = tcp_get_connection_from_transport (tconn);
1498   if (!tc)
1499     {
1500       vlib_cli_output (vm, "connection not found");
1501       return 0;
1502     }
1503   str = tcp_scoreboard_replay (str, tc, 1);
1504   vlib_cli_output (vm, "%v", str);
1505   return 0;
1506 }
1507
1508 /* *INDENT-OFF* */
1509 VLIB_CLI_COMMAND (tcp_replay_scoreboard_command, static) =
1510 {
1511   .path = "tcp replay scoreboard",
1512   .short_help = "tcp replay scoreboard <connection>",
1513   .function = tcp_scoreboard_trace_fn,
1514 };
1515 /* *INDENT-ON* */
1516
1517 /*
1518  * fd.io coding-style-patch-verification: ON
1519  *
1520  * Local Variables:
1521  * eval: (c-set-style "gnu")
1522  * End:
1523  */