LCOV - code coverage report
Current view: top level - vnet/tcp - tcp_cli.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 143 421 34.0 %
Date: 2023-10-26 01:39:38 Functions: 27 42 64.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 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/tcp/tcp.h>
      17             : #include <vnet/tcp/tcp_inlines.h>
      18             : #include <vnet/dpo/receive_dpo.h>
      19             : #include <vnet/ip-neighbor/ip_neighbor.h>
      20             : 
      21             : const char *tcp_fsm_states[] = {
      22             : #define _(sym, str) str,
      23             :   foreach_tcp_fsm_state
      24             : #undef _
      25             : };
      26             : 
      27             : u8 *
      28          26 : format_tcp_state (u8 * s, va_list * args)
      29             : {
      30          26 :   u32 state = va_arg (*args, u32);
      31             : 
      32          26 :   if (state < TCP_N_STATES)
      33          26 :     s = format (s, "%s", tcp_fsm_states[state]);
      34             :   else
      35           0 :     s = format (s, "UNKNOWN (%d (0x%x))", state, state);
      36          26 :   return s;
      37             : }
      38             : 
      39             : const char *tcp_cfg_flags_str[] = {
      40             : #define _(sym, str) str,
      41             :   foreach_tcp_cfg_flag
      42             : #undef _
      43             : };
      44             : 
      45             : static u8 *
      46          12 : format_tcp_cfg_flags (u8 * s, va_list * args)
      47             : {
      48          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
      49          12 :   int i, last = -1;
      50             : 
      51          72 :   for (i = 0; i < TCP_CFG_N_FLAG_BITS; i++)
      52          60 :     if (tc->cfg_flags & (1 << i))
      53          12 :       last = i;
      54          36 :   for (i = 0; i < last; i++)
      55             :     {
      56          24 :       if (tc->cfg_flags & (1 << i))
      57           0 :         s = format (s, "%s, ", tcp_cfg_flags_str[i]);
      58             :     }
      59          12 :   if (last >= 0)
      60          12 :     s = format (s, "%s", tcp_cfg_flags_str[last]);
      61          12 :   return s;
      62             : }
      63             : 
      64             : const char *tcp_connection_flags_str[] = {
      65             : #define _(sym, str) str,
      66             :   foreach_tcp_connection_flag
      67             : #undef _
      68             : };
      69             : 
      70             : static u8 *
      71          12 : format_tcp_connection_flags (u8 * s, va_list * args)
      72             : {
      73          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
      74          12 :   int i, last = -1;
      75             : 
      76         168 :   for (i = 0; i < TCP_CONN_N_FLAG_BITS; i++)
      77         156 :     if (tc->flags & (1 << i))
      78          18 :       last = i;
      79          78 :   for (i = 0; i < last; i++)
      80             :     {
      81          66 :       if (tc->flags & (1 << i))
      82           6 :         s = format (s, "%s, ", tcp_connection_flags_str[i]);
      83             :     }
      84          12 :   if (last >= 0)
      85          12 :     s = format (s, "%s", tcp_connection_flags_str[last]);
      86          12 :   return s;
      87             : }
      88             : 
      89             : const char *tcp_conn_timers[] = {
      90             : #define _(sym, str) str,
      91             :   foreach_tcp_timer
      92             : #undef _
      93             : };
      94             : 
      95             : static u8 *
      96          12 : format_tcp_timers (u8 * s, va_list * args)
      97             : {
      98          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
      99          12 :   int i, last = -1;
     100             : 
     101          60 :   for (i = 0; i < TCP_N_TIMERS; i++)
     102          48 :     if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
     103          12 :       last = i;
     104             : 
     105          36 :   for (i = 0; i < last; i++)
     106             :     {
     107          24 :       if (tc->timers[i] != TCP_TIMER_HANDLE_INVALID)
     108           0 :         s = format (s, "%s,", tcp_conn_timers[i]);
     109             :     }
     110             : 
     111          12 :   if (last >= 0)
     112          12 :     s = format (s, "%s", tcp_conn_timers[i]);
     113             : 
     114          12 :   return s;
     115             : }
     116             : 
     117             : static u8 *
     118          12 : format_tcp_congestion_status (u8 * s, va_list * args)
     119             : {
     120          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     121          12 :   if (tcp_in_recovery (tc))
     122           0 :     s = format (s, "recovery");
     123          12 :   else if (tcp_in_fastrecovery (tc))
     124           0 :     s = format (s, "fastrecovery");
     125             :   else
     126          12 :     s = format (s, "none");
     127          12 :   return s;
     128             : }
     129             : 
     130             : static i32
     131          12 : tcp_rcv_wnd_available (tcp_connection_t * tc)
     132             : {
     133          12 :   return (i32) tc->rcv_wnd - (tc->rcv_nxt - tc->rcv_las);
     134             : }
     135             : 
     136             : static u8 *
     137          12 : format_tcp_congestion (u8 * s, va_list * args)
     138             : {
     139          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     140          12 :   u32 indent = format_get_indent (s), prr_space = 0;
     141             : 
     142          12 :   s = format (s, "%U ", format_tcp_congestion_status, tc);
     143          12 :   s = format (s, "algo %s cwnd %u ssthresh %u bytes_acked %u\n",
     144          12 :               tc->cc_algo->name, tc->cwnd, tc->ssthresh, tc->bytes_acked);
     145          12 :   s = format (s, "%Ucc space %u prev_cwnd %u prev_ssthresh %u\n",
     146             :               format_white_space, indent, tcp_available_cc_snd_space (tc),
     147             :               tc->prev_cwnd, tc->prev_ssthresh);
     148          12 :   s = format (s, "%Usnd_cong %u dupack %u limited_tx %u\n",
     149          12 :               format_white_space, indent, tc->snd_congestion - tc->iss,
     150          12 :               tc->rcv_dupacks, tc->limited_transmit - tc->iss);
     151          12 :   s = format (s, "%Urxt_bytes %u rxt_delivered %u rxt_head %u rxt_ts %u\n",
     152             :               format_white_space, indent, tc->snd_rxt_bytes, tc->rxt_delivered,
     153          12 :               tc->rxt_head - tc->iss, tcp_tstamp (tc) - tc->snd_rxt_ts);
     154          12 :   if (tcp_in_fastrecovery (tc))
     155           0 :     prr_space = tcp_fastrecovery_prr_snd_space (tc);
     156          12 :   s = format (s, "%Uprr_start %u prr_delivered %u prr space %u\n",
     157          12 :               format_white_space, indent, tc->prr_start - tc->iss,
     158             :               tc->prr_delivered, prr_space);
     159          12 :   return s;
     160             : }
     161             : 
     162             : static u8 *
     163          12 : format_tcp_stats (u8 * s, va_list * args)
     164             : {
     165          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     166          12 :   u32 indent = format_get_indent (s);
     167          12 :   s = format (s, "in segs %lu dsegs %lu bytes %lu dupacks %u\n",
     168             :               tc->segs_in, tc->data_segs_in, tc->bytes_in, tc->dupacks_in);
     169          12 :   s = format (s, "%Uout segs %lu dsegs %lu bytes %lu dupacks %u\n",
     170             :               format_white_space, indent, tc->segs_out,
     171             :               tc->data_segs_out, tc->bytes_out, tc->dupacks_out);
     172          12 :   s = format (s, "%Ufr %u tr %u rxt segs %lu bytes %lu duration %.3f\n",
     173             :               format_white_space, indent, tc->fr_occurences,
     174             :               tc->tr_occurences, tc->segs_retrans, tc->bytes_retrans,
     175          12 :               tcp_time_now_us (tc->c_thread_index) - tc->start_ts);
     176          12 :   s = format (s, "%Uerr wnd data below %u above %u ack below %u above %u",
     177             :               format_white_space, indent, tc->errors.below_data_wnd,
     178             :               tc->errors.above_data_wnd, tc->errors.below_ack_wnd,
     179             :               tc->errors.above_ack_wnd);
     180          12 :   return s;
     181             : }
     182             : 
     183             : static u8 *
     184          12 : format_tcp_vars (u8 * s, va_list * args)
     185             : {
     186          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     187          12 :   s = format (s, " index: %u cfg: %U flags: %U timers: %U\n", tc->c_c_index,
     188             :               format_tcp_cfg_flags, tc, format_tcp_connection_flags, tc,
     189             :               format_tcp_timers, tc);
     190          12 :   s = format (s, " snd_una %u snd_nxt %u", tc->snd_una - tc->iss,
     191          12 :               tc->snd_nxt - tc->iss);
     192          12 :   s = format (s, " rcv_nxt %u rcv_las %u\n",
     193          12 :               tc->rcv_nxt - tc->irs, tc->rcv_las - tc->irs);
     194          12 :   s = format (s, " snd_wnd %u rcv_wnd %u rcv_wscale %u ",
     195          12 :               tc->snd_wnd, tc->rcv_wnd, tc->rcv_wscale);
     196          12 :   s = format (s, "snd_wl1 %u snd_wl2 %u\n", tc->snd_wl1 - tc->irs,
     197          12 :               tc->snd_wl2 - tc->iss);
     198          12 :   s = format (s, " flight size %u out space %u rcv_wnd_av %u",
     199             :               tcp_flight_size (tc), tcp_available_output_snd_space (tc),
     200             :               tcp_rcv_wnd_available (tc));
     201          12 :   s = format (s, " tsval_recent %u\n", tc->tsval_recent);
     202          12 :   s = format (s, " tsecr %u tsecr_last_ack %u tsval_recent_age %u",
     203             :               tc->rcv_opts.tsecr, tc->tsecr_last_ack,
     204          12 :               tcp_time_tstamp (tc->c_thread_index) - tc->tsval_recent_age);
     205          12 :   s = format (s, " snd_mss %u\n", tc->snd_mss);
     206          12 :   s = format (s, " rto %u rto_boff %u srtt %.1f us %.3f rttvar %.1f",
     207          12 :               tc->rto / 1000, tc->rto_boff, tc->srtt / 1000.0,
     208          12 :               tc->mrtt_us * 1e3, tc->rttvar / 1000.0);
     209          12 :   s = format (s, " rtt_ts %.4f rtt_seq %u\n", tc->rtt_ts,
     210          12 :               tc->rtt_seq - tc->iss);
     211          12 :   s = format (s, " next_node %u opaque 0x%x fib_index %u sw_if_index %d\n",
     212             :               tc->next_node_index, tc->next_node_opaque, tc->c_fib_index,
     213             :               tc->sw_if_index);
     214          12 :   s = format (s, " cong:   %U", format_tcp_congestion, tc);
     215             : 
     216          12 :   if (tc->state >= TCP_STATE_ESTABLISHED)
     217             :     {
     218          12 :       s = format (s, " sboard: %U\n", format_tcp_scoreboard, &tc->sack_sb,
     219             :                   tc);
     220          12 :       s = format (s, " stats: %U\n", format_tcp_stats, tc);
     221             :     }
     222          12 :   if (vec_len (tc->snd_sacks))
     223           0 :     s = format (s, " sacks tx: %U\n", format_tcp_sacks, tc);
     224             : 
     225          12 :   return s;
     226             : }
     227             : 
     228             : u8 *
     229          20 : format_tcp_connection_id (u8 * s, va_list * args)
     230             : {
     231          20 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     232          20 :   if (!tc)
     233           0 :     return s;
     234          20 :   if (tc->c_is_ip4)
     235             :     {
     236          20 :       s = format (s, "[%d:%d][%s] %U:%d->%U:%d", tc->c_thread_index,
     237             :                   tc->c_s_index, "T", format_ip4_address, &tc->c_lcl_ip4,
     238          20 :                   clib_net_to_host_u16 (tc->c_lcl_port), format_ip4_address,
     239          20 :                   &tc->c_rmt_ip4, clib_net_to_host_u16 (tc->c_rmt_port));
     240             :     }
     241             :   else
     242             :     {
     243           0 :       s = format (s, "[%d:%d][%s] %U:%d->%U:%d", tc->c_thread_index,
     244             :                   tc->c_s_index, "T", format_ip6_address, &tc->c_lcl_ip6,
     245           0 :                   clib_net_to_host_u16 (tc->c_lcl_port), format_ip6_address,
     246           0 :                   &tc->c_rmt_ip6, clib_net_to_host_u16 (tc->c_rmt_port));
     247             :     }
     248             : 
     249          20 :   return s;
     250             : }
     251             : 
     252             : u8 *
     253          14 : format_tcp_connection (u8 * s, va_list * args)
     254             : {
     255          14 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     256          14 :   u32 verbose = va_arg (*args, u32);
     257             : 
     258          14 :   if (!tc)
     259           0 :     return s;
     260          14 :   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_tcp_connection_id, tc);
     261          14 :   if (verbose)
     262             :     {
     263          14 :       s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_tcp_state,
     264          14 :                   tc->state);
     265          14 :       if (verbose > 1)
     266          12 :         s = format (s, "\n%U", format_tcp_vars, tc);
     267             :     }
     268             : 
     269          14 :   return s;
     270             : }
     271             : 
     272             : u8 *
     273           0 : format_tcp_sacks (u8 * s, va_list * args)
     274             : {
     275           0 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     276           0 :   sack_block_t *sacks = tc->snd_sacks;
     277             :   sack_block_t *block;
     278           0 :   int i, len = 0;
     279             : 
     280           0 :   len = vec_len (sacks);
     281           0 :   for (i = 0; i < len - 1; i++)
     282             :     {
     283           0 :       block = &sacks[i];
     284           0 :       s = format (s, " start %u end %u\n", block->start - tc->irs,
     285           0 :                   block->end - tc->irs);
     286             :     }
     287           0 :   if (len)
     288             :     {
     289           0 :       block = &sacks[len - 1];
     290           0 :       s = format (s, " start %u end %u", block->start - tc->irs,
     291           0 :                   block->end - tc->irs);
     292             :     }
     293           0 :   return s;
     294             : }
     295             : 
     296             : u8 *
     297           0 : format_tcp_rcv_sacks (u8 * s, va_list * args)
     298             : {
     299           0 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     300           0 :   sack_block_t *sacks = tc->rcv_opts.sacks;
     301             :   sack_block_t *block;
     302           0 :   int i, len = 0;
     303             : 
     304           0 :   len = vec_len (sacks);
     305           0 :   for (i = 0; i < len - 1; i++)
     306             :     {
     307           0 :       block = &sacks[i];
     308           0 :       s = format (s, " start %u end %u\n", block->start - tc->iss,
     309           0 :                   block->end - tc->iss);
     310             :     }
     311           0 :   if (len)
     312             :     {
     313           0 :       block = &sacks[len - 1];
     314           0 :       s = format (s, " start %u end %u", block->start - tc->iss,
     315           0 :                   block->end - tc->iss);
     316             :     }
     317           0 :   return s;
     318             : }
     319             : 
     320             : static u8 *
     321           0 : format_tcp_sack_hole (u8 * s, va_list * args)
     322             : {
     323           0 :   sack_scoreboard_hole_t *hole = va_arg (*args, sack_scoreboard_hole_t *);
     324           0 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     325           0 :   if (tc)
     326           0 :     s = format (s, "  [%u, %u]", hole->start - tc->iss, hole->end - tc->iss);
     327             :   else
     328           0 :     s = format (s, "  [%u, %u]", hole->start, hole->end);
     329           0 :   return s;
     330             : }
     331             : 
     332             : u8 *
     333          12 : format_tcp_scoreboard (u8 * s, va_list * args)
     334             : {
     335          12 :   sack_scoreboard_t *sb = va_arg (*args, sack_scoreboard_t *);
     336          12 :   tcp_connection_t *tc = va_arg (*args, tcp_connection_t *);
     337             :   sack_scoreboard_hole_t *hole;
     338          12 :   u32 indent = format_get_indent (s);
     339             : 
     340          12 :   s = format (s, "sacked %u last_sacked %u lost %u last_lost %u"
     341             :               " rxt_sacked %u\n",
     342             :               sb->sacked_bytes, sb->last_sacked_bytes, sb->lost_bytes,
     343             :               sb->last_lost_bytes, sb->rxt_sacked);
     344          12 :   s = format (s, "%Ulast_delivered %u high_sacked %u is_reneging %u",
     345             :               format_white_space, indent, sb->last_bytes_delivered,
     346          12 :               sb->high_sacked - tc->iss, sb->is_reneging);
     347          12 :   s = format (s, " reorder %u\n", sb->reorder);
     348          12 :   s = format (s, "%Ucur_rxt_hole %u high_rxt %u rescue_rxt %u",
     349             :               format_white_space, indent, sb->cur_rxt_hole,
     350          12 :               sb->high_rxt - tc->iss, sb->rescue_rxt - tc->iss);
     351             : 
     352          12 :   hole = scoreboard_first_hole (sb);
     353          12 :   if (hole)
     354           0 :     s = format (s, "\n%Uhead %u tail %u %u holes:\n%U", format_white_space,
     355           0 :                 indent, sb->head, sb->tail, pool_elts (sb->holes),
     356             :                 format_white_space, indent);
     357             : 
     358          12 :   while (hole)
     359             :     {
     360           0 :       s = format (s, "%U", format_tcp_sack_hole, hole, tc);
     361           0 :       hole = scoreboard_next_hole (sb, hole);
     362             :     }
     363             : 
     364          12 :   return s;
     365             : }
     366             : 
     367             : /**
     368             :  * \brief Configure an ipv4 source address range
     369             :  * @param vm vlib_main_t pointer
     370             :  * @param start first ipv4 address in the source address range
     371             :  * @param end last ipv4 address in the source address range
     372             :  * @param table_id VRF / table ID, 0 for the default FIB
     373             :  * @return 0 if all OK, else an error indication from api_errno.h
     374             :  */
     375             : 
     376             : int
     377           0 : tcp_configure_v4_source_address_range (vlib_main_t * vm,
     378             :                                        ip4_address_t * start,
     379             :                                        ip4_address_t * end, u32 table_id)
     380             : {
     381             :   u32 start_host_byte_order, end_host_byte_order;
     382             :   fib_prefix_t prefix;
     383             :   fib_node_index_t fei;
     384           0 :   u32 fib_index = 0;
     385             :   u32 sw_if_index;
     386             :   int rv;
     387             : 
     388           0 :   clib_memset (&prefix, 0, sizeof (prefix));
     389             : 
     390           0 :   fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
     391             : 
     392           0 :   if (fib_index == ~0)
     393           0 :     return VNET_API_ERROR_NO_SUCH_FIB;
     394             : 
     395           0 :   start_host_byte_order = clib_net_to_host_u32 (start->as_u32);
     396           0 :   end_host_byte_order = clib_net_to_host_u32 (end->as_u32);
     397             : 
     398             :   /* sanity check for reversed args or some such */
     399           0 :   if ((end_host_byte_order - start_host_byte_order) > (10 << 10))
     400           0 :     return VNET_API_ERROR_INVALID_ARGUMENT;
     401             : 
     402             :   /* Lookup the last address, to identify the interface involved */
     403           0 :   prefix.fp_len = 32;
     404           0 :   prefix.fp_proto = FIB_PROTOCOL_IP4;
     405           0 :   memcpy (&prefix.fp_addr.ip4, end, sizeof (ip4_address_t));
     406             : 
     407           0 :   fei = fib_table_lookup (fib_index, &prefix);
     408             : 
     409             :   /* Couldn't find route to destination. Bail out. */
     410           0 :   if (fei == FIB_NODE_INDEX_INVALID)
     411           0 :     return VNET_API_ERROR_NEXT_HOP_NOT_IN_FIB;
     412             : 
     413           0 :   sw_if_index = fib_entry_get_resolving_interface (fei);
     414           0 :   if (sw_if_index == (u32) ~0)
     415           0 :     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
     416             : 
     417             :   /* Configure proxy arp across the range */
     418           0 :   rv = ip4_neighbor_proxy_add (fib_index, start, end);
     419             : 
     420           0 :   if (rv)
     421           0 :     return rv;
     422             : 
     423           0 :   rv = ip4_neighbor_proxy_enable (sw_if_index);
     424             : 
     425           0 :   if (rv)
     426           0 :     return rv;
     427             : 
     428             :   do
     429             :     {
     430           0 :       dpo_id_t dpo = DPO_INVALID;
     431             : 
     432           0 :       vec_add1 (tcp_cfg.ip4_src_addrs, start[0]);
     433             : 
     434             :       /* Add local adjacencies for the range */
     435             : 
     436           0 :       receive_dpo_add_or_lock (DPO_PROTO_IP4, sw_if_index /* sw_if_index */,
     437             :                                NULL, &dpo);
     438           0 :       prefix.fp_len = 32;
     439           0 :       prefix.fp_proto = FIB_PROTOCOL_IP4;
     440           0 :       prefix.fp_addr.ip4.as_u32 = start->as_u32;
     441             : 
     442           0 :       fib_table_entry_special_dpo_update (fib_index,
     443             :                                           &prefix,
     444             :                                           FIB_SOURCE_API,
     445             :                                           FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
     446           0 :       dpo_reset (&dpo);
     447             : 
     448           0 :       start_host_byte_order++;
     449           0 :       start->as_u32 = clib_host_to_net_u32 (start_host_byte_order);
     450             :     }
     451           0 :   while (start_host_byte_order <= end_host_byte_order);
     452             : 
     453           0 :   return 0;
     454             : }
     455             : 
     456             : /**
     457             :  * \brief Configure an ipv6 source address range
     458             :  * @param vm vlib_main_t pointer
     459             :  * @param start first ipv6 address in the source address range
     460             :  * @param end last ipv6 address in the source address range
     461             :  * @param table_id VRF / table ID, 0 for the default FIB
     462             :  * @return 0 if all OK, else an error indication from api_errno.h
     463             :  */
     464             : 
     465             : int
     466           0 : tcp_configure_v6_source_address_range (vlib_main_t * vm,
     467             :                                        ip6_address_t * start,
     468             :                                        ip6_address_t * end, u32 table_id)
     469             : {
     470             :   fib_prefix_t prefix;
     471           0 :   u32 fib_index = 0;
     472             :   fib_node_index_t fei;
     473             :   u32 sw_if_index;
     474             : 
     475           0 :   clib_memset (&prefix, 0, sizeof (prefix));
     476             : 
     477           0 :   fib_index = fib_table_find (FIB_PROTOCOL_IP6, table_id);
     478             : 
     479           0 :   if (fib_index == ~0)
     480           0 :     return VNET_API_ERROR_NO_SUCH_FIB;
     481             : 
     482             :   while (1)
     483           0 :     {
     484             :       int i;
     485             :       ip6_address_t tmp;
     486           0 :       dpo_id_t dpo = DPO_INVALID;
     487             : 
     488             :       /* Remember this address */
     489           0 :       vec_add1 (tcp_cfg.ip6_src_addrs, start[0]);
     490             : 
     491             :       /* Lookup the prefix, to identify the interface involved */
     492           0 :       prefix.fp_len = 128;
     493           0 :       prefix.fp_proto = FIB_PROTOCOL_IP6;
     494           0 :       memcpy (&prefix.fp_addr.ip6, start, sizeof (ip6_address_t));
     495             : 
     496           0 :       fei = fib_table_lookup (fib_index, &prefix);
     497             : 
     498             :       /* Couldn't find route to destination. Bail out. */
     499           0 :       if (fei == FIB_NODE_INDEX_INVALID)
     500           0 :         return VNET_API_ERROR_NEXT_HOP_NOT_IN_FIB;
     501             : 
     502           0 :       sw_if_index = fib_entry_get_resolving_interface (fei);
     503             : 
     504           0 :       if (sw_if_index == (u32) ~ 0)
     505           0 :         return VNET_API_ERROR_NO_MATCHING_INTERFACE;
     506             : 
     507             :       /* Add a proxy neighbor discovery entry for this address */
     508           0 :       ip6_neighbor_proxy_add (sw_if_index, start);
     509             : 
     510             :       /* Add a receive adjacency for this address */
     511           0 :       receive_dpo_add_or_lock (DPO_PROTO_IP6, sw_if_index /* sw_if_index */,
     512             :                                NULL, &dpo);
     513             : 
     514           0 :       fib_table_entry_special_dpo_update (fib_index,
     515             :                                           &prefix,
     516             :                                           FIB_SOURCE_API,
     517             :                                           FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
     518           0 :       dpo_reset (&dpo);
     519             : 
     520             :       /* Done with the entire range? */
     521           0 :       if (!memcmp (start, end, sizeof (start[0])))
     522           0 :         break;
     523             : 
     524             :       /* Increment the address. DGMS. */
     525           0 :       tmp = start[0];
     526           0 :       for (i = 15; i >= 0; i--)
     527             :         {
     528           0 :           tmp.as_u8[i] += 1;
     529           0 :           if (tmp.as_u8[i] != 0)
     530           0 :             break;
     531             :         }
     532           0 :       start[0] = tmp;
     533             :     }
     534           0 :   return 0;
     535             : }
     536             : 
     537             : static clib_error_t *
     538           0 : tcp_src_address_fn (vlib_main_t * vm,
     539             :                     unformat_input_t * input, vlib_cli_command_t * cmd_arg)
     540             : {
     541             :   ip4_address_t v4start, v4end;
     542             :   ip6_address_t v6start, v6end;
     543           0 :   u32 table_id = 0;
     544           0 :   int v4set = 0;
     545           0 :   int v6set = 0;
     546             :   int rv;
     547             : 
     548           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     549             :     {
     550           0 :       if (unformat (input, "%U - %U", unformat_ip4_address, &v4start,
     551             :                     unformat_ip4_address, &v4end))
     552           0 :         v4set = 1;
     553           0 :       else if (unformat (input, "%U", unformat_ip4_address, &v4start))
     554             :         {
     555           0 :           memcpy (&v4end, &v4start, sizeof (v4start));
     556           0 :           v4set = 1;
     557             :         }
     558           0 :       else if (unformat (input, "%U - %U", unformat_ip6_address, &v6start,
     559             :                          unformat_ip6_address, &v6end))
     560           0 :         v6set = 1;
     561           0 :       else if (unformat (input, "%U", unformat_ip6_address, &v6start))
     562             :         {
     563           0 :           memcpy (&v6end, &v6start, sizeof (v6start));
     564           0 :           v6set = 1;
     565             :         }
     566           0 :       else if (unformat (input, "fib-table %d", &table_id))
     567             :         ;
     568             :       else
     569           0 :         break;
     570             :     }
     571             : 
     572           0 :   if (!v4set && !v6set)
     573           0 :     return clib_error_return (0, "at least one v4 or v6 address required");
     574             : 
     575           0 :   if (v4set)
     576             :     {
     577           0 :       rv = tcp_configure_v4_source_address_range (vm, &v4start, &v4end,
     578             :                                                   table_id);
     579           0 :       switch (rv)
     580             :         {
     581           0 :         case 0:
     582           0 :           break;
     583             : 
     584           0 :         case VNET_API_ERROR_NO_SUCH_FIB:
     585           0 :           return clib_error_return (0, "Invalid table-id %d", table_id);
     586             : 
     587           0 :         case VNET_API_ERROR_INVALID_ARGUMENT:
     588           0 :           return clib_error_return (0, "Invalid address range %U - %U",
     589             :                                     format_ip4_address, &v4start,
     590             :                                     format_ip4_address, &v4end);
     591           0 :         default:
     592           0 :           return clib_error_return (0, "error %d", rv);
     593             :           break;
     594             :         }
     595           0 :     }
     596           0 :   if (v6set)
     597             :     {
     598           0 :       rv = tcp_configure_v6_source_address_range (vm, &v6start, &v6end,
     599             :                                                   table_id);
     600           0 :       switch (rv)
     601             :         {
     602           0 :         case 0:
     603           0 :           break;
     604             : 
     605           0 :         case VNET_API_ERROR_NO_SUCH_FIB:
     606           0 :           return clib_error_return (0, "Invalid table-id %d", table_id);
     607             : 
     608           0 :         default:
     609           0 :           return clib_error_return (0, "error %d", rv);
     610             :           break;
     611             :         }
     612           0 :     }
     613           0 :   return 0;
     614             : }
     615             : 
     616             : /* *INDENT-OFF* */
     617      285289 : VLIB_CLI_COMMAND (tcp_src_address_command, static) =
     618             : {
     619             :   .path = "tcp src-address",
     620             :   .short_help = "tcp src-address <ip-addr> [- <ip-addr>] add src address range",
     621             :   .function = tcp_src_address_fn,
     622             : };
     623             : /* *INDENT-ON* */
     624             : 
     625             : static u8 *
     626           0 : tcp_scoreboard_dump_trace (u8 * s, sack_scoreboard_t * sb)
     627             : {
     628             : #if TCP_SCOREBOARD_TRACE
     629             : 
     630             :   scoreboard_trace_elt_t *block;
     631             :   int i = 0;
     632             : 
     633             :   if (!sb->trace)
     634             :     return s;
     635             : 
     636             :   s = format (s, "scoreboard trace:");
     637             :   vec_foreach (block, sb->trace)
     638             :   {
     639             :     s = format (s, "{%u, %u, %u, %u, %u}, ", block->start, block->end,
     640             :                 block->ack, block->snd_una_max, block->group);
     641             :     if ((++i % 3) == 0)
     642             :       s = format (s, "\n");
     643             :   }
     644             :   return s;
     645             : #else
     646           0 :   return 0;
     647             : #endif
     648             : }
     649             : 
     650             : static clib_error_t *
     651           0 : tcp_show_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
     652             :                               vlib_cli_command_t * cmd_arg)
     653             : {
     654           0 :   transport_connection_t *tconn = 0;
     655             :   tcp_connection_t *tc;
     656           0 :   u8 *s = 0;
     657           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     658             :     {
     659           0 :       if (unformat (input, "%U", unformat_transport_connection, &tconn,
     660             :                     TRANSPORT_PROTO_TCP))
     661             :         ;
     662             :       else
     663           0 :         return clib_error_return (0, "unknown input `%U'",
     664             :                                   format_unformat_error, input);
     665             :     }
     666             : 
     667             :   if (!TCP_SCOREBOARD_TRACE)
     668             :     {
     669           0 :       vlib_cli_output (vm, "scoreboard tracing not enabled");
     670           0 :       return 0;
     671             :     }
     672             : 
     673             :   tc = tcp_get_connection_from_transport (tconn);
     674             :   s = tcp_scoreboard_dump_trace (s, &tc->sack_sb);
     675             :   vlib_cli_output (vm, "%v", s);
     676             :   return 0;
     677             : }
     678             : 
     679             : /* *INDENT-OFF* */
     680      285289 : VLIB_CLI_COMMAND (tcp_show_scoreboard_trace_command, static) =
     681             : {
     682             :   .path = "show tcp scoreboard trace",
     683             :   .short_help = "show tcp scoreboard trace <connection>",
     684             :   .function = tcp_show_scoreboard_trace_fn,
     685             : };
     686             : /* *INDENT-ON* */
     687             : 
     688             : u8 *
     689           0 : tcp_scoreboard_replay (u8 * s, tcp_connection_t * tc, u8 verbose)
     690             : {
     691             :   int i, trace_len;
     692             :   scoreboard_trace_elt_t *trace;
     693           0 :   u32 next_ack, left, group, has_new_ack = 0;
     694           0 :   tcp_connection_t _placeholder_tc, *placeholder_tc = &_placeholder_tc;
     695             :   sack_block_t *block;
     696             : 
     697             :   if (!TCP_SCOREBOARD_TRACE)
     698             :     {
     699           0 :       s = format (s, "scoreboard tracing not enabled");
     700           0 :       return s;
     701             :     }
     702             : 
     703             :   if (!tc)
     704             :     return s;
     705             : 
     706             :   clib_memset (placeholder_tc, 0, sizeof (*placeholder_tc));
     707             :   tcp_connection_timers_init (placeholder_tc);
     708             :   scoreboard_init (&placeholder_tc->sack_sb);
     709             :   placeholder_tc->rcv_opts.flags |= TCP_OPTS_FLAG_SACK;
     710             : 
     711             : #if TCP_SCOREBOARD_TRACE
     712             :   trace = tc->sack_sb.trace;
     713             :   trace_len = vec_len (tc->sack_sb.trace);
     714             : #endif
     715             : 
     716             :   for (i = 0; i < trace_len; i++)
     717             :     {
     718             :       if (trace[i].ack != 0)
     719             :         {
     720             :           placeholder_tc->snd_una = trace[i].ack - 1448;
     721             :           placeholder_tc->snd_nxt = trace[i].ack;
     722             :         }
     723             :     }
     724             : 
     725             :   left = 0;
     726             :   while (left < trace_len)
     727             :     {
     728             :       group = trace[left].group;
     729             :       vec_reset_length (placeholder_tc->rcv_opts.sacks);
     730             :       has_new_ack = 0;
     731             :       while (trace[left].group == group)
     732             :         {
     733             :           if (trace[left].ack != 0)
     734             :             {
     735             :               if (verbose)
     736             :                 s = format (s, "Adding ack %u, snd_una_max %u, segs: ",
     737             :                             trace[left].ack, trace[left].snd_nxt);
     738             :               placeholder_tc->snd_nxt = trace[left].snd_nxt;
     739             :               next_ack = trace[left].ack;
     740             :               has_new_ack = 1;
     741             :             }
     742             :           else
     743             :             {
     744             :               if (verbose)
     745             :                 s = format (s, "[%u, %u], ", trace[left].start,
     746             :                             trace[left].end);
     747             :               vec_add2 (placeholder_tc->rcv_opts.sacks, block, 1);
     748             :               block->start = trace[left].start;
     749             :               block->end = trace[left].end;
     750             :             }
     751             :           left++;
     752             :         }
     753             : 
     754             :       /* Push segments */
     755             :       tcp_rcv_sacks (placeholder_tc, next_ack);
     756             :       if (has_new_ack)
     757             :         placeholder_tc->snd_una = next_ack;
     758             : 
     759             :       if (verbose)
     760             :         s = format (s, "result: %U", format_tcp_scoreboard,
     761             :                     &placeholder_tc->sack_sb);
     762             : 
     763             :     }
     764             :   s =
     765             :     format (s, "result: %U", format_tcp_scoreboard, &placeholder_tc->sack_sb);
     766             : 
     767             :   return s;
     768             : }
     769             : 
     770             : static clib_error_t *
     771           0 : tcp_scoreboard_trace_fn (vlib_main_t * vm, unformat_input_t * input,
     772             :                          vlib_cli_command_t * cmd_arg)
     773             : {
     774           0 :   transport_connection_t *tconn = 0;
     775           0 :   tcp_connection_t *tc = 0;
     776           0 :   u8 *str = 0;
     777           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     778             :     {
     779           0 :       if (unformat (input, "%U", unformat_transport_connection, &tconn,
     780             :                     TRANSPORT_PROTO_TCP))
     781             :         ;
     782             :       else
     783           0 :         return clib_error_return (0, "unknown input `%U'",
     784             :                                   format_unformat_error, input);
     785             :     }
     786             : 
     787             :   if (!TCP_SCOREBOARD_TRACE)
     788             :     {
     789           0 :       vlib_cli_output (vm, "scoreboard tracing not enabled");
     790           0 :       return 0;
     791             :     }
     792             : 
     793             :   tc = tcp_get_connection_from_transport (tconn);
     794             :   if (!tc)
     795             :     {
     796             :       vlib_cli_output (vm, "connection not found");
     797             :       return 0;
     798             :     }
     799             :   str = tcp_scoreboard_replay (str, tc, 1);
     800             :   vlib_cli_output (vm, "%v", str);
     801             :   return 0;
     802             : }
     803             : 
     804             : /* *INDENT-OFF* */
     805      285289 : VLIB_CLI_COMMAND (tcp_replay_scoreboard_command, static) =
     806             : {
     807             :   .path = "tcp replay scoreboard",
     808             :   .short_help = "tcp replay scoreboard <connection>",
     809             :   .function = tcp_scoreboard_trace_fn,
     810             : };
     811             : /* *INDENT-ON* */
     812             : 
     813             : static clib_error_t *
     814           0 : show_tcp_punt_fn (vlib_main_t * vm, unformat_input_t * input,
     815             :                   vlib_cli_command_t * cmd_arg)
     816             : {
     817           0 :   tcp_main_t *tm = vnet_get_tcp_main ();
     818           0 :   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     819           0 :     return clib_error_return (0, "unknown input `%U'", format_unformat_error,
     820             :                               input);
     821           0 :   vlib_cli_output (vm, "IPv4 TCP punt: %s",
     822           0 :                    tm->punt_unknown4 ? "enabled" : "disabled");
     823           0 :   vlib_cli_output (vm, "IPv6 TCP punt: %s",
     824           0 :                    tm->punt_unknown6 ? "enabled" : "disabled");
     825           0 :   return 0;
     826             : }
     827             : /* *INDENT-OFF* */
     828      285289 : VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
     829             : {
     830             :   .path = "show tcp punt",
     831             :   .short_help = "show tcp punt",
     832             :   .function = show_tcp_punt_fn,
     833             : };
     834             : /* *INDENT-ON* */
     835             : 
     836             : static clib_error_t *
     837           0 : show_tcp_stats_fn (vlib_main_t * vm, unformat_input_t * input,
     838             :                    vlib_cli_command_t * cmd)
     839             : {
     840           0 :   tcp_main_t *tm = vnet_get_tcp_main ();
     841             :   tcp_worker_ctx_t *wrk;
     842             :   u32 thread;
     843             : 
     844           0 :   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     845           0 :     return clib_error_return (0, "unknown input `%U'", format_unformat_error,
     846             :                               input);
     847           0 :   for (thread = 0; thread < vec_len (tm->wrk_ctx); thread++)
     848             :     {
     849           0 :       wrk = tcp_get_worker (thread);
     850           0 :       vlib_cli_output (vm, "Thread %u:\n", thread);
     851             : 
     852           0 :       if (clib_fifo_elts (wrk->pending_timers))
     853           0 :         vlib_cli_output (vm, " %lu pending timers",
     854           0 :                          clib_fifo_elts (wrk->pending_timers));
     855             : 
     856             : #define _(name,type,str)                                        \
     857             :   if (wrk->stats.name)                                               \
     858             :     vlib_cli_output (vm, " %lu %s", wrk->stats.name, str);
     859           0 :       foreach_tcp_wrk_stat
     860             : #undef _
     861             :     }
     862             : 
     863           0 :   return 0;
     864             : }
     865             : 
     866             : /* *INDENT-OFF* */
     867      285289 : VLIB_CLI_COMMAND (show_tcp_stats_command, static) =
     868             : {
     869             :   .path = "show tcp stats",
     870             :   .short_help = "show tcp stats",
     871             :   .function = show_tcp_stats_fn,
     872             : };
     873             : /* *INDENT-ON* */
     874             : 
     875             : static clib_error_t *
     876           0 : clear_tcp_stats_fn (vlib_main_t * vm, unformat_input_t * input,
     877             :                     vlib_cli_command_t * cmd)
     878             : {
     879           0 :   tcp_main_t *tm = vnet_get_tcp_main ();
     880             :   tcp_worker_ctx_t *wrk;
     881             :   u32 thread;
     882             : 
     883           0 :   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     884           0 :     return clib_error_return (0, "unknown input `%U'", format_unformat_error,
     885             :                               input);
     886             : 
     887           0 :   for (thread = 0; thread < vec_len (tm->wrk_ctx); thread++)
     888             :     {
     889           0 :       wrk = tcp_get_worker (thread);
     890           0 :       clib_memset (&wrk->stats, 0, sizeof (wrk->stats));
     891             :     }
     892             : 
     893           0 :   return 0;
     894             : }
     895             : 
     896             : /* *INDENT-OFF* */
     897      285289 : VLIB_CLI_COMMAND (clear_tcp_stats_command, static) =
     898             : {
     899             :   .path = "clear tcp stats",
     900             :   .short_help = "clear tcp stats",
     901             :   .function = clear_tcp_stats_fn,
     902             : };
     903             : /* *INDENT-ON* */
     904             : 
     905             : uword
     906           0 : unformat_tcp_cc_algo (unformat_input_t * input, va_list * va)
     907             : {
     908           0 :   tcp_cc_algorithm_type_e *result = va_arg (*va, tcp_cc_algorithm_type_e *);
     909           0 :   tcp_main_t *tm = &tcp_main;
     910             :   char *cc_algo_name;
     911           0 :   u8 found = 0;
     912             :   uword *p;
     913             : 
     914           0 :   if (unformat (input, "%s", &cc_algo_name)
     915           0 :       && ((p = hash_get_mem (tm->cc_algo_by_name, cc_algo_name))))
     916             :     {
     917           0 :       *result = *p;
     918           0 :       found = 1;
     919             :     }
     920             : 
     921           0 :   vec_free (cc_algo_name);
     922           0 :   return found;
     923             : }
     924             : 
     925             : uword
     926           0 : unformat_tcp_cc_algo_cfg (unformat_input_t * input, va_list * va)
     927             : {
     928           0 :   tcp_main_t *tm = vnet_get_tcp_main ();
     929             :   tcp_cc_algorithm_t *cc_alg;
     930             :   unformat_input_t sub_input;
     931           0 :   int found = 0;
     932             : 
     933           0 :   vec_foreach (cc_alg, tm->cc_algos)
     934             :   {
     935           0 :     if (!unformat (input, cc_alg->name))
     936           0 :       continue;
     937             : 
     938           0 :     if (cc_alg->unformat_cfg
     939           0 :         && unformat (input, "%U", unformat_vlib_cli_sub_input, &sub_input))
     940             :       {
     941           0 :         if (cc_alg->unformat_cfg (&sub_input))
     942           0 :           found = 1;
     943             :       }
     944             :   }
     945           0 :   return found;
     946             : }
     947             : 
     948             : static clib_error_t *
     949         575 : tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
     950             : {
     951             :   u32 cwnd_multiplier, tmp_time, mtu, max_gso_size, tmp;
     952             :   uword memory_size;
     953             : 
     954         575 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     955             :     {
     956           0 :       if (unformat (input, "preallocated-connections %d",
     957             :                     &tcp_cfg.preallocated_connections))
     958             :         ;
     959             :       /* Config deprecated. Will be removed in a later release */
     960           0 :       else if (unformat (input, "preallocated-half-open-connections %d", &tmp))
     961             :         ;
     962           0 :       else if (unformat (input, "buffer-fail-fraction %f",
     963             :                          &tcp_cfg.buffer_fail_fraction))
     964             :         ;
     965           0 :       else if (unformat (input, "max-rx-fifo %U", unformat_memory_size,
     966             :                          &memory_size))
     967             :         {
     968           0 :           if (memory_size >= 0x100000000)
     969             :             {
     970           0 :               return clib_error_return
     971             :                 (0, "max-rx-fifo %llu (0x%llx) too large", memory_size,
     972             :                  memory_size);
     973             :             }
     974           0 :           tcp_cfg.max_rx_fifo = memory_size;
     975             :         }
     976           0 :       else if (unformat (input, "min-rx-fifo %U", unformat_memory_size,
     977             :                          &memory_size))
     978             :         {
     979           0 :           if (memory_size >= 0x100000000)
     980             :             {
     981           0 :               return clib_error_return
     982             :                 (0, "min-rx-fifo %llu (0x%llx) too large", memory_size,
     983             :                  memory_size);
     984             :             }
     985           0 :           tcp_cfg.min_rx_fifo = memory_size;
     986             :         }
     987           0 :       else if (unformat (input, "mtu %u", &mtu))
     988           0 :         tcp_cfg.default_mtu = mtu;
     989           0 :       else if (unformat (input, "rwnd-min-update-ack %d",
     990             :                          &tcp_cfg.rwnd_min_update_ack))
     991             :         ;
     992           0 :       else if (unformat (input, "initial-cwnd-multiplier %u",
     993             :                          &cwnd_multiplier))
     994           0 :         tcp_cfg.initial_cwnd_multiplier = cwnd_multiplier;
     995           0 :       else if (unformat (input, "no-tx-pacing"))
     996           0 :         tcp_cfg.enable_tx_pacing = 0;
     997           0 :       else if (unformat (input, "tso"))
     998           0 :         tcp_cfg.allow_tso = 1;
     999           0 :       else if (unformat (input, "no-csum-offload"))
    1000           0 :         tcp_cfg.csum_offload = 0;
    1001           0 :       else if (unformat (input, "max-gso-size %u", &max_gso_size))
    1002           0 :         tcp_cfg.max_gso_size = clib_min (max_gso_size, TCP_MAX_GSO_SZ);
    1003           0 :       else if (unformat (input, "cc-algo %U", unformat_tcp_cc_algo,
    1004             :                          &tcp_cfg.cc_algo))
    1005             :         ;
    1006           0 :       else if (unformat (input, "%U", unformat_tcp_cc_algo_cfg))
    1007             :         ;
    1008           0 :       else if (unformat (input, "closewait-time %u", &tmp_time))
    1009           0 :         tcp_cfg.closewait_time = tmp_time / TCP_TIMER_TICK;
    1010           0 :       else if (unformat (input, "timewait-time %u", &tmp_time))
    1011           0 :         tcp_cfg.timewait_time = tmp_time / TCP_TIMER_TICK;
    1012           0 :       else if (unformat (input, "finwait1-time %u", &tmp_time))
    1013           0 :         tcp_cfg.finwait1_time = tmp_time / TCP_TIMER_TICK;
    1014           0 :       else if (unformat (input, "finwait2-time %u", &tmp_time))
    1015           0 :         tcp_cfg.finwait2_time = tmp_time / TCP_TIMER_TICK;
    1016           0 :       else if (unformat (input, "lastack-time %u", &tmp_time))
    1017           0 :         tcp_cfg.lastack_time = tmp_time / TCP_TIMER_TICK;
    1018           0 :       else if (unformat (input, "closing-time %u", &tmp_time))
    1019           0 :         tcp_cfg.closing_time = tmp_time / TCP_TIMER_TICK;
    1020           0 :       else if (unformat (input, "alloc-err-timeout %u", &tmp_time))
    1021           0 :         tcp_cfg.alloc_err_timeout = tmp_time / TCP_TIMER_TICK;
    1022           0 :       else if (unformat (input, "cleanup-time %u", &tmp_time))
    1023           0 :         tcp_cfg.cleanup_time = tmp_time / 1000.0;
    1024             :       else
    1025           0 :         return clib_error_return (0, "unknown input `%U'",
    1026             :                                   format_unformat_error, input);
    1027             :     }
    1028         575 :   return 0;
    1029             : }
    1030             : 
    1031        7514 : VLIB_CONFIG_FUNCTION (tcp_config_fn, "tcp");
    1032             : 
    1033             : /*
    1034             :  * fd.io coding-style-patch-verification: ON
    1035             :  *
    1036             :  * Local Variables:
    1037             :  * eval: (c-set-style "gnu")
    1038             :  * End:
    1039             :  */

Generated by: LCOV version 1.14