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

Generated by: LCOV version 1.14