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