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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
22 format_icmp4_input_trace (u8 * s, va_list * va)
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 *);
29 format_ip4_header, t->packet_data, sizeof (t->packet_data));
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.
44 signal_ip46_icmp_reply_event (vlib_main_t * vm,
45 u8 event_type, vlib_buffer_t * b0)
47 ping_main_t *pm = &ping_main;
53 case PING_RESPONSE_IP4:
55 icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
56 net_icmp_id = h0->icmp_echo.id;
59 case PING_RESPONSE_IP6:
61 icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
62 net_icmp_id = h0->icmp_echo.id;
69 uword *p = hash_get (pm->ping_run_by_icmp_id,
70 clib_net_to_host_u16 (net_icmp_id));
74 ping_run_t *pr = vec_elt_at_index (pm->ping_runs, p[0]);
75 if (vlib_buffer_alloc (vm, &bi0_copy, 1) == 1)
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);
80 /* If buffer_alloc failed, bi0_copy == 0 - just signaling an event. */
82 vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy);
86 * Process ICMPv6 echo replies
89 ip6_icmp_echo_reply_node_fn (vlib_main_t * vm,
90 vlib_node_runtime_t * node, vlib_frame_t * frame)
92 u32 n_left_from, *from;
94 from = vlib_frame_vector_args (frame); /* array of buffer indices */
95 n_left_from = frame->n_vectors; /* number of buffer indices */
97 while (n_left_from > 0)
104 b0 = vlib_get_buffer (vm, bi0);
106 signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0);
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);
116 return frame->n_vectors;
120 VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) =
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,
128 [ICMP6_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
134 * Process ICMPv4 echo replies
137 ip4_icmp_echo_reply_node_fn (vlib_main_t * vm,
138 vlib_node_runtime_t * node, vlib_frame_t * frame)
140 u32 n_left_from, *from;
142 from = vlib_frame_vector_args (frame); /* array of buffer indices */
143 n_left_from = frame->n_vectors; /* number of buffer indices */
145 while (n_left_from > 0)
152 b0 = vlib_get_buffer (vm, bi0);
154 /* push this pkt to the next graph node, always error-drop */
155 signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0);
157 next0 = ICMP4_ECHO_REPLY_NEXT_NORMAL;
158 vlib_set_next_frame_buffer (vm, node, next0, bi0);
164 return frame->n_vectors;
168 VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) =
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,
176 [ICMP4_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
181 char *ip6_lookup_next_nodes[] = IP6_LOOKUP_NEXT_NODES;
182 char *ip4_lookup_next_nodes[] = IP4_LOOKUP_NEXT_NODES;
184 /* get first interface address */
185 static ip6_address_t *
186 ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index)
188 ip_lookup_main_t *lm = &im->lookup_main;
189 ip_interface_address_t *ia = 0;
190 ip6_address_t *result = 0;
192 foreach_ip_interface_address (lm, ia, sw_if_index,
193 1 /* honor unnumbered */ ,
197 ip_interface_address_get_address (lm, ia);
205 /* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */
207 init_icmp46_echo_request (icmp46_echo_request_t * icmp46_echo,
208 u16 seq_host, u16 id_host, u16 data_len)
211 icmp46_echo->seq = clib_host_to_net_u16 (seq_host);
212 icmp46_echo->id = clib_host_to_net_u16 (id_host);
214 for (i = 0; i < sizeof (icmp46_echo->data); i++)
216 icmp46_echo->data[i] = i % 256;
219 if (data_len > sizeof (icmp46_echo_request_t))
221 data_len = sizeof (icmp46_echo_request_t);
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.
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)
237 ip_adjacency_t *adj0 = ip_get_adjacency (lm, adj_index0);
238 u32 sw_if_index0 = adj0->rewrite_header.sw_if_index;
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);
248 if (~0 != sw_if_index)
250 sw_if_index0 = sw_if_index;
253 vlib_cli_output (vm, "Forced set interface: %d\n", sw_if_index0);
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,
264 icmp6_echo_request_header_t *h0;
267 ip_lookup_main_t *lm = &im->lookup_main;
268 int bogus_length = 0;
275 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
276 return SEND_PING_ALLOC_FAIL;
278 p0 = vlib_get_buffer (vm, bi0);
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 */
284 adj_index0 = fib_entry_get_adj(ip6_fib_table_lookup(fib_index0, pa6, 128));
286 if (ADJ_INDEX_INVALID == adj_index0)
288 vlib_buffer_free (vm, &bi0, 1);
289 return SEND_PING_NO_INTERFACE;
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))
297 vlib_buffer_free (vm, &bi0, 1);
298 return SEND_PING_NO_INTERFACE;
300 vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
302 h0 = vlib_buffer_get_current (p0);
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;
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];
317 /* Fill in icmp fields */
318 h0->icmp.type = ICMP6_echo_request;
320 h0->icmp.checksum = 0;
323 init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
324 h0->icmp_echo.time_sent = vlib_time_now (vm);
326 /* Fix up the lengths */
327 h0->ip6.payload_length =
328 clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t));
330 p0->current_length = clib_net_to_host_u16 (h0->ip6.payload_length) +
331 STRUCT_OFFSET_OF (icmp6_echo_request_header_t, icmp);
333 /* Calculate the ICMP checksum */
334 h0->icmp.checksum = 0;
336 ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip6, &bogus_length);
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);
343 vlib_put_frame_to_node (vm, ip6_lookup_node.index, f);
348 static send_ip46_ping_result_t
349 send_ip4_ping (vlib_main_t * vm,
353 u16 seq_host, u16 id_host, u16 data_len, u8 verbose)
355 icmp4_echo_request_header_t *h0;
358 ip_lookup_main_t *lm = &im->lookup_main;
366 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
367 return SEND_PING_ALLOC_FAIL;
369 p0 = vlib_get_buffer (vm, bi0);
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 */
375 adj_index0 = fib_entry_get_adj(ip4_fib_table_lookup(
376 ip4_fib_get(fib_index0), pa4, 32));
378 if (ADJ_INDEX_INVALID == adj_index0)
380 vlib_buffer_free (vm, &bi0, 1);
381 return SEND_PING_NO_INTERFACE;
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))
389 vlib_buffer_free (vm, &bi0, 1);
390 return SEND_PING_NO_INTERFACE;
392 vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
394 h0 = vlib_buffer_get_current (p0);
396 /* Fill in ip4 header fields */
397 h0->ip4.checksum = 0;
398 h0->ip4.ip_version_and_header_length = 0x45;
400 h0->ip4.length = 0; /* Set below */
401 h0->ip4.fragment_id = 0;
402 h0->ip4.flags_and_fragment_offset = 0;
404 h0->ip4.protocol = IP_PROTOCOL_ICMP;
405 h0->ip4.dst_address = *pa4;
406 h0->ip4.src_address = *pa4;
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))
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;
418 vlib_cli_output (vm, "Source address: %U",
419 format_ip4_address, &h0->ip4.src_address);
423 /* Fill in icmp fields */
424 h0->icmp.type = ICMP4_echo_request;
426 h0->icmp.checksum = 0;
429 init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
430 h0->icmp_echo.time_sent = vlib_time_now (vm);
432 /* Fix up the lengths */
434 clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t) +
435 sizeof (ip4_header_t));
437 p0->current_length = clib_net_to_host_u16 (h0->ip4.length);
439 /* Calculate the IP and ICMP checksums */
440 h0->ip4.checksum = ip4_header_checksum (&(h0->ip4));
442 ~ip_csum_fold (ip_incremental_checksum (0, &(h0->icmp),
443 p0->current_length - sizeof (ip4_header_t)));
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);
450 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
457 print_ip6_icmp_reply (vlib_main_t * vm, u32 bi0)
459 vlib_buffer_t *b0 = vlib_get_buffer (vm,
461 icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
462 f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
465 "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
466 clib_host_to_net_u16 (h0->ip6.payload_length),
468 &h0->ip6.src_address,
469 clib_host_to_net_u16 (h0->icmp_echo.seq),
470 h0->ip6.hop_limit, rtt * 1000.0);
474 print_ip4_icmp_reply (vlib_main_t * vm, u32 bi0)
476 vlib_buffer_t *b0 = vlib_get_buffer (vm,
478 icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
479 f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
481 clib_host_to_net_u16 (h0->ip4.length) -
482 (4 * (0xF & h0->ip4.ip_version_and_header_length));
485 "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
488 &h0->ip4.src_address,
489 clib_host_to_net_u16 (h0->icmp_echo.seq),
490 h0->ip4.ttl, rtt * 1000.0);
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.
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,
508 ping_main_t *pm = &ping_main;
509 uword curr_proc = vlib_current_process (vm);
513 u32 ping_run_index = 0;
516 static u32 rand_seed = 0;
518 if (PREDICT_FALSE(!rand_seed))
519 rand_seed = random_default_seed();
521 icmp_id = random_u32(&rand_seed) & 0xffff;
523 while (hash_get (pm->ping_run_by_icmp_id, icmp_id))
525 vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
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++)
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);
541 (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, pa6,
542 sw_if_index, i, icmp_id, data_len,
548 (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, pa4,
549 sw_if_index, i, icmp_id, data_len,
554 while ((i <= ping_repeat)
557 time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
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);
564 case ~0: /* no events => timeout */
566 case PING_RESPONSE_IP6:
569 for (i = 0; i < vec_len (event_data); i++)
571 u32 bi0 = event_data[0];
572 print_ip6_icmp_reply (vm, bi0);
576 vlib_buffer_free (vm, &bi0, 1);
581 case PING_RESPONSE_IP4:
584 for (i = 0; i < vec_len (event_data); i++)
586 u32 bi0 = event_data[0];
587 print_ip4_icmp_reply (vm, bi0);
591 vlib_buffer_free (vm, &bi0, 1);
597 /* someone pressed a key, abort */
598 vlib_cli_output (vm, "Aborted due to a keypress.");
604 vlib_cli_output (vm, "\n");
608 n_requests) ? 0 : 100.0 * ((float) n_requests -
609 (float) n_replies) / (float) n_requests;
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);
624 static clib_error_t *
625 ping_ip_address (vlib_main_t * vm,
626 unformat_input_t * input, vlib_cli_command_t * cmd)
630 clib_error_t *error = 0;
632 u8 ping_ip4, ping_ip6;
633 vnet_main_t *vnm = vnet_get_main ();
634 u32 data_len = PING_DEFAULT_DATA_LEN;
636 f64 ping_interval = PING_DEFAULT_INTERVAL;
637 ping_ip4 = ping_ip6 = 0;
640 if (unformat (input, "%U", unformat_ip4_address, &a4))
644 else if (unformat (input, "%U", unformat_ip6_address, &a6))
648 else if (unformat (input, "ipv4"))
650 if (unformat (input, "%U", unformat_ip4_address, &a4))
657 clib_error_return (0,
658 "expecting IPv4 address but got `%U'",
659 format_unformat_error, input);
662 else if (unformat (input, "ipv6"))
664 if (unformat (input, "%U", unformat_ip6_address, &a6))
671 clib_error_return (0,
672 "expecting IPv6 address but got `%U'",
673 format_unformat_error, input);
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);
685 /* allow for the second AF in the same ping */
686 if (!ping_ip4 && (unformat (input, "ipv4")))
688 if (unformat (input, "%U", unformat_ip4_address, &a4))
693 else if (!ping_ip6 && (unformat (input, "ipv6")))
695 if (unformat (input, "%U", unformat_ip6_address, &a6))
701 /* parse the rest of the parameters in a cycle */
702 while (!unformat_eof (input, NULL))
704 if (unformat (input, "source"))
707 (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
710 clib_error_return (0,
711 "unknown interface `%U'",
712 format_unformat_error, input);
716 else if (unformat (input, "size"))
718 if (!unformat (input, "%u", &data_len))
721 clib_error_return (0,
722 "expecting size but got `%U'",
723 format_unformat_error, input);
727 else if (unformat (input, "interval"))
729 if (!unformat (input, "%f", &ping_interval))
732 clib_error_return (0,
733 "expecting interval (floating point number) got `%U'",
734 format_unformat_error, input);
738 else if (unformat (input, "repeat"))
740 if (!unformat (input, "%u", &ping_repeat))
743 clib_error_return (0,
744 "expecting repeat count but got `%U'",
745 format_unformat_error, input);
749 else if (unformat (input, "verbose"))
755 error = clib_error_return (0, "unknown input `%U'",
756 format_unformat_error, input);
761 run_ping_ip46_address (vm, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL,
762 sw_if_index, ping_interval, ping_repeat, data_len,
769 VLIB_CLI_COMMAND (ping_command, static) =
772 .function = ping_ip_address,
773 .short_help = "Ping IP4/IP6 address from interface",
775 "Ping IPv4/IPv6 address (or both at the same time)\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"
790 static clib_error_t *
791 ping_cli_init (vlib_main_t * vm)
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);
802 VLIB_INIT_FUNCTION (ping_cli_init);