LCOV - code coverage report
Current view: top level - vnet/session - transport.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 341 430 79.3 %
Date: 2023-07-05 22:20:52 Functions: 48 62 77.4 %

          Line data    Source code
       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             : typedef struct local_endpoint_
      26             : {
      27             :   transport_endpoint_t ep;
      28             :   transport_proto_t proto;
      29             :   int refcnt;
      30             : } local_endpoint_t;
      31             : 
      32             : typedef struct transport_main_
      33             : {
      34             :   transport_endpoint_table_t local_endpoints_table;
      35             :   local_endpoint_t *local_endpoints;
      36             :   u32 *lcl_endpts_freelist;
      37             :   u32 port_allocator_seed;
      38             :   u8 lcl_endpts_cleanup_pending;
      39             :   clib_spinlock_t local_endpoints_lock;
      40             : } transport_main_t;
      41             : 
      42             : static transport_main_t tp_main;
      43             : 
      44             : u8 *
      45           0 : format_transport_proto (u8 * s, va_list * args)
      46             : {
      47           0 :   u32 transport_proto = va_arg (*args, u32);
      48             : 
      49           0 :   if (tp_vfts[transport_proto].transport_options.name)
      50           0 :     s = format (s, "%s", tp_vfts[transport_proto].transport_options.name);
      51             :   else
      52           0 :     s = format (s, "n/a");
      53             : 
      54           0 :   return s;
      55             : }
      56             : 
      57             : u8 *
      58           0 : format_transport_proto_short (u8 * s, va_list * args)
      59             : {
      60           0 :   u32 transport_proto = va_arg (*args, u32);
      61             :   char *short_name;
      62             : 
      63           0 :   short_name = tp_vfts[transport_proto].transport_options.short_name;
      64           0 :   if (short_name)
      65           0 :     s = format (s, "%s", short_name);
      66             :   else
      67           0 :     s = format (s, "NA");
      68             : 
      69           0 :   return s;
      70             : }
      71             : 
      72             : const char *transport_flags_str[] = {
      73             : #define _(sym, str) str,
      74             :   foreach_transport_connection_flag
      75             : #undef _
      76             : };
      77             : 
      78             : u8 *
      79          15 : format_transport_flags (u8 *s, va_list *args)
      80             : {
      81             :   transport_connection_flags_t flags;
      82          15 :   int i, last = -1;
      83             : 
      84          15 :   flags = va_arg (*args, transport_connection_flags_t);
      85             : 
      86          75 :   for (i = 0; i < TRANSPORT_CONNECTION_N_FLAGS; i++)
      87          60 :     if (flags & (1 << i))
      88          14 :       last = i;
      89             : 
      90          17 :   for (i = 0; i < last; i++)
      91             :     {
      92           2 :       if (flags & (1 << i))
      93           0 :         s = format (s, "%s, ", transport_flags_str[i]);
      94             :     }
      95          15 :   if (last >= 0)
      96          14 :     s = format (s, "%s", transport_flags_str[last]);
      97             : 
      98          15 :   return s;
      99             : }
     100             : 
     101             : u8 *
     102          17 : format_transport_connection (u8 * s, va_list * args)
     103             : {
     104          17 :   u32 transport_proto = va_arg (*args, u32);
     105          17 :   u32 conn_index = va_arg (*args, u32);
     106          17 :   u32 thread_index = va_arg (*args, u32);
     107          17 :   u32 verbose = va_arg (*args, u32);
     108             :   transport_proto_vft_t *tp_vft;
     109             :   transport_connection_t *tc;
     110             :   u32 indent;
     111             : 
     112          17 :   tp_vft = transport_protocol_get_vft (transport_proto);
     113          17 :   if (!tp_vft)
     114           0 :     return s;
     115             : 
     116          17 :   s = format (s, "%U", tp_vft->format_connection, conn_index, thread_index,
     117             :               verbose);
     118          17 :   tc = tp_vft->get_connection (conn_index, thread_index);
     119          17 :   if (tc && verbose > 1)
     120             :     {
     121          15 :       indent = format_get_indent (s) + 1;
     122          15 :       if (transport_connection_is_tx_paced (tc))
     123          12 :         s = format (s, "%Upacer: %U\n", format_white_space, indent,
     124             :                     format_transport_pacer, &tc->pacer, tc->thread_index);
     125          15 :       s = format (s, "%Utransport: flags: %U\n", format_white_space, indent,
     126          15 :                   format_transport_flags, tc->flags);
     127             :     }
     128          17 :   return s;
     129             : }
     130             : 
     131             : u8 *
     132           1 : format_transport_listen_connection (u8 * s, va_list * args)
     133             : {
     134           1 :   u32 transport_proto = va_arg (*args, u32);
     135             :   transport_proto_vft_t *tp_vft;
     136             : 
     137           1 :   tp_vft = transport_protocol_get_vft (transport_proto);
     138           1 :   if (!tp_vft)
     139           0 :     return s;
     140             : 
     141           1 :   s = (tp_vft->format_listener) (s, args);
     142           1 :   return s;
     143             : }
     144             : 
     145             : u8 *
     146           0 : format_transport_half_open_connection (u8 * s, va_list * args)
     147             : {
     148           0 :   u32 transport_proto = va_arg (*args, u32);
     149             :   transport_proto_vft_t *tp_vft;
     150             : 
     151           0 :   tp_vft = transport_protocol_get_vft (transport_proto);
     152           0 :   if (!tp_vft)
     153           0 :     return s;
     154             : 
     155           0 :   s = (tp_vft->format_half_open) (s, args);
     156           0 :   return s;
     157             : }
     158             : 
     159             : static u8
     160          57 : unformat_transport_str_match (unformat_input_t * input, const char *str)
     161             : {
     162             :   int i;
     163             : 
     164          57 :   if (strlen (str) > vec_len (input->buffer) - input->index)
     165           0 :     return 0;
     166             : 
     167          92 :   for (i = 0; i < strlen (str); i++)
     168             :     {
     169          83 :       if (input->buffer[i + input->index] != str[i])
     170          48 :         return 0;
     171             :     }
     172           9 :   return 1;
     173             : }
     174             : 
     175             : uword
     176           9 : unformat_transport_proto (unformat_input_t * input, va_list * args)
     177             : {
     178           9 :   u32 *proto = va_arg (*args, u32 *);
     179             :   transport_proto_vft_t *tp_vft;
     180           9 :   u8 longest_match = 0, match;
     181           9 :   char *str, *str_match = 0;
     182             :   transport_proto_t tp;
     183             : 
     184          81 :   for (tp = 0; tp < vec_len (tp_vfts); tp++)
     185             :     {
     186          72 :       tp_vft = &tp_vfts[tp];
     187          72 :       str = tp_vft->transport_options.name;
     188          72 :       if (!str)
     189          15 :         continue;
     190          57 :       if (unformat_transport_str_match (input, str))
     191             :         {
     192           9 :           match = strlen (str);
     193           9 :           if (match > longest_match)
     194             :             {
     195           9 :               *proto = tp;
     196           9 :               longest_match = match;
     197           9 :               str_match = str;
     198             :             }
     199             :         }
     200             :     }
     201           9 :   if (longest_match)
     202             :     {
     203           9 :       (void) unformat (input, str_match);
     204           9 :       return 1;
     205             :     }
     206             : 
     207           0 :   return 0;
     208             : }
     209             : 
     210             : u8 *
     211           0 : format_transport_protos (u8 * s, va_list * args)
     212             : {
     213             :   transport_proto_vft_t *tp_vft;
     214             : 
     215           0 :   vec_foreach (tp_vft, tp_vfts)
     216           0 :     s = format (s, "%s\n", tp_vft->transport_options.name);
     217             : 
     218           0 :   return s;
     219             : }
     220             : 
     221             : u32
     222         354 : transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
     223             :                            ip46_address_t * ip, u16 port)
     224             : {
     225             :   clib_bihash_kv_24_8_t kv;
     226             :   int rv;
     227             : 
     228         354 :   kv.key[0] = ip->as_u64[0];
     229         354 :   kv.key[1] = ip->as_u64[1];
     230         354 :   kv.key[2] = (u64) port << 8 | (u64) proto;
     231             : 
     232         354 :   rv = clib_bihash_search_inline_24_8 (ht, &kv);
     233         354 :   if (rv == 0)
     234          40 :     return kv.value;
     235             : 
     236         314 :   return ENDPOINT_INVALID_INDEX;
     237             : }
     238             : 
     239             : void
     240         155 : transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
     241             :                               transport_endpoint_t * te, u32 value)
     242             : {
     243             :   clib_bihash_kv_24_8_t kv;
     244             : 
     245         155 :   kv.key[0] = te->ip.as_u64[0];
     246         155 :   kv.key[1] = te->ip.as_u64[1];
     247         155 :   kv.key[2] = (u64) te->port << 8 | (u64) proto;
     248         155 :   kv.value = value;
     249             : 
     250         155 :   clib_bihash_add_del_24_8 (ht, &kv, 1);
     251         155 : }
     252             : 
     253             : void
     254           4 : transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
     255             :                               transport_endpoint_t * te)
     256             : {
     257             :   clib_bihash_kv_24_8_t kv;
     258             : 
     259           4 :   kv.key[0] = te->ip.as_u64[0];
     260           4 :   kv.key[1] = te->ip.as_u64[1];
     261           4 :   kv.key[2] = (u64) te->port << 8 | (u64) proto;
     262             : 
     263           4 :   clib_bihash_add_del_24_8 (ht, &kv, 0);
     264           4 : }
     265             : 
     266             : void
     267        6758 : transport_register_protocol (transport_proto_t transport_proto,
     268             :                              const transport_proto_vft_t * vft,
     269             :                              fib_protocol_t fib_proto, u32 output_node)
     270             : {
     271        6758 :   u8 is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
     272             : 
     273        6758 :   vec_validate (tp_vfts, transport_proto);
     274        6758 :   tp_vfts[transport_proto] = *vft;
     275             : 
     276        6758 :   session_register_transport (transport_proto, vft, is_ip4, output_node);
     277        6758 : }
     278             : 
     279             : transport_proto_t
     280           0 : transport_register_new_protocol (const transport_proto_vft_t * vft,
     281             :                                  fib_protocol_t fib_proto, u32 output_node)
     282             : {
     283             :   transport_proto_t transport_proto;
     284             :   u8 is_ip4;
     285             : 
     286           0 :   transport_proto = session_add_transport_proto ();
     287           0 :   is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
     288             : 
     289           0 :   vec_validate (tp_vfts, transport_proto);
     290           0 :   tp_vfts[transport_proto] = *vft;
     291             : 
     292           0 :   session_register_transport (transport_proto, vft, is_ip4, output_node);
     293             : 
     294           0 :   return transport_proto;
     295             : }
     296             : 
     297             : /**
     298             :  * Get transport virtual function table
     299             :  *
     300             :  * @param type - session type (not protocol type)
     301             :  */
     302             : transport_proto_vft_t *
     303       88205 : transport_protocol_get_vft (transport_proto_t transport_proto)
     304             : {
     305       88205 :   if (transport_proto >= vec_len (tp_vfts))
     306           0 :     return 0;
     307       88205 :   return &tp_vfts[transport_proto];
     308             : }
     309             : 
     310             : transport_service_type_t
     311         256 : transport_protocol_service_type (transport_proto_t tp)
     312             : {
     313         256 :   return tp_vfts[tp].transport_options.service_type;
     314             : }
     315             : 
     316             : transport_tx_fn_type_t
     317           0 : transport_protocol_tx_fn_type (transport_proto_t tp)
     318             : {
     319           0 :   return tp_vfts[tp].transport_options.tx_type;
     320             : }
     321             : 
     322             : void
     323           1 : transport_cleanup (transport_proto_t tp, u32 conn_index, u8 thread_index)
     324             : {
     325           1 :   tp_vfts[tp].cleanup (conn_index, thread_index);
     326           1 : }
     327             : 
     328             : void
     329           0 : transport_cleanup_half_open (transport_proto_t tp, u32 conn_index)
     330             : {
     331           0 :   if (tp_vfts[tp].cleanup_ho)
     332           0 :     tp_vfts[tp].cleanup_ho (conn_index);
     333           0 : }
     334             : 
     335             : int
     336         213 : transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
     337             : {
     338         213 :   if (PREDICT_FALSE (!tp_vfts[tp].connect))
     339           0 :     return SESSION_E_TRANSPORT_NO_REG;
     340         213 :   return tp_vfts[tp].connect (tep);
     341             : }
     342             : 
     343             : void
     344           0 : transport_half_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
     345             : {
     346           0 :   if (tp_vfts[tp].half_close)
     347           0 :     tp_vfts[tp].half_close (conn_index, thread_index);
     348           0 : }
     349             : 
     350             : void
     351         388 : transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
     352             : {
     353         388 :   tp_vfts[tp].close (conn_index, thread_index);
     354         388 : }
     355             : 
     356             : void
     357           0 : transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index)
     358             : {
     359           0 :   if (tp_vfts[tp].reset)
     360           0 :     tp_vfts[tp].reset (conn_index, thread_index);
     361             :   else
     362           0 :     tp_vfts[tp].close (conn_index, thread_index);
     363           0 : }
     364             : 
     365             : u32
     366          78 : transport_start_listen (transport_proto_t tp, u32 session_index,
     367             :                         transport_endpoint_cfg_t *tep)
     368             : {
     369          78 :   if (PREDICT_FALSE (!tp_vfts[tp].start_listen))
     370           0 :     return SESSION_E_TRANSPORT_NO_REG;
     371          78 :   return tp_vfts[tp].start_listen (session_index, tep);
     372             : }
     373             : 
     374             : u32
     375          62 : transport_stop_listen (transport_proto_t tp, u32 conn_index)
     376             : {
     377          62 :   return tp_vfts[tp].stop_listen (conn_index);
     378             : }
     379             : 
     380             : u8
     381           0 : transport_protocol_is_cl (transport_proto_t tp)
     382             : {
     383           0 :   return (tp_vfts[tp].transport_options.service_type == TRANSPORT_SERVICE_CL);
     384             : }
     385             : 
     386             : always_inline void
     387         146 : default_get_transport_endpoint (transport_connection_t * tc,
     388             :                                 transport_endpoint_t * tep, u8 is_lcl)
     389             : {
     390         146 :   if (is_lcl)
     391             :     {
     392         113 :       tep->port = tc->lcl_port;
     393         113 :       tep->is_ip4 = tc->is_ip4;
     394         113 :       clib_memcpy_fast (&tep->ip, &tc->lcl_ip, sizeof (tc->lcl_ip));
     395             :     }
     396             :   else
     397             :     {
     398          33 :       tep->port = tc->rmt_port;
     399          33 :       tep->is_ip4 = tc->is_ip4;
     400          33 :       clib_memcpy_fast (&tep->ip, &tc->rmt_ip, sizeof (tc->rmt_ip));
     401             :     }
     402         146 : }
     403             : 
     404             : void
     405         120 : transport_get_endpoint (transport_proto_t tp, u32 conn_index,
     406             :                         u32 thread_index, transport_endpoint_t * tep,
     407             :                         u8 is_lcl)
     408             : {
     409         120 :   if (tp_vfts[tp].get_transport_endpoint)
     410          17 :     tp_vfts[tp].get_transport_endpoint (conn_index, thread_index, tep,
     411             :                                         is_lcl);
     412             :   else
     413             :     {
     414             :       transport_connection_t *tc;
     415         103 :       tc = transport_get_connection (tp, conn_index, thread_index);
     416         103 :       default_get_transport_endpoint (tc, tep, is_lcl);
     417             :     }
     418         120 : }
     419             : 
     420             : void
     421          52 : transport_get_listener_endpoint (transport_proto_t tp, u32 conn_index,
     422             :                                  transport_endpoint_t * tep, u8 is_lcl)
     423             : {
     424          52 :   if (tp_vfts[tp].get_transport_listener_endpoint)
     425           9 :     tp_vfts[tp].get_transport_listener_endpoint (conn_index, tep, is_lcl);
     426             :   else
     427             :     {
     428             :       transport_connection_t *tc;
     429          43 :       tc = transport_get_listener (tp, conn_index);
     430          43 :       default_get_transport_endpoint (tc, tep, is_lcl);
     431             :     }
     432          52 : }
     433             : 
     434             : int
     435           5 : transport_connection_attribute (transport_proto_t tp, u32 conn_index,
     436             :                                 u8 thread_index, u8 is_get,
     437             :                                 transport_endpt_attr_t *attr)
     438             : {
     439           5 :   if (!tp_vfts[tp].attribute)
     440           2 :     return -1;
     441             : 
     442           3 :   return tp_vfts[tp].attribute (conn_index, thread_index, is_get, attr);
     443             : }
     444             : 
     445             : #define PORT_MASK ((1 << 16)- 1)
     446             : 
     447             : void
     448           4 : transport_endpoint_free (u32 tepi)
     449             : {
     450           4 :   transport_main_t *tm = &tp_main;
     451           4 :   pool_put_index (tm->local_endpoints, tepi);
     452           4 : }
     453             : 
     454             : always_inline local_endpoint_t *
     455         155 : transport_endpoint_alloc (void)
     456             : {
     457         155 :   transport_main_t *tm = &tp_main;
     458             :   local_endpoint_t *lep;
     459             : 
     460         155 :   ASSERT (vlib_get_thread_index () <= transport_cl_thread ());
     461             : 
     462         155 :   pool_get_aligned_safe (tm->local_endpoints, lep, 0);
     463         155 :   return lep;
     464             : }
     465             : 
     466             : static void
     467           4 : transport_cleanup_freelist (void)
     468             : {
     469           4 :   transport_main_t *tm = &tp_main;
     470             :   local_endpoint_t *lep;
     471             :   u32 *lep_indexp;
     472             : 
     473           4 :   clib_spinlock_lock (&tm->local_endpoints_lock);
     474             : 
     475           8 :   vec_foreach (lep_indexp, tm->lcl_endpts_freelist)
     476             :     {
     477           4 :       lep = pool_elt_at_index (tm->local_endpoints, *lep_indexp);
     478             : 
     479             :       /* Port re-shared after attempt to cleanup */
     480           4 :       if (lep->refcnt > 0)
     481           0 :         continue;
     482             : 
     483           4 :       transport_endpoint_table_del (&tm->local_endpoints_table, lep->proto,
     484             :                                     &lep->ep);
     485           4 :       transport_endpoint_free (*lep_indexp);
     486             :     }
     487             : 
     488           4 :   vec_reset_length (tm->lcl_endpts_freelist);
     489             : 
     490           4 :   tm->lcl_endpts_cleanup_pending = 0;
     491             : 
     492           4 :   clib_spinlock_unlock (&tm->local_endpoints_lock);
     493           4 : }
     494             : 
     495             : void
     496          38 : transport_program_endpoint_cleanup (u32 lepi)
     497             : {
     498          38 :   transport_main_t *tm = &tp_main;
     499          38 :   u8 flush_fl = 0;
     500             : 
     501             :   /* All workers can free connections. Synchronize access to freelist */
     502          38 :   clib_spinlock_lock (&tm->local_endpoints_lock);
     503             : 
     504          38 :   vec_add1 (tm->lcl_endpts_freelist, lepi);
     505             : 
     506             :   /* Avoid accumulating lots of endpoints for cleanup */
     507          38 :   if (!tm->lcl_endpts_cleanup_pending &&
     508          38 :       vec_len (tm->lcl_endpts_freelist) > 32)
     509             :     {
     510           0 :       tm->lcl_endpts_cleanup_pending = 1;
     511           0 :       flush_fl = 1;
     512             :     }
     513             : 
     514          38 :   clib_spinlock_unlock (&tm->local_endpoints_lock);
     515             : 
     516          38 :   if (flush_fl)
     517           0 :     session_send_rpc_evt_to_thread_force (transport_cl_thread (),
     518             :                                           transport_cleanup_freelist, 0);
     519          38 : }
     520             : 
     521             : int
     522         177 : transport_release_local_endpoint (u8 proto, ip46_address_t *lcl_ip, u16 port)
     523             : {
     524         177 :   transport_main_t *tm = &tp_main;
     525             :   local_endpoint_t *lep;
     526             :   u32 lepi;
     527             : 
     528         177 :   lepi = transport_endpoint_lookup (&tm->local_endpoints_table, proto, lcl_ip,
     529         177 :                                     clib_net_to_host_u16 (port));
     530         177 :   if (lepi == ENDPOINT_INVALID_INDEX)
     531         138 :     return -1;
     532             : 
     533          39 :   lep = pool_elt_at_index (tm->local_endpoints, lepi);
     534             : 
     535             :   /* Local endpoint no longer in use, program cleanup */
     536          39 :   if (!clib_atomic_sub_fetch (&lep->refcnt, 1))
     537             :     {
     538          38 :       transport_program_endpoint_cleanup (lepi);
     539          38 :       return 0;
     540             :     }
     541             : 
     542             :   /* Not an error, just in idication that endpoint was not cleaned up */
     543           1 :   return -1;
     544             : }
     545             : 
     546             : static int
     547         156 : transport_endpoint_mark_used (u8 proto, ip46_address_t *ip, u16 port)
     548             : {
     549         156 :   transport_main_t *tm = &tp_main;
     550             :   local_endpoint_t *lep;
     551             :   u32 tei;
     552             : 
     553         156 :   ASSERT (vlib_get_thread_index () <= transport_cl_thread ());
     554             : 
     555             :   tei =
     556         156 :     transport_endpoint_lookup (&tm->local_endpoints_table, proto, ip, port);
     557         156 :   if (tei != ENDPOINT_INVALID_INDEX)
     558           1 :     return SESSION_E_PORTINUSE;
     559             : 
     560             :   /* Pool reallocs with worker barrier */
     561         155 :   lep = transport_endpoint_alloc ();
     562         155 :   clib_memcpy_fast (&lep->ep.ip, ip, sizeof (*ip));
     563         155 :   lep->ep.port = port;
     564         155 :   lep->proto = proto;
     565         155 :   lep->refcnt = 1;
     566             : 
     567         155 :   transport_endpoint_table_add (&tm->local_endpoints_table, proto, &lep->ep,
     568         155 :                                 lep - tm->local_endpoints);
     569             : 
     570         155 :   return 0;
     571             : }
     572             : 
     573             : void
     574          21 : transport_share_local_endpoint (u8 proto, ip46_address_t * lcl_ip, u16 port)
     575             : {
     576          21 :   transport_main_t *tm = &tp_main;
     577             :   local_endpoint_t *lep;
     578             :   u32 lepi;
     579             : 
     580             :   /* Active opens should call this only from a control thread, which are also
     581             :    * used to allocate and free ports. So, pool has only one writer and
     582             :    * potentially many readers. Listeners are allocated with barrier */
     583          21 :   lepi = transport_endpoint_lookup (&tm->local_endpoints_table, proto, lcl_ip,
     584          21 :                                     clib_net_to_host_u16 (port));
     585          21 :   if (lepi != ENDPOINT_INVALID_INDEX)
     586             :     {
     587           0 :       lep = pool_elt_at_index (tm->local_endpoints, lepi);
     588           0 :       clib_atomic_add_fetch (&lep->refcnt, 1);
     589             :     }
     590          21 : }
     591             : 
     592             : /**
     593             :  * Allocate local port and add if successful add entry to local endpoint
     594             :  * table to mark the pair as used.
     595             :  */
     596             : int
     597         153 : transport_alloc_local_port (u8 proto, ip46_address_t *lcl_addr,
     598             :                             transport_endpoint_cfg_t *rmt)
     599             : {
     600         153 :   u16 min = 1024, max = 65535;  /* XXX configurable ? */
     601         153 :   transport_main_t *tm = &tp_main;
     602             :   int tries, limit;
     603             : 
     604         153 :   limit = max - min;
     605             : 
     606             :   /* Only support active opens from one of ctrl threads */
     607         153 :   ASSERT (vlib_get_thread_index () <= transport_cl_thread ());
     608             : 
     609             :   /* Search for first free slot */
     610         153 :   for (tries = 0; tries < limit; tries++)
     611             :     {
     612         153 :       u16 port = 0;
     613             : 
     614             :       /* Find a port in the specified range */
     615             :       while (1)
     616             :         {
     617         154 :           port = random_u32 (&tm->port_allocator_seed) & PORT_MASK;
     618         154 :           if (PREDICT_TRUE (port >= min && port < max))
     619         153 :             break;
     620             :         }
     621             : 
     622         153 :       if (!transport_endpoint_mark_used (proto, lcl_addr, port))
     623         153 :         return port;
     624             : 
     625             :       /* IP:port pair already in use, check if 6-tuple available */
     626           0 :       if (session_lookup_connection (rmt->fib_index, lcl_addr, &rmt->ip, port,
     627           0 :                                      rmt->port, proto, rmt->is_ip4))
     628           0 :         continue;
     629             : 
     630             :       /* 6-tuple is available so increment lcl endpoint refcount */
     631           0 :       transport_share_local_endpoint (proto, lcl_addr, port);
     632             : 
     633           0 :       return port;
     634             :     }
     635           0 :   return -1;
     636             : }
     637             : 
     638             : static session_error_t
     639         153 : transport_get_interface_ip (u32 sw_if_index, u8 is_ip4, ip46_address_t * addr)
     640             : {
     641         153 :   if (is_ip4)
     642             :     {
     643             :       ip4_address_t *ip4;
     644         152 :       ip4 = ip_interface_get_first_ip (sw_if_index, 1);
     645         152 :       if (!ip4)
     646           0 :         return SESSION_E_NOIP;
     647         152 :       addr->ip4.as_u32 = ip4->as_u32;
     648             :     }
     649             :   else
     650             :     {
     651             :       ip6_address_t *ip6;
     652           1 :       ip6 = ip_interface_get_first_ip (sw_if_index, 0);
     653           1 :       if (ip6 == 0)
     654           0 :         return SESSION_E_NOIP;
     655           1 :       clib_memcpy_fast (&addr->ip6, ip6, sizeof (*ip6));
     656             :     }
     657         153 :   return 0;
     658             : }
     659             : 
     660             : static session_error_t
     661         154 : transport_find_local_ip_for_remote (u32 *sw_if_index,
     662             :                                     transport_endpoint_t *rmt,
     663             :                                     ip46_address_t *lcl_addr)
     664             : {
     665             :   fib_node_index_t fei;
     666             :   fib_prefix_t prefix;
     667             : 
     668         154 :   if (*sw_if_index == ENDPOINT_INVALID_INDEX)
     669             :     {
     670             :       /* Find a FIB path to the destination */
     671           1 :       clib_memcpy_fast (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
     672           1 :       prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
     673           1 :       prefix.fp_len = rmt->is_ip4 ? 32 : 128;
     674             : 
     675           1 :       ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
     676           1 :       fei = fib_table_lookup (rmt->fib_index, &prefix);
     677             : 
     678             :       /* Couldn't find route to destination. Bail out. */
     679           1 :       if (fei == FIB_NODE_INDEX_INVALID)
     680           0 :         return SESSION_E_NOROUTE;
     681             : 
     682           1 :       *sw_if_index = fib_entry_get_resolving_interface (fei);
     683           1 :       if (*sw_if_index == ENDPOINT_INVALID_INDEX)
     684           1 :         return SESSION_E_NOINTF;
     685             :     }
     686             : 
     687         153 :   clib_memset (lcl_addr, 0, sizeof (*lcl_addr));
     688         153 :   return transport_get_interface_ip (*sw_if_index, rmt->is_ip4, lcl_addr);
     689             : }
     690             : 
     691             : int
     692         157 : transport_alloc_local_endpoint (u8 proto, transport_endpoint_cfg_t * rmt_cfg,
     693             :                                 ip46_address_t * lcl_addr, u16 * lcl_port)
     694             : {
     695         157 :   transport_endpoint_t *rmt = (transport_endpoint_t *) rmt_cfg;
     696         157 :   transport_main_t *tm = &tp_main;
     697             :   session_error_t error;
     698             :   int port;
     699             : 
     700             :   /*
     701             :    * Find the local address
     702             :    */
     703         157 :   if (ip_is_zero (&rmt_cfg->peer.ip, rmt_cfg->peer.is_ip4))
     704             :     {
     705         154 :       error = transport_find_local_ip_for_remote (&rmt_cfg->peer.sw_if_index,
     706             :                                                   rmt, lcl_addr);
     707         154 :       if (error)
     708           1 :         return error;
     709             :     }
     710             :   else
     711             :     {
     712             :       /* Assume session layer vetted this address */
     713           3 :       clib_memcpy_fast (lcl_addr, &rmt_cfg->peer.ip,
     714             :                         sizeof (rmt_cfg->peer.ip));
     715             :     }
     716             : 
     717             :   /* Cleanup freelist if need be */
     718         156 :   if (vec_len (tm->lcl_endpts_freelist))
     719           4 :     transport_cleanup_freelist ();
     720             : 
     721             :   /*
     722             :    * Allocate source port
     723             :    */
     724         156 :   if (rmt_cfg->peer.port == 0)
     725             :     {
     726         153 :       port = transport_alloc_local_port (proto, lcl_addr, rmt_cfg);
     727         153 :       if (port < 1)
     728           0 :         return SESSION_E_NOPORT;
     729         153 :       *lcl_port = port;
     730             :     }
     731             :   else
     732             :     {
     733           3 :       port = clib_net_to_host_u16 (rmt_cfg->peer.port);
     734           3 :       *lcl_port = port;
     735             : 
     736           3 :       if (!transport_endpoint_mark_used (proto, lcl_addr, port))
     737           2 :         return 0;
     738             : 
     739             :       /* IP:port pair already in use, check if 6-tuple available */
     740           1 :       if (session_lookup_connection (rmt->fib_index, lcl_addr, &rmt->ip, port,
     741           1 :                                      rmt->port, proto, rmt->is_ip4))
     742           0 :         return SESSION_E_PORTINUSE;
     743             : 
     744             :       /* 6-tuple is available so increment lcl endpoint refcount */
     745           1 :       transport_share_local_endpoint (proto, lcl_addr, port);
     746             : 
     747           1 :       return 0;
     748             :     }
     749             : 
     750         153 :   return 0;
     751             : }
     752             : 
     753             : u8 *
     754          12 : format_clib_us_time (u8 * s, va_list * args)
     755             : {
     756          12 :   clib_us_time_t t = va_arg (*args, clib_us_time_t);
     757          12 :   if (t < 1e3)
     758           0 :     s = format (s, "%u us", t);
     759             :   else
     760          12 :     s = format (s, "%.3f s", (f64) t * CLIB_US_TIME_PERIOD);
     761          12 :   return s;
     762             : }
     763             : 
     764             : u8 *
     765          12 : format_transport_pacer (u8 * s, va_list * args)
     766             : {
     767          12 :   spacer_t *pacer = va_arg (*args, spacer_t *);
     768          12 :   u32 thread_index = va_arg (*args, int);
     769             :   clib_us_time_t now, diff;
     770             : 
     771          12 :   now = transport_us_time_now (thread_index);
     772          12 :   diff = now - pacer->last_update;
     773          12 :   s = format (s, "rate %lu bucket %ld t/p %.3f last_update %U burst %u",
     774          12 :               pacer->bytes_per_sec, pacer->bucket, pacer->tokens_per_period,
     775             :               format_clib_us_time, diff, pacer->max_burst);
     776          12 :   return s;
     777             : }
     778             : 
     779             : static inline u32
     780       69806 : spacer_max_burst (spacer_t * pacer, clib_us_time_t time_now)
     781             : {
     782       69806 :   u64 n_periods = (time_now - pacer->last_update);
     783             :   i64 inc;
     784             : 
     785       69806 :   if ((inc = (f32) n_periods * pacer->tokens_per_period) > 10)
     786             :     {
     787       65010 :       pacer->last_update = time_now;
     788       65010 :       pacer->bucket = clib_min (pacer->bucket + inc, (i64) pacer->max_burst);
     789             :     }
     790             : 
     791       69806 :   return pacer->bucket >= 0 ? pacer->max_burst : 0;
     792             : }
     793             : 
     794             : static inline void
     795       42153 : spacer_update_bucket (spacer_t * pacer, u32 bytes)
     796             : {
     797       42153 :   pacer->bucket -= bytes;
     798       42153 : }
     799             : 
     800             : static inline void
     801       41205 : spacer_set_pace_rate (spacer_t * pacer, u64 rate_bytes_per_sec,
     802             :                       clib_us_time_t rtt, clib_time_type_t sec_per_loop)
     803             : {
     804             :   clib_us_time_t max_time;
     805             : 
     806       41205 :   ASSERT (rate_bytes_per_sec != 0);
     807       41205 :   pacer->bytes_per_sec = rate_bytes_per_sec;
     808       41205 :   pacer->tokens_per_period = rate_bytes_per_sec * CLIB_US_TIME_PERIOD;
     809             : 
     810             :   /* Allow a min number of bursts per rtt, if their size is acceptable. Goal
     811             :    * is to spread the sending of data over the rtt but to also allow for some
     812             :    * coalescing that can potentially
     813             :    * 1) reduce load on session layer by reducing scheduling frequency for a
     814             :    *    session and
     815             :    * 2) optimize sending when tso if available
     816             :    *
     817             :    * Max "time-length" of a burst cannot be less than 1us or more than 1ms.
     818             :    */
     819       41205 :   max_time = clib_max (rtt / TRANSPORT_PACER_BURSTS_PER_RTT,
     820             :                        (clib_us_time_t) (sec_per_loop * CLIB_US_TIME_FREQ));
     821       41205 :   max_time = clib_clamp (max_time, 1 /* 1us */ , 1000 /* 1ms */ );
     822       41205 :   pacer->max_burst = (rate_bytes_per_sec * max_time) * CLIB_US_TIME_PERIOD;
     823       41205 :   pacer->max_burst = clib_clamp (pacer->max_burst, TRANSPORT_PACER_MIN_BURST,
     824             :                                  TRANSPORT_PACER_MAX_BURST);
     825       41205 : }
     826             : 
     827             : static inline u64
     828           0 : spacer_pace_rate (spacer_t * pacer)
     829             : {
     830           0 :   return pacer->bytes_per_sec;
     831             : }
     832             : 
     833             : static inline void
     834       59837 : spacer_reset (spacer_t * pacer, clib_us_time_t time_now, u64 bucket)
     835             : {
     836       59837 :   pacer->last_update = time_now;
     837       59837 :   pacer->bucket = bucket;
     838       59837 : }
     839             : 
     840             : void
     841         267 : transport_connection_tx_pacer_reset (transport_connection_t * tc,
     842             :                                      u64 rate_bytes_per_sec, u32 start_bucket,
     843             :                                      clib_us_time_t rtt)
     844             : {
     845         267 :   spacer_set_pace_rate (&tc->pacer, rate_bytes_per_sec, rtt,
     846             :                         transport_seconds_per_loop (tc->thread_index));
     847         267 :   spacer_reset (&tc->pacer, transport_us_time_now (tc->thread_index),
     848             :                 start_bucket);
     849         267 : }
     850             : 
     851             : void
     852       59570 : transport_connection_tx_pacer_reset_bucket (transport_connection_t * tc,
     853             :                                             u32 bucket)
     854             : {
     855       59570 :   spacer_reset (&tc->pacer, transport_us_time_now (tc->thread_index), bucket);
     856       59570 : }
     857             : 
     858             : void
     859         267 : transport_connection_tx_pacer_init (transport_connection_t * tc,
     860             :                                     u64 rate_bytes_per_sec,
     861             :                                     u32 initial_bucket)
     862             : {
     863         267 :   tc->flags |= TRANSPORT_CONNECTION_F_IS_TX_PACED;
     864         267 :   transport_connection_tx_pacer_reset (tc, rate_bytes_per_sec,
     865             :                                        initial_bucket, 1e6);
     866         267 : }
     867             : 
     868             : void
     869       40938 : transport_connection_tx_pacer_update (transport_connection_t * tc,
     870             :                                       u64 bytes_per_sec, clib_us_time_t rtt)
     871             : {
     872       40938 :   spacer_set_pace_rate (&tc->pacer, bytes_per_sec, rtt,
     873             :                         transport_seconds_per_loop (tc->thread_index));
     874       40938 : }
     875             : 
     876             : u32
     877       69806 : transport_connection_tx_pacer_burst (transport_connection_t * tc)
     878             : {
     879       69806 :   return spacer_max_burst (&tc->pacer,
     880             :                            transport_us_time_now (tc->thread_index));
     881             : }
     882             : 
     883             : u64
     884           0 : transport_connection_tx_pacer_rate (transport_connection_t * tc)
     885             : {
     886           0 :   return spacer_pace_rate (&tc->pacer);
     887             : }
     888             : 
     889             : void
     890           0 : transport_connection_update_tx_bytes (transport_connection_t * tc, u32 bytes)
     891             : {
     892           0 :   if (transport_connection_is_tx_paced (tc))
     893           0 :     spacer_update_bucket (&tc->pacer, bytes);
     894           0 : }
     895             : 
     896             : void
     897       42153 : transport_connection_tx_pacer_update_bytes (transport_connection_t * tc,
     898             :                                             u32 bytes)
     899             : {
     900       42153 :   spacer_update_bucket (&tc->pacer, bytes);
     901       42153 : }
     902             : 
     903             : void
     904       60843 : transport_update_pacer_time (u32 thread_index, clib_time_type_t now)
     905             : {
     906       60843 :   session_wrk_update_time (session_main_get_worker (thread_index), now);
     907       60843 : }
     908             : 
     909             : void
     910       27210 : transport_connection_reschedule (transport_connection_t * tc)
     911             : {
     912       27210 :   tc->flags &= ~TRANSPORT_CONNECTION_F_DESCHED;
     913       27210 :   transport_connection_tx_pacer_reset_bucket (tc, 0 /* bucket */);
     914       27210 :   if (transport_max_tx_dequeue (tc))
     915        3402 :     sesssion_reschedule_tx (tc);
     916             :   else
     917             :     {
     918       23808 :       session_t *s = session_get (tc->s_index, tc->thread_index);
     919       23808 :       svm_fifo_unset_event (s->tx_fifo);
     920       23808 :       if (svm_fifo_max_dequeue_cons (s->tx_fifo))
     921           0 :         if (svm_fifo_set_event (s->tx_fifo))
     922           0 :           sesssion_reschedule_tx (tc);
     923             :     }
     924       27210 : }
     925             : 
     926             : void
     927         267 : transport_fifos_init_ooo (transport_connection_t * tc)
     928             : {
     929         267 :   session_t *s = session_get (tc->s_index, tc->thread_index);
     930         267 :   svm_fifo_init_ooo_lookup (s->rx_fifo, 0 /* ooo enq */ );
     931         267 :   svm_fifo_init_ooo_lookup (s->tx_fifo, 1 /* ooo deq */ );
     932         267 : }
     933             : 
     934             : void
     935           0 : transport_update_time (clib_time_type_t time_now, u8 thread_index)
     936             : {
     937             :   transport_proto_vft_t *vft;
     938           0 :   vec_foreach (vft, tp_vfts)
     939             :   {
     940           0 :     if (vft->update_time)
     941           0 :       (vft->update_time) (time_now, thread_index);
     942             :   }
     943           0 : }
     944             : 
     945             : void
     946          57 : transport_enable_disable (vlib_main_t * vm, u8 is_en)
     947             : {
     948             :   transport_proto_vft_t *vft;
     949         513 :   vec_foreach (vft, tp_vfts)
     950             :   {
     951         456 :     if (vft->enable)
     952         285 :       (vft->enable) (vm, is_en);
     953             : 
     954         456 :     if (vft->update_time)
     955          81 :       session_register_update_time_fn (vft->update_time, is_en);
     956             :   }
     957          57 : }
     958             : 
     959             : void
     960          49 : transport_init (void)
     961             : {
     962          49 :   vlib_thread_main_t *vtm = vlib_get_thread_main ();
     963          49 :   session_main_t *smm = vnet_get_session_main ();
     964          49 :   transport_main_t *tm = &tp_main;
     965             :   u32 num_threads;
     966             : 
     967          49 :   if (smm->local_endpoints_table_buckets == 0)
     968          28 :     smm->local_endpoints_table_buckets = 250000;
     969          49 :   if (smm->local_endpoints_table_memory == 0)
     970          28 :     smm->local_endpoints_table_memory = 512 << 20;
     971             : 
     972             :   /* Initialize [port-allocator] random number seed */
     973          49 :   tm->port_allocator_seed = (u32) clib_cpu_time_now ();
     974             : 
     975          49 :   clib_bihash_init_24_8 (&tm->local_endpoints_table, "local endpoints table",
     976             :                          smm->local_endpoints_table_buckets,
     977          49 :                          smm->local_endpoints_table_memory);
     978          49 :   clib_spinlock_init (&tm->local_endpoints_lock);
     979             : 
     980          49 :   num_threads = 1 /* main thread */  + vtm->n_threads;
     981          49 :   if (num_threads > 1)
     982             :     {
     983             :       /* Main not polled if there are workers */
     984          21 :       smm->transport_cl_thread = 1;
     985             :     }
     986          49 : }
     987             : 
     988             : /*
     989             :  * fd.io coding-style-patch-verification: ON
     990             :  *
     991             :  * Local Variables:
     992             :  * eval: (c-set-style "gnu")
     993             :  * End:
     994             :  */

Generated by: LCOV version 1.14