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