tcp: improve pacing after idle send periods
[vpp.git] / src / vnet / session / transport.c
1 /*
2  * Copyright (c) 2017-2019 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/session/transport.h>
17 #include <vnet/session/session.h>
18 #include <vnet/fib/fib.h>
19
20 /**
21  * Per-type vector of transport protocol virtual function tables
22  */
23 transport_proto_vft_t *tp_vfts;
24
25 /*
26  * Port allocator seed
27  */
28 static u32 port_allocator_seed;
29
30 /*
31  * Local endpoints table
32  */
33 static transport_endpoint_table_t local_endpoints_table;
34
35 /*
36  * Pool of local endpoints
37  */
38 static transport_endpoint_t *local_endpoints;
39
40 /*
41  * Local endpoints pool lock
42  */
43 static clib_spinlock_t local_endpoints_lock;
44
45 /*
46  * Period used by transport pacers. Initialized by session layer
47  */
48 static double transport_pacer_period;
49
50 u8 *
51 format_transport_proto (u8 * s, va_list * args)
52 {
53   u32 transport_proto = va_arg (*args, u32);
54   switch (transport_proto)
55     {
56 #define _(sym, str, sstr)                       \
57     case TRANSPORT_PROTO_ ## sym:               \
58       s = format (s, str);                      \
59       break;
60       foreach_transport_proto
61 #undef _
62     default:
63       s = format (s, "UNKNOWN");
64       break;
65     }
66   return s;
67 }
68
69 u8 *
70 format_transport_proto_short (u8 * s, va_list * args)
71 {
72   u32 transport_proto = va_arg (*args, u32);
73   switch (transport_proto)
74     {
75 #define _(sym, str, sstr)                       \
76     case TRANSPORT_PROTO_ ## sym:               \
77       s = format (s, sstr);                     \
78       break;
79       foreach_transport_proto
80 #undef _
81     default:
82       s = format (s, "?");
83       break;
84     }
85   return s;
86 }
87
88 u8 *
89 format_transport_connection (u8 * s, va_list * args)
90 {
91   u32 transport_proto = va_arg (*args, u32);
92   u32 conn_index = va_arg (*args, u32);
93   u32 thread_index = va_arg (*args, u32);
94   u32 verbose = va_arg (*args, u32);
95   transport_proto_vft_t *tp_vft;
96   transport_connection_t *tc;
97   u32 indent;
98
99   tp_vft = transport_protocol_get_vft (transport_proto);
100   if (!tp_vft)
101     return s;
102
103   s = format (s, "%U", tp_vft->format_connection, conn_index, thread_index,
104               verbose);
105   tc = tp_vft->get_connection (conn_index, thread_index);
106   if (tc && transport_connection_is_tx_paced (tc) && verbose > 1)
107     {
108       indent = format_get_indent (s) + 1;
109       s = format (s, "%Upacer: %U\n", format_white_space, indent,
110                   format_transport_pacer, &tc->pacer);
111     }
112   return s;
113 }
114
115 u8 *
116 format_transport_listen_connection (u8 * s, va_list * args)
117 {
118   u32 transport_proto = va_arg (*args, u32);
119   transport_proto_vft_t *tp_vft;
120
121   tp_vft = transport_protocol_get_vft (transport_proto);
122   if (!tp_vft)
123     return s;
124
125   s = (tp_vft->format_listener) (s, args);
126   return s;
127 }
128
129 u8 *
130 format_transport_half_open_connection (u8 * s, va_list * args)
131 {
132   u32 transport_proto = va_arg (*args, u32);
133   u32 listen_index = va_arg (*args, u32);
134   transport_proto_vft_t *tp_vft;
135
136   tp_vft = transport_protocol_get_vft (transport_proto);
137   if (!tp_vft)
138     return s;
139
140   s = format (s, "%U", tp_vft->format_half_open, listen_index);
141   return s;
142 }
143
144 uword
145 unformat_transport_proto (unformat_input_t * input, va_list * args)
146 {
147   u32 *proto = va_arg (*args, u32 *);
148
149 #define _(sym, str, sstr)                                               \
150   if (unformat (input, str))                                            \
151     {                                                                   \
152       *proto = TRANSPORT_PROTO_ ## sym;                                 \
153       return 1;                                                         \
154     }
155   foreach_transport_proto
156 #undef _
157     return 0;
158 }
159
160 u32
161 transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
162                            ip46_address_t * ip, u16 port)
163 {
164   clib_bihash_kv_24_8_t kv;
165   int rv;
166
167   kv.key[0] = ip->as_u64[0];
168   kv.key[1] = ip->as_u64[1];
169   kv.key[2] = (u64) port << 8 | (u64) proto;
170
171   rv = clib_bihash_search_inline_24_8 (ht, &kv);
172   if (rv == 0)
173     return kv.value;
174
175   return ENDPOINT_INVALID_INDEX;
176 }
177
178 void
179 transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
180                               transport_endpoint_t * te, u32 value)
181 {
182   clib_bihash_kv_24_8_t kv;
183
184   kv.key[0] = te->ip.as_u64[0];
185   kv.key[1] = te->ip.as_u64[1];
186   kv.key[2] = (u64) te->port << 8 | (u64) proto;
187   kv.value = value;
188
189   clib_bihash_add_del_24_8 (ht, &kv, 1);
190 }
191
192 void
193 transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
194                               transport_endpoint_t * te)
195 {
196   clib_bihash_kv_24_8_t kv;
197
198   kv.key[0] = te->ip.as_u64[0];
199   kv.key[1] = te->ip.as_u64[1];
200   kv.key[2] = (u64) te->port << 8 | (u64) proto;
201
202   clib_bihash_add_del_24_8 (ht, &kv, 0);
203 }
204
205 /**
206  * Register transport virtual function table.
207  *
208  * @param transport_proto - transport protocol type (i.e., TCP, UDP ..)
209  * @param vft - virtual function table for transport proto
210  * @param fib_proto - network layer protocol
211  * @param output_node - output node index that session layer will hand off
212  *                      buffers to, for requested fib proto
213  */
214 void
215 transport_register_protocol (transport_proto_t transport_proto,
216                              const transport_proto_vft_t * vft,
217                              fib_protocol_t fib_proto, u32 output_node)
218 {
219   u8 is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
220
221   vec_validate (tp_vfts, transport_proto);
222   tp_vfts[transport_proto] = *vft;
223
224   session_register_transport (transport_proto, vft, is_ip4, output_node);
225 }
226
227 /**
228  * Get transport virtual function table
229  *
230  * @param type - session type (not protocol type)
231  */
232 transport_proto_vft_t *
233 transport_protocol_get_vft (transport_proto_t transport_proto)
234 {
235   if (transport_proto >= vec_len (tp_vfts))
236     return 0;
237   return &tp_vfts[transport_proto];
238 }
239
240 u8
241 transport_half_open_has_fifos (transport_proto_t tp)
242 {
243   return tp_vfts[tp].transport_options.half_open_has_fifos;
244 }
245
246 transport_service_type_t
247 transport_protocol_service_type (transport_proto_t tp)
248 {
249   return tp_vfts[tp].transport_options.service_type;
250 }
251
252 transport_tx_fn_type_t
253 transport_protocol_tx_fn_type (transport_proto_t tp)
254 {
255   return tp_vfts[tp].transport_options.tx_type;
256 }
257
258 void
259 transport_cleanup (transport_proto_t tp, u32 conn_index, u8 thread_index)
260 {
261   tp_vfts[tp].cleanup (conn_index, thread_index);
262 }
263
264 int
265 transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
266 {
267   return tp_vfts[tp].connect (tep);
268 }
269
270 void
271 transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
272 {
273   tp_vfts[tp].close (conn_index, thread_index);
274 }
275
276 void
277 transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index)
278 {
279   if (tp_vfts[tp].reset)
280     tp_vfts[tp].reset (conn_index, thread_index);
281   else
282     tp_vfts[tp].close (conn_index, thread_index);
283 }
284
285 u32
286 transport_start_listen (transport_proto_t tp, u32 session_index,
287                         transport_endpoint_t * tep)
288 {
289   return tp_vfts[tp].start_listen (session_index, tep);
290 }
291
292 u32
293 transport_stop_listen (transport_proto_t tp, u32 conn_index)
294 {
295   return tp_vfts[tp].stop_listen (conn_index);
296 }
297
298 u8
299 transport_protocol_is_cl (transport_proto_t tp)
300 {
301   return (tp_vfts[tp].transport_options.service_type == TRANSPORT_SERVICE_CL);
302 }
303
304 always_inline void
305 default_get_transport_endpoint (transport_connection_t * tc,
306                                 transport_endpoint_t * tep, u8 is_lcl)
307 {
308   if (is_lcl)
309     {
310       tep->port = tc->lcl_port;
311       tep->is_ip4 = tc->is_ip4;
312       clib_memcpy_fast (&tep->ip, &tc->lcl_ip, sizeof (tc->lcl_ip));
313     }
314   else
315     {
316       tep->port = tc->rmt_port;
317       tep->is_ip4 = tc->is_ip4;
318       clib_memcpy_fast (&tep->ip, &tc->rmt_ip, sizeof (tc->rmt_ip));
319     }
320 }
321
322 void
323 transport_get_endpoint (transport_proto_t tp, u32 conn_index,
324                         u32 thread_index, transport_endpoint_t * tep,
325                         u8 is_lcl)
326 {
327   if (tp_vfts[tp].get_transport_endpoint)
328     tp_vfts[tp].get_transport_endpoint (conn_index, thread_index, tep,
329                                         is_lcl);
330   else
331     {
332       transport_connection_t *tc;
333       tc = transport_get_connection (tp, conn_index, thread_index);
334       default_get_transport_endpoint (tc, tep, is_lcl);
335     }
336 }
337
338 void
339 transport_get_listener_endpoint (transport_proto_t tp, u32 conn_index,
340                                  transport_endpoint_t * tep, u8 is_lcl)
341 {
342   if (tp_vfts[tp].get_transport_listener_endpoint)
343     tp_vfts[tp].get_transport_listener_endpoint (conn_index, tep, is_lcl);
344   else
345     {
346       transport_connection_t *tc;
347       tc = transport_get_listener (tp, conn_index);
348       default_get_transport_endpoint (tc, tep, is_lcl);
349     }
350 }
351
352 #define PORT_MASK ((1 << 16)- 1)
353
354 void
355 transport_endpoint_del (u32 tepi)
356 {
357   clib_spinlock_lock_if_init (&local_endpoints_lock);
358   pool_put_index (local_endpoints, tepi);
359   clib_spinlock_unlock_if_init (&local_endpoints_lock);
360 }
361
362 always_inline transport_endpoint_t *
363 transport_endpoint_new (void)
364 {
365   transport_endpoint_t *tep;
366   pool_get_zero (local_endpoints, tep);
367   return tep;
368 }
369
370 void
371 transport_endpoint_cleanup (u8 proto, ip46_address_t * lcl_ip, u16 port)
372 {
373   u32 tepi;
374   transport_endpoint_t *tep;
375
376   /* Cleanup local endpoint if this was an active connect */
377   tepi = transport_endpoint_lookup (&local_endpoints_table, proto, lcl_ip,
378                                     clib_net_to_host_u16 (port));
379   if (tepi != ENDPOINT_INVALID_INDEX)
380     {
381       tep = pool_elt_at_index (local_endpoints, tepi);
382       transport_endpoint_table_del (&local_endpoints_table, proto, tep);
383       transport_endpoint_del (tepi);
384     }
385 }
386
387 static void
388 transport_endpoint_mark_used (u8 proto, ip46_address_t * ip, u16 port)
389 {
390   transport_endpoint_t *tep;
391   clib_spinlock_lock_if_init (&local_endpoints_lock);
392   tep = transport_endpoint_new ();
393   clib_memcpy_fast (&tep->ip, ip, sizeof (*ip));
394   tep->port = port;
395   transport_endpoint_table_add (&local_endpoints_table, proto, tep,
396                                 tep - local_endpoints);
397   clib_spinlock_unlock_if_init (&local_endpoints_lock);
398 }
399
400 /**
401  * Allocate local port and add if successful add entry to local endpoint
402  * table to mark the pair as used.
403  */
404 int
405 transport_alloc_local_port (u8 proto, ip46_address_t * ip)
406 {
407   u16 min = 1024, max = 65535;  /* XXX configurable ? */
408   int tries, limit;
409   u32 tei;
410
411   limit = max - min;
412
413   /* Only support active opens from thread 0 */
414   ASSERT (vlib_get_thread_index () == 0);
415
416   /* Search for first free slot */
417   for (tries = 0; tries < limit; tries++)
418     {
419       u16 port = 0;
420
421       /* Find a port in the specified range */
422       while (1)
423         {
424           port = random_u32 (&port_allocator_seed) & PORT_MASK;
425           if (PREDICT_TRUE (port >= min && port < max))
426             break;
427         }
428
429       /* Look it up. If not found, we're done */
430       tei = transport_endpoint_lookup (&local_endpoints_table, proto, ip,
431                                        port);
432       if (tei == ENDPOINT_INVALID_INDEX)
433         {
434           transport_endpoint_mark_used (proto, ip, port);
435           return port;
436         }
437     }
438   return -1;
439 }
440
441 static clib_error_t *
442 transport_get_interface_ip (u32 sw_if_index, u8 is_ip4, ip46_address_t * addr)
443 {
444   if (is_ip4)
445     {
446       ip4_address_t *ip4;
447       ip4 = ip_interface_get_first_ip (sw_if_index, 1);
448       if (!ip4)
449         return clib_error_return (0, "no routable ip4 address on %U",
450                                   format_vnet_sw_if_index_name,
451                                   vnet_get_main (), sw_if_index);
452       addr->ip4.as_u32 = ip4->as_u32;
453     }
454   else
455     {
456       ip6_address_t *ip6;
457       ip6 = ip_interface_get_first_ip (sw_if_index, 0);
458       if (ip6 == 0)
459         return clib_error_return (0, "no routable ip6 addresses on %U",
460                                   format_vnet_sw_if_index_name,
461                                   vnet_get_main (), sw_if_index);
462       clib_memcpy_fast (&addr->ip6, ip6, sizeof (*ip6));
463     }
464   return 0;
465 }
466
467 static clib_error_t *
468 transport_find_local_ip_for_remote (u32 sw_if_index,
469                                     transport_endpoint_t * rmt,
470                                     ip46_address_t * lcl_addr)
471 {
472   fib_node_index_t fei;
473   fib_prefix_t prefix;
474
475   if (sw_if_index == ENDPOINT_INVALID_INDEX)
476     {
477       /* Find a FIB path to the destination */
478       clib_memcpy_fast (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
479       prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
480       prefix.fp_len = rmt->is_ip4 ? 32 : 128;
481
482       ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
483       fei = fib_table_lookup (rmt->fib_index, &prefix);
484
485       /* Couldn't find route to destination. Bail out. */
486       if (fei == FIB_NODE_INDEX_INVALID)
487         return clib_error_return (0, "no route to %U", format_ip46_address,
488                                   &rmt->ip, (rmt->is_ip4 == 0) + 1);
489
490       sw_if_index = fib_entry_get_resolving_interface (fei);
491       if (sw_if_index == ENDPOINT_INVALID_INDEX)
492         return clib_error_return (0, "no resolving interface for %U",
493                                   format_ip46_address, &rmt->ip,
494                                   (rmt->is_ip4 == 0) + 1);
495     }
496
497   clib_memset (lcl_addr, 0, sizeof (*lcl_addr));
498   return transport_get_interface_ip (sw_if_index, rmt->is_ip4, lcl_addr);
499 }
500
501 int
502 transport_alloc_local_endpoint (u8 proto, transport_endpoint_cfg_t * rmt_cfg,
503                                 ip46_address_t * lcl_addr, u16 * lcl_port)
504 {
505   transport_endpoint_t *rmt = (transport_endpoint_t *) rmt_cfg;
506   clib_error_t *error;
507   int port;
508   u32 tei;
509
510   /*
511    * Find the local address
512    */
513   if (ip_is_zero (&rmt_cfg->peer.ip, rmt_cfg->peer.is_ip4))
514     {
515       error = transport_find_local_ip_for_remote (rmt_cfg->peer.sw_if_index,
516                                                   rmt, lcl_addr);
517       if (error)
518         {
519           clib_error_report (error);
520           return -1;
521         }
522     }
523   else
524     {
525       /* Assume session layer vetted this address */
526       clib_memcpy_fast (lcl_addr, &rmt_cfg->peer.ip,
527                         sizeof (rmt_cfg->peer.ip));
528     }
529
530   /*
531    * Allocate source port
532    */
533   if (rmt_cfg->peer.port == 0)
534     {
535       port = transport_alloc_local_port (proto, lcl_addr);
536       if (port < 1)
537         {
538           clib_warning ("Failed to allocate src port");
539           return -1;
540         }
541       *lcl_port = port;
542     }
543   else
544     {
545       port = clib_net_to_host_u16 (rmt_cfg->peer.port);
546       tei = transport_endpoint_lookup (&local_endpoints_table, proto,
547                                        lcl_addr, port);
548       if (tei != ENDPOINT_INVALID_INDEX)
549         return -1;
550
551       transport_endpoint_mark_used (proto, lcl_addr, port);
552       *lcl_port = port;
553     }
554
555   return 0;
556 }
557
558 #define SPACER_CPU_TICKS_PER_PERIOD_SHIFT 10
559 #define SPACER_CPU_TICKS_PER_PERIOD (1 << SPACER_CPU_TICKS_PER_PERIOD_SHIFT)
560
561 u8 *
562 format_transport_pacer (u8 * s, va_list * args)
563 {
564   spacer_t *pacer = va_arg (*args, spacer_t *);
565   vlib_main_t *vm = vlib_get_main ();
566   u64 now, diff;
567
568   now = vm->clib_time.last_cpu_time;
569   diff = now - (pacer->last_update << SPACER_CPU_TICKS_PER_PERIOD_SHIFT);
570   s = format (s, "rate %u bucket %u t/p %.3f last_update %.3f",
571               pacer->bytes_per_sec, pacer->bucket, pacer->tokens_per_period,
572               diff * vm->clib_time.seconds_per_clock);
573   return s;
574 }
575
576 static inline u32
577 spacer_max_burst (spacer_t * pacer, u64 norm_time_now)
578 {
579   u64 n_periods = norm_time_now - pacer->last_update;
580   u64 inc;
581
582   if (PREDICT_FALSE (n_periods > 5e5))
583     {
584       pacer->last_update = norm_time_now;
585       pacer->bucket = TRANSPORT_PACER_MIN_BURST;
586       return TRANSPORT_PACER_MIN_BURST;
587     }
588
589   if (n_periods > 0
590       && (inc = (f32) n_periods * pacer->tokens_per_period) > 10)
591     {
592       pacer->last_update = norm_time_now;
593       pacer->bucket = clib_min (pacer->bucket + inc, pacer->bytes_per_sec);
594     }
595
596   return clib_min (pacer->bucket, TRANSPORT_PACER_MAX_BURST);
597 }
598
599 static inline void
600 spacer_update_bucket (spacer_t * pacer, u32 bytes)
601 {
602   ASSERT (pacer->bucket >= bytes);
603   pacer->bucket -= bytes;
604 }
605
606 static inline void
607 spacer_set_pace_rate (spacer_t * pacer, u64 rate_bytes_per_sec)
608 {
609   ASSERT (rate_bytes_per_sec != 0);
610   pacer->bytes_per_sec = rate_bytes_per_sec;
611   pacer->tokens_per_period = rate_bytes_per_sec / transport_pacer_period;
612 }
613
614 static inline u64
615 spacer_pace_rate (spacer_t * pacer)
616 {
617   return pacer->bytes_per_sec;
618 }
619
620 static inline void
621 spacer_reset_bucket (spacer_t * pacer, u64 norm_time_now)
622 {
623   pacer->last_update = norm_time_now;
624   pacer->bucket = 0;
625 }
626
627 void
628 transport_connection_tx_pacer_reset (transport_connection_t * tc,
629                                      u32 rate_bytes_per_sec,
630                                      u32 start_bucket, u64 time_now)
631 {
632   spacer_t *pacer = &tc->pacer;
633   spacer_set_pace_rate (&tc->pacer, rate_bytes_per_sec);
634   pacer->last_update = time_now >> SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
635   pacer->bucket = start_bucket;
636 }
637
638 void
639 transport_connection_tx_pacer_init (transport_connection_t * tc,
640                                     u32 rate_bytes_per_sec,
641                                     u32 initial_bucket)
642 {
643   vlib_main_t *vm = vlib_get_main ();
644   tc->flags |= TRANSPORT_CONNECTION_F_IS_TX_PACED;
645   transport_connection_tx_pacer_reset (tc, rate_bytes_per_sec,
646                                        initial_bucket,
647                                        vm->clib_time.last_cpu_time);
648 }
649
650 void
651 transport_connection_tx_pacer_update (transport_connection_t * tc,
652                                       u64 bytes_per_sec)
653 {
654   spacer_set_pace_rate (&tc->pacer, bytes_per_sec);
655 }
656
657 u32
658 transport_connection_tx_pacer_burst (transport_connection_t * tc,
659                                      u64 time_now)
660 {
661   time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
662   return spacer_max_burst (&tc->pacer, time_now);
663 }
664
665 void
666 transport_connection_tx_pacer_reset_bucket (transport_connection_t * tc,
667                                             u64 time_now)
668 {
669   time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
670   spacer_reset_bucket (&tc->pacer, time_now);
671 }
672
673 u32
674 transport_connection_snd_space (transport_connection_t * tc, u64 time_now,
675                                 u16 mss)
676 {
677   u32 snd_space, max_paced_burst;
678
679   snd_space = tp_vfts[tc->proto].send_space (tc);
680   if (snd_space && transport_connection_is_tx_paced (tc))
681     {
682       time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
683       max_paced_burst = spacer_max_burst (&tc->pacer, time_now);
684       max_paced_burst =
685         (max_paced_burst < TRANSPORT_PACER_MIN_BURST) ? 0 : max_paced_burst;
686       snd_space = clib_min (snd_space, max_paced_burst);
687       return snd_space >= mss ? snd_space - snd_space % mss : snd_space;
688     }
689   return snd_space;
690 }
691
692 u64
693 transport_connection_tx_pacer_rate (transport_connection_t * tc)
694 {
695   return spacer_pace_rate (&tc->pacer);
696 }
697
698 void
699 transport_connection_update_tx_bytes (transport_connection_t * tc, u32 bytes)
700 {
701   if (transport_connection_is_tx_paced (tc))
702     spacer_update_bucket (&tc->pacer, bytes);
703 }
704
705 void
706 transport_connection_tx_pacer_update_bytes (transport_connection_t * tc,
707                                             u32 bytes)
708 {
709   spacer_update_bucket (&tc->pacer, bytes);
710 }
711
712 void
713 transport_init_tx_pacers_period (void)
714 {
715   f64 cpu_freq = os_cpu_clock_frequency ();
716   transport_pacer_period = cpu_freq / SPACER_CPU_TICKS_PER_PERIOD;
717 }
718
719 void
720 transport_update_time (f64 time_now, u8 thread_index)
721 {
722   transport_proto_vft_t *vft;
723   vec_foreach (vft, tp_vfts)
724   {
725     if (vft->update_time)
726       (vft->update_time) (time_now, thread_index);
727   }
728 }
729
730 void
731 transport_enable_disable (vlib_main_t * vm, u8 is_en)
732 {
733   transport_proto_vft_t *vft;
734   vec_foreach (vft, tp_vfts)
735   {
736     if (vft->enable)
737       (vft->enable) (vm, is_en);
738   }
739 }
740
741 void
742 transport_init (void)
743 {
744   vlib_thread_main_t *vtm = vlib_get_thread_main ();
745   session_main_t *smm = vnet_get_session_main ();
746   u32 num_threads;
747
748   if (smm->local_endpoints_table_buckets == 0)
749     smm->local_endpoints_table_buckets = 250000;
750   if (smm->local_endpoints_table_memory == 0)
751     smm->local_endpoints_table_memory = 512 << 20;
752
753   /* Initialize [port-allocator] random number seed */
754   port_allocator_seed = (u32) clib_cpu_time_now ();
755
756   clib_bihash_init_24_8 (&local_endpoints_table, "local endpoints table",
757                          smm->local_endpoints_table_buckets,
758                          smm->local_endpoints_table_memory);
759   num_threads = 1 /* main thread */  + vtm->n_threads;
760   if (num_threads > 1)
761     clib_spinlock_init (&local_endpoints_lock);
762 }
763
764 /*
765  * fd.io coding-style-patch-verification: ON
766  *
767  * Local Variables:
768  * eval: (c-set-style "gnu")
769  * End:
770  */