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