Implement IPv6/IPv4 ping.
[vpp.git] / vnet / vnet / ip / 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 <vnet/ip/ping.h>
17
18 u8 *
19 format_icmp4_input_trace (u8 * s, va_list * va)
20 {
21   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
22   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
23   icmp4_input_trace_t *t = va_arg (*va, icmp4_input_trace_t *);
24
25   s = format (s, "%U",
26               format_ip4_header, t->packet_data, sizeof (t->packet_data));
27
28   return s;
29 }
30
31 /*
32  * If we can find the ping run by an ICMP ID, then we send the signal
33  * to the CLI process referenced by that ping run, alongside with
34  * a freshly made copy of the packet.
35  * I opted for a packet copy to keep the main packet processing path
36  * the same as for all the other nodes.
37  *
38  */
39
40 static void
41 signal_ip46_icmp_reply_event (vlib_main_t * vm,
42                               u8 event_type, vlib_buffer_t * b0)
43 {
44   ping_main_t *pm = &ping_main;
45   u16 net_icmp_id = 0;
46   u32 bi0_copy = 0;
47
48   switch (event_type)
49     {
50     case PING_RESPONSE_IP4:
51       {
52         icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
53         net_icmp_id = h0->icmp_echo.id;
54       }
55       break;
56     case PING_RESPONSE_IP6:
57       {
58         icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
59         net_icmp_id = h0->icmp_echo.id;
60       }
61       break;
62     default:
63       return;
64     }
65
66   uword *p = hash_get (pm->ping_run_by_icmp_id,
67                        clib_net_to_host_u16 (net_icmp_id));
68   if (!p)
69     return;
70
71   ping_run_t *pr = vec_elt_at_index (pm->ping_runs, p[0]);
72   if (vlib_buffer_alloc (vm, &bi0_copy, 1) == 1)
73     {
74       void *dst = vlib_buffer_get_current (vlib_get_buffer (vm, bi0_copy));
75       clib_memcpy (dst, vlib_buffer_get_current (b0), b0->current_length);
76     }
77   /* If buffer_alloc failed, bi0_copy == 0 - just signaling an event. */
78
79   vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy);
80 }
81
82 /*
83  * Process ICMPv6 echo replies
84  */
85 static uword
86 ip6_icmp_echo_reply_node_fn (vlib_main_t * vm,
87                              vlib_node_runtime_t * node, vlib_frame_t * frame)
88 {
89   u32 n_left_from, *from;
90
91   from = vlib_frame_vector_args (frame);        /* array of buffer indices */
92   n_left_from = frame->n_vectors;       /* number of buffer indices */
93
94   while (n_left_from > 0)
95     {
96       u32 bi0;
97       vlib_buffer_t *b0;
98       u32 next0;
99
100       bi0 = from[0];
101       b0 = vlib_get_buffer (vm, bi0);
102
103       signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0);
104
105       /* push this pkt to the next graph node, always error-drop */
106       next0 = ICMP6_ECHO_REPLY_NEXT_NORMAL;
107       vlib_set_next_frame_buffer (vm, node, next0, bi0);
108
109       from += 1;
110       n_left_from -= 1;
111     }
112
113   return frame->n_vectors;
114 }
115
116 /* *INDENT-OFF* */
117 VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) =
118 {
119   .function = ip6_icmp_echo_reply_node_fn,
120   .name = "ip6-icmp-echo-reply",
121   .vector_size = sizeof (u32),
122   .format_trace = format_icmp6_input_trace,
123   .n_next_nodes = ICMP6_ECHO_REPLY_N_NEXT,
124   .next_nodes = {
125     [ICMP6_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
126   },
127 };
128 /* *INDENT-ON* */
129
130 /*
131  * Process ICMPv4 echo replies
132  */
133 static uword
134 ip4_icmp_echo_reply_node_fn (vlib_main_t * vm,
135                              vlib_node_runtime_t * node, vlib_frame_t * frame)
136 {
137   u32 n_left_from, *from;
138
139   from = vlib_frame_vector_args (frame);        /* array of buffer indices */
140   n_left_from = frame->n_vectors;       /* number of buffer indices */
141
142   while (n_left_from > 0)
143     {
144       u32 bi0;
145       vlib_buffer_t *b0;
146       u32 next0;
147
148       bi0 = from[0];
149       b0 = vlib_get_buffer (vm, bi0);
150
151       /* push this pkt to the next graph node, always error-drop */
152       signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0);
153
154       next0 = ICMP4_ECHO_REPLY_NEXT_NORMAL;
155       vlib_set_next_frame_buffer (vm, node, next0, bi0);
156
157       from += 1;
158       n_left_from -= 1;
159     }
160
161   return frame->n_vectors;
162 }
163
164 /* *INDENT-OFF* */
165 VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) =
166 {
167   .function = ip4_icmp_echo_reply_node_fn,
168   .name = "ip4-icmp-echo-reply",
169   .vector_size = sizeof (u32),
170   .format_trace = format_icmp4_input_trace,
171   .n_next_nodes = ICMP4_ECHO_REPLY_N_NEXT,
172   .next_nodes = {
173     [ICMP4_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
174   },
175 };
176 /* *INDENT-ON* */
177
178 char *ip6_lookup_next_nodes[] = IP6_LOOKUP_NEXT_NODES;
179 char *ip4_lookup_next_nodes[] = IP4_LOOKUP_NEXT_NODES;
180
181 /* get first interface address */
182 static ip6_address_t *
183 ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index)
184 {
185   ip_lookup_main_t *lm = &im->lookup_main;
186   ip_interface_address_t *ia = 0;
187   ip6_address_t *result = 0;
188
189   foreach_ip_interface_address (lm, ia, sw_if_index,
190                                 1 /* honor unnumbered */ ,
191                                 (
192                                   {
193                                   ip6_address_t * a =
194                                   ip_interface_address_get_address (lm, ia);
195                                   result = a;
196                                   break;
197                                   }
198                                 ));
199   return result;
200 }
201
202 /* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */
203 static u16
204 init_icmp46_echo_request (icmp46_echo_request_t * icmp46_echo,
205                           u16 seq_host, u16 id_host, u16 data_len)
206 {
207   int i;
208   icmp46_echo->seq = clib_host_to_net_u16 (seq_host);
209   icmp46_echo->id = clib_host_to_net_u16 (id_host);
210
211   for (i = 0; i < sizeof (icmp46_echo->data); i++)
212     {
213       icmp46_echo->data[i] = i % 256;
214     }
215
216   if (data_len > sizeof (icmp46_echo_request_t))
217     {
218       data_len = sizeof (icmp46_echo_request_t);
219     }
220   return data_len;
221 }
222
223 /*
224  * Given adj index, return sw_if_index, possibly overwritten
225  * by a parameter. There is mostly debug outputs here,
226  * but it turned out handy to have these.
227  */
228
229 static u32
230 adj_index_to_sw_if_index (vlib_main_t * vm, ip_lookup_main_t * lm,
231                           char *lookup_next_nodes[], u32 adj_index0,
232                           u32 sw_if_index, u8 verbose)
233 {
234   ip_adjacency_t *adj0 = ip_get_adjacency (lm, adj_index0);
235   u32 sw_if_index0 = adj0->rewrite_header.sw_if_index;
236   if (verbose)
237     {
238       vlib_cli_output (vm, "Adjacency index: %u, sw_if_index: %u\n",
239                        adj_index0, sw_if_index0);
240       vlib_cli_output (vm, "Adj: %s\n",
241                        lookup_next_nodes[adj0->lookup_next_index]);
242       vlib_cli_output (vm, "Adj Interface: %d\n", adj0->if_address_index);
243     }
244
245   if (~0 != sw_if_index)
246     {
247       sw_if_index0 = sw_if_index;
248       if (verbose)
249         {
250           vlib_cli_output (vm, "Forced set interface: %d\n", sw_if_index0);
251         }
252     }
253   return sw_if_index0;
254 }
255
256 static send_ip46_ping_result_t
257 send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, ip6_address_t * pa6,
258                u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len,
259                u8 verbose)
260 {
261   icmp6_echo_request_header_t *h0;
262   u32 bi0 = 0;
263   u32 sw_if_index0;
264   ip_lookup_main_t *lm = &im->lookup_main;
265   int bogus_length = 0;
266   u32 adj_index0;
267   vlib_buffer_t *p0;
268   vlib_frame_t *f;
269   u32 *to_next;
270   u32 fib_index0;
271
272   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
273     return SEND_PING_ALLOC_FAIL;
274
275   p0 = vlib_get_buffer (vm, bi0);
276
277   /* Determine sw_if_index0 of source intf, may be force-set via sw_if_index. */
278   vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
279   vnet_buffer (p0)->sw_if_index[VLIB_TX] = ~0;  /* use interface VRF */
280   fib_index0 = 0;
281   adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, pa6);
282   sw_if_index0 =
283     adj_index_to_sw_if_index (vm, lm, ip6_lookup_next_nodes, adj_index0,
284                               sw_if_index, verbose);
285   if ((~0 == sw_if_index0) && (~0 == sw_if_index))
286     {
287       vlib_buffer_free (vm, &bi0, 1);
288       return SEND_PING_NO_INTERFACE;
289     }
290   vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
291
292   h0 = vlib_buffer_get_current (p0);
293
294   /* Fill in ip6 header fields */
295   h0->ip6.ip_version_traffic_class_and_flow_label =
296     clib_host_to_net_u32 (0x6 << 28);
297   h0->ip6.payload_length = 0;   /* Set below */
298   h0->ip6.protocol = IP_PROTOCOL_ICMP6;
299   h0->ip6.hop_limit = 255;
300   h0->ip6.dst_address = *pa6;
301   h0->ip6.src_address = *pa6;
302
303   /* Fill in the correct source now */
304   ip6_address_t *a = ip6_interface_first_address (im, sw_if_index0);
305   h0->ip6.src_address = a[0];
306
307   /* Fill in icmp fields */
308   h0->icmp.type = ICMP6_echo_request;
309   h0->icmp.code = 0;
310   h0->icmp.checksum = 0;
311
312   data_len =
313     init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
314   h0->icmp_echo.time_sent = vlib_time_now (vm);
315
316   /* Fix up the lengths */
317   h0->ip6.payload_length =
318     clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t));
319
320   p0->current_length = clib_net_to_host_u16 (h0->ip6.payload_length) +
321     STRUCT_OFFSET_OF (icmp6_echo_request_header_t, icmp);
322
323   /* Calculate the ICMP checksum */
324   h0->icmp.checksum = 0;
325   h0->icmp.checksum =
326     ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip6, &bogus_length);
327
328   /* Enqueue the packet right now */
329   f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
330   to_next = vlib_frame_vector_args (f);
331   to_next[0] = bi0;
332   f->n_vectors = 1;
333   vlib_put_frame_to_node (vm, ip6_lookup_node.index, f);
334
335   return SEND_PING_OK;
336 }
337
338 static send_ip46_ping_result_t
339 send_ip4_ping (vlib_main_t * vm,
340                ip4_main_t * im,
341                ip4_address_t * pa4,
342                u32 sw_if_index,
343                u16 seq_host, u16 id_host, u16 data_len, u8 verbose)
344 {
345   icmp4_echo_request_header_t *h0;
346   u32 bi0 = 0;
347   u32 sw_if_index0;
348   ip_lookup_main_t *lm = &im->lookup_main;
349   u32 adj_index0;
350   vlib_buffer_t *p0;
351   vlib_frame_t *f;
352   u32 *to_next;
353   u32 fib_index0;
354   u32 if_add_index0;
355
356   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
357     return SEND_PING_ALLOC_FAIL;
358
359   p0 = vlib_get_buffer (vm, bi0);
360
361   /* Determine sw_if_index0 of the source intf, may be force-set via sw_if_index.  */
362   vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
363   vnet_buffer (p0)->sw_if_index[VLIB_TX] = ~0;  /* use interface VRF */
364   fib_index0 = 0;
365   adj_index0 = ip4_fib_lookup_with_table (im, fib_index0, pa4, 0);
366   sw_if_index0 =
367     adj_index_to_sw_if_index (vm, lm, ip4_lookup_next_nodes, adj_index0,
368                               sw_if_index, verbose);
369   if ((~0 == sw_if_index0) && (~0 == sw_if_index))
370     {
371       vlib_buffer_free (vm, &bi0, 1);
372       return SEND_PING_NO_INTERFACE;
373     }
374   vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
375
376   h0 = vlib_buffer_get_current (p0);
377
378   /* Fill in ip4 header fields */
379   h0->ip4.checksum = 0;
380   h0->ip4.ip_version_and_header_length = 0x45;
381   h0->ip4.tos = 0;
382   h0->ip4.length = 0;           /* Set below */
383   h0->ip4.fragment_id = 0;
384   h0->ip4.flags_and_fragment_offset = 0;
385   h0->ip4.ttl = 0xff;
386   h0->ip4.protocol = IP_PROTOCOL_ICMP;
387   h0->ip4.dst_address = *pa4;
388   h0->ip4.src_address = *pa4;
389
390   /* Fill in the correct source now */
391   if_add_index0 = lm->if_address_pool_index_by_sw_if_index[sw_if_index0];
392   if (PREDICT_TRUE (if_add_index0 != ~0))
393     {
394       ip_interface_address_t *if_add =
395         pool_elt_at_index (lm->if_address_pool, if_add_index0);
396       ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add);
397       h0->ip4.src_address = *if_ip;
398       if (verbose)
399         {
400           vlib_cli_output (vm, "Source address: %U",
401                            format_ip4_address, &h0->ip4.src_address);
402         }
403     }
404
405   /* Fill in icmp fields */
406   h0->icmp.type = ICMP4_echo_request;
407   h0->icmp.code = 0;
408   h0->icmp.checksum = 0;
409
410   data_len =
411     init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
412   h0->icmp_echo.time_sent = vlib_time_now (vm);
413
414   /* Fix up the lengths */
415   h0->ip4.length =
416     clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t) +
417                           sizeof (ip4_header_t));
418
419   p0->current_length = clib_net_to_host_u16 (h0->ip4.length);
420
421   /* Calculate the IP and ICMP checksums */
422   h0->ip4.checksum = ip4_header_checksum (&(h0->ip4));
423   h0->icmp.checksum =
424     ~ip_csum_fold (ip_incremental_checksum (0, &(h0->icmp),
425                     p0->current_length - sizeof (ip4_header_t)));
426
427   /* Enqueue the packet right now */
428   f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
429   to_next = vlib_frame_vector_args (f);
430   to_next[0] = bi0;
431   f->n_vectors = 1;
432   vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
433
434   return SEND_PING_OK;
435 }
436
437
438 static void
439 print_ip6_icmp_reply (vlib_main_t * vm, u32 bi0)
440 {
441   vlib_buffer_t *b0 = vlib_get_buffer (vm,
442                                        bi0);
443   icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
444   f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
445
446   vlib_cli_output (vm,
447                    "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
448                    clib_host_to_net_u16 (h0->ip6.payload_length),
449                    format_ip6_address,
450                    &h0->ip6.src_address,
451                    clib_host_to_net_u16 (h0->icmp_echo.seq),
452                    h0->ip6.hop_limit, rtt * 1000.0);
453 }
454
455 static void
456 print_ip4_icmp_reply (vlib_main_t * vm, u32 bi0)
457 {
458   vlib_buffer_t *b0 = vlib_get_buffer (vm,
459                                        bi0);
460   icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
461   f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
462   u32 rcvd_icmp_len =
463     clib_host_to_net_u16 (h0->ip4.length) -
464     (4 * (0xF & h0->ip4.ip_version_and_header_length));
465
466   vlib_cli_output (vm,
467                    "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
468                    rcvd_icmp_len,
469                    format_ip4_address,
470                    &h0->ip4.src_address,
471                    clib_host_to_net_u16 (h0->icmp_echo.seq),
472                    h0->ip4.ttl, rtt * 1000.0);
473 }
474
475
476 /*
477  * Perform the ping run with the given parameters in the current CLI process.
478  * Depending on whether pa4 or pa6 is set, runs IPv4 or IPv6 ping.
479  * The amusing side effect is of course if both are set, then both pings are sent.
480  * This behavior can be used to ping a dualstack host over IPv4 and IPv6 at once.
481  */
482
483 static void
484 run_ping_ip46_address (vlib_main_t * vm, ip4_address_t * pa4,
485                        ip6_address_t * pa6, u32 sw_if_index,
486                        f64 ping_interval, u32 ping_repeat, u32 data_len,
487                        u32 verbose)
488 {
489   int i;
490   ping_main_t *pm = &ping_main;
491   uword curr_proc = vlib_current_process (vm);
492   u32 n_replies = 0;
493   u32 n_requests = 0;
494   ping_run_t *pr = 0;
495   u32 ping_run_index = 0;
496   u16 icmp_id = rand ();
497   while (hash_get (pm->ping_run_by_icmp_id, icmp_id))
498     {
499       vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
500       icmp_id++;
501     }
502   pool_get (pm->ping_runs, pr);
503   ping_run_index = pr - pm->ping_runs;
504   pr->cli_process_id = curr_proc;
505   pr->icmp_id = icmp_id;
506   hash_set (pm->ping_run_by_icmp_id, icmp_id, ping_run_index);
507   for (i = 1; i <= ping_repeat; i++)
508     {
509       f64 sleep_interval;
510       f64 time_ping_sent = vlib_time_now (vm);
511       /* Reset pr: running ping in other process could have changed pm->ping_runs */
512       pr = vec_elt_at_index (pm->ping_runs, ping_run_index);
513       pr->curr_seq = i;
514       if (pa6 &&
515           (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, pa6,
516                                           sw_if_index, i, icmp_id, data_len,
517                                           verbose)))
518         {
519           n_requests++;
520         }
521       if (pa4 &&
522           (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, pa4,
523                                           sw_if_index, i, icmp_id, data_len,
524                                           verbose)))
525         {
526           n_requests++;
527         }
528       while ((i <= ping_repeat)
529              &&
530              ((sleep_interval =
531                time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
532         {
533           uword event_type, *event_data = 0;
534           vlib_process_wait_for_event_or_clock (vm, sleep_interval);
535           event_type = vlib_process_get_events (vm, &event_data);
536           switch (event_type)
537             {
538             case ~0:           /* no events => timeout */
539               break;
540             case PING_RESPONSE_IP6:
541               {
542                 int i;
543                 for (i = 0; i < vec_len (event_data); i++)
544                   {
545                     u32 bi0 = event_data[0];
546                     print_ip6_icmp_reply (vm, bi0);
547                     n_replies++;
548                     if (0 != bi0)
549                       {
550                         vlib_buffer_free (vm, &bi0, 1);
551                       }
552                   }
553               }
554               break;
555             case PING_RESPONSE_IP4:
556               {
557                 int i;
558                 for (i = 0; i < vec_len (event_data); i++)
559                   {
560                     u32 bi0 = event_data[0];
561                     print_ip4_icmp_reply (vm, bi0);
562                     n_replies++;
563                     if (0 != bi0)
564                       {
565                         vlib_buffer_free (vm, &bi0, 1);
566                       }
567                   }
568               }
569               break;
570             default:
571               /* someone pressed a key, abort */
572               vlib_cli_output (vm, "Aborted due to a keypress.");
573               i = 1 + ping_repeat;
574               break;
575             }
576         }
577     }
578   vlib_cli_output (vm, "\n");
579   {
580     float loss =
581       (0 ==
582        n_requests) ? 0 : 100.0 * ((float) n_requests -
583                                   (float) n_replies) / (float) n_requests;
584     vlib_cli_output (vm,
585                      "Statistics: %u sent, %u received, %f%% packet loss\n",
586                      n_requests, n_replies, loss);
587     /* Reset pr: running ping in other process could have changed pm->ping_runs */
588     pr = vec_elt_at_index (pm->ping_runs, ping_run_index);
589     hash_unset (pm->ping_run_by_icmp_id, icmp_id);
590     pool_put (pm->ping_runs, pr);
591   }
592 }
593
594
595
596
597
598 static clib_error_t *
599 ping_ip_address (vlib_main_t * vm,
600                  unformat_input_t * input, vlib_cli_command_t * cmd)
601 {
602   ip4_address_t a4;
603   ip6_address_t a6;
604   clib_error_t *error = 0;
605   u32 ping_repeat = 5;
606   u8 ping_ip4, ping_ip6;
607   vnet_main_t *vnm = vnet_get_main ();
608   u32 data_len = PING_DEFAULT_DATA_LEN;
609   u32 verbose = 0;
610   f64 ping_interval = PING_DEFAULT_INTERVAL;
611   ping_ip4 = ping_ip6 = 0;
612   u32 sw_if_index;
613   sw_if_index = ~0;
614   if (unformat (input, "%U", unformat_ip4_address, &a4))
615     {
616       ping_ip4 = 1;
617     }
618   else if (unformat (input, "%U", unformat_ip6_address, &a6))
619     {
620       ping_ip6 = 1;
621     }
622   else if (unformat (input, "ipv4"))
623     {
624       if (unformat (input, "%U", unformat_ip4_address, &a4))
625         {
626           ping_ip4 = 1;
627         }
628       else
629         {
630           error =
631             clib_error_return (0,
632                                "expecting IPv4 address but got `%U'",
633                                format_unformat_error, input);
634         }
635     }
636   else if (unformat (input, "ipv6"))
637     {
638       if (unformat (input, "%U", unformat_ip6_address, &a6))
639         {
640           ping_ip6 = 1;
641         }
642       else
643         {
644           error =
645             clib_error_return (0,
646                                "expecting IPv6 address but got `%U'",
647                                format_unformat_error, input);
648         }
649     }
650   else
651     {
652       error =
653         clib_error_return (0,
654                            "expecting IP4/IP6 address `%U'. Usage: ping <addr> [source <intf>] [size <datasz>] [repeat <count>] [verbose]",
655                            format_unformat_error, input);
656       goto done;
657     }
658
659   /* allow for the second AF in the same ping */
660   if (!ping_ip4 && (unformat (input, "ipv4")))
661     {
662       if (unformat (input, "%U", unformat_ip4_address, &a4))
663         {
664           ping_ip4 = 1;
665         }
666     }
667   else if (!ping_ip6 && (unformat (input, "ipv6")))
668     {
669       if (unformat (input, "%U", unformat_ip6_address, &a6))
670         {
671           ping_ip6 = 1;
672         }
673     }
674
675   /* parse the rest of the parameters  in a cycle */
676   while (!unformat_eof (input, NULL))
677     {
678       if (unformat (input, "source"))
679         {
680           if (!unformat_user
681               (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
682             {
683               error =
684                 clib_error_return (0,
685                                    "unknown interface `%U'",
686                                    format_unformat_error, input);
687               goto done;
688             }
689         }
690       else if (unformat (input, "size"))
691         {
692           if (!unformat (input, "%u", &data_len))
693             {
694               error =
695                 clib_error_return (0,
696                                    "expecting size but got `%U'",
697                                    format_unformat_error, input);
698               goto done;
699             }
700         }
701       else if (unformat (input, "interval"))
702         {
703           if (!unformat (input, "%f", &ping_interval))
704             {
705               error =
706                 clib_error_return (0,
707                                    "expecting interval (floating point number) got `%U'",
708                                    format_unformat_error, input);
709               goto done;
710             }
711         }
712       else if (unformat (input, "repeat"))
713         {
714           if (!unformat (input, "%u", &ping_repeat))
715             {
716               error =
717                 clib_error_return (0,
718                                    "expecting repeat count but got `%U'",
719                                    format_unformat_error, input);
720               goto done;
721             }
722         }
723       else if (unformat (input, "verbose"))
724         {
725           verbose = 1;
726         }
727       else
728         {
729           error = clib_error_return (0, "unknown input `%U'",
730                                      format_unformat_error, input);
731           goto done;
732         }
733     }
734
735   run_ping_ip46_address (vm, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL,
736                          sw_if_index, ping_interval, ping_repeat, data_len,
737                          verbose);
738 done:
739   return error;
740 }
741
742 /* *INDENT-OFF* */
743 VLIB_CLI_COMMAND (ping_command, static) =
744 {
745   .path = "ping",
746   .function = ping_ip_address,
747   .short_help = "Ping IP4/IP6 address from interface",
748   .long_help =
749   "Ping IPv4/IPv6 address (or both at the same time)\n"
750   "\n"
751   "Arguments:\n"
752   "\n"
753   "ADDRESS              target (IPv4/IPv6)\n"
754   "ipv4 ADDRESS         target IPv4 address\n"
755   "ipv6 ADDRESS         target IPv6 address\n"
756   "interface STRING     interface for the source address\n"
757   "size NUMBER          size to send\n"
758   "repeat NUMBER        how many echo requests to send\n"
759   "interval NUMBER      interval between echo requests, in seconds (integer or fractional)\n"
760   "verbose              print various low-level information\n"
761 };
762 /* *INDENT-ON* */
763
764 static clib_error_t *
765 ping_cli_init (vlib_main_t * vm)
766 {
767   ping_main_t *pm = &ping_main;
768   pm->ip6_main = &ip6_main;
769   pm->ip4_main = &ip4_main;
770   icmp6_register_type (vm, ICMP6_echo_reply, ip6_icmp_echo_reply_node.index);
771   ip4_icmp_register_type (vm, ICMP4_echo_reply,
772                           ip4_icmp_echo_reply_node.index);
773   return 0;
774 }
775
776 VLIB_INIT_FUNCTION (ping_cli_init);