LCOV - code coverage report
Current view: top level - plugins/ping - ping.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 464 656 70.7 %
Date: 2023-10-26 01:39:38 Functions: 38 45 84.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 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 <stddef.h>
      17             : 
      18             : #include <vlib/vlib.h>
      19             : #include <vlib/unix/unix.h>
      20             : #include <vnet/fib/ip6_fib.h>
      21             : #include <vnet/fib/ip4_fib.h>
      22             : #include <vnet/ip/ip_sas.h>
      23             : #include <vnet/ip/ip6_link.h>
      24             : #include <vnet/ip/ip6_ll_table.h>
      25             : #include <vnet/plugin/plugin.h>
      26             : #include <vpp/app/version.h>
      27             : 
      28             : #include <vnet/ip/icmp4.h>
      29             : #include <ping/ping.h>
      30             : 
      31             : ping_main_t ping_main;
      32             : 
      33             : /**
      34             :  * @file
      35             :  * @brief IPv4 and IPv6 ICMP Ping.
      36             :  *
      37             :  * This file contains code to support IPv4 or IPv6 ICMP ECHO_REQUEST to
      38             :  * network hosts.
      39             :  *
      40             :  */
      41             : 
      42             : typedef struct
      43             : {
      44             :   u16 id;
      45             :   u16 seq;
      46             :   u32 cli_process_node;
      47             :   u8 is_ip6;
      48             : } icmp_echo_trace_t;
      49             : 
      50             : 
      51             : u8 *
      52           2 : format_icmp_echo_trace (u8 * s, va_list * va)
      53             : {
      54           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
      55           2 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
      56           2 :   icmp_echo_trace_t *t = va_arg (*va, icmp_echo_trace_t *);
      57             : 
      58             :   s =
      59           2 :     format (s, "ICMP%s echo id %d seq %d", t->is_ip6 ? "6" : "4", t->id,
      60           2 :             t->seq);
      61           2 :   if (t->cli_process_node == PING_CLI_UNKNOWN_NODE)
      62             :     {
      63           2 :       s = format (s, " (unknown)");
      64             :     }
      65             :   else
      66             :     {
      67           0 :       s = format (s, " send to cli node %d", t->cli_process_node);
      68             :     }
      69             : 
      70           2 :   return s;
      71             : }
      72             : 
      73             : 
      74             : static u8 *
      75           0 : format_ip46_ping_result (u8 * s, va_list * args)
      76             : {
      77           0 :   send_ip46_ping_result_t res = va_arg (*args, send_ip46_ping_result_t);
      78             : 
      79           0 :   switch (res)
      80             :     {
      81             : #define _(v, n) case SEND_PING_##v: s = format(s, "%s", n);break;
      82           0 :       foreach_ip46_ping_result
      83             : #undef _
      84             :     }
      85             : 
      86           0 :   return (s);
      87             : }
      88             : 
      89             : 
      90             : /*
      91             :  * Poor man's get-set-clear functions
      92             :  * for manipulation of icmp_id -> cli_process_id
      93             :  * mappings.
      94             :  *
      95             :  * There should normally be very few (0..1..2) of these
      96             :  * mappings, so the linear search is a good strategy.
      97             :  *
      98             :  * Make them thread-safe via a simple spinlock.
      99             :  *
     100             :  */
     101             : 
     102             : static_always_inline int
     103           1 : ip46_get_icmp_id_and_seq (vlib_main_t * vm, vlib_buffer_t * b0,
     104             :                           u16 * out_icmp_id, u16 * out_icmp_seq, int is_ip6)
     105             : {
     106             :   int l4_offset;
     107           1 :   if (is_ip6)
     108             :     {
     109           1 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
     110           1 :       if (ip6->protocol != IP_PROTOCOL_ICMP6)
     111             :         {
     112           0 :           return 0;
     113             :         }
     114           1 :       l4_offset = sizeof (*ip6);        // IPv6 EH
     115             :     }
     116             :   else
     117             :     {
     118           0 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
     119           0 :       l4_offset = ip4_header_bytes (ip4);
     120             : 
     121             :     }
     122           1 :   icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
     123           1 :   icmp46_echo_request_t *icmp46_echo = (icmp46_echo_request_t *) (icmp46 + 1);
     124             : 
     125           1 :   *out_icmp_id = clib_net_to_host_u16 (icmp46_echo->id);
     126           1 :   *out_icmp_seq = clib_net_to_host_u16 (icmp46_echo->seq);
     127           1 :   return 1;
     128             : }
     129             : 
     130             : /*
     131             :  * post the buffer to a given cli process node - the caller should forget bi0 after return.
     132             :  */
     133             : 
     134             : static_always_inline void
     135           0 : ip46_post_icmp_reply_event (vlib_main_t * vm, uword cli_process_id, u32 bi0,
     136             :                             int is_ip6)
     137             : {
     138           0 :   vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
     139           0 :   u64 nowts = clib_cpu_time_now ();
     140             : 
     141             :   /* Pass the timestamp to the cli_process thanks to the vnet_buffer unused metadata field */
     142             : 
     143             :   /* Camping on unused data... just ensure statically that there is enough space */
     144             :   STATIC_ASSERT (ARRAY_LEN (vnet_buffer (b0)->unused) *
     145             :                  sizeof (vnet_buffer (b0)->unused[0]) > sizeof (nowts),
     146             :                  "ping reply timestamp fits within remaining space of vnet_buffer unused data");
     147           0 :   u64 *pnowts = (void *) &vnet_buffer (b0)->unused[0];
     148           0 :   *pnowts = nowts;
     149             : 
     150           0 :   u32 event_id = is_ip6 ? PING_RESPONSE_IP6 : PING_RESPONSE_IP4;
     151           0 :   vlib_process_signal_event_mt (vm, cli_process_id, event_id, bi0);
     152           0 : }
     153             : 
     154             : 
     155             : static_always_inline void
     156           1 : ip46_echo_reply_maybe_trace_buffer (vlib_main_t * vm,
     157             :                                     vlib_node_runtime_t * node,
     158             :                                     uword cli_process_id, u16 id, u16 seq,
     159             :                                     vlib_buffer_t * b0, int is_ip6)
     160             : {
     161           1 :   if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
     162             :     {
     163           1 :       icmp_echo_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
     164           1 :       tr->id = id;
     165           1 :       tr->seq = seq;
     166           1 :       tr->cli_process_node = cli_process_id;
     167           1 :       tr->is_ip6 = is_ip6;
     168             :     }
     169           1 : }
     170             : 
     171             : 
     172             : static_always_inline uword
     173           1 : ip46_icmp_echo_reply_inner_node_fn (vlib_main_t * vm,
     174             :                                     vlib_node_runtime_t * node,
     175             :                                     vlib_frame_t * frame, int do_trace,
     176             :                                     int is_ip6)
     177             : {
     178             :   u32 n_left_from, *from, *to_next;
     179             :   icmp46_echo_reply_next_t next_index;
     180             : 
     181           1 :   from = vlib_frame_vector_args (frame);
     182           1 :   n_left_from = frame->n_vectors;
     183             : 
     184           1 :   next_index = node->cached_next_index;
     185             : 
     186           2 :   while (n_left_from > 0)
     187             :     {
     188             :       u32 n_left_to_next;
     189           1 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     190             : 
     191           2 :       while (n_left_from > 0 && n_left_to_next > 0)
     192             :         {
     193             :           u32 bi0;
     194             :           vlib_buffer_t *b0;
     195             :           /*
     196             :            * The buffers (replies) are either posted to the CLI thread
     197             :            * awaiting for them for subsequent analysis and disposal,
     198             :            * or are sent to the punt node.
     199             :            *
     200             :            * So the only "next" node is a punt, normally.
     201             :            */
     202           1 :           u32 next0 = ICMP46_ECHO_REPLY_NEXT_PUNT;
     203             : 
     204           1 :           bi0 = from[0];
     205           1 :           b0 = vlib_get_buffer (vm, bi0);
     206           1 :           from += 1;
     207           1 :           n_left_from -= 1;
     208             : 
     209           1 :           u16 icmp_id = ~0;
     210           1 :           u16 icmp_seq = ~0;
     211           1 :           uword cli_process_id = PING_CLI_UNKNOWN_NODE;
     212             : 
     213           1 :           if (ip46_get_icmp_id_and_seq (vm, b0, &icmp_id, &icmp_seq, is_ip6))
     214             :             {
     215           1 :               cli_process_id = get_cli_process_id_by_icmp_id_mt (vm, icmp_id);
     216             :             }
     217             : 
     218           1 :           if (do_trace)
     219           1 :             ip46_echo_reply_maybe_trace_buffer (vm, node, cli_process_id,
     220             :                                                 icmp_id, icmp_seq, b0,
     221             :                                                 is_ip6);
     222             : 
     223           1 :           if (~0 == cli_process_id)
     224             :             {
     225             :               /* no outstanding requests for this reply, punt */
     226             :               /* speculatively enqueue b0 to the current next frame */
     227           1 :               to_next[0] = bi0;
     228           1 :               to_next += 1;
     229           1 :               n_left_to_next -= 1;
     230             :               /* verify speculative enqueue, maybe switch current next frame */
     231           1 :               vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     232             :                                                to_next, n_left_to_next,
     233             :                                                bi0, next0);
     234             :             }
     235             :           else
     236             :             {
     237             :               /* Post the buffer to CLI thread. It will take care of freeing it. */
     238           0 :               ip46_post_icmp_reply_event (vm, cli_process_id, bi0, is_ip6);
     239             :             }
     240             :         }
     241           1 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     242             :     }
     243           1 :   return frame->n_vectors;
     244             : }
     245             : 
     246             : /*
     247             :  * select "with-trace" or "without-trace" codepaths upfront.
     248             :  */
     249             : static_always_inline uword
     250           1 : ip46_icmp_echo_reply_outer_node_fn (vlib_main_t * vm,
     251             :                                     vlib_node_runtime_t * node,
     252             :                                     vlib_frame_t * frame, int is_ip6)
     253             : {
     254           1 :   if (node->flags & VLIB_NODE_FLAG_TRACE)
     255           1 :     return ip46_icmp_echo_reply_inner_node_fn (vm, node, frame,
     256             :                                                1 /* do_trace */ , is_ip6);
     257             :   else
     258           0 :     return ip46_icmp_echo_reply_inner_node_fn (vm, node, frame,
     259             :                                                0 /* do_trace */ , is_ip6);
     260             : }
     261             : 
     262             : static uword
     263           0 : ip4_icmp_echo_reply_node_fn (vlib_main_t * vm,
     264             :                              vlib_node_runtime_t * node, vlib_frame_t * frame)
     265             : {
     266           0 :   return ip46_icmp_echo_reply_outer_node_fn (vm, node, frame,
     267             :                                              0 /* is_ip6 */ );
     268             : }
     269             : 
     270             : static uword
     271           1 : ip6_icmp_echo_reply_node_fn (vlib_main_t * vm,
     272             :                              vlib_node_runtime_t * node, vlib_frame_t * frame)
     273             : {
     274           1 :   return ip46_icmp_echo_reply_outer_node_fn (vm, node, frame,
     275             :                                              1 /* is_ip6 */ );
     276             : }
     277             : 
     278             : /* *INDENT-OFF* */
     279       47832 : VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) =
     280             : {
     281             :   .function = ip6_icmp_echo_reply_node_fn,
     282             :   .name = "ip6-icmp-echo-reply",
     283             :   .vector_size = sizeof (u32),
     284             :   .format_trace = format_icmp_echo_trace,
     285             :   .n_next_nodes = ICMP46_ECHO_REPLY_N_NEXT,
     286             :   .next_nodes = {
     287             :     [ICMP46_ECHO_REPLY_NEXT_DROP] = "ip6-drop",
     288             :     [ICMP46_ECHO_REPLY_NEXT_PUNT] = "ip6-punt",
     289             :   },
     290             : };
     291             : 
     292       47832 : VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) =
     293             : {
     294             :   .function = ip4_icmp_echo_reply_node_fn,
     295             :   .name = "ip4-icmp-echo-reply",
     296             :   .vector_size = sizeof (u32),
     297             :   .format_trace = format_icmp_echo_trace,
     298             :   .n_next_nodes = ICMP46_ECHO_REPLY_N_NEXT,
     299             :   .next_nodes = {
     300             :     [ICMP46_ECHO_REPLY_NEXT_DROP] = "ip4-drop",
     301             :     [ICMP46_ECHO_REPLY_NEXT_PUNT] = "ip4-punt",
     302             :   },
     303             : };
     304             : /* *INDENT-ON* */
     305             : 
     306             : static uword
     307        4132 : ip4_icmp_echo_request (vlib_main_t * vm,
     308             :                        vlib_node_runtime_t * node, vlib_frame_t * frame)
     309             : {
     310        4132 :   uword n_packets = frame->n_vectors;
     311             :   u32 *from, *to_next;
     312             :   u32 n_left_from, n_left_to_next, next;
     313        4132 :   ip4_main_t *i4m = &ip4_main;
     314             :   u16 *fragment_ids, *fid;
     315        4132 :   u8 host_config_ttl = i4m->host_config.ttl;
     316             : 
     317        4132 :   from = vlib_frame_vector_args (frame);
     318        4132 :   n_left_from = n_packets;
     319        4132 :   next = node->cached_next_index;
     320             : 
     321        4132 :   if (node->flags & VLIB_NODE_FLAG_TRACE)
     322        3392 :     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
     323             :                                    /* stride */ 1,
     324             :                                    sizeof (icmp_input_trace_t));
     325             : 
     326             :   /* Get random fragment IDs for replies. */
     327        4132 :   fid = fragment_ids = clib_random_buffer_get_data (&vm->random_buffer,
     328             :                                                     n_packets *
     329             :                                                     sizeof (fragment_ids[0]));
     330             : 
     331        8264 :   while (n_left_from > 0)
     332             :     {
     333        4132 :       vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);
     334             : 
     335       65119 :       while (n_left_from > 2 && n_left_to_next > 2)
     336             :         {
     337             :           vlib_buffer_t *p0, *p1;
     338             :           ip4_header_t *ip0, *ip1;
     339             :           icmp46_header_t *icmp0, *icmp1;
     340             :           u32 bi0, src0, dst0;
     341             :           u32 bi1, src1, dst1;
     342             :           ip_csum_t sum0, sum1;
     343             : 
     344       60987 :           bi0 = to_next[0] = from[0];
     345       60987 :           bi1 = to_next[1] = from[1];
     346             : 
     347       60987 :           from += 2;
     348       60987 :           n_left_from -= 2;
     349       60987 :           to_next += 2;
     350       60987 :           n_left_to_next -= 2;
     351             : 
     352       60987 :           p0 = vlib_get_buffer (vm, bi0);
     353       60987 :           p1 = vlib_get_buffer (vm, bi1);
     354       60987 :           ip0 = vlib_buffer_get_current (p0);
     355       60987 :           ip1 = vlib_buffer_get_current (p1);
     356       60987 :           icmp0 = ip4_next_header (ip0);
     357       60987 :           icmp1 = ip4_next_header (ip1);
     358             : 
     359       60987 :           vnet_buffer (p0)->sw_if_index[VLIB_RX] =
     360       60987 :             vnet_main.local_interface_sw_if_index;
     361       60987 :           vnet_buffer (p1)->sw_if_index[VLIB_RX] =
     362       60987 :             vnet_main.local_interface_sw_if_index;
     363             : 
     364             :           /* Update ICMP checksum. */
     365       60987 :           sum0 = icmp0->checksum;
     366       60987 :           sum1 = icmp1->checksum;
     367             : 
     368       60987 :           ASSERT (icmp0->type == ICMP4_echo_request);
     369       60987 :           ASSERT (icmp1->type == ICMP4_echo_request);
     370       60987 :           sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
     371             :                                  icmp46_header_t, type);
     372       60987 :           sum1 = ip_csum_update (sum1, ICMP4_echo_request, ICMP4_echo_reply,
     373             :                                  icmp46_header_t, type);
     374       60987 :           icmp0->type = ICMP4_echo_reply;
     375       60987 :           icmp1->type = ICMP4_echo_reply;
     376             : 
     377       60987 :           icmp0->checksum = ip_csum_fold (sum0);
     378       60987 :           icmp1->checksum = ip_csum_fold (sum1);
     379             : 
     380       60987 :           src0 = ip0->src_address.data_u32;
     381       60987 :           src1 = ip1->src_address.data_u32;
     382       60987 :           dst0 = ip0->dst_address.data_u32;
     383       60987 :           dst1 = ip1->dst_address.data_u32;
     384             : 
     385             :           /* Swap source and destination address.
     386             :              Does not change checksum. */
     387       60987 :           ip0->src_address.data_u32 = dst0;
     388       60987 :           ip1->src_address.data_u32 = dst1;
     389       60987 :           ip0->dst_address.data_u32 = src0;
     390       60987 :           ip1->dst_address.data_u32 = src1;
     391             : 
     392             :           /* Update IP checksum. */
     393       60987 :           sum0 = ip0->checksum;
     394       60987 :           sum1 = ip1->checksum;
     395             : 
     396       60987 :           sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
     397             :                                  ip4_header_t, ttl);
     398       60987 :           sum1 = ip_csum_update (sum1, ip1->ttl, host_config_ttl,
     399             :                                  ip4_header_t, ttl);
     400       60987 :           ip0->ttl = host_config_ttl;
     401       60987 :           ip1->ttl = host_config_ttl;
     402             : 
     403             :           /* New fragment id. */
     404       60987 :           sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
     405             :                                  ip4_header_t, fragment_id);
     406       60987 :           sum1 = ip_csum_update (sum1, ip1->fragment_id, fid[1],
     407             :                                  ip4_header_t, fragment_id);
     408       60987 :           ip0->fragment_id = fid[0];
     409       60987 :           ip1->fragment_id = fid[1];
     410       60987 :           fid += 2;
     411             : 
     412       60987 :           ip0->checksum = ip_csum_fold (sum0);
     413       60987 :           ip1->checksum = ip_csum_fold (sum1);
     414             : 
     415       60987 :           ASSERT (ip4_header_checksum_is_valid (ip0));
     416       60987 :           ASSERT (ip4_header_checksum_is_valid (ip1));
     417             : 
     418       60987 :           p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     419       60987 :           p1->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     420             :         }
     421             : 
     422        9618 :       while (n_left_from > 0 && n_left_to_next > 0)
     423             :         {
     424             :           vlib_buffer_t *p0;
     425             :           ip4_header_t *ip0;
     426             :           icmp46_header_t *icmp0;
     427             :           u32 bi0, src0, dst0;
     428             :           ip_csum_t sum0;
     429             : 
     430        5486 :           bi0 = to_next[0] = from[0];
     431             : 
     432        5486 :           from += 1;
     433        5486 :           n_left_from -= 1;
     434        5486 :           to_next += 1;
     435        5486 :           n_left_to_next -= 1;
     436             : 
     437        5486 :           p0 = vlib_get_buffer (vm, bi0);
     438        5486 :           ip0 = vlib_buffer_get_current (p0);
     439        5486 :           icmp0 = ip4_next_header (ip0);
     440             : 
     441        5486 :           vnet_buffer (p0)->sw_if_index[VLIB_RX] =
     442        5486 :             vnet_main.local_interface_sw_if_index;
     443             : 
     444             :           /* Update ICMP checksum. */
     445        5486 :           sum0 = icmp0->checksum;
     446             : 
     447        5486 :           ASSERT (icmp0->type == ICMP4_echo_request);
     448        5486 :           sum0 = ip_csum_update (sum0, ICMP4_echo_request, ICMP4_echo_reply,
     449             :                                  icmp46_header_t, type);
     450        5486 :           icmp0->type = ICMP4_echo_reply;
     451        5486 :           icmp0->checksum = ip_csum_fold (sum0);
     452             : 
     453        5486 :           src0 = ip0->src_address.data_u32;
     454        5486 :           dst0 = ip0->dst_address.data_u32;
     455        5486 :           ip0->src_address.data_u32 = dst0;
     456        5486 :           ip0->dst_address.data_u32 = src0;
     457             : 
     458             :           /* Update IP checksum. */
     459        5486 :           sum0 = ip0->checksum;
     460             : 
     461        5486 :           sum0 = ip_csum_update (sum0, ip0->ttl, host_config_ttl,
     462             :                                  ip4_header_t, ttl);
     463        5486 :           ip0->ttl = host_config_ttl;
     464             : 
     465        5486 :           sum0 = ip_csum_update (sum0, ip0->fragment_id, fid[0],
     466             :                                  ip4_header_t, fragment_id);
     467        5486 :           ip0->fragment_id = fid[0];
     468        5486 :           fid += 1;
     469             : 
     470        5486 :           ip0->checksum = ip_csum_fold (sum0);
     471             : 
     472        5486 :           ASSERT (ip4_header_checksum_is_valid (ip0));
     473             : 
     474        5486 :           p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     475             :         }
     476             : 
     477        4132 :       vlib_put_next_frame (vm, node, next, n_left_to_next);
     478             :     }
     479             : 
     480        4132 :   vlib_error_count (vm, ip4_icmp_input_node.index,
     481        4132 :                     ICMP4_ERROR_ECHO_REPLIES_SENT, frame->n_vectors);
     482             : 
     483        4132 :   return frame->n_vectors;
     484             : }
     485             : 
     486             : static u8 *
     487       85312 : format_icmp_input_trace (u8 * s, va_list * va)
     488             : {
     489       85312 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
     490       85312 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
     491       85312 :   icmp_input_trace_t *t = va_arg (*va, icmp_input_trace_t *);
     492             : 
     493       85312 :   s = format (s, "%U",
     494       85312 :               format_ip4_header, t->packet_data, sizeof (t->packet_data));
     495             : 
     496       85312 :   return s;
     497             : }
     498             : 
     499             : /* *INDENT-OFF* */
     500       47832 : VLIB_REGISTER_NODE (ip4_icmp_echo_request_node,static) = {
     501             :   .function = ip4_icmp_echo_request,
     502             :   .name = "ip4-icmp-echo-request",
     503             : 
     504             :   .vector_size = sizeof (u32),
     505             : 
     506             :   .format_trace = format_icmp_input_trace,
     507             : 
     508             :   .n_next_nodes = 1,
     509             :   .next_nodes = {
     510             :     [0] = "ip4-load-balance",
     511             :   },
     512             : };
     513             : /* *INDENT-ON* */
     514             : 
     515             : typedef enum
     516             : {
     517             :   ICMP6_ECHO_REQUEST_NEXT_LOOKUP,
     518             :   ICMP6_ECHO_REQUEST_NEXT_OUTPUT,
     519             :   ICMP6_ECHO_REQUEST_N_NEXT,
     520             : } icmp6_echo_request_next_t;
     521             : 
     522             : static uword
     523         620 : ip6_icmp_echo_request (vlib_main_t *vm, vlib_node_runtime_t *node,
     524             :                        vlib_frame_t *frame)
     525             : {
     526             :   u32 *from, *to_next;
     527             :   u32 n_left_from, n_left_to_next, next_index;
     528         620 :   ip6_main_t *im = &ip6_main;
     529             : 
     530         620 :   from = vlib_frame_vector_args (frame);
     531         620 :   n_left_from = frame->n_vectors;
     532         620 :   next_index = node->cached_next_index;
     533             : 
     534         620 :   if (node->flags & VLIB_NODE_FLAG_TRACE)
     535         420 :     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
     536             :                                    /* stride */ 1,
     537             :                                    sizeof (icmp6_input_trace_t));
     538             : 
     539        1240 :   while (n_left_from > 0)
     540             :     {
     541         620 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     542             : 
     543       16570 :       while (n_left_from > 2 && n_left_to_next > 2)
     544             :         {
     545             :           vlib_buffer_t *p0, *p1;
     546             :           ip6_header_t *ip0, *ip1;
     547             :           icmp46_header_t *icmp0, *icmp1;
     548             :           ip6_address_t tmp0, tmp1;
     549             :           ip_csum_t sum0, sum1;
     550             :           u32 bi0, bi1;
     551             :           u32 fib_index0, fib_index1;
     552       15950 :           u32 next0 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
     553       15950 :           u32 next1 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
     554             : 
     555       15950 :           bi0 = to_next[0] = from[0];
     556       15950 :           bi1 = to_next[1] = from[1];
     557             : 
     558       15950 :           from += 2;
     559       15950 :           n_left_from -= 2;
     560       15950 :           to_next += 2;
     561       15950 :           n_left_to_next -= 2;
     562             : 
     563       15950 :           p0 = vlib_get_buffer (vm, bi0);
     564       15950 :           p1 = vlib_get_buffer (vm, bi1);
     565       15950 :           ip0 = vlib_buffer_get_current (p0);
     566       15950 :           ip1 = vlib_buffer_get_current (p1);
     567       15950 :           icmp0 = ip6_next_header (ip0);
     568       15950 :           icmp1 = ip6_next_header (ip1);
     569             : 
     570             :           /* Check icmp type to echo reply and update icmp checksum. */
     571       15950 :           sum0 = icmp0->checksum;
     572       15950 :           sum1 = icmp1->checksum;
     573             : 
     574       15950 :           ASSERT (icmp0->type == ICMP6_echo_request);
     575       15950 :           ASSERT (icmp1->type == ICMP6_echo_request);
     576       15950 :           sum0 = ip_csum_update (sum0, ICMP6_echo_request, ICMP6_echo_reply,
     577             :                                  icmp46_header_t, type);
     578       15950 :           sum1 = ip_csum_update (sum1, ICMP6_echo_request, ICMP6_echo_reply,
     579             :                                  icmp46_header_t, type);
     580             : 
     581       15950 :           icmp0->checksum = ip_csum_fold (sum0);
     582       15950 :           icmp1->checksum = ip_csum_fold (sum1);
     583             : 
     584       15950 :           icmp0->type = ICMP6_echo_reply;
     585       15950 :           icmp1->type = ICMP6_echo_reply;
     586             : 
     587             :           /* Swap source and destination address. */
     588       15950 :           tmp0 = ip0->src_address;
     589       15950 :           tmp1 = ip1->src_address;
     590             : 
     591       15950 :           ip0->src_address = ip0->dst_address;
     592       15950 :           ip1->src_address = ip1->dst_address;
     593             : 
     594       15950 :           ip0->dst_address = tmp0;
     595       15950 :           ip1->dst_address = tmp1;
     596             : 
     597             :           /* New hop count. */
     598       15950 :           ip0->hop_limit = im->host_config.ttl;
     599       15950 :           ip1->hop_limit = im->host_config.ttl;
     600             : 
     601       15950 :           if (ip6_address_is_link_local_unicast (&ip0->src_address) &&
     602           0 :               !ip6_address_is_link_local_unicast (&ip0->dst_address))
     603             :             {
     604           0 :               fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
     605             :                                     vnet_buffer (p0)->sw_if_index[VLIB_RX]);
     606           0 :               vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index0;
     607             :             }
     608       15950 :           if (ip6_address_is_link_local_unicast (&ip1->src_address) &&
     609           0 :               !ip6_address_is_link_local_unicast (&ip1->dst_address))
     610             :             {
     611           0 :               fib_index1 = vec_elt (im->fib_index_by_sw_if_index,
     612             :                                     vnet_buffer (p1)->sw_if_index[VLIB_RX]);
     613           0 :               vnet_buffer (p1)->sw_if_index[VLIB_TX] = fib_index1;
     614             :             }
     615       15950 :           p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     616       15950 :           p1->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     617             : 
     618             :           /* verify speculative enqueues, maybe switch current next frame */
     619             :           /* if next0==next1==next_index then nothing special needs to be done
     620             :            */
     621       15950 :           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
     622             :                                            n_left_to_next, bi0, bi1, next0,
     623             :                                            next1);
     624             :         }
     625             : 
     626        1343 :       while (n_left_from > 0 && n_left_to_next > 0)
     627             :         {
     628             :           vlib_buffer_t *p0;
     629             :           ip6_header_t *ip0;
     630             :           icmp46_header_t *icmp0;
     631             :           u32 bi0;
     632             :           ip6_address_t tmp0;
     633             :           ip_csum_t sum0;
     634             :           u32 fib_index0;
     635         723 :           u32 next0 = ICMP6_ECHO_REQUEST_NEXT_LOOKUP;
     636             : 
     637         723 :           bi0 = to_next[0] = from[0];
     638             : 
     639         723 :           from += 1;
     640         723 :           n_left_from -= 1;
     641         723 :           to_next += 1;
     642         723 :           n_left_to_next -= 1;
     643             : 
     644         723 :           p0 = vlib_get_buffer (vm, bi0);
     645         723 :           ip0 = vlib_buffer_get_current (p0);
     646         723 :           icmp0 = ip6_next_header (ip0);
     647             : 
     648             :           /* Check icmp type to echo reply and update icmp checksum. */
     649         723 :           sum0 = icmp0->checksum;
     650             : 
     651         723 :           ASSERT (icmp0->type == ICMP6_echo_request);
     652         723 :           sum0 = ip_csum_update (sum0, ICMP6_echo_request, ICMP6_echo_reply,
     653             :                                  icmp46_header_t, type);
     654             : 
     655         723 :           icmp0->checksum = ip_csum_fold (sum0);
     656             : 
     657         723 :           icmp0->type = ICMP6_echo_reply;
     658             : 
     659             :           /* Swap source and destination address. */
     660         723 :           tmp0 = ip0->src_address;
     661         723 :           ip0->src_address = ip0->dst_address;
     662         723 :           ip0->dst_address = tmp0;
     663             : 
     664         723 :           ip0->hop_limit = im->host_config.ttl;
     665             : 
     666         731 :           if (ip6_address_is_link_local_unicast (&ip0->src_address) &&
     667           8 :               !ip6_address_is_link_local_unicast (&ip0->dst_address))
     668             :             {
     669             :               /* if original packet was to the link local, then the
     670             :                * fib index is that of the LL table, we can't use that
     671             :                * to foward the response if the new destination
     672             :                * is global, so reset to the fib index of the link.
     673             :                * In other case, the fib index we need has been written
     674             :                * to the buffer already. */
     675           1 :               fib_index0 = vec_elt (im->fib_index_by_sw_if_index,
     676             :                                     vnet_buffer (p0)->sw_if_index[VLIB_RX]);
     677           1 :               vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index0;
     678             :             }
     679         723 :           p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     680             :           /* Verify speculative enqueue, maybe switch current next frame */
     681         723 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     682             :                                            n_left_to_next, bi0, next0);
     683             :         }
     684             : 
     685         620 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     686             :     }
     687             : 
     688         620 :   vlib_error_count (vm, ip6_icmp_input_node.index,
     689         620 :                     ICMP6_ERROR_ECHO_REPLIES_SENT, frame->n_vectors);
     690             : 
     691         620 :   return frame->n_vectors;
     692             : }
     693             : 
     694       47832 : VLIB_REGISTER_NODE (ip6_icmp_echo_request_node,static) = {
     695             :   .function = ip6_icmp_echo_request,
     696             :   .name = "ip6-icmp-echo-request",
     697             : 
     698             :   .vector_size = sizeof (u32),
     699             : 
     700             :   .format_trace = format_icmp6_input_trace,
     701             : 
     702             :   .n_next_nodes = ICMP6_ECHO_REQUEST_N_NEXT,
     703             :   .next_nodes = {
     704             :     [ICMP6_ECHO_REQUEST_NEXT_LOOKUP] = "ip6-lookup",
     705             :     [ICMP6_ECHO_REQUEST_NEXT_OUTPUT] = "interface-output",
     706             :   },
     707             : };
     708             : 
     709             : /*
     710             :  * A swarm of address-family agnostic helper functions
     711             :  * for building and sending the ICMP echo request.
     712             :  *
     713             :  * Deliberately mostly "static" rather than "static inline"
     714             :  * so one can trace them sanely if needed in debugger, if needed.
     715             :  *
     716             :  */
     717             : 
     718             : static_always_inline u8
     719        1680 : get_icmp_echo_payload_byte (int offset)
     720             : {
     721        1680 :   return (offset % 256);
     722             : }
     723             : 
     724             : /* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */
     725             : static u16
     726          28 : init_icmp46_echo_request (vlib_main_t * vm, vlib_buffer_t * b0,
     727             :                           int l4_header_offset,
     728             :                           icmp46_echo_request_t * icmp46_echo, u16 seq_host,
     729             :                           u16 id_host, u64 now, u16 data_len)
     730             : {
     731             :   int i;
     732             : 
     733             : 
     734          28 :   int l34_len =
     735          28 :     l4_header_offset + sizeof (icmp46_header_t) +
     736             :     offsetof (icmp46_echo_request_t, data);
     737          28 :   int max_data_len = vlib_buffer_get_default_data_size (vm) - l34_len;
     738             : 
     739          28 :   int first_buf_data_len = data_len < max_data_len ? data_len : max_data_len;
     740             : 
     741          28 :   int payload_offset = 0;
     742        1708 :   for (i = 0; i < first_buf_data_len; i++)
     743        1680 :     icmp46_echo->data[i] = get_icmp_echo_payload_byte (payload_offset++);
     744             : 
     745             :   /* inspired by vlib_buffer_add_data */
     746          28 :   vlib_buffer_t *hb = b0;
     747          28 :   int remaining_data_len = data_len - first_buf_data_len;
     748          28 :   while (remaining_data_len)
     749             :     {
     750           0 :       int this_buf_data_len =
     751           0 :         remaining_data_len <
     752           0 :         vlib_buffer_get_default_data_size (vm) ? remaining_data_len :
     753           0 :         vlib_buffer_get_default_data_size (vm);
     754           0 :       int n_alloc = vlib_buffer_alloc (vm, &b0->next_buffer, 1);
     755           0 :       if (n_alloc < 1)
     756             :         {
     757             :           /* That is how much we have so far - return it... */
     758           0 :           return (data_len - remaining_data_len);
     759             :         }
     760           0 :       b0->flags |= VLIB_BUFFER_NEXT_PRESENT;
     761             :       /* move on to the newly acquired buffer */
     762           0 :       b0 = vlib_get_buffer (vm, b0->next_buffer);
     763             :       /* initialize the data */
     764           0 :       for (i = 0; i < this_buf_data_len; i++)
     765             :         {
     766           0 :           b0->data[i] = get_icmp_echo_payload_byte (payload_offset++);
     767             :         }
     768           0 :       b0->current_length = this_buf_data_len;
     769           0 :       b0->current_data = 0;
     770           0 :       remaining_data_len -= this_buf_data_len;
     771             :     }
     772          28 :   hb->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
     773          28 :   hb->current_length = l34_len + first_buf_data_len;
     774          28 :   hb->total_length_not_including_first_buffer = data_len - first_buf_data_len;
     775             : 
     776          28 :   icmp46_echo->time_sent = now;
     777          28 :   icmp46_echo->seq = clib_host_to_net_u16 (seq_host);
     778          28 :   icmp46_echo->id = clib_host_to_net_u16 (id_host);
     779          28 :   return data_len;
     780             : }
     781             : 
     782             : 
     783             : static u32
     784          28 : ip46_fib_index_from_table_id (u32 table_id, int is_ip6)
     785             : {
     786          28 :   u32 fib_index = is_ip6 ?
     787          28 :     ip6_fib_index_from_table_id (table_id) :
     788          28 :     ip4_fib_index_from_table_id (table_id);
     789          28 :   return fib_index;
     790             : }
     791             : 
     792             : static fib_node_index_t
     793          28 : ip46_fib_table_lookup_host (u32 fib_index, ip46_address_t * pa46, int is_ip6)
     794             : {
     795          28 :   fib_node_index_t fib_entry_index = is_ip6 ?
     796          28 :     ip6_fib_table_lookup (fib_index, &pa46->ip6, 128) :
     797          28 :     ip4_fib_table_lookup (ip4_fib_get (fib_index), &pa46->ip4, 32);
     798          28 :   return fib_entry_index;
     799             : }
     800             : 
     801             : static u32
     802          28 : ip46_get_resolving_interface (u32 fib_index, ip46_address_t * pa46,
     803             :                               int is_ip6)
     804             : {
     805          28 :   u32 sw_if_index = ~0;
     806          28 :   if (~0 != fib_index)
     807             :     {
     808             :       fib_node_index_t fib_entry_index;
     809          28 :       fib_entry_index = ip46_fib_table_lookup_host (fib_index, pa46, is_ip6);
     810          28 :       sw_if_index = fib_entry_get_resolving_interface (fib_entry_index);
     811             :     }
     812          28 :   return sw_if_index;
     813             : }
     814             : 
     815             : static u32
     816           0 : ip46_fib_table_get_index_for_sw_if_index (u32 sw_if_index, int is_ip6,
     817             :                                           ip46_address_t *pa46)
     818             : {
     819           0 :   if (is_ip6)
     820             :     {
     821           0 :       if (ip6_address_is_link_local_unicast (&pa46->ip6))
     822           0 :         return ip6_ll_fib_get (sw_if_index);
     823           0 :       return ip6_fib_table_get_index_for_sw_if_index (sw_if_index);
     824             :     }
     825           0 :   return ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
     826             : }
     827             : 
     828             : 
     829             : static int
     830          28 : ip46_fill_l3_header (ip46_address_t * pa46, vlib_buffer_t * b0, int is_ip6)
     831             : {
     832          28 :   if (is_ip6)
     833             :     {
     834           0 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
     835             :       /* Fill in ip6 header fields */
     836           0 :       ip6->ip_version_traffic_class_and_flow_label =
     837           0 :         clib_host_to_net_u32 (0x6 << 28);
     838           0 :       ip6->payload_length = 0;       /* will be set later */
     839           0 :       ip6->protocol = IP_PROTOCOL_ICMP6;
     840           0 :       ip6->hop_limit = 255;
     841           0 :       ip6->dst_address = pa46->ip6;
     842           0 :       ip6->src_address = pa46->ip6;
     843           0 :       return (sizeof (ip6_header_t));
     844             :     }
     845             :   else
     846             :     {
     847          28 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
     848             :       /* Fill in ip4 header fields */
     849          28 :       ip4->checksum = 0;
     850          28 :       ip4->ip_version_and_header_length = 0x45;
     851          28 :       ip4->tos = 0;
     852          28 :       ip4->length = 0;               /* will be set later */
     853          28 :       ip4->fragment_id = 0;
     854          28 :       ip4->flags_and_fragment_offset = 0;
     855          28 :       ip4->ttl = 0xff;
     856          28 :       ip4->protocol = IP_PROTOCOL_ICMP;
     857          28 :       ip4->src_address = pa46->ip4;
     858          28 :       ip4->dst_address = pa46->ip4;
     859          28 :       return (sizeof (ip4_header_t));
     860             :     }
     861             : }
     862             : 
     863             : static bool
     864          28 : ip46_set_src_address (u32 sw_if_index, vlib_buffer_t * b0, int is_ip6)
     865             : {
     866          28 :   bool res = false;
     867             : 
     868          28 :   if (is_ip6)
     869             :     {
     870           0 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
     871             : 
     872           0 :       res = ip6_sas_by_sw_if_index (sw_if_index, &ip6->dst_address,
     873             :                                     &ip6->src_address);
     874             :     }
     875             :   else
     876             :     {
     877          28 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
     878             : 
     879          28 :       res = ip4_sas_by_sw_if_index (sw_if_index, &ip4->dst_address,
     880             :                                     &ip4->src_address);
     881             :     }
     882          28 :   return res;
     883             : }
     884             : 
     885             : static void
     886           0 : ip46_print_buffer_src_address (vlib_main_t * vm, vlib_buffer_t * b0,
     887             :                                int is_ip6)
     888             : {
     889             :   void *format_addr_func;
     890             :   void *paddr;
     891           0 :   if (is_ip6)
     892             :     {
     893           0 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
     894           0 :       format_addr_func = format_ip6_address;
     895           0 :       paddr = &ip6->src_address;
     896             :     }
     897             :   else
     898             :     {
     899           0 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
     900           0 :       format_addr_func = format_ip4_address;
     901           0 :       paddr = &ip4->src_address;
     902             :     }
     903           0 :   vlib_cli_output (vm, "Source address: %U ", format_addr_func, paddr);
     904           0 : }
     905             : 
     906             : static u16
     907          28 : ip46_fill_icmp_request_at (vlib_main_t * vm, int l4_offset, u16 seq_host,
     908             :                            u16 id_host, u16 data_len, vlib_buffer_t * b0,
     909             :                            int is_ip6)
     910             : {
     911          28 :   icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
     912             : 
     913          28 :   icmp46->type = is_ip6 ? ICMP6_echo_request : ICMP4_echo_request;
     914          28 :   icmp46->code = 0;
     915          28 :   icmp46->checksum = 0;
     916             : 
     917          28 :   icmp46_echo_request_t *icmp46_echo = (icmp46_echo_request_t *) (icmp46 + 1);
     918             : 
     919             :   data_len =
     920          28 :     init_icmp46_echo_request (vm, b0, l4_offset, icmp46_echo, seq_host,
     921             :                               id_host, clib_cpu_time_now (), data_len);
     922          28 :   return data_len;
     923             : }
     924             : 
     925             : 
     926             : /* Compute ICMP4 checksum with multibuffer support. */
     927             : u16
     928          28 : ip4_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0,
     929             :                            ip4_header_t * ip0)
     930             : {
     931             :   ip_csum_t sum0;
     932             :   u32 ip_header_length, payload_length_host_byte_order;
     933             :   u32 n_this_buffer, n_bytes_left, n_ip_bytes_this_buffer;
     934             :   u16 sum16;
     935             :   void *data_this_buffer;
     936             : 
     937          28 :   ip_header_length = ip4_header_bytes (ip0);
     938          28 :   payload_length_host_byte_order =
     939          28 :     clib_net_to_host_u16 (ip0->length) - ip_header_length;
     940             : 
     941             :   /* ICMP4 checksum does not include the IP header */
     942          28 :   sum0 = 0;
     943             : 
     944          28 :   n_bytes_left = n_this_buffer = payload_length_host_byte_order;
     945          28 :   data_this_buffer = (void *) ip0 + ip_header_length;
     946          28 :   n_ip_bytes_this_buffer =
     947          28 :     p0->current_length - (((u8 *) ip0 - p0->data) - p0->current_data);
     948          28 :   if (n_this_buffer + ip_header_length > n_ip_bytes_this_buffer)
     949             :     {
     950           0 :       n_this_buffer = n_ip_bytes_this_buffer > ip_header_length ?
     951           0 :         n_ip_bytes_this_buffer - ip_header_length : 0;
     952             :     }
     953             :   while (1)
     954             :     {
     955          28 :       sum0 = ip_incremental_checksum (sum0, data_this_buffer, n_this_buffer);
     956          28 :       n_bytes_left -= n_this_buffer;
     957          28 :       if (n_bytes_left == 0)
     958          28 :         break;
     959             : 
     960           0 :       ASSERT (p0->flags & VLIB_BUFFER_NEXT_PRESENT);
     961           0 :       p0 = vlib_get_buffer (vm, p0->next_buffer);
     962           0 :       data_this_buffer = vlib_buffer_get_current (p0);
     963           0 :       n_this_buffer = p0->current_length;
     964             :     }
     965             : 
     966          28 :   sum16 = ~ip_csum_fold (sum0);
     967             : 
     968          28 :   return sum16;
     969             : }
     970             : 
     971             : 
     972             : static void
     973          28 : ip46_fix_len_and_csum (vlib_main_t * vm, int l4_offset, u16 data_len,
     974             :                        vlib_buffer_t * b0, int is_ip6)
     975             : {
     976          28 :   u16 payload_length =
     977             :     data_len + sizeof (icmp46_header_t) + offsetof (icmp46_echo_request_t,
     978             :                                                     data);
     979          28 :   u16 total_length = payload_length + l4_offset;
     980          28 :   icmp46_header_t *icmp46 = vlib_buffer_get_current (b0) + l4_offset;
     981          28 :   icmp46->checksum = 0;
     982             : 
     983          28 :   if (is_ip6)
     984             :     {
     985           0 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
     986           0 :       ip6->payload_length = clib_host_to_net_u16 (payload_length);
     987             : 
     988           0 :       int bogus_length = 0;
     989           0 :       icmp46->checksum =
     990           0 :         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6, &bogus_length);
     991             :     }
     992             :   else
     993             :     {
     994          28 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
     995          28 :       ip4->length = clib_host_to_net_u16 (total_length);
     996             : 
     997          28 :       ip4->checksum = ip4_header_checksum (ip4);
     998          28 :       icmp46->checksum = ip4_icmp_compute_checksum (vm, b0, ip4);
     999             :     }
    1000          28 : }
    1001             : 
    1002             : static u16
    1003          56 : at_most_a_frame (u32 count)
    1004             : {
    1005          56 :   return count > VLIB_FRAME_SIZE ? VLIB_FRAME_SIZE : count;
    1006             : }
    1007             : 
    1008             : static int
    1009          28 : ip46_enqueue_packet (vlib_main_t *vm, vlib_buffer_t *b0, u32 burst,
    1010             :                      u32 lookup_node_index)
    1011             : {
    1012          28 :   vlib_frame_t *f = 0;
    1013          28 :   int n_sent = 0;
    1014             : 
    1015             :   u16 n_to_send;
    1016             : 
    1017             :   /*
    1018             :    * Enqueue the packet, possibly as one or more frames of copies to make
    1019             :    * bursts. We enqueue b0 as the very last buffer, when there is no possibility
    1020             :    * for error in vlib_buffer_copy, so as to allow the caller to free it
    1021             :    * in case we encounter the error in the middle of the loop.
    1022             :    */
    1023          56 :   for (n_to_send = at_most_a_frame (burst), burst -= n_to_send; n_to_send > 0;
    1024          28 :        n_to_send = at_most_a_frame (burst), burst -= n_to_send)
    1025             :     {
    1026          28 :       f = vlib_get_frame_to_node (vm, lookup_node_index);
    1027             :       /* f can not be NULL here - frame allocation failure causes panic */
    1028             : 
    1029          28 :       u32 *to_next = vlib_frame_vector_args (f);
    1030          28 :       f->n_vectors = n_to_send;
    1031             : 
    1032          38 :       while (n_to_send > 1)
    1033             :         {
    1034          10 :           vlib_buffer_t *b0copy = vlib_buffer_copy (vm, b0);
    1035          10 :           if (PREDICT_FALSE (b0copy == NULL))
    1036           0 :             goto ship_and_ret;
    1037          10 :           *to_next++ = vlib_get_buffer_index (vm, b0copy);
    1038          10 :           n_to_send--;
    1039          10 :           n_sent++;
    1040             :         }
    1041             : 
    1042             :       /* n_to_send is guaranteed to equal 1 here */
    1043          28 :       if (burst > 0)
    1044             :         {
    1045             :           /* not the last burst, so still make a copy for the last buffer */
    1046           0 :           vlib_buffer_t *b0copy = vlib_buffer_copy (vm, b0);
    1047           0 :           if (PREDICT_FALSE (b0copy == NULL))
    1048           0 :             goto ship_and_ret;
    1049           0 :           n_to_send--;
    1050           0 :           *to_next++ = vlib_get_buffer_index (vm, b0copy);
    1051             :         }
    1052             :       else
    1053             :         {
    1054             :           /* put the original buffer as the last one of an error-free run */
    1055          28 :           *to_next++ = vlib_get_buffer_index (vm, b0);
    1056             :         }
    1057          28 :       vlib_put_frame_to_node (vm, lookup_node_index, f);
    1058          28 :       n_sent += f->n_vectors;
    1059             :     }
    1060          28 :   return n_sent;
    1061             :   /*
    1062             :    * We reach here in case we already enqueued one or more buffers
    1063             :    * and maybe one or more frames but could not make more copies.
    1064             :    * There is an outstanding frame - so ship it and return.
    1065             :    * Caller will have to free the b0 in this case, since
    1066             :    * we did not enqueue it here yet.
    1067             :    */
    1068           0 : ship_and_ret:
    1069           0 :   ASSERT (n_to_send <= f->n_vectors);
    1070           0 :   f->n_vectors -= n_to_send;
    1071           0 :   n_sent += f->n_vectors;
    1072           0 :   vlib_put_frame_to_node (vm, lookup_node_index, f);
    1073           0 :   return n_sent;
    1074             : }
    1075             : 
    1076             : 
    1077             : /*
    1078             :  * An address-family agnostic ping send function.
    1079             :  */
    1080             : 
    1081             : #define ERROR_OUT(e) do { err = e; goto done; } while (0)
    1082             : 
    1083             : static send_ip46_ping_result_t
    1084          28 : send_ip46_ping (vlib_main_t * vm,
    1085             :                 u32 table_id,
    1086             :                 ip46_address_t * pa46,
    1087             :                 u32 sw_if_index,
    1088             :                 u16 seq_host, u16 id_host, u16 data_len, u32 burst,
    1089             :                 u8 verbose, int is_ip6)
    1090             : {
    1091          28 :   int err = SEND_PING_OK;
    1092          28 :   u32 bi0 = 0;
    1093          28 :   int n_buf0 = 0;
    1094             :   vlib_buffer_t *b0;
    1095             : 
    1096          28 :   n_buf0 = vlib_buffer_alloc (vm, &bi0, 1);
    1097          28 :   if (n_buf0 < 1)
    1098           0 :     ERROR_OUT (SEND_PING_ALLOC_FAIL);
    1099             : 
    1100          28 :   b0 = vlib_get_buffer (vm, bi0);
    1101             : 
    1102             :   /*
    1103             :    * if the user did not provide a source interface,
    1104             :    * perform a resolution and use an interface
    1105             :    * via which it succeeds.
    1106             :    */
    1107             :   u32 fib_index;
    1108          28 :   if (~0 == sw_if_index)
    1109             :     {
    1110          28 :       fib_index = ip46_fib_index_from_table_id (table_id, is_ip6);
    1111          28 :       sw_if_index = ip46_get_resolving_interface (fib_index, pa46, is_ip6);
    1112             :     }
    1113             :   else
    1114             :     fib_index =
    1115           0 :       ip46_fib_table_get_index_for_sw_if_index (sw_if_index, is_ip6, pa46);
    1116             : 
    1117          28 :   if (~0 == fib_index)
    1118           0 :     ERROR_OUT (SEND_PING_NO_TABLE);
    1119          28 :   if (~0 == sw_if_index)
    1120           0 :     ERROR_OUT (SEND_PING_NO_INTERFACE);
    1121             : 
    1122          28 :   vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index;
    1123          28 :   vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;
    1124             : 
    1125          28 :   int l4_header_offset = ip46_fill_l3_header (pa46, b0, is_ip6);
    1126             : 
    1127             :   /* set the src address in the buffer */
    1128          28 :   if (!ip46_set_src_address (sw_if_index, b0, is_ip6))
    1129           0 :     ERROR_OUT (SEND_PING_NO_SRC_ADDRESS);
    1130          28 :   if (verbose)
    1131           0 :     ip46_print_buffer_src_address (vm, b0, is_ip6);
    1132             : 
    1133             :   data_len =
    1134          28 :     ip46_fill_icmp_request_at (vm, l4_header_offset, seq_host, id_host,
    1135             :                                data_len, b0, is_ip6);
    1136             : 
    1137          28 :   ip46_fix_len_and_csum (vm, l4_header_offset, data_len, b0, is_ip6);
    1138             : 
    1139          28 :   u32 node_index = ip6_lookup_node.index;
    1140          28 :   if (is_ip6)
    1141             :     {
    1142           0 :       if (pa46->ip6.as_u32[0] == clib_host_to_net_u32 (0xff020000))
    1143             :         {
    1144           0 :           node_index = ip6_rewrite_mcast_node.index;
    1145           0 :           vnet_buffer (b0)->sw_if_index[VLIB_RX] = sw_if_index;
    1146           0 :           vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
    1147           0 :           vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
    1148           0 :             ip6_link_get_mcast_adj (sw_if_index);
    1149             :         }
    1150             :     }
    1151             :   else
    1152             :     {
    1153          28 :       node_index = ip4_lookup_node.index;
    1154             :     }
    1155          28 :   int n_sent = ip46_enqueue_packet (vm, b0, burst, node_index);
    1156          28 :   if (n_sent < burst)
    1157           0 :     err = SEND_PING_NO_BUFFERS;
    1158             : 
    1159          28 : done:
    1160          28 :   if (err != SEND_PING_OK)
    1161             :     {
    1162           0 :       if (n_buf0 > 0)
    1163           0 :         vlib_buffer_free (vm, &bi0, 1);
    1164             :     }
    1165          28 :   return err;
    1166             : }
    1167             : 
    1168             : send_ip46_ping_result_t
    1169           0 : send_ip6_ping (vlib_main_t *vm, u32 table_id, ip6_address_t *pa6,
    1170             :                u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
    1171             :                u32 burst, u8 verbose)
    1172             : {
    1173             :   ip46_address_t target;
    1174           0 :   target.ip6 = *pa6;
    1175           0 :   return send_ip46_ping (vm, table_id, &target, sw_if_index, seq_host,
    1176             :                          id_host, data_len, burst, verbose, 1 /* is_ip6 */ );
    1177             : }
    1178             : 
    1179             : send_ip46_ping_result_t
    1180          28 : send_ip4_ping (vlib_main_t *vm, u32 table_id, ip4_address_t *pa4,
    1181             :                u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
    1182             :                u32 burst, u8 verbose)
    1183             : {
    1184             :   ip46_address_t target;
    1185          28 :   ip46_address_set_ip4 (&target, pa4);
    1186          28 :   return send_ip46_ping (vm, table_id, &target, sw_if_index, seq_host,
    1187             :                          id_host, data_len, burst, verbose, 0 /* is_ip6 */ );
    1188             : }
    1189             : 
    1190             : static void
    1191           0 : print_ip46_icmp_reply (vlib_main_t * vm, u32 bi0, int is_ip6)
    1192             : {
    1193           0 :   vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
    1194             :   int l4_offset;
    1195             :   void *paddr;
    1196             :   void *format_addr_func;
    1197             :   u16 payload_length;
    1198             :   u8 ttl;
    1199           0 :   if (is_ip6)
    1200             :     {
    1201           0 :       ip6_header_t *ip6 = vlib_buffer_get_current (b0);
    1202           0 :       paddr = (void *) &ip6->src_address;
    1203           0 :       format_addr_func = (void *) format_ip6_address;
    1204           0 :       ttl = ip6->hop_limit;
    1205           0 :       l4_offset = sizeof (ip6_header_t);        // FIXME - EH processing ?
    1206           0 :       payload_length = clib_net_to_host_u16 (ip6->payload_length);
    1207             :     }
    1208             :   else
    1209             :     {
    1210           0 :       ip4_header_t *ip4 = vlib_buffer_get_current (b0);
    1211           0 :       paddr = (void *) &ip4->src_address;
    1212           0 :       format_addr_func = (void *) format_ip4_address;
    1213           0 :       ttl = ip4->ttl;
    1214           0 :       l4_offset = ip4_header_bytes (ip4);
    1215           0 :       payload_length =
    1216           0 :         clib_net_to_host_u16 (ip4->length) + ip4_header_bytes (ip4);
    1217             :     }
    1218           0 :   icmp46_header_t *icmp = vlib_buffer_get_current (b0) + l4_offset;
    1219           0 :   icmp46_echo_request_t *icmp_echo = (icmp46_echo_request_t *) (icmp + 1);
    1220           0 :   u64 *dataplane_ts = (u64 *) & vnet_buffer (b0)->unused[0];
    1221             : 
    1222           0 :   f64 clocks_per_second = ((f64) vm->clib_time.clocks_per_second);
    1223           0 :   f64 rtt =
    1224           0 :     ((f64) (*dataplane_ts - icmp_echo->time_sent)) / clocks_per_second;
    1225             : 
    1226           0 :   vlib_cli_output (vm,
    1227             :                    "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
    1228             :                    payload_length,
    1229             :                    format_addr_func,
    1230             :                    paddr,
    1231           0 :                    clib_host_to_net_u16 (icmp_echo->seq), ttl, rtt * 1000.0);
    1232           0 : }
    1233             : 
    1234             : /*
    1235             :  * Perform the ping run with the given parameters in the current CLI process.
    1236             :  * Depending on whether pa4 or pa6 is set, runs IPv4 or IPv6 ping.
    1237             :  * The amusing side effect is of course if both are set, then both pings are sent.
    1238             :  * This behavior can be used to ping a dualstack host over IPv4 and IPv6 at once.
    1239             :  */
    1240             : 
    1241             : static void
    1242           5 : run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4,
    1243             :                        ip6_address_t * pa6, u32 sw_if_index,
    1244             :                        f64 ping_interval, u32 ping_repeat, u32 data_len,
    1245             :                        u32 ping_burst, u32 verbose)
    1246             : {
    1247             :   int i;
    1248           5 :   uword curr_proc = vlib_current_process (vm);
    1249           5 :   u32 n_replies = 0;
    1250           5 :   u32 n_requests = 0;
    1251             :   u16 icmp_id;
    1252             : 
    1253             :   static u32 rand_seed = 0;
    1254             : 
    1255           5 :   if (PREDICT_FALSE (!rand_seed))
    1256           1 :     rand_seed = random_default_seed ();
    1257             : 
    1258           5 :   icmp_id = random_u32 (&rand_seed) & 0xffff;
    1259             : 
    1260           5 :   while (~0 != get_cli_process_id_by_icmp_id_mt (vm, icmp_id))
    1261             :     {
    1262           0 :       vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
    1263           0 :       icmp_id++;
    1264             :     }
    1265             : 
    1266           5 :   set_cli_process_id_by_icmp_id_mt (vm, icmp_id, curr_proc);
    1267             : 
    1268          29 :   for (i = 1; i <= ping_repeat; i++)
    1269             :     {
    1270          24 :       send_ip46_ping_result_t res = SEND_PING_OK;
    1271             :       f64 sleep_interval;
    1272          24 :       f64 time_ping_sent = vlib_time_now (vm);
    1273          24 :       if (pa6)
    1274             :         {
    1275           0 :           res = send_ip6_ping (vm, table_id,
    1276             :                                pa6, sw_if_index, i, icmp_id,
    1277             :                                data_len, ping_burst, verbose);
    1278           0 :           if (SEND_PING_OK == res)
    1279           0 :             n_requests += ping_burst;
    1280             :           else
    1281           0 :             vlib_cli_output (vm, "Failed: %U", format_ip46_ping_result, res);
    1282             :         }
    1283          24 :       if (pa4)
    1284             :         {
    1285          24 :           res = send_ip4_ping (vm, table_id, pa4,
    1286             :                                sw_if_index, i, icmp_id, data_len,
    1287             :                                ping_burst, verbose);
    1288          24 :           if (SEND_PING_OK == res)
    1289          24 :             n_requests += ping_burst;
    1290             :           else
    1291           0 :             vlib_cli_output (vm, "Failed: %U", format_ip46_ping_result, res);
    1292             :         }
    1293             : 
    1294             :       /* Collect and print the responses until it is time to send a next ping */
    1295             : 
    1296         177 :       while ((i <= ping_repeat)
    1297         177 :              &&
    1298         177 :              ((sleep_interval =
    1299         177 :                time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
    1300             :         {
    1301         153 :           uword event_type, *event_data = 0;
    1302         153 :           vlib_process_wait_for_event_or_clock (vm, sleep_interval);
    1303         153 :           event_type = vlib_process_get_events (vm, &event_data);
    1304         153 :           switch (event_type)
    1305             :             {
    1306         153 :             case ~0:            /* no events => timeout */
    1307         153 :               break;
    1308           0 :             case PING_RESPONSE_IP6:
    1309             :               /* fall-through */
    1310             :             case PING_RESPONSE_IP4:
    1311             :               {
    1312             :                 int ii;
    1313           0 :                 int is_ip6 = (event_type == PING_RESPONSE_IP6);
    1314           0 :                 for (ii = 0; ii < vec_len (event_data); ii++)
    1315             :                   {
    1316           0 :                     u32 bi0 = event_data[ii];
    1317           0 :                     print_ip46_icmp_reply (vm, bi0, is_ip6);
    1318           0 :                     n_replies++;
    1319           0 :                     if (0 != bi0)
    1320           0 :                       vlib_buffer_free (vm, &bi0, 1);
    1321             :                   }
    1322             :               }
    1323           0 :               break;
    1324           0 :             case UNIX_CLI_PROCESS_EVENT_READ_READY:
    1325             :             case UNIX_CLI_PROCESS_EVENT_QUIT:
    1326             :               /* someone pressed a key, abort */
    1327           0 :               vlib_cli_output (vm, "Aborted due to a keypress.");
    1328           0 :               goto double_break;
    1329             :             }
    1330         153 :           vec_free (event_data);
    1331             :         }
    1332             :     }
    1333           5 : double_break:
    1334           5 :   vlib_cli_output (vm, "\n");
    1335             :   {
    1336           5 :     float loss =
    1337             :       (0 ==
    1338           5 :        n_requests) ? 0 : 100.0 * ((float) n_requests -
    1339           5 :                                   (float) n_replies) / (float) n_requests;
    1340           5 :     vlib_cli_output (vm,
    1341             :                      "Statistics: %u sent, %u received, %f%% packet loss\n",
    1342             :                      n_requests, n_replies, loss);
    1343           5 :     clear_cli_process_id_by_icmp_id_mt (vm, icmp_id);
    1344             :   }
    1345           5 : }
    1346             : 
    1347             : 
    1348             : 
    1349             : static clib_error_t *
    1350           5 : ping_ip_address (vlib_main_t * vm,
    1351             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
    1352             : {
    1353             :   ip4_address_t a4;
    1354             :   ip6_address_t a6;
    1355           5 :   clib_error_t *error = 0;
    1356           5 :   u32 ping_repeat = 5;
    1357           5 :   u32 ping_burst = 1;
    1358             :   u8 ping_ip4, ping_ip6;
    1359           5 :   vnet_main_t *vnm = vnet_get_main ();
    1360           5 :   u32 data_len = PING_DEFAULT_DATA_LEN;
    1361           5 :   u32 verbose = 0;
    1362           5 :   f64 ping_interval = PING_DEFAULT_INTERVAL;
    1363             :   u32 sw_if_index, table_id;
    1364             : 
    1365           5 :   table_id = 0;
    1366           5 :   ping_ip4 = ping_ip6 = 0;
    1367           5 :   sw_if_index = ~0;
    1368             : 
    1369           5 :   if (unformat (input, "%U", unformat_ip4_address, &a4))
    1370             :     {
    1371           5 :       ping_ip4 = 1;
    1372             :     }
    1373           0 :   else if (unformat (input, "%U", unformat_ip6_address, &a6))
    1374             :     {
    1375           0 :       ping_ip6 = 1;
    1376             :     }
    1377           0 :   else if (unformat (input, "ipv4"))
    1378             :     {
    1379           0 :       if (unformat (input, "%U", unformat_ip4_address, &a4))
    1380             :         {
    1381           0 :           ping_ip4 = 1;
    1382             :         }
    1383             :       else
    1384             :         {
    1385             :           error =
    1386           0 :             clib_error_return (0,
    1387             :                                "expecting IPv4 address but got `%U'",
    1388             :                                format_unformat_error, input);
    1389             :         }
    1390             :     }
    1391           0 :   else if (unformat (input, "ipv6"))
    1392             :     {
    1393           0 :       if (unformat (input, "%U", unformat_ip6_address, &a6))
    1394             :         {
    1395           0 :           ping_ip6 = 1;
    1396             :         }
    1397             :       else
    1398             :         {
    1399             :           error =
    1400           0 :             clib_error_return (0,
    1401             :                                "expecting IPv6 address but got `%U'",
    1402             :                                format_unformat_error, input);
    1403             :         }
    1404             :     }
    1405             :   else
    1406             :     {
    1407             :       error =
    1408           0 :         clib_error_return (0,
    1409             :                            "expecting IP4/IP6 address `%U'. Usage: ping <addr> [source <intf>] [size <datasz>] [repeat <count>] [verbose]",
    1410             :                            format_unformat_error, input);
    1411           0 :       goto done;
    1412             :     }
    1413             : 
    1414             :   /* allow for the second AF in the same ping */
    1415           5 :   if (!ping_ip4 && (unformat (input, "ipv4")))
    1416             :     {
    1417           0 :       if (unformat (input, "%U", unformat_ip4_address, &a4))
    1418             :         {
    1419           0 :           ping_ip4 = 1;
    1420             :         }
    1421             :     }
    1422           5 :   else if (!ping_ip6 && (unformat (input, "ipv6")))
    1423             :     {
    1424           0 :       if (unformat (input, "%U", unformat_ip6_address, &a6))
    1425             :         {
    1426           0 :           ping_ip6 = 1;
    1427             :         }
    1428             :     }
    1429             : 
    1430             :   /* parse the rest of the parameters  in a cycle */
    1431          15 :   while (!unformat_eof (input, NULL))
    1432             :     {
    1433          10 :       if (unformat (input, "source"))
    1434             :         {
    1435           0 :           if (!unformat_user
    1436             :               (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
    1437             :             {
    1438             :               error =
    1439           0 :                 clib_error_return (0,
    1440             :                                    "unknown interface `%U'",
    1441             :                                    format_unformat_error, input);
    1442           0 :               goto done;
    1443             :             }
    1444             :         }
    1445          10 :       else if (unformat (input, "size"))
    1446             :         {
    1447           0 :           if (!unformat (input, "%u", &data_len))
    1448             :             {
    1449             :               error =
    1450           0 :                 clib_error_return (0,
    1451             :                                    "expecting size but got `%U'",
    1452             :                                    format_unformat_error, input);
    1453           0 :               goto done;
    1454             :             }
    1455           0 :           if (data_len > PING_MAXIMUM_DATA_SIZE)
    1456             :             {
    1457             :               error =
    1458           0 :                 clib_error_return (0,
    1459             :                                    "%d is bigger than maximum allowed payload size %d",
    1460             :                                    data_len, PING_MAXIMUM_DATA_SIZE);
    1461           0 :               goto done;
    1462             :             }
    1463             :         }
    1464          10 :       else if (unformat (input, "table-id"))
    1465             :         {
    1466           0 :           if (!unformat (input, "%u", &table_id))
    1467             :             {
    1468             :               error =
    1469           0 :                 clib_error_return (0,
    1470             :                                    "expecting table-id but got `%U'",
    1471             :                                    format_unformat_error, input);
    1472           0 :               goto done;
    1473             :             }
    1474             :         }
    1475          10 :       else if (unformat (input, "interval"))
    1476             :         {
    1477           5 :           if (!unformat (input, "%f", &ping_interval))
    1478             :             {
    1479             :               error =
    1480           0 :                 clib_error_return (0,
    1481             :                                    "expecting interval (floating point number) got `%U'",
    1482             :                                    format_unformat_error, input);
    1483           0 :               goto done;
    1484             :             }
    1485             :         }
    1486           5 :       else if (unformat (input, "repeat"))
    1487             :         {
    1488           4 :           if (!unformat (input, "%u", &ping_repeat))
    1489             :             {
    1490             :               error =
    1491           0 :                 clib_error_return (0,
    1492             :                                    "expecting repeat count but got `%U'",
    1493             :                                    format_unformat_error, input);
    1494           0 :               goto done;
    1495             :             }
    1496             :         }
    1497           1 :       else if (unformat (input, "burst"))
    1498             :         {
    1499           1 :           if (!unformat (input, "%u", &ping_burst))
    1500             :             {
    1501             :               error =
    1502           0 :                 clib_error_return (0,
    1503             :                                    "expecting burst count but got `%U'",
    1504             :                                    format_unformat_error, input);
    1505           0 :               goto done;
    1506             :             }
    1507             :         }
    1508           0 :       else if (unformat (input, "verbose"))
    1509             :         {
    1510           0 :           verbose = 1;
    1511             :         }
    1512             :       else
    1513             :         {
    1514           0 :           error = clib_error_return (0, "unknown input `%U'",
    1515             :                                      format_unformat_error, input);
    1516           0 :           goto done;
    1517             :         }
    1518             :     }
    1519             : 
    1520             : /*
    1521             :  * Operationally, one won't (and shouldn't) need to send more than a frame worth of pings.
    1522             :  * But it may be handy during the debugging.
    1523             :  */
    1524             : 
    1525             : #ifdef CLIB_DEBUG
    1526             : #define MAX_PING_BURST (10*VLIB_FRAME_SIZE)
    1527             : #else
    1528             : #define MAX_PING_BURST (VLIB_FRAME_SIZE)
    1529             : #endif
    1530             : 
    1531           5 :   if (ping_burst < 1 || ping_burst > MAX_PING_BURST)
    1532           0 :     return clib_error_return (0, "burst size must be between 1 and %u",
    1533             :                               MAX_PING_BURST);
    1534             : 
    1535           5 :   run_ping_ip46_address (vm, table_id, ping_ip4 ? &a4 : NULL,
    1536             :                          ping_ip6 ? &a6 : NULL, sw_if_index, ping_interval,
    1537             :                          ping_repeat, data_len, ping_burst, verbose);
    1538           5 : done:
    1539           5 :   return error;
    1540             : }
    1541             : 
    1542             : /*?
    1543             :  * This command sends an ICMP ECHO_REQUEST to network hosts. The address
    1544             :  * can be an IPv4 or IPv6 address (or both at the same time).
    1545             :  *
    1546             :  * @cliexpar
    1547             :  * @parblock
    1548             :  * Example of how ping an IPv4 address:
    1549             :  * @cliexstart{ping 172.16.1.2 source GigabitEthernet2/0/0 repeat 2}
    1550             :  * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1090 ms
    1551             :  * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0914 ms
    1552             :  *
    1553             :  * Statistics: 2 sent, 2 received, 0% packet loss
    1554             :  * @cliexend
    1555             :  *
    1556             :  * Example of how ping both an IPv4 address and IPv6 address at the same time:
    1557             :  * @cliexstart{ping 172.16.1.2 ipv6 fe80::24a5:f6ff:fe9c:3a36 source GigabitEthernet2/0/0 repeat 2 verbose}
    1558             :  * Adjacency index: 10, sw_if_index: 1
    1559             :  * Adj: ip6-discover-neighbor
    1560             :  * Adj Interface: 0
    1561             :  * Forced set interface: 1
    1562             :  * Adjacency index: 0, sw_if_index: 4294967295
    1563             :  * Adj: ip4-miss
    1564             :  * Adj Interface: 0
    1565             :  * Forced set interface: 1
    1566             :  * Source address: 172.16.1.1
    1567             :  * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1899 ms
    1568             :  * Adjacency index: 10, sw_if_index: 1
    1569             :  * Adj: ip6-discover-neighbor
    1570             :  * Adj Interface: 0
    1571             :  * Forced set interface: 1
    1572             :  * Adjacency index: 0, sw_if_index: 4294967295
    1573             :  * Adj: ip4-miss
    1574             :  * Adj Interface: 0
    1575             :  * Forced set interface: 1
    1576             :  * Source address: 172.16.1.1
    1577             :  * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0910 ms
    1578             :  *
    1579             :  * Statistics: 4 sent, 2 received, 50% packet loss
    1580             :  * @cliexend
    1581             :  * @endparblock
    1582             : ?*/
    1583             : /* *INDENT-OFF* */
    1584       51989 : VLIB_CLI_COMMAND (ping_command, static) =
    1585             : {
    1586             :   .path = "ping",
    1587             :   .function = ping_ip_address,
    1588             :   .short_help = "ping {<ip-addr> | ipv4 <ip4-addr> | ipv6 <ip6-addr>}"
    1589             :   " [ipv4 <ip4-addr> | ipv6 <ip6-addr>] [source <interface>]"
    1590             :   " [size <pktsize:60>] [interval <sec:1>] [repeat <cnt:5>] [table-id <id:0>]"
    1591             :   " [burst <count:1>] [verbose]",
    1592             :   .is_mp_safe = 1,
    1593             : };
    1594             : /* *INDENT-ON* */
    1595             : 
    1596             : static clib_error_t *
    1597         575 : ping_cli_init (vlib_main_t * vm)
    1598             : {
    1599         575 :   vlib_thread_main_t *tm = vlib_get_thread_main ();
    1600         575 :   ping_main_t *pm = &ping_main;
    1601             : 
    1602         575 :   pm->ip6_main = &ip6_main;
    1603         575 :   pm->ip4_main = &ip4_main;
    1604         575 :   icmp6_register_type (vm, ICMP6_echo_reply, ip6_icmp_echo_reply_node.index);
    1605         575 :   ip4_icmp_register_type (vm, ICMP4_echo_reply,
    1606             :                           ip4_icmp_echo_reply_node.index);
    1607         575 :   if (tm->n_vlib_mains > 1)
    1608          37 :     clib_spinlock_init (&pm->ping_run_check_lock);
    1609             : 
    1610         575 :   ip4_icmp_register_type (vm, ICMP4_echo_request,
    1611             :                           ip4_icmp_echo_request_node.index);
    1612         575 :   icmp6_register_type (vm, ICMP6_echo_request,
    1613             :                        ip6_icmp_echo_request_node.index);
    1614             : 
    1615         575 :   ping_plugin_api_hookup (vm);
    1616             : 
    1617         575 :   return 0;
    1618             : }
    1619             : 
    1620        1151 : VLIB_INIT_FUNCTION (ping_cli_init);
    1621             : 
    1622             : /* *INDENT-OFF* */
    1623             : VLIB_PLUGIN_REGISTER () = {
    1624             :     .version = VPP_BUILD_VER,
    1625             :     .description = "Ping (ping)",
    1626             : };
    1627             : /* *INDENT-ON* */
    1628             : 
    1629             : /*
    1630             :  * fd.io coding-style-patch-verification: ON
    1631             :  *
    1632             :  * Local Variables:
    1633             :  * eval: (c-set-style "gnu")
    1634             :  * End:
    1635             :  */

Generated by: LCOV version 1.14