b5842a69c5094361b2d8c98708ae8be561af965f
[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;
497
498   static u32 rand_seed = 0;
499
500   if (PREDICT_FALSE(!rand_seed))
501       rand_seed = random_default_seed();
502
503   icmp_id = random_u32(&rand_seed) & 0xffff;
504
505   while (hash_get (pm->ping_run_by_icmp_id, icmp_id))
506     {
507       vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
508       icmp_id++;
509     }
510   pool_get (pm->ping_runs, pr);
511   ping_run_index = pr - pm->ping_runs;
512   pr->cli_process_id = curr_proc;
513   pr->icmp_id = icmp_id;
514   hash_set (pm->ping_run_by_icmp_id, icmp_id, ping_run_index);
515   for (i = 1; i <= ping_repeat; i++)
516     {
517       f64 sleep_interval;
518       f64 time_ping_sent = vlib_time_now (vm);
519       /* Reset pr: running ping in other process could have changed pm->ping_runs */
520       pr = vec_elt_at_index (pm->ping_runs, ping_run_index);
521       pr->curr_seq = i;
522       if (pa6 &&
523           (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, pa6,
524                                           sw_if_index, i, icmp_id, data_len,
525                                           verbose)))
526         {
527           n_requests++;
528         }
529       if (pa4 &&
530           (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, pa4,
531                                           sw_if_index, i, icmp_id, data_len,
532                                           verbose)))
533         {
534           n_requests++;
535         }
536       while ((i <= ping_repeat)
537              &&
538              ((sleep_interval =
539                time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
540         {
541           uword event_type, *event_data = 0;
542           vlib_process_wait_for_event_or_clock (vm, sleep_interval);
543           event_type = vlib_process_get_events (vm, &event_data);
544           switch (event_type)
545             {
546             case ~0:           /* no events => timeout */
547               break;
548             case PING_RESPONSE_IP6:
549               {
550                 int i;
551                 for (i = 0; i < vec_len (event_data); i++)
552                   {
553                     u32 bi0 = event_data[0];
554                     print_ip6_icmp_reply (vm, bi0);
555                     n_replies++;
556                     if (0 != bi0)
557                       {
558                         vlib_buffer_free (vm, &bi0, 1);
559                       }
560                   }
561               }
562               break;
563             case PING_RESPONSE_IP4:
564               {
565                 int i;
566                 for (i = 0; i < vec_len (event_data); i++)
567                   {
568                     u32 bi0 = event_data[0];
569                     print_ip4_icmp_reply (vm, bi0);
570                     n_replies++;
571                     if (0 != bi0)
572                       {
573                         vlib_buffer_free (vm, &bi0, 1);
574                       }
575                   }
576               }
577               break;
578             default:
579               /* someone pressed a key, abort */
580               vlib_cli_output (vm, "Aborted due to a keypress.");
581               i = 1 + ping_repeat;
582               break;
583             }
584         }
585     }
586   vlib_cli_output (vm, "\n");
587   {
588     float loss =
589       (0 ==
590        n_requests) ? 0 : 100.0 * ((float) n_requests -
591                                   (float) n_replies) / (float) n_requests;
592     vlib_cli_output (vm,
593                      "Statistics: %u sent, %u received, %f%% packet loss\n",
594                      n_requests, n_replies, loss);
595     /* Reset pr: running ping in other process could have changed pm->ping_runs */
596     pr = vec_elt_at_index (pm->ping_runs, ping_run_index);
597     hash_unset (pm->ping_run_by_icmp_id, icmp_id);
598     pool_put (pm->ping_runs, pr);
599   }
600 }
601
602
603
604
605
606 static clib_error_t *
607 ping_ip_address (vlib_main_t * vm,
608                  unformat_input_t * input, vlib_cli_command_t * cmd)
609 {
610   ip4_address_t a4;
611   ip6_address_t a6;
612   clib_error_t *error = 0;
613   u32 ping_repeat = 5;
614   u8 ping_ip4, ping_ip6;
615   vnet_main_t *vnm = vnet_get_main ();
616   u32 data_len = PING_DEFAULT_DATA_LEN;
617   u32 verbose = 0;
618   f64 ping_interval = PING_DEFAULT_INTERVAL;
619   ping_ip4 = ping_ip6 = 0;
620   u32 sw_if_index;
621   sw_if_index = ~0;
622   if (unformat (input, "%U", unformat_ip4_address, &a4))
623     {
624       ping_ip4 = 1;
625     }
626   else if (unformat (input, "%U", unformat_ip6_address, &a6))
627     {
628       ping_ip6 = 1;
629     }
630   else if (unformat (input, "ipv4"))
631     {
632       if (unformat (input, "%U", unformat_ip4_address, &a4))
633         {
634           ping_ip4 = 1;
635         }
636       else
637         {
638           error =
639             clib_error_return (0,
640                                "expecting IPv4 address but got `%U'",
641                                format_unformat_error, input);
642         }
643     }
644   else if (unformat (input, "ipv6"))
645     {
646       if (unformat (input, "%U", unformat_ip6_address, &a6))
647         {
648           ping_ip6 = 1;
649         }
650       else
651         {
652           error =
653             clib_error_return (0,
654                                "expecting IPv6 address but got `%U'",
655                                format_unformat_error, input);
656         }
657     }
658   else
659     {
660       error =
661         clib_error_return (0,
662                            "expecting IP4/IP6 address `%U'. Usage: ping <addr> [source <intf>] [size <datasz>] [repeat <count>] [verbose]",
663                            format_unformat_error, input);
664       goto done;
665     }
666
667   /* allow for the second AF in the same ping */
668   if (!ping_ip4 && (unformat (input, "ipv4")))
669     {
670       if (unformat (input, "%U", unformat_ip4_address, &a4))
671         {
672           ping_ip4 = 1;
673         }
674     }
675   else if (!ping_ip6 && (unformat (input, "ipv6")))
676     {
677       if (unformat (input, "%U", unformat_ip6_address, &a6))
678         {
679           ping_ip6 = 1;
680         }
681     }
682
683   /* parse the rest of the parameters  in a cycle */
684   while (!unformat_eof (input, NULL))
685     {
686       if (unformat (input, "source"))
687         {
688           if (!unformat_user
689               (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
690             {
691               error =
692                 clib_error_return (0,
693                                    "unknown interface `%U'",
694                                    format_unformat_error, input);
695               goto done;
696             }
697         }
698       else if (unformat (input, "size"))
699         {
700           if (!unformat (input, "%u", &data_len))
701             {
702               error =
703                 clib_error_return (0,
704                                    "expecting size but got `%U'",
705                                    format_unformat_error, input);
706               goto done;
707             }
708         }
709       else if (unformat (input, "interval"))
710         {
711           if (!unformat (input, "%f", &ping_interval))
712             {
713               error =
714                 clib_error_return (0,
715                                    "expecting interval (floating point number) got `%U'",
716                                    format_unformat_error, input);
717               goto done;
718             }
719         }
720       else if (unformat (input, "repeat"))
721         {
722           if (!unformat (input, "%u", &ping_repeat))
723             {
724               error =
725                 clib_error_return (0,
726                                    "expecting repeat count but got `%U'",
727                                    format_unformat_error, input);
728               goto done;
729             }
730         }
731       else if (unformat (input, "verbose"))
732         {
733           verbose = 1;
734         }
735       else
736         {
737           error = clib_error_return (0, "unknown input `%U'",
738                                      format_unformat_error, input);
739           goto done;
740         }
741     }
742
743   run_ping_ip46_address (vm, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL,
744                          sw_if_index, ping_interval, ping_repeat, data_len,
745                          verbose);
746 done:
747   return error;
748 }
749
750 /* *INDENT-OFF* */
751 VLIB_CLI_COMMAND (ping_command, static) =
752 {
753   .path = "ping",
754   .function = ping_ip_address,
755   .short_help = "Ping IP4/IP6 address from interface",
756   .long_help =
757   "Ping IPv4/IPv6 address (or both at the same time)\n"
758   "\n"
759   "Arguments:\n"
760   "\n"
761   "ADDRESS              target (IPv4/IPv6)\n"
762   "ipv4 ADDRESS         target IPv4 address\n"
763   "ipv6 ADDRESS         target IPv6 address\n"
764   "interface STRING     interface for the source address\n"
765   "size NUMBER          size to send\n"
766   "repeat NUMBER        how many echo requests to send\n"
767   "interval NUMBER      interval between echo requests, in seconds (integer or fractional)\n"
768   "verbose              print various low-level information\n"
769 };
770 /* *INDENT-ON* */
771
772 static clib_error_t *
773 ping_cli_init (vlib_main_t * vm)
774 {
775   ping_main_t *pm = &ping_main;
776   pm->ip6_main = &ip6_main;
777   pm->ip4_main = &ip4_main;
778   icmp6_register_type (vm, ICMP6_echo_reply, ip6_icmp_echo_reply_node.index);
779   ip4_icmp_register_type (vm, ICMP4_echo_reply,
780                           ip4_icmp_echo_reply_node.index);
781   return 0;
782 }
783
784 VLIB_INIT_FUNCTION (ping_cli_init);