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