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

Generated by: LCOV version 1.14