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>
19 format_icmp4_input_trace (u8 * s, va_list * va)
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 *);
26 format_ip4_header, t->packet_data, sizeof (t->packet_data));
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.
41 signal_ip46_icmp_reply_event (vlib_main_t * vm,
42 u8 event_type, vlib_buffer_t * b0)
44 ping_main_t *pm = &ping_main;
50 case PING_RESPONSE_IP4:
52 icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
53 net_icmp_id = h0->icmp_echo.id;
56 case PING_RESPONSE_IP6:
58 icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
59 net_icmp_id = h0->icmp_echo.id;
66 uword *p = hash_get (pm->ping_run_by_icmp_id,
67 clib_net_to_host_u16 (net_icmp_id));
71 ping_run_t *pr = vec_elt_at_index (pm->ping_runs, p[0]);
72 if (vlib_buffer_alloc (vm, &bi0_copy, 1) == 1)
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);
77 /* If buffer_alloc failed, bi0_copy == 0 - just signaling an event. */
79 vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy);
83 * Process ICMPv6 echo replies
86 ip6_icmp_echo_reply_node_fn (vlib_main_t * vm,
87 vlib_node_runtime_t * node, vlib_frame_t * frame)
89 u32 n_left_from, *from;
91 from = vlib_frame_vector_args (frame); /* array of buffer indices */
92 n_left_from = frame->n_vectors; /* number of buffer indices */
94 while (n_left_from > 0)
101 b0 = vlib_get_buffer (vm, bi0);
103 signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0);
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);
113 return frame->n_vectors;
117 VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) =
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,
125 [ICMP6_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
131 * Process ICMPv4 echo replies
134 ip4_icmp_echo_reply_node_fn (vlib_main_t * vm,
135 vlib_node_runtime_t * node, vlib_frame_t * frame)
137 u32 n_left_from, *from;
139 from = vlib_frame_vector_args (frame); /* array of buffer indices */
140 n_left_from = frame->n_vectors; /* number of buffer indices */
142 while (n_left_from > 0)
149 b0 = vlib_get_buffer (vm, bi0);
151 /* push this pkt to the next graph node, always error-drop */
152 signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0);
154 next0 = ICMP4_ECHO_REPLY_NEXT_NORMAL;
155 vlib_set_next_frame_buffer (vm, node, next0, bi0);
161 return frame->n_vectors;
165 VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) =
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,
173 [ICMP4_ECHO_REPLY_NEXT_NORMAL] = "error-drop",
178 char *ip6_lookup_next_nodes[] = IP6_LOOKUP_NEXT_NODES;
179 char *ip4_lookup_next_nodes[] = IP4_LOOKUP_NEXT_NODES;
181 /* get first interface address */
182 static ip6_address_t *
183 ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index)
185 ip_lookup_main_t *lm = &im->lookup_main;
186 ip_interface_address_t *ia = 0;
187 ip6_address_t *result = 0;
189 foreach_ip_interface_address (lm, ia, sw_if_index,
190 1 /* honor unnumbered */ ,
194 ip_interface_address_get_address (lm, ia);
202 /* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */
204 init_icmp46_echo_request (icmp46_echo_request_t * icmp46_echo,
205 u16 seq_host, u16 id_host, u16 data_len)
208 icmp46_echo->seq = clib_host_to_net_u16 (seq_host);
209 icmp46_echo->id = clib_host_to_net_u16 (id_host);
211 for (i = 0; i < sizeof (icmp46_echo->data); i++)
213 icmp46_echo->data[i] = i % 256;
216 if (data_len > sizeof (icmp46_echo_request_t))
218 data_len = sizeof (icmp46_echo_request_t);
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.
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)
234 ip_adjacency_t *adj0 = ip_get_adjacency (lm, adj_index0);
235 u32 sw_if_index0 = adj0->rewrite_header.sw_if_index;
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);
245 if (~0 != sw_if_index)
247 sw_if_index0 = sw_if_index;
250 vlib_cli_output (vm, "Forced set interface: %d\n", sw_if_index0);
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,
261 icmp6_echo_request_header_t *h0;
264 ip_lookup_main_t *lm = &im->lookup_main;
265 int bogus_length = 0;
272 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
273 return SEND_PING_ALLOC_FAIL;
275 p0 = vlib_get_buffer (vm, bi0);
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 */
281 adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, pa6);
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))
287 vlib_buffer_free (vm, &bi0, 1);
288 return SEND_PING_NO_INTERFACE;
290 vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
292 h0 = vlib_buffer_get_current (p0);
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;
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];
307 /* Fill in icmp fields */
308 h0->icmp.type = ICMP6_echo_request;
310 h0->icmp.checksum = 0;
313 init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
314 h0->icmp_echo.time_sent = vlib_time_now (vm);
316 /* Fix up the lengths */
317 h0->ip6.payload_length =
318 clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t));
320 p0->current_length = clib_net_to_host_u16 (h0->ip6.payload_length) +
321 STRUCT_OFFSET_OF (icmp6_echo_request_header_t, icmp);
323 /* Calculate the ICMP checksum */
324 h0->icmp.checksum = 0;
326 ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip6, &bogus_length);
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);
333 vlib_put_frame_to_node (vm, ip6_lookup_node.index, f);
338 static send_ip46_ping_result_t
339 send_ip4_ping (vlib_main_t * vm,
343 u16 seq_host, u16 id_host, u16 data_len, u8 verbose)
345 icmp4_echo_request_header_t *h0;
348 ip_lookup_main_t *lm = &im->lookup_main;
356 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
357 return SEND_PING_ALLOC_FAIL;
359 p0 = vlib_get_buffer (vm, bi0);
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 */
365 adj_index0 = ip4_fib_lookup_with_table (im, fib_index0, pa4, 0);
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))
371 vlib_buffer_free (vm, &bi0, 1);
372 return SEND_PING_NO_INTERFACE;
374 vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0;
376 h0 = vlib_buffer_get_current (p0);
378 /* Fill in ip4 header fields */
379 h0->ip4.checksum = 0;
380 h0->ip4.ip_version_and_header_length = 0x45;
382 h0->ip4.length = 0; /* Set below */
383 h0->ip4.fragment_id = 0;
384 h0->ip4.flags_and_fragment_offset = 0;
386 h0->ip4.protocol = IP_PROTOCOL_ICMP;
387 h0->ip4.dst_address = *pa4;
388 h0->ip4.src_address = *pa4;
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))
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;
400 vlib_cli_output (vm, "Source address: %U",
401 format_ip4_address, &h0->ip4.src_address);
405 /* Fill in icmp fields */
406 h0->icmp.type = ICMP4_echo_request;
408 h0->icmp.checksum = 0;
411 init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len);
412 h0->icmp_echo.time_sent = vlib_time_now (vm);
414 /* Fix up the lengths */
416 clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t) +
417 sizeof (ip4_header_t));
419 p0->current_length = clib_net_to_host_u16 (h0->ip4.length);
421 /* Calculate the IP and ICMP checksums */
422 h0->ip4.checksum = ip4_header_checksum (&(h0->ip4));
424 ~ip_csum_fold (ip_incremental_checksum (0, &(h0->icmp),
425 p0->current_length - sizeof (ip4_header_t)));
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);
432 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
439 print_ip6_icmp_reply (vlib_main_t * vm, u32 bi0)
441 vlib_buffer_t *b0 = vlib_get_buffer (vm,
443 icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
444 f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
447 "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
448 clib_host_to_net_u16 (h0->ip6.payload_length),
450 &h0->ip6.src_address,
451 clib_host_to_net_u16 (h0->icmp_echo.seq),
452 h0->ip6.hop_limit, rtt * 1000.0);
456 print_ip4_icmp_reply (vlib_main_t * vm, u32 bi0)
458 vlib_buffer_t *b0 = vlib_get_buffer (vm,
460 icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0);
461 f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent;
463 clib_host_to_net_u16 (h0->ip4.length) -
464 (4 * (0xF & h0->ip4.ip_version_and_header_length));
467 "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms",
470 &h0->ip4.src_address,
471 clib_host_to_net_u16 (h0->icmp_echo.seq),
472 h0->ip4.ttl, rtt * 1000.0);
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.
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,
490 ping_main_t *pm = &ping_main;
491 uword curr_proc = vlib_current_process (vm);
495 u32 ping_run_index = 0;
498 static u32 rand_seed = 0;
500 if (PREDICT_FALSE(!rand_seed))
501 rand_seed = random_default_seed();
503 icmp_id = random_u32(&rand_seed) & 0xffff;
505 while (hash_get (pm->ping_run_by_icmp_id, icmp_id))
507 vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id);
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++)
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);
523 (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, pa6,
524 sw_if_index, i, icmp_id, data_len,
530 (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, pa4,
531 sw_if_index, i, icmp_id, data_len,
536 while ((i <= ping_repeat)
539 time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0))
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);
546 case ~0: /* no events => timeout */
548 case PING_RESPONSE_IP6:
551 for (i = 0; i < vec_len (event_data); i++)
553 u32 bi0 = event_data[0];
554 print_ip6_icmp_reply (vm, bi0);
558 vlib_buffer_free (vm, &bi0, 1);
563 case PING_RESPONSE_IP4:
566 for (i = 0; i < vec_len (event_data); i++)
568 u32 bi0 = event_data[0];
569 print_ip4_icmp_reply (vm, bi0);
573 vlib_buffer_free (vm, &bi0, 1);
579 /* someone pressed a key, abort */
580 vlib_cli_output (vm, "Aborted due to a keypress.");
586 vlib_cli_output (vm, "\n");
590 n_requests) ? 0 : 100.0 * ((float) n_requests -
591 (float) n_replies) / (float) n_requests;
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);
606 static clib_error_t *
607 ping_ip_address (vlib_main_t * vm,
608 unformat_input_t * input, vlib_cli_command_t * cmd)
612 clib_error_t *error = 0;
614 u8 ping_ip4, ping_ip6;
615 vnet_main_t *vnm = vnet_get_main ();
616 u32 data_len = PING_DEFAULT_DATA_LEN;
618 f64 ping_interval = PING_DEFAULT_INTERVAL;
619 ping_ip4 = ping_ip6 = 0;
622 if (unformat (input, "%U", unformat_ip4_address, &a4))
626 else if (unformat (input, "%U", unformat_ip6_address, &a6))
630 else if (unformat (input, "ipv4"))
632 if (unformat (input, "%U", unformat_ip4_address, &a4))
639 clib_error_return (0,
640 "expecting IPv4 address but got `%U'",
641 format_unformat_error, input);
644 else if (unformat (input, "ipv6"))
646 if (unformat (input, "%U", unformat_ip6_address, &a6))
653 clib_error_return (0,
654 "expecting IPv6 address but got `%U'",
655 format_unformat_error, input);
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);
667 /* allow for the second AF in the same ping */
668 if (!ping_ip4 && (unformat (input, "ipv4")))
670 if (unformat (input, "%U", unformat_ip4_address, &a4))
675 else if (!ping_ip6 && (unformat (input, "ipv6")))
677 if (unformat (input, "%U", unformat_ip6_address, &a6))
683 /* parse the rest of the parameters in a cycle */
684 while (!unformat_eof (input, NULL))
686 if (unformat (input, "source"))
689 (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
692 clib_error_return (0,
693 "unknown interface `%U'",
694 format_unformat_error, input);
698 else if (unformat (input, "size"))
700 if (!unformat (input, "%u", &data_len))
703 clib_error_return (0,
704 "expecting size but got `%U'",
705 format_unformat_error, input);
709 else if (unformat (input, "interval"))
711 if (!unformat (input, "%f", &ping_interval))
714 clib_error_return (0,
715 "expecting interval (floating point number) got `%U'",
716 format_unformat_error, input);
720 else if (unformat (input, "repeat"))
722 if (!unformat (input, "%u", &ping_repeat))
725 clib_error_return (0,
726 "expecting repeat count but got `%U'",
727 format_unformat_error, input);
731 else if (unformat (input, "verbose"))
737 error = clib_error_return (0, "unknown input `%U'",
738 format_unformat_error, input);
743 run_ping_ip46_address (vm, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL,
744 sw_if_index, ping_interval, ping_repeat, data_len,
751 VLIB_CLI_COMMAND (ping_command, static) =
754 .function = ping_ip_address,
755 .short_help = "Ping IP4/IP6 address from interface",
757 "Ping IPv4/IPv6 address (or both at the same time)\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"
772 static clib_error_t *
773 ping_cli_init (vlib_main_t * vm)
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);
784 VLIB_INIT_FUNCTION (ping_cli_init);