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