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