diff options
author | 2016-08-04 13:39:35 +0000 | |
---|---|---|
committer | 2016-09-13 12:56:00 +0000 | |
commit | 7e68220de8c9aba533bfb85b642b0b6b8caaee29 (patch) | |
tree | 57077bb8302894442039fddb11417a4bb6beffba /vnet | |
parent | 614480262738dc6de382023a03656e1045d17f04 (diff) |
Implement IPv6/IPv4 ping.
Change-Id: Id53797c795c17fbacce3659c945f5126dc9dee86
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
Diffstat (limited to 'vnet')
-rw-r--r-- | vnet/Makefile.am | 1 | ||||
-rw-r--r-- | vnet/vnet/ip/ping.c | 776 | ||||
-rw-r--r-- | vnet/vnet/ip/ping.h | 97 |
3 files changed, 874 insertions, 0 deletions
diff --git a/vnet/Makefile.am b/vnet/Makefile.am index 71680b99..1c28ebc4 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -258,6 +258,7 @@ libvnet_la_SOURCES += \ vnet/ip/icmp6.c \ vnet/ip/ip_feature_registration.c \ vnet/ip/ip46_cli.c \ + vnet/ip/ping.c \ vnet/ip/ip4_format.c \ vnet/ip/ip4_forward.c \ vnet/ip/ip4_input.c \ diff --git a/vnet/vnet/ip/ping.c b/vnet/vnet/ip/ping.c new file mode 100644 index 00000000..0c25118c --- /dev/null +++ b/vnet/vnet/ip/ping.c @@ -0,0 +1,776 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/ip/ping.h> + +u8 * +format_icmp4_input_trace (u8 * s, va_list * va) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *); + icmp4_input_trace_t *t = va_arg (*va, icmp4_input_trace_t *); + + s = format (s, "%U", + format_ip4_header, t->packet_data, sizeof (t->packet_data)); + + return s; +} + +/* + * If we can find the ping run by an ICMP ID, then we send the signal + * to the CLI process referenced by that ping run, alongside with + * a freshly made copy of the packet. + * I opted for a packet copy to keep the main packet processing path + * the same as for all the other nodes. + * + */ + +static void +signal_ip46_icmp_reply_event (vlib_main_t * vm, + u8 event_type, vlib_buffer_t * b0) +{ + ping_main_t *pm = &ping_main; + u16 net_icmp_id = 0; + u32 bi0_copy = 0; + + switch (event_type) + { + case PING_RESPONSE_IP4: + { + icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + net_icmp_id = h0->icmp_echo.id; + } + break; + case PING_RESPONSE_IP6: + { + icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + net_icmp_id = h0->icmp_echo.id; + } + break; + default: + return; + } + + uword *p = hash_get (pm->ping_run_by_icmp_id, + clib_net_to_host_u16 (net_icmp_id)); + if (!p) + return; + + ping_run_t *pr = vec_elt_at_index (pm->ping_runs, p[0]); + if (vlib_buffer_alloc (vm, &bi0_copy, 1) == 1) + { + void *dst = vlib_buffer_get_current (vlib_get_buffer (vm, bi0_copy)); + clib_memcpy (dst, vlib_buffer_get_current (b0), b0->current_length); + } + /* If buffer_alloc failed, bi0_copy == 0 - just signaling an event. */ + + vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy); +} + +/* + * Process ICMPv6 echo replies + */ +static uword +ip6_icmp_echo_reply_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from; + + from = vlib_frame_vector_args (frame); /* array of buffer indices */ + n_left_from = frame->n_vectors; /* number of buffer indices */ + + while (n_left_from > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + + bi0 = from[0]; + b0 = vlib_get_buffer (vm, bi0); + + signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0); + + /* push this pkt to the next graph node, always error-drop */ + next0 = ICMP6_ECHO_REPLY_NEXT_NORMAL; + vlib_set_next_frame_buffer (vm, node, next0, bi0); + + from += 1; + n_left_from -= 1; + } + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_icmp_echo_reply_node, static) = +{ + .function = ip6_icmp_echo_reply_node_fn, + .name = "ip6-icmp-echo-reply", + .vector_size = sizeof (u32), + .format_trace = format_icmp6_input_trace, + .n_next_nodes = ICMP6_ECHO_REPLY_N_NEXT, + .next_nodes = { + [ICMP6_ECHO_REPLY_NEXT_NORMAL] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * Process ICMPv4 echo replies + */ +static uword +ip4_icmp_echo_reply_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from; + + from = vlib_frame_vector_args (frame); /* array of buffer indices */ + n_left_from = frame->n_vectors; /* number of buffer indices */ + + while (n_left_from > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + + bi0 = from[0]; + b0 = vlib_get_buffer (vm, bi0); + + /* push this pkt to the next graph node, always error-drop */ + signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0); + + next0 = ICMP4_ECHO_REPLY_NEXT_NORMAL; + vlib_set_next_frame_buffer (vm, node, next0, bi0); + + from += 1; + n_left_from -= 1; + } + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) = +{ + .function = ip4_icmp_echo_reply_node_fn, + .name = "ip4-icmp-echo-reply", + .vector_size = sizeof (u32), + .format_trace = format_icmp4_input_trace, + .n_next_nodes = ICMP4_ECHO_REPLY_N_NEXT, + .next_nodes = { + [ICMP4_ECHO_REPLY_NEXT_NORMAL] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +char *ip6_lookup_next_nodes[] = IP6_LOOKUP_NEXT_NODES; +char *ip4_lookup_next_nodes[] = IP4_LOOKUP_NEXT_NODES; + +/* get first interface address */ +static ip6_address_t * +ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index) +{ + ip_lookup_main_t *lm = &im->lookup_main; + ip_interface_address_t *ia = 0; + ip6_address_t *result = 0; + + foreach_ip_interface_address (lm, ia, sw_if_index, + 1 /* honor unnumbered */ , + ( + { + ip6_address_t * a = + ip_interface_address_get_address (lm, ia); + result = a; + break; + } + )); + return result; +} + +/* Fill in the ICMP ECHO structure, return the safety-checked and possibly shrunk data_len */ +static u16 +init_icmp46_echo_request (icmp46_echo_request_t * icmp46_echo, + u16 seq_host, u16 id_host, u16 data_len) +{ + int i; + icmp46_echo->seq = clib_host_to_net_u16 (seq_host); + icmp46_echo->id = clib_host_to_net_u16 (id_host); + + for (i = 0; i < sizeof (icmp46_echo->data); i++) + { + icmp46_echo->data[i] = i % 256; + } + + if (data_len > sizeof (icmp46_echo_request_t)) + { + data_len = sizeof (icmp46_echo_request_t); + } + return data_len; +} + +/* + * Given adj index, return sw_if_index, possibly overwritten + * by a parameter. There is mostly debug outputs here, + * but it turned out handy to have these. + */ + +static u32 +adj_index_to_sw_if_index (vlib_main_t * vm, ip_lookup_main_t * lm, + char *lookup_next_nodes[], u32 adj_index0, + u32 sw_if_index, u8 verbose) +{ + ip_adjacency_t *adj0 = ip_get_adjacency (lm, adj_index0); + u32 sw_if_index0 = adj0->rewrite_header.sw_if_index; + if (verbose) + { + vlib_cli_output (vm, "Adjacency index: %u, sw_if_index: %u\n", + adj_index0, sw_if_index0); + vlib_cli_output (vm, "Adj: %s\n", + lookup_next_nodes[adj0->lookup_next_index]); + vlib_cli_output (vm, "Adj Interface: %d\n", adj0->if_address_index); + } + + if (~0 != sw_if_index) + { + sw_if_index0 = sw_if_index; + if (verbose) + { + vlib_cli_output (vm, "Forced set interface: %d\n", sw_if_index0); + } + } + return sw_if_index0; +} + +static send_ip46_ping_result_t +send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, ip6_address_t * pa6, + u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len, + u8 verbose) +{ + icmp6_echo_request_header_t *h0; + u32 bi0 = 0; + u32 sw_if_index0; + ip_lookup_main_t *lm = &im->lookup_main; + int bogus_length = 0; + u32 adj_index0; + vlib_buffer_t *p0; + vlib_frame_t *f; + u32 *to_next; + u32 fib_index0; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + return SEND_PING_ALLOC_FAIL; + + p0 = vlib_get_buffer (vm, bi0); + + /* Determine sw_if_index0 of source intf, may be force-set via sw_if_index. */ + vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0; + vnet_buffer (p0)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */ + fib_index0 = 0; + adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, pa6); + sw_if_index0 = + adj_index_to_sw_if_index (vm, lm, ip6_lookup_next_nodes, adj_index0, + sw_if_index, verbose); + if ((~0 == sw_if_index0) && (~0 == sw_if_index)) + { + vlib_buffer_free (vm, &bi0, 1); + return SEND_PING_NO_INTERFACE; + } + vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0; + + h0 = vlib_buffer_get_current (p0); + + /* Fill in ip6 header fields */ + h0->ip6.ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (0x6 << 28); + h0->ip6.payload_length = 0; /* Set below */ + h0->ip6.protocol = IP_PROTOCOL_ICMP6; + h0->ip6.hop_limit = 255; + h0->ip6.dst_address = *pa6; + h0->ip6.src_address = *pa6; + + /* Fill in the correct source now */ + ip6_address_t *a = ip6_interface_first_address (im, sw_if_index0); + h0->ip6.src_address = a[0]; + + /* Fill in icmp fields */ + h0->icmp.type = ICMP6_echo_request; + h0->icmp.code = 0; + h0->icmp.checksum = 0; + + data_len = + init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len); + h0->icmp_echo.time_sent = vlib_time_now (vm); + + /* Fix up the lengths */ + h0->ip6.payload_length = + clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t)); + + p0->current_length = clib_net_to_host_u16 (h0->ip6.payload_length) + + STRUCT_OFFSET_OF (icmp6_echo_request_header_t, icmp); + + /* Calculate the ICMP checksum */ + h0->icmp.checksum = 0; + h0->icmp.checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip6, &bogus_length); + + /* Enqueue the packet right now */ + f = vlib_get_frame_to_node (vm, ip6_lookup_node.index); + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + vlib_put_frame_to_node (vm, ip6_lookup_node.index, f); + + return SEND_PING_OK; +} + +static send_ip46_ping_result_t +send_ip4_ping (vlib_main_t * vm, + ip4_main_t * im, + ip4_address_t * pa4, + u32 sw_if_index, + u16 seq_host, u16 id_host, u16 data_len, u8 verbose) +{ + icmp4_echo_request_header_t *h0; + u32 bi0 = 0; + u32 sw_if_index0; + ip_lookup_main_t *lm = &im->lookup_main; + u32 adj_index0; + vlib_buffer_t *p0; + vlib_frame_t *f; + u32 *to_next; + u32 fib_index0; + u32 if_add_index0; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + return SEND_PING_ALLOC_FAIL; + + p0 = vlib_get_buffer (vm, bi0); + + /* Determine sw_if_index0 of the source intf, may be force-set via sw_if_index. */ + vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0; + vnet_buffer (p0)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */ + fib_index0 = 0; + adj_index0 = ip4_fib_lookup_with_table (im, fib_index0, pa4, 0); + sw_if_index0 = + adj_index_to_sw_if_index (vm, lm, ip4_lookup_next_nodes, adj_index0, + sw_if_index, verbose); + if ((~0 == sw_if_index0) && (~0 == sw_if_index)) + { + vlib_buffer_free (vm, &bi0, 1); + return SEND_PING_NO_INTERFACE; + } + vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index0; + + h0 = vlib_buffer_get_current (p0); + + /* Fill in ip4 header fields */ + h0->ip4.checksum = 0; + h0->ip4.ip_version_and_header_length = 0x45; + h0->ip4.tos = 0; + h0->ip4.length = 0; /* Set below */ + h0->ip4.fragment_id = 0; + h0->ip4.flags_and_fragment_offset = 0; + h0->ip4.ttl = 0xff; + h0->ip4.protocol = IP_PROTOCOL_ICMP; + h0->ip4.dst_address = *pa4; + h0->ip4.src_address = *pa4; + + /* Fill in the correct source now */ + if_add_index0 = lm->if_address_pool_index_by_sw_if_index[sw_if_index0]; + if (PREDICT_TRUE (if_add_index0 != ~0)) + { + ip_interface_address_t *if_add = + pool_elt_at_index (lm->if_address_pool, if_add_index0); + ip4_address_t *if_ip = ip_interface_address_get_address (lm, if_add); + h0->ip4.src_address = *if_ip; + if (verbose) + { + vlib_cli_output (vm, "Source address: %U", + format_ip4_address, &h0->ip4.src_address); + } + } + + /* Fill in icmp fields */ + h0->icmp.type = ICMP4_echo_request; + h0->icmp.code = 0; + h0->icmp.checksum = 0; + + data_len = + init_icmp46_echo_request (&h0->icmp_echo, seq_host, id_host, data_len); + h0->icmp_echo.time_sent = vlib_time_now (vm); + + /* Fix up the lengths */ + h0->ip4.length = + clib_host_to_net_u16 (data_len + sizeof (icmp46_header_t) + + sizeof (ip4_header_t)); + + p0->current_length = clib_net_to_host_u16 (h0->ip4.length); + + /* Calculate the IP and ICMP checksums */ + h0->ip4.checksum = ip4_header_checksum (&(h0->ip4)); + h0->icmp.checksum = + ~ip_csum_fold (ip_incremental_checksum (0, &(h0->icmp), + p0->current_length - sizeof (ip4_header_t))); + + /* Enqueue the packet right now */ + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); + + return SEND_PING_OK; +} + + +static void +print_ip6_icmp_reply (vlib_main_t * vm, u32 bi0) +{ + vlib_buffer_t *b0 = vlib_get_buffer (vm, + bi0); + icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent; + + vlib_cli_output (vm, + "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms", + clib_host_to_net_u16 (h0->ip6.payload_length), + format_ip6_address, + &h0->ip6.src_address, + clib_host_to_net_u16 (h0->icmp_echo.seq), + h0->ip6.hop_limit, rtt * 1000.0); +} + +static void +print_ip4_icmp_reply (vlib_main_t * vm, u32 bi0) +{ + vlib_buffer_t *b0 = vlib_get_buffer (vm, + bi0); + icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + f64 rtt = vlib_time_now (vm) - h0->icmp_echo.time_sent; + u32 rcvd_icmp_len = + clib_host_to_net_u16 (h0->ip4.length) - + (4 * (0xF & h0->ip4.ip_version_and_header_length)); + + vlib_cli_output (vm, + "%d bytes from %U: icmp_seq=%d ttl=%d time=%.4f ms", + rcvd_icmp_len, + format_ip4_address, + &h0->ip4.src_address, + clib_host_to_net_u16 (h0->icmp_echo.seq), + h0->ip4.ttl, rtt * 1000.0); +} + + +/* + * Perform the ping run with the given parameters in the current CLI process. + * Depending on whether pa4 or pa6 is set, runs IPv4 or IPv6 ping. + * The amusing side effect is of course if both are set, then both pings are sent. + * This behavior can be used to ping a dualstack host over IPv4 and IPv6 at once. + */ + +static void +run_ping_ip46_address (vlib_main_t * vm, ip4_address_t * pa4, + ip6_address_t * pa6, u32 sw_if_index, + f64 ping_interval, u32 ping_repeat, u32 data_len, + u32 verbose) +{ + int i; + ping_main_t *pm = &ping_main; + uword curr_proc = vlib_current_process (vm); + u32 n_replies = 0; + u32 n_requests = 0; + ping_run_t *pr = 0; + u32 ping_run_index = 0; + u16 icmp_id = rand (); + while (hash_get (pm->ping_run_by_icmp_id, icmp_id)) + { + vlib_cli_output (vm, "ICMP ID collision at %d, incrementing", icmp_id); + icmp_id++; + } + pool_get (pm->ping_runs, pr); + ping_run_index = pr - pm->ping_runs; + pr->cli_process_id = curr_proc; + pr->icmp_id = icmp_id; + hash_set (pm->ping_run_by_icmp_id, icmp_id, ping_run_index); + for (i = 1; i <= ping_repeat; i++) + { + f64 sleep_interval; + f64 time_ping_sent = vlib_time_now (vm); + /* Reset pr: running ping in other process could have changed pm->ping_runs */ + pr = vec_elt_at_index (pm->ping_runs, ping_run_index); + pr->curr_seq = i; + if (pa6 && + (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, pa6, + sw_if_index, i, icmp_id, data_len, + verbose))) + { + n_requests++; + } + if (pa4 && + (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, pa4, + sw_if_index, i, icmp_id, data_len, + verbose))) + { + n_requests++; + } + while ((i <= ping_repeat) + && + ((sleep_interval = + time_ping_sent + ping_interval - vlib_time_now (vm)) > 0.0)) + { + uword event_type, *event_data = 0; + vlib_process_wait_for_event_or_clock (vm, sleep_interval); + event_type = vlib_process_get_events (vm, &event_data); + switch (event_type) + { + case ~0: /* no events => timeout */ + break; + case PING_RESPONSE_IP6: + { + int i; + for (i = 0; i < vec_len (event_data); i++) + { + u32 bi0 = event_data[0]; + print_ip6_icmp_reply (vm, bi0); + n_replies++; + if (0 != bi0) + { + vlib_buffer_free (vm, &bi0, 1); + } + } + } + break; + case PING_RESPONSE_IP4: + { + int i; + for (i = 0; i < vec_len (event_data); i++) + { + u32 bi0 = event_data[0]; + print_ip4_icmp_reply (vm, bi0); + n_replies++; + if (0 != bi0) + { + vlib_buffer_free (vm, &bi0, 1); + } + } + } + break; + default: + /* someone pressed a key, abort */ + vlib_cli_output (vm, "Aborted due to a keypress."); + i = 1 + ping_repeat; + break; + } + } + } + vlib_cli_output (vm, "\n"); + { + float loss = + (0 == + n_requests) ? 0 : 100.0 * ((float) n_requests - + (float) n_replies) / (float) n_requests; + vlib_cli_output (vm, + "Statistics: %u sent, %u received, %f%% packet loss\n", + n_requests, n_replies, loss); + /* Reset pr: running ping in other process could have changed pm->ping_runs */ + pr = vec_elt_at_index (pm->ping_runs, ping_run_index); + hash_unset (pm->ping_run_by_icmp_id, icmp_id); + pool_put (pm->ping_runs, pr); + } +} + + + + + +static clib_error_t * +ping_ip_address (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + ip4_address_t a4; + ip6_address_t a6; + clib_error_t *error = 0; + u32 ping_repeat = 5; + u8 ping_ip4, ping_ip6; + vnet_main_t *vnm = vnet_get_main (); + u32 data_len = PING_DEFAULT_DATA_LEN; + u32 verbose = 0; + f64 ping_interval = PING_DEFAULT_INTERVAL; + ping_ip4 = ping_ip6 = 0; + u32 sw_if_index; + sw_if_index = ~0; + if (unformat (input, "%U", unformat_ip4_address, &a4)) + { + ping_ip4 = 1; + } + else if (unformat (input, "%U", unformat_ip6_address, &a6)) + { + ping_ip6 = 1; + } + else if (unformat (input, "ipv4")) + { + if (unformat (input, "%U", unformat_ip4_address, &a4)) + { + ping_ip4 = 1; + } + else + { + error = + clib_error_return (0, + "expecting IPv4 address but got `%U'", + format_unformat_error, input); + } + } + else if (unformat (input, "ipv6")) + { + if (unformat (input, "%U", unformat_ip6_address, &a6)) + { + ping_ip6 = 1; + } + else + { + error = + clib_error_return (0, + "expecting IPv6 address but got `%U'", + format_unformat_error, input); + } + } + else + { + error = + clib_error_return (0, + "expecting IP4/IP6 address `%U'. Usage: ping <addr> [source <intf>] [size <datasz>] [repeat <count>] [verbose]", + format_unformat_error, input); + goto done; + } + + /* allow for the second AF in the same ping */ + if (!ping_ip4 && (unformat (input, "ipv4"))) + { + if (unformat (input, "%U", unformat_ip4_address, &a4)) + { + ping_ip4 = 1; + } + } + else if (!ping_ip6 && (unformat (input, "ipv6"))) + { + if (unformat (input, "%U", unformat_ip6_address, &a6)) + { + ping_ip6 = 1; + } + } + + /* parse the rest of the parameters in a cycle */ + while (!unformat_eof (input, NULL)) + { + if (unformat (input, "source")) + { + if (!unformat_user + (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + { + error = + clib_error_return (0, + "unknown interface `%U'", + format_unformat_error, input); + goto done; + } + } + else if (unformat (input, "size")) + { + if (!unformat (input, "%u", &data_len)) + { + error = + clib_error_return (0, + "expecting size but got `%U'", + format_unformat_error, input); + goto done; + } + } + else if (unformat (input, "interval")) + { + if (!unformat (input, "%f", &ping_interval)) + { + error = + clib_error_return (0, + "expecting interval (floating point number) got `%U'", + format_unformat_error, input); + goto done; + } + } + else if (unformat (input, "repeat")) + { + if (!unformat (input, "%u", &ping_repeat)) + { + error = + clib_error_return (0, + "expecting repeat count but got `%U'", + format_unformat_error, input); + goto done; + } + } + else if (unformat (input, "verbose")) + { + verbose = 1; + } + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + goto done; + } + } + + run_ping_ip46_address (vm, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL, + sw_if_index, ping_interval, ping_repeat, data_len, + verbose); +done: + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ping_command, static) = +{ + .path = "ping", + .function = ping_ip_address, + .short_help = "Ping IP4/IP6 address from interface", + .long_help = + "Ping IPv4/IPv6 address (or both at the same time)\n" + "\n" + "Arguments:\n" + "\n" + "ADDRESS target (IPv4/IPv6)\n" + "ipv4 ADDRESS target IPv4 address\n" + "ipv6 ADDRESS target IPv6 address\n" + "interface STRING interface for the source address\n" + "size NUMBER size to send\n" + "repeat NUMBER how many echo requests to send\n" + "interval NUMBER interval between echo requests, in seconds (integer or fractional)\n" + "verbose print various low-level information\n" +}; +/* *INDENT-ON* */ + +static clib_error_t * +ping_cli_init (vlib_main_t * vm) +{ + ping_main_t *pm = &ping_main; + pm->ip6_main = &ip6_main; + pm->ip4_main = &ip4_main; + icmp6_register_type (vm, ICMP6_echo_reply, ip6_icmp_echo_reply_node.index); + ip4_icmp_register_type (vm, ICMP4_echo_reply, + ip4_icmp_echo_reply_node.index); + return 0; +} + +VLIB_INIT_FUNCTION (ping_cli_init); diff --git a/vnet/vnet/ip/ping.h b/vnet/vnet/ip/ping.h new file mode 100644 index 00000000..7d2db423 --- /dev/null +++ b/vnet/vnet/ip/ping.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef included_vnet_ping_h +#define included_vnet_ping_h + + +#include <vnet/ip/ip.h> + +#include <vnet/ip/lookup.h> + +typedef enum { + PING_RESPONSE_IP6 = 42, + PING_RESPONSE_IP4, +} ping_response_type_t; + +typedef enum { + SEND_PING_OK = 0, + SEND_PING_ALLOC_FAIL, + SEND_PING_NO_INTERFACE, +} send_ip46_ping_result_t; + +/* + * Currently running ping command. + */ +typedef struct ping_run_t { + u16 icmp_id; + u16 curr_seq; + uword cli_process_id; +} ping_run_t; + +typedef struct ping_main_t { + ip6_main_t *ip6_main; + ip4_main_t *ip4_main; + ping_run_t *ping_runs; + /* hash table to find back the CLI process for a reply */ + // uword *cli_proc_by_icmp_id; + ping_run_t *ping_run_by_icmp_id; +} ping_main_t; + +ping_main_t ping_main; + +#define PING_DEFAULT_DATA_LEN 60 +#define PING_DEFAULT_INTERVAL 1.0 + +#define PING_MAXIMUM_DATA_SIZE 2000 + +typedef CLIB_PACKED (struct { + u16 id; + u16 seq; + f64 time_sent; + u8 data[PING_MAXIMUM_DATA_SIZE]; +}) icmp46_echo_request_t; + + +typedef CLIB_PACKED (struct { + ip6_header_t ip6; + icmp46_header_t icmp; + icmp46_echo_request_t icmp_echo; +}) icmp6_echo_request_header_t; + +typedef CLIB_PACKED(struct { + ip4_header_t ip4; + icmp46_header_t icmp; + icmp46_echo_request_t icmp_echo; +}) icmp4_echo_request_header_t; + + +typedef struct { + u8 packet_data[64]; +} icmp4_input_trace_t; + + + + +typedef enum { + ICMP6_ECHO_REPLY_NEXT_NORMAL, + ICMP6_ECHO_REPLY_N_NEXT, +} icmp6_echo_reply_next_t; + +typedef enum { + ICMP4_ECHO_REPLY_NEXT_NORMAL, + ICMP4_ECHO_REPLY_N_NEXT, +} icmp4_echo_reply_next_t; + +#endif /* included_vnet_ping_h */ |