VPP-659 TCP improvements
[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 <math.h>
20
21 tcp_main_t tcp_main;
22
23 static u32
24 tcp_connection_bind (vlib_main_t * vm, u32 session_index, ip46_address_t * ip,
25                      u16 port_host_byte_order, u8 is_ip4)
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 (port_host_byte_order);
35
36   if (is_ip4)
37     listener->c_lcl_ip4.as_u32 = ip->ip4.as_u32;
38   else
39     clib_memcpy (&listener->c_lcl_ip6, &ip->ip6, sizeof (ip6_address_t));
40
41   listener->c_s_index = session_index;
42   listener->c_proto = SESSION_TYPE_IP4_TCP;
43   listener->state = TCP_STATE_LISTEN;
44   listener->c_is_ip4 = 1;
45
46   return listener->c_c_index;
47 }
48
49 u32
50 tcp_session_bind_ip4 (vlib_main_t * vm, u32 session_index,
51                       ip46_address_t * ip, u16 port_host_byte_order)
52 {
53   return tcp_connection_bind (vm, session_index, ip, port_host_byte_order, 1);
54 }
55
56 u32
57 tcp_session_bind_ip6 (vlib_main_t * vm, u32 session_index,
58                       ip46_address_t * ip, u16 port_host_byte_order)
59 {
60   return tcp_connection_bind (vm, session_index, ip, port_host_byte_order, 0);
61
62 }
63
64 static void
65 tcp_session_unbind (u32 listener_index)
66 {
67   tcp_main_t *tm = vnet_get_tcp_main ();
68   pool_put_index (tm->listener_pool, listener_index);
69 }
70
71 u32
72 tcp_session_unbind_ip4 (vlib_main_t * vm, u32 listener_index)
73 {
74   tcp_session_unbind (listener_index);
75   return 0;
76 }
77
78 u32
79 tcp_session_unbind_ip6 (vlib_main_t * vm, u32 listener_index)
80 {
81   tcp_session_unbind (listener_index);
82   return 0;
83 }
84
85 transport_connection_t *
86 tcp_session_get_listener (u32 listener_index)
87 {
88   tcp_main_t *tm = vnet_get_tcp_main ();
89   tcp_connection_t *tc;
90   tc = pool_elt_at_index (tm->listener_pool, listener_index);
91   return &tc->connection;
92 }
93
94 /**
95  * Cleans up connection state.
96  *
97  * No notifications.
98  */
99 void
100 tcp_connection_cleanup (tcp_connection_t * tc)
101 {
102   tcp_main_t *tm = &tcp_main;
103   u32 tepi;
104   transport_endpoint_t *tep;
105
106   /* Cleanup local endpoint if this was an active connect */
107   tepi = transport_endpoint_lookup (&tm->local_endpoints_table, &tc->c_lcl_ip,
108                                     tc->c_lcl_port);
109
110   /*XXX lock */
111   if (tepi != TRANSPORT_ENDPOINT_INVALID_INDEX)
112     {
113       tep = pool_elt_at_index (tm->local_endpoints, tepi);
114       transport_endpoint_table_del (&tm->local_endpoints_table, tep);
115       pool_put (tm->local_endpoints, tep);
116     }
117
118   /* Make sure all timers are cleared */
119   tcp_connection_timers_reset (tc);
120
121   /* Check if half-open */
122   if (tc->state == TCP_STATE_SYN_SENT)
123     pool_put (tm->half_open_connections, tc);
124   else
125     pool_put (tm->connections[tc->c_thread_index], tc);
126 }
127
128 /**
129  * Connection removal.
130  *
131  * This should be called only once connection enters CLOSED state. Note
132  * that it notifies the session of the removal event, so if the goal is to
133  * just remove the connection, call tcp_connection_cleanup instead.
134  */
135 void
136 tcp_connection_del (tcp_connection_t * tc)
137 {
138   stream_session_delete_notify (&tc->connection);
139   tcp_connection_cleanup (tc);
140 }
141
142 /** Notify session that connection has been reset.
143  *
144  * Switch state to closed and wait for session to call cleanup.
145  */
146 void
147 tcp_connection_reset (tcp_connection_t * tc)
148 {
149   if (tc->state == TCP_STATE_CLOSED)
150     return;
151
152   tc->state = TCP_STATE_CLOSED;
153   stream_session_reset_notify (&tc->connection);
154 }
155
156 /**
157  * Begin connection closing procedure.
158  *
159  * If at the end the connection is not in CLOSED state, it is not removed.
160  * Instead, we rely on on TCP to advance through state machine to either
161  * 1) LAST_ACK (passive close) whereby when the last ACK is received
162  * tcp_connection_del is called. This notifies session of the delete and
163  * calls cleanup.
164  * 2) TIME_WAIT (active close) whereby after 2MSL the 2MSL timer triggers
165  * and cleanup is called.
166  *
167  * N.B. Half-close connections are not supported
168  */
169 void
170 tcp_connection_close (tcp_connection_t * tc)
171 {
172   /* Send FIN if needed */
173   if (tc->state == TCP_STATE_ESTABLISHED || tc->state == TCP_STATE_SYN_RCVD
174       || tc->state == TCP_STATE_CLOSE_WAIT)
175     tcp_send_fin (tc);
176
177   /* Switch state */
178   if (tc->state == TCP_STATE_ESTABLISHED || tc->state == TCP_STATE_SYN_RCVD)
179     tc->state = TCP_STATE_FIN_WAIT_1;
180   else if (tc->state == TCP_STATE_SYN_SENT)
181     tc->state = TCP_STATE_CLOSED;
182   else if (tc->state == TCP_STATE_CLOSE_WAIT)
183     tc->state = TCP_STATE_LAST_ACK;
184
185   /* If in CLOSED and WAITCLOSE timer is not set, delete connection now */
186   if (tc->timers[TCP_TIMER_WAITCLOSE] == TCP_TIMER_HANDLE_INVALID
187       && tc->state == TCP_STATE_CLOSED)
188     tcp_connection_del (tc);
189 }
190
191 void
192 tcp_session_close (u32 conn_index, u32 thread_index)
193 {
194   tcp_connection_t *tc;
195   tc = tcp_connection_get (conn_index, thread_index);
196   tcp_connection_close (tc);
197 }
198
199 void
200 tcp_session_cleanup (u32 conn_index, u32 thread_index)
201 {
202   tcp_connection_t *tc;
203   tc = tcp_connection_get (conn_index, thread_index);
204
205   /* Wait for the session tx events to clear */
206   tc->state = TCP_STATE_CLOSED;
207   tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
208 }
209
210 void *
211 ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4)
212 {
213   ip_lookup_main_t *lm4 = &ip4_main.lookup_main;
214   ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
215   ip_interface_address_t *ia = 0;
216
217   if (is_ip4)
218     {
219       /* *INDENT-OFF* */
220       foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ ,
221       ({
222         return ip_interface_address_get_address (lm4, ia);
223       }));
224       /* *INDENT-ON* */
225     }
226   else
227     {
228       /* *INDENT-OFF* */
229       foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ ,
230       ({
231         return ip_interface_address_get_address (lm6, ia);
232       }));
233       /* *INDENT-ON* */
234     }
235
236   return 0;
237 }
238
239 #define PORT_MASK ((1 << 16)- 1)
240 /**
241  * Allocate local port and add if successful add entry to local endpoint
242  * table to mark the pair as used.
243  */
244 u16
245 tcp_allocate_local_port (tcp_main_t * tm, ip46_address_t * ip)
246 {
247   transport_endpoint_t *tep;
248   u32 time_now, tei;
249   u16 min = 1024, max = 65535;  /* XXX configurable ? */
250   int tries;
251
252   tries = max - min;
253   time_now = tcp_time_now ();
254
255   /* Start at random point or max */
256   pool_get (tm->local_endpoints, tep);
257   clib_memcpy (&tep->ip, ip, sizeof (*ip));
258
259   /* Search for first free slot */
260   for (; tries >= 0; tries--)
261     {
262       u16 port = 0;
263
264       /* Find a port in the specified range */
265       while (1)
266         {
267           port = random_u32 (&time_now) & PORT_MASK;
268           if (PREDICT_TRUE (port >= min && port < max))
269             break;
270         }
271
272       tep->port = port;
273
274       /* Look it up */
275       tei = transport_endpoint_lookup (&tm->local_endpoints_table, &tep->ip,
276                                        tep->port);
277       /* If not found, we're done */
278       if (tei == TRANSPORT_ENDPOINT_INVALID_INDEX)
279         {
280           transport_endpoint_table_add (&tm->local_endpoints_table, tep,
281                                         tep - tm->local_endpoints);
282           return tep->port;
283         }
284     }
285   /* No free ports */
286   pool_put (tm->local_endpoints, tep);
287   return -1;
288 }
289
290 /**
291  * Initialize all connection timers as invalid
292  */
293 void
294 tcp_connection_timers_init (tcp_connection_t * tc)
295 {
296   int i;
297
298   /* Set all to invalid */
299   for (i = 0; i < TCP_N_TIMERS; i++)
300     {
301       tc->timers[i] = TCP_TIMER_HANDLE_INVALID;
302     }
303
304   tc->rto = TCP_RTO_INIT;
305 }
306
307 /**
308  * Stop all connection timers
309  */
310 void
311 tcp_connection_timers_reset (tcp_connection_t * tc)
312 {
313   int i;
314   for (i = 0; i < TCP_N_TIMERS; i++)
315     {
316       tcp_timer_reset (tc, i);
317     }
318 }
319
320 /** Initialize tcp connection variables
321  *
322  * Should be called after having received a msg from the peer, i.e., a SYN or
323  * a SYNACK, such that connection options have already been exchanged. */
324 void
325 tcp_connection_init_vars (tcp_connection_t * tc)
326 {
327   tcp_connection_timers_init (tc);
328   tcp_set_snd_mss (tc);
329   tc->sack_sb.head = TCP_INVALID_SACK_HOLE_INDEX;
330   tcp_cc_init (tc);
331 }
332
333 int
334 tcp_connection_open (ip46_address_t * rmt_addr, u16 rmt_port, u8 is_ip4)
335 {
336   tcp_main_t *tm = vnet_get_tcp_main ();
337   tcp_connection_t *tc;
338   fib_prefix_t prefix;
339   u32 fei, sw_if_index;
340   ip46_address_t lcl_addr;
341   u16 lcl_port;
342
343   /*
344    * Find the local address and allocate port
345    */
346   memset (&lcl_addr, 0, sizeof (lcl_addr));
347
348   /* Find a FIB path to the destination */
349   clib_memcpy (&prefix.fp_addr, rmt_addr, sizeof (*rmt_addr));
350   prefix.fp_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
351   prefix.fp_len = is_ip4 ? 32 : 128;
352
353   fei = fib_table_lookup (0, &prefix);
354
355   /* Couldn't find route to destination. Bail out. */
356   if (fei == FIB_NODE_INDEX_INVALID)
357     return -1;
358
359   sw_if_index = fib_entry_get_resolving_interface (fei);
360
361   if (sw_if_index == (u32) ~ 0)
362     return -1;
363
364   if (is_ip4)
365     {
366       ip4_address_t *ip4;
367       ip4 = ip_interface_get_first_ip (sw_if_index, 1);
368       lcl_addr.ip4.as_u32 = ip4->as_u32;
369     }
370   else
371     {
372       ip6_address_t *ip6;
373       ip6 = ip_interface_get_first_ip (sw_if_index, 0);
374       clib_memcpy (&lcl_addr.ip6, ip6, sizeof (*ip6));
375     }
376
377   /* Allocate source port */
378   lcl_port = tcp_allocate_local_port (tm, &lcl_addr);
379   if (lcl_port < 1)
380     {
381       clib_warning ("Failed to allocate src port");
382       return -1;
383     }
384
385   /*
386    * Create connection and send SYN
387    */
388
389   pool_get (tm->half_open_connections, tc);
390   memset (tc, 0, sizeof (*tc));
391
392   clib_memcpy (&tc->c_rmt_ip, rmt_addr, sizeof (ip46_address_t));
393   clib_memcpy (&tc->c_lcl_ip, &lcl_addr, sizeof (ip46_address_t));
394   tc->c_rmt_port = clib_host_to_net_u16 (rmt_port);
395   tc->c_lcl_port = clib_host_to_net_u16 (lcl_port);
396   tc->c_c_index = tc - tm->half_open_connections;
397   tc->c_is_ip4 = is_ip4;
398
399   /* The other connection vars will be initialized after SYN ACK */
400   tcp_connection_timers_init (tc);
401
402   tcp_send_syn (tc);
403
404   tc->state = TCP_STATE_SYN_SENT;
405
406   return tc->c_c_index;
407 }
408
409 int
410 tcp_session_open_ip4 (ip46_address_t * addr, u16 port)
411 {
412   return tcp_connection_open (addr, port, 1);
413 }
414
415 int
416 tcp_session_open_ip6 (ip46_address_t * addr, u16 port)
417 {
418   return tcp_connection_open (addr, port, 0);
419 }
420
421 u8 *
422 format_tcp_session_ip4 (u8 * s, va_list * args)
423 {
424   u32 tci = va_arg (*args, u32);
425   u32 thread_index = va_arg (*args, u32);
426   tcp_connection_t *tc;
427
428   tc = tcp_connection_get (tci, thread_index);
429
430   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip4_address,
431               &tc->c_lcl_ip4, clib_net_to_host_u16 (tc->c_lcl_port),
432               format_ip4_address, &tc->c_rmt_ip4,
433               clib_net_to_host_u16 (tc->c_rmt_port));
434
435   return s;
436 }
437
438 u8 *
439 format_tcp_session_ip6 (u8 * s, va_list * args)
440 {
441   u32 tci = va_arg (*args, u32);
442   u32 thread_index = va_arg (*args, u32);
443   tcp_connection_t *tc = tcp_connection_get (tci, thread_index);
444   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip6_address,
445               &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port),
446               format_ip6_address, &tc->c_rmt_ip6,
447               clib_net_to_host_u16 (tc->c_rmt_port));
448   return s;
449 }
450
451 u8 *
452 format_tcp_listener_session_ip4 (u8 * s, va_list * args)
453 {
454   u32 tci = va_arg (*args, u32);
455   tcp_connection_t *tc = tcp_listener_get (tci);
456   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip4_address,
457               &tc->c_lcl_ip4, clib_net_to_host_u16 (tc->c_lcl_port),
458               format_ip4_address, &tc->c_rmt_ip4,
459               clib_net_to_host_u16 (tc->c_rmt_port));
460   return s;
461 }
462
463 u8 *
464 format_tcp_listener_session_ip6 (u8 * s, va_list * args)
465 {
466   u32 tci = va_arg (*args, u32);
467   tcp_connection_t *tc = tcp_listener_get (tci);
468   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip6_address,
469               &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port),
470               format_ip6_address, &tc->c_rmt_ip6,
471               clib_net_to_host_u16 (tc->c_rmt_port));
472   return s;
473 }
474
475 u8 *
476 format_tcp_half_open_session_ip4 (u8 * s, va_list * args)
477 {
478   u32 tci = va_arg (*args, u32);
479   tcp_connection_t *tc = tcp_half_open_connection_get (tci);
480   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip4_address,
481               &tc->c_lcl_ip4, clib_net_to_host_u16 (tc->c_lcl_port),
482               format_ip4_address, &tc->c_rmt_ip4,
483               clib_net_to_host_u16 (tc->c_rmt_port));
484   return s;
485 }
486
487 u8 *
488 format_tcp_half_open_session_ip6 (u8 * s, va_list * args)
489 {
490   u32 tci = va_arg (*args, u32);
491   tcp_connection_t *tc = tcp_half_open_connection_get (tci);
492   s = format (s, "[%s] %U:%d->%U:%d", "tcp", format_ip6_address,
493               &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port),
494               format_ip6_address, &tc->c_rmt_ip6,
495               clib_net_to_host_u16 (tc->c_rmt_port));
496   return s;
497 }
498
499 transport_connection_t *
500 tcp_session_get_transport (u32 conn_index, u32 thread_index)
501 {
502   tcp_connection_t *tc = tcp_connection_get (conn_index, thread_index);
503   return &tc->connection;
504 }
505
506 transport_connection_t *
507 tcp_half_open_session_get_transport (u32 conn_index)
508 {
509   tcp_connection_t *tc = tcp_half_open_connection_get (conn_index);
510   return &tc->connection;
511 }
512
513 u16
514 tcp_session_send_mss (transport_connection_t * trans_conn)
515 {
516   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
517   return tc->snd_mss;
518 }
519
520 u32
521 tcp_session_send_space (transport_connection_t * trans_conn)
522 {
523   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
524   return tcp_available_snd_space (tc);
525 }
526
527 u32
528 tcp_session_tx_fifo_offset (transport_connection_t * trans_conn)
529 {
530   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
531   return (tc->snd_nxt - tc->snd_una);
532 }
533
534 /* *INDENT-OFF* */
535 const static transport_proto_vft_t tcp4_proto = {
536   .bind = tcp_session_bind_ip4,
537   .unbind = tcp_session_unbind_ip4,
538   .push_header = tcp_push_header,
539   .get_connection = tcp_session_get_transport,
540   .get_listener = tcp_session_get_listener,
541   .get_half_open = tcp_half_open_session_get_transport,
542   .open = tcp_session_open_ip4,
543   .close = tcp_session_close,
544   .cleanup = tcp_session_cleanup,
545   .send_mss = tcp_session_send_mss,
546   .send_space = tcp_session_send_space,
547   .tx_fifo_offset = tcp_session_tx_fifo_offset,
548   .format_connection = format_tcp_session_ip4,
549   .format_listener = format_tcp_listener_session_ip4,
550   .format_half_open = format_tcp_half_open_session_ip4
551 };
552
553 const static transport_proto_vft_t tcp6_proto = {
554   .bind = tcp_session_bind_ip6,
555   .unbind = tcp_session_unbind_ip6,
556   .push_header = tcp_push_header,
557   .get_connection = tcp_session_get_transport,
558   .get_listener = tcp_session_get_listener,
559   .get_half_open = tcp_half_open_session_get_transport,
560   .open = tcp_session_open_ip6,
561   .close = tcp_session_close,
562   .cleanup = tcp_session_cleanup,
563   .send_mss = tcp_session_send_mss,
564   .send_space = tcp_session_send_space,
565   .tx_fifo_offset = tcp_session_tx_fifo_offset,
566   .format_connection = format_tcp_session_ip6,
567   .format_listener = format_tcp_listener_session_ip6,
568   .format_half_open = format_tcp_half_open_session_ip6
569 };
570 /* *INDENT-ON* */
571
572 void
573 tcp_timer_keep_handler (u32 conn_index)
574 {
575   u32 cpu_index = os_get_cpu_number ();
576   tcp_connection_t *tc;
577
578   tc = tcp_connection_get (conn_index, cpu_index);
579   tc->timers[TCP_TIMER_KEEP] = TCP_TIMER_HANDLE_INVALID;
580
581   tcp_connection_close (tc);
582 }
583
584 void
585 tcp_timer_establish_handler (u32 conn_index)
586 {
587   tcp_connection_t *tc;
588   u8 sst;
589
590   tc = tcp_half_open_connection_get (conn_index);
591   tc->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
592
593   ASSERT (tc->state == TCP_STATE_SYN_SENT);
594
595   sst = tc->c_is_ip4 ? SESSION_TYPE_IP4_TCP : SESSION_TYPE_IP6_TCP;
596   stream_session_connect_notify (&tc->connection, sst, 1 /* fail */ );
597
598   tcp_connection_cleanup (tc);
599 }
600
601 void
602 tcp_timer_waitclose_handler (u32 conn_index)
603 {
604   u32 cpu_index = os_get_cpu_number ();
605   tcp_connection_t *tc;
606
607   tc = tcp_connection_get (conn_index, cpu_index);
608   tc->timers[TCP_TIMER_WAITCLOSE] = TCP_TIMER_HANDLE_INVALID;
609
610   /* Session didn't come back with a close(). Send FIN either way
611    * and switch to LAST_ACK. */
612   if (tc->state == TCP_STATE_CLOSE_WAIT)
613     {
614       if (tc->flags & TCP_CONN_FINSNT)
615         {
616           clib_warning ("FIN was sent and still in CLOSE WAIT. Weird!");
617         }
618
619       tcp_send_fin (tc);
620       tc->state = TCP_STATE_LAST_ACK;
621
622       /* Make sure we don't wait in LAST ACK forever */
623       tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
624
625       /* Don't delete the connection yet */
626       return;
627     }
628
629   tcp_connection_del (tc);
630 }
631
632 /* *INDENT-OFF* */
633 static timer_expiration_handler *timer_expiration_handlers[TCP_N_TIMERS] =
634 {
635     tcp_timer_retransmit_handler,
636     tcp_timer_delack_handler,
637     0,
638     tcp_timer_keep_handler,
639     tcp_timer_waitclose_handler,
640     tcp_timer_retransmit_syn_handler,
641     tcp_timer_establish_handler
642 };
643 /* *INDENT-ON* */
644
645 static void
646 tcp_expired_timers_dispatch (u32 * expired_timers)
647 {
648   int i;
649   u32 connection_index, timer_id;
650
651   for (i = 0; i < vec_len (expired_timers); i++)
652     {
653       /* Get session index and timer id */
654       connection_index = expired_timers[i] & 0x0FFFFFFF;
655       timer_id = expired_timers[i] >> 28;
656
657       /* Handle expiration */
658       (*timer_expiration_handlers[timer_id]) (connection_index);
659     }
660 }
661
662 void
663 tcp_initialize_timer_wheels (tcp_main_t * tm)
664 {
665   tw_timer_wheel_16t_2w_512sl_t *tw;
666   vec_foreach (tw, tm->timer_wheels)
667   {
668     tw_timer_wheel_init_16t_2w_512sl (tw, tcp_expired_timers_dispatch,
669                                       100e-3 /* timer period 100ms */ , ~0);
670     tw->last_run_time = vlib_time_now (tm->vlib_main);
671   }
672 }
673
674 clib_error_t *
675 tcp_main_enable (vlib_main_t * vm)
676 {
677   tcp_main_t *tm = vnet_get_tcp_main ();
678   ip_protocol_info_t *pi;
679   ip_main_t *im = &ip_main;
680   vlib_thread_main_t *vtm = vlib_get_thread_main ();
681   clib_error_t *error = 0;
682   u32 num_threads;
683
684   if ((error = vlib_call_init_function (vm, ip_main_init)))
685     return error;
686   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
687     return error;
688   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
689     return error;
690
691   /*
692    * Registrations
693    */
694
695   /* Register with IP */
696   pi = ip_get_protocol_info (im, IP_PROTOCOL_TCP);
697   if (pi == 0)
698     return clib_error_return (0, "TCP protocol info AWOL");
699   pi->format_header = format_tcp_header;
700   pi->unformat_pg_edit = unformat_pg_tcp_header;
701
702   ip4_register_protocol (IP_PROTOCOL_TCP, tcp4_input_node.index);
703
704   /* Register as transport with URI */
705   session_register_transport (SESSION_TYPE_IP4_TCP, &tcp4_proto);
706   session_register_transport (SESSION_TYPE_IP6_TCP, &tcp6_proto);
707
708   /*
709    * Initialize data structures
710    */
711
712   num_threads = 1 /* main thread */  + vtm->n_threads;
713   vec_validate (tm->connections, num_threads - 1);
714
715   /* Initialize per worker thread tx buffers (used for control messages) */
716   vec_validate (tm->tx_buffers, num_threads - 1);
717
718   /* Initialize timer wheels */
719   vec_validate (tm->timer_wheels, num_threads - 1);
720   tcp_initialize_timer_wheels (tm);
721
722   vec_validate (tm->delack_connections, num_threads - 1);
723
724   /* Initialize clocks per tick for TCP timestamp. Used to compute
725    * monotonically increasing timestamps. */
726   tm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
727     / TCP_TSTAMP_RESOLUTION;
728
729   clib_bihash_init_24_8 (&tm->local_endpoints_table, "local endpoint table",
730                          200000 /* $$$$ config parameter nbuckets */ ,
731                          (64 << 20) /*$$$ config parameter table size */ );
732
733   return error;
734 }
735
736 clib_error_t *
737 vnet_tcp_enable_disable (vlib_main_t * vm, u8 is_en)
738 {
739   if (is_en)
740     {
741       if (tcp_main.is_enabled)
742         return 0;
743
744       return tcp_main_enable (vm);
745     }
746   else
747     {
748       tcp_main.is_enabled = 0;
749     }
750
751   return 0;
752 }
753
754 clib_error_t *
755 tcp_init (vlib_main_t * vm)
756 {
757   tcp_main_t *tm = vnet_get_tcp_main ();
758
759   tm->vlib_main = vm;
760   tm->vnet_main = vnet_get_main ();
761   tm->is_enabled = 0;
762
763   return 0;
764 }
765
766 VLIB_INIT_FUNCTION (tcp_init);
767
768 /*
769  * fd.io coding-style-patch-verification: ON
770  *
771  * Local Variables:
772  * eval: (c-set-style "gnu")
773  * End:
774  */