From 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 19 Dec 2016 23:05:39 +0100 Subject: Reorganize source tree to use single autotools instance Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion --- src/vnet/ip/ping.c | 888 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 888 insertions(+) create mode 100644 src/vnet/ip/ping.c (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c new file mode 100644 index 00000000..68dbe759 --- /dev/null +++ b/src/vnet/ip/ping.c @@ -0,0 +1,888 @@ +/* + * 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 +#include +#include +#include + +/** + * @file + * @brief IPv4 and IPv6 ICMP Ping. + * + * This file contains code to suppport IPv4 or IPv6 ICMP ECHO_REQUEST to + * network hosts. + * + */ + + +u8 * +format_icmp_echo_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 *); + icmp_echo_trace_t *t = va_arg (*va, icmp_echo_trace_t *); + + s = format (s, "ICMP echo id %d seq %d%s", + clib_net_to_host_u16 (t->id), + clib_net_to_host_u16 (t->seq), t->bound ? "" : " (unknown)"); + + 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 int +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 0; + } + + uword *p = hash_get (pm->ping_run_by_icmp_id, + clib_net_to_host_u16 (net_icmp_id)); + if (!p) + return 0; + + 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); + return 1; +} + +/* + * 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); + + next0 = signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0) ? + ICMP6_ECHO_REPLY_NEXT_DROP : ICMP6_ECHO_REPLY_NEXT_PUNT; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + icmp6_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + icmp_echo_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->id = h0->icmp_echo.id; + tr->seq = h0->icmp_echo.seq; + tr->bound = (next0 == ICMP6_ECHO_REPLY_NEXT_DROP); + } + + /* push this pkt to the next graph node */ + 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_icmp_echo_trace, + .n_next_nodes = ICMP6_ECHO_REPLY_N_NEXT, + .next_nodes = { + [ICMP6_ECHO_REPLY_NEXT_DROP] = "error-drop", + [ICMP6_ECHO_REPLY_NEXT_PUNT] = "error-punt", + }, +}; +/* *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); + + next0 = signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0) ? + ICMP4_ECHO_REPLY_NEXT_DROP : ICMP4_ECHO_REPLY_NEXT_PUNT; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + icmp4_echo_request_header_t *h0 = vlib_buffer_get_current (b0); + icmp_echo_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->id = h0->icmp_echo.id; + tr->seq = h0->icmp_echo.seq; + tr->bound = (next0 == ICMP4_ECHO_REPLY_NEXT_DROP); + } + + /* push this pkt to the next graph node */ + 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_icmp_echo_trace, + .n_next_nodes = ICMP4_ECHO_REPLY_N_NEXT, + .next_nodes = { + [ICMP4_ECHO_REPLY_NEXT_DROP] = "error-drop", + [ICMP4_ECHO_REPLY_NEXT_PUNT] = "error-punt", + }, +}; +/* *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; + + /* *INDENT-OFF* */ + 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; + })); + /* *INDENT-ON* */ + 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; +} + +static send_ip46_ping_result_t +send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, + u32 table_id, 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; + int bogus_length = 0; + vlib_buffer_t *p0; + vlib_frame_t *f; + u32 *to_next; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + return SEND_PING_ALLOC_FAIL; + + p0 = vlib_get_buffer (vm, bi0); + + /* + * if the user did not provide a source interface, use the any interface + * that the destination resolves via. + */ + if (~0 == sw_if_index) + { + fib_node_index_t fib_entry_index; + u32 fib_index; + + fib_index = ip6_fib_index_from_table_id (table_id); + + if (~0 == fib_index) + { + vlib_buffer_free (vm, &bi0, 1); + return SEND_PING_NO_TABLE; + } + + fib_entry_index = ip6_fib_table_lookup (fib_index, pa6, 128); + sw_if_index = fib_entry_get_resolving_interface (fib_entry_index); + /* + * Set the TX interface to force ip-lookup to use its table ID + */ + vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index; + } + else + { + /* + * force an IP lookup in the table bound to the user's chosen + * source interface. + */ + vnet_buffer (p0)->sw_if_index[VLIB_TX] = + ip6_fib_table_get_index_for_sw_if_index (sw_if_index); + } + + if (~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_index; + + 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_index); + 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, + u32 table_id, + 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; + ip_lookup_main_t *lm = &im->lookup_main; + vlib_buffer_t *p0; + vlib_frame_t *f; + u32 *to_next; + u32 if_add_index0; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + return SEND_PING_ALLOC_FAIL; + + p0 = vlib_get_buffer (vm, bi0); + + /* + * if the user did not provide a source interface, use the any interface + * that the destination resolves via. + */ + if (~0 == sw_if_index) + { + fib_node_index_t fib_entry_index; + u32 fib_index; + + fib_index = ip4_fib_index_from_table_id (table_id); + + if (~0 == fib_index) + { + vlib_buffer_free (vm, &bi0, 1); + return SEND_PING_NO_TABLE; + } + + fib_entry_index = + ip4_fib_table_lookup (ip4_fib_get (fib_index), pa4, 32); + sw_if_index = fib_entry_get_resolving_interface (fib_entry_index); + /* + * Set the TX interface to force ip-lookup to use the user's table ID + */ + vnet_buffer (p0)->sw_if_index[VLIB_TX] = fib_index; + } + else + { + /* + * force an IP lookup in the table bound to the user's chosen + * source interface. + */ + vnet_buffer (p0)->sw_if_index[VLIB_TX] = + ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + } + + if (~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_index; + + 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_index]; + 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, u32 table_id, 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; + + static u32 rand_seed = 0; + + if (PREDICT_FALSE (!rand_seed)) + rand_seed = random_default_seed (); + + icmp_id = random_u32 (&rand_seed) & 0xffff; + + 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, table_id, 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, table_id, 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; + u32 sw_if_index, table_id; + + table_id = 0; + ping_ip4 = ping_ip6 = 0; + 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 [source ] [size ] [repeat ] [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, "table-id")) + { + if (!unformat (input, "du", &table_id)) + { + error = + clib_error_return (0, + "expecting table-id 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, table_id, ping_ip4 ? &a4 : NULL, + ping_ip6 ? &a6 : NULL, sw_if_index, ping_interval, + ping_repeat, data_len, verbose); +done: + return error; +} + +/*? + * This command sends an ICMP ECHO_REQUEST to network hosts. The address + * can be an IPv4 or IPv6 address (or both at the same time). + * + * @cliexpar + * @parblock + * Example of how ping an IPv4 address: + * @cliexstart{ping 172.16.1.2 source GigabitEthernet2/0/0 repeat 2} + * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1090 ms + * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0914 ms + * + * Statistics: 2 sent, 2 received, 0% packet loss + * @cliexend + * + * Example of how ping both an IPv4 address and IPv6 address at the same time: + * @cliexstart{ping 172.16.1.2 ipv6 fe80::24a5:f6ff:fe9c:3a36 source GigabitEthernet2/0/0 repeat 2 verbose} + * Adjacency index: 10, sw_if_index: 1 + * Adj: ip6-discover-neighbor + * Adj Interface: 0 + * Forced set interface: 1 + * Adjacency index: 0, sw_if_index: 4294967295 + * Adj: ip4-miss + * Adj Interface: 0 + * Forced set interface: 1 + * Source address: 172.16.1.1 + * 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=.1899 ms + * Adjacency index: 10, sw_if_index: 1 + * Adj: ip6-discover-neighbor + * Adj Interface: 0 + * Forced set interface: 1 + * Adjacency index: 0, sw_if_index: 4294967295 + * Adj: ip4-miss + * Adj Interface: 0 + * Forced set interface: 1 + * Source address: 172.16.1.1 + * 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=.0910 ms + * + * Statistics: 4 sent, 2 received, 50% packet loss + * @cliexend + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ping_command, static) = +{ + .path = "ping", + .function = ping_ip_address, + .short_help = "ping { | ipv4 | ipv6 }" + " [ipv4 | ipv6 ] [source ]" + " [size ] [interval ] [repeat ] [table-id ]" + " [verbose]", +}; +/* *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); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg From f69ecfe09db52c672ccbe47e714bc9c9a70d5539 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Tue, 24 Jan 2017 15:47:27 +0100 Subject: ping: fix double-free crash under VMWare hypervisor bi0 retrieval from the ping reply events vector was incorrectly done always from the first element. For TBD reason the sending of the ping requests under VMWare was batched, as a result the replies arrive close enough to make the events arrive as an array, which exposed this bug. KVM never exhibited this behavior, which explains not seeing this issue there. Change-Id: I485d6f983571e25baa9407c21ef604937586d8bd Signed-off-by: Andrew Yourtchenko --- src/vnet/ip/ping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index 68dbe759..88882629 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -601,7 +601,7 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, int i; for (i = 0; i < vec_len (event_data); i++) { - u32 bi0 = event_data[0]; + u32 bi0 = event_data[i]; print_ip6_icmp_reply (vm, bi0); n_replies++; if (0 != bi0) @@ -616,7 +616,7 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, int i; for (i = 0; i < vec_len (event_data); i++) { - u32 bi0 = event_data[0]; + u32 bi0 = event_data[i]; print_ip4_icmp_reply (vm, bi0); n_replies++; if (0 != bi0) -- cgit 1.2.3-korg From 61459c9be0f620f738cf049b1b33e1a2d13dc9a6 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Sat, 28 Jan 2017 15:31:19 +0000 Subject: VPP-621: ping: ICMP echo data size must be bounded by VLIB_BUFFER_DATA_SIZE minus headers. Before the commit 878c6098 the VLIB_BUFFER_DATA_SIZE was different depending on whether building "vpp" or "vpp_lite", resulting in an overrun in vpp_lite build. Avoid the hardcoded value and make the upper bound for ICMP echo data size dependent on the buffer size. Change-Id: Id6c4d7fc73766a95af2610eb237881b5fe9ce9aa Signed-off-by: Andrew Yourtchenko --- src/vnet/ip/ping.c | 30 +++++++++++++++++++++--------- src/vnet/ip/ping.h | 45 +++++++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 29 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index 88882629..00e2bfb1 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include #include #include @@ -243,15 +244,10 @@ init_icmp46_echo_request (icmp46_echo_request_t * icmp46_echo, 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); - } + if (data_len > PING_MAXIMUM_DATA_SIZE) + data_len = PING_MAXIMUM_DATA_SIZE; + for (i = 0; i < data_len; i++) + icmp46_echo->data[i] = i % 256; return data_len; } @@ -267,11 +263,15 @@ send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, vlib_buffer_t *p0; vlib_frame_t *f; u32 *to_next; + vlib_buffer_free_list_t *fl; if (vlib_buffer_alloc (vm, &bi0, 1) != 1) return SEND_PING_ALLOC_FAIL; p0 = vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (p0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (p0); /* * if the user did not provide a source interface, use the any interface @@ -376,11 +376,15 @@ send_ip4_ping (vlib_main_t * vm, vlib_frame_t *f; u32 *to_next; u32 if_add_index0; + vlib_buffer_free_list_t *fl; if (vlib_buffer_alloc (vm, &bi0, 1) != 1) return SEND_PING_ALLOC_FAIL; p0 = vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (p0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (p0); /* * if the user did not provide a source interface, use the any interface @@ -759,6 +763,14 @@ ping_ip_address (vlib_main_t * vm, format_unformat_error, input); goto done; } + if (data_len > PING_MAXIMUM_DATA_SIZE) + { + error = + clib_error_return (0, + "%d is bigger than maximum allowed payload size %d", + data_len, PING_MAXIMUM_DATA_SIZE); + goto done; + } } else if (unformat (input, "table-id")) { diff --git a/src/vnet/ip/ping.h b/src/vnet/ip/ping.h index 8f41f45c..1887314a 100644 --- a/src/vnet/ip/ping.h +++ b/src/vnet/ip/ping.h @@ -59,26 +59,31 @@ 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; +#define PING_MAXIMUM_DATA_SIZE (VLIB_BUFFER_DATA_SIZE - sizeof(ip6_header_t) - sizeof(icmp46_header_t) - offsetof(icmp46_echo_request_t, data)) + +/* *INDENT-OFF* */ + +typedef CLIB_PACKED (struct { + u16 id; + u16 seq; + f64 time_sent; + u8 data[0]; +}) 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; + +/* *INDENT-ON* */ typedef struct -- cgit 1.2.3-korg From 6cfc39c3e9522470e82f4cd43e6cd992a0d67ed1 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 14 Feb 2017 01:44:25 -0800 Subject: Remove duplicate ip6 get interface address code Change-Id: I5e0057b36bc4221e688a27fc1c0f602f78132991 Signed-off-by: Neale Ranns --- src/vnet/dhcpv6/proxy_node.c | 22 ++-------------------- src/vnet/ip/ip6.h | 5 +++++ src/vnet/ip/ip6_forward.c | 8 ++------ src/vnet/ip/ping.c | 21 --------------------- 4 files changed, 9 insertions(+), 47 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/dhcpv6/proxy_node.c b/src/vnet/dhcpv6/proxy_node.c index 77afef2a..4137624c 100644 --- a/src/vnet/dhcpv6/proxy_node.c +++ b/src/vnet/dhcpv6/proxy_node.c @@ -110,24 +110,6 @@ ip6_interface_first_global_or_site_address (ip6_main_t * im, u32 sw_if_index) return result; } -/* 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; -} - static inline void copy_ip6_address (ip6_address_t *dst, ip6_address_t *src) { @@ -813,8 +795,8 @@ int dhcpv6_proxy_set_server (ip6_address_t *addr, ip6_address_t *src_address, } int dhcpv6_proxy_set_server_2 (ip6_address_t *addr, ip6_address_t *src_address, - u32 rx_fib_id, u32 server_fib_id, - int insert_vss, int is_del) + u32 rx_fib_id, u32 server_fib_id, + int insert_vss, int is_del) { dhcpv6_proxy_main_t * dm = &dhcpv6_proxy_main; dhcpv6_server_t * server = 0; diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h index 1844d727..5456f0f2 100644 --- a/src/vnet/ip/ip6.h +++ b/src/vnet/ip/ip6.h @@ -332,6 +332,11 @@ clib_error_t *ip6_add_del_interface_address (vlib_main_t * vm, u32 address_length, u32 is_del); void ip6_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable); +/** + * @brie get first IPv6 interface address + */ +ip6_address_t *ip6_interface_first_address (ip6_main_t * im, u32 sw_if_index); + int ip6_address_compare (ip6_address_t * a1, ip6_address_t * a2); clib_error_t *ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index c15683e2..5dd22b99 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -428,9 +428,7 @@ ip6_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable) /* get first interface address */ ip6_address_t * -ip6_interface_first_address (ip6_main_t * im, - u32 sw_if_index, - ip_interface_address_t ** result_ia) +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; @@ -445,8 +443,6 @@ ip6_interface_first_address (ip6_main_t * im, break; })); /* *INDENT-ON* */ - if (result_ia) - *result_ia = result ? ia : 0; return result; } @@ -3318,7 +3314,7 @@ vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index, vec_validate (lm->classify_table_index_by_sw_if_index, sw_if_index); lm->classify_table_index_by_sw_if_index[sw_if_index] = table_index; - if_addr = ip6_interface_first_address (ipm, sw_if_index, NULL); + if_addr = ip6_interface_first_address (ipm, sw_if_index); if (NULL != if_addr) { diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index 00e2bfb1..e4f2ee72 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -214,27 +214,6 @@ VLIB_REGISTER_NODE (ip4_icmp_echo_reply_node, static) = 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; - - /* *INDENT-OFF* */ - 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; - })); - /* *INDENT-ON* */ - 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, -- cgit 1.2.3-korg From 1b33fde6bdf54de22fb97f1b0a6730a40fd6560f Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Thu, 16 Mar 2017 12:24:24 +0000 Subject: ping: Fix coverity error 163907 Return an error if the ip6_interface_first_address decides to return NULL. Change-Id: Iea0184382b8fbe2af81a781c3671687a1fbb5b32 Signed-off-by: Andrew Yourtchenko --- src/vnet/ip/ping.c | 5 +++++ src/vnet/ip/ping.h | 1 + 2 files changed, 6 insertions(+) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index e4f2ee72..a2b96ab6 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -307,6 +307,11 @@ send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, /* Fill in the correct source now */ ip6_address_t *a = ip6_interface_first_address (im, sw_if_index); + if (!a) + { + vlib_buffer_free (vm, &bi0, 1); + return SEND_PING_NO_SRC_ADDRESS; + } h0->ip6.src_address = a[0]; /* Fill in icmp fields */ diff --git a/src/vnet/ip/ping.h b/src/vnet/ip/ping.h index 1887314a..0af9d587 100644 --- a/src/vnet/ip/ping.h +++ b/src/vnet/ip/ping.h @@ -32,6 +32,7 @@ typedef enum SEND_PING_ALLOC_FAIL, SEND_PING_NO_INTERFACE, SEND_PING_NO_TABLE, + SEND_PING_NO_SRC_ADDRESS, } send_ip46_ping_result_t; /* -- cgit 1.2.3-korg From 864b25d0777d3226f475ef7d0232cacec7127770 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 22 Mar 2017 10:51:14 +0100 Subject: ping: allow the user to send bursts of N packets instead of single packet Change-Id: I3c1d3a2db56487473123e6fec2b076a063473313 Signed-off-by: Andrew Yourtchenko --- src/vnet/ip/ping.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index a2b96ab6..4131bd71 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -234,7 +234,7 @@ static send_ip46_ping_result_t send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, u32 table_id, ip6_address_t * pa6, u32 sw_if_index, u16 seq_host, u16 id_host, u16 data_len, - u8 verbose) + u32 burst, u8 verbose) { icmp6_echo_request_header_t *h0; u32 bi0 = 0; @@ -339,7 +339,15 @@ send_ip6_ping (vlib_main_t * vm, ip6_main_t * im, 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; + + ASSERT (burst <= VLIB_FRAME_SIZE); + f->n_vectors = burst; + while (--burst) + { + vlib_buffer_t *c0 = vlib_buffer_copy (vm, p0); + to_next++; + to_next[0] = vlib_get_buffer_index (vm, c0); + } vlib_put_frame_to_node (vm, ip6_lookup_node.index, f); return SEND_PING_OK; @@ -351,7 +359,7 @@ send_ip4_ping (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, u32 sw_if_index, - u16 seq_host, u16 id_host, u16 data_len, u8 verbose) + u16 seq_host, u16 id_host, u16 data_len, u32 burst, u8 verbose) { icmp4_echo_request_header_t *h0; u32 bi0 = 0; @@ -469,7 +477,15 @@ send_ip4_ping (vlib_main_t * vm, 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; + + ASSERT (burst <= VLIB_FRAME_SIZE); + f->n_vectors = burst; + while (--burst) + { + vlib_buffer_t *c0 = vlib_buffer_copy (vm, p0); + to_next++; + to_next[0] = vlib_get_buffer_index (vm, c0); + } vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); return SEND_PING_OK; @@ -523,7 +539,7 @@ static void run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, ip6_address_t * pa6, u32 sw_if_index, f64 ping_interval, u32 ping_repeat, u32 data_len, - u32 verbose) + u32 ping_burst, u32 verbose) { int i; ping_main_t *pm = &ping_main; @@ -561,16 +577,16 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, if (pa6 && (SEND_PING_OK == send_ip6_ping (vm, ping_main.ip6_main, table_id, pa6, sw_if_index, - i, icmp_id, data_len, verbose))) + i, icmp_id, data_len, ping_burst, verbose))) { - n_requests++; + n_requests += ping_burst; } if (pa4 && (SEND_PING_OK == send_ip4_ping (vm, ping_main.ip4_main, table_id, pa4, sw_if_index, - i, icmp_id, data_len, verbose))) + i, icmp_id, data_len, ping_burst, verbose))) { - n_requests++; + n_requests += ping_burst; } while ((i <= ping_repeat) && @@ -650,6 +666,7 @@ ping_ip_address (vlib_main_t * vm, ip6_address_t a6; clib_error_t *error = 0; u32 ping_repeat = 5; + u32 ping_burst = 1; u8 ping_ip4, ping_ip6; vnet_main_t *vnm = vnet_get_main (); u32 data_len = PING_DEFAULT_DATA_LEN; @@ -789,6 +806,17 @@ ping_ip_address (vlib_main_t * vm, goto done; } } + else if (unformat (input, "burst")) + { + if (!unformat (input, "%u", &ping_burst)) + { + error = + clib_error_return (0, + "expecting burst count but got `%U'", + format_unformat_error, input); + goto done; + } + } else if (unformat (input, "verbose")) { verbose = 1; @@ -801,9 +829,13 @@ ping_ip_address (vlib_main_t * vm, } } + if (ping_burst < 1 || ping_burst > VLIB_FRAME_SIZE) + return clib_error_return (0, "burst size must be between 1 and %u", + VLIB_FRAME_SIZE); + run_ping_ip46_address (vm, table_id, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL, sw_if_index, ping_interval, - ping_repeat, data_len, verbose); + ping_repeat, ping_burst, data_len, verbose); done: return error; } -- cgit 1.2.3-korg From e3d52803149d58737aad38435361e692d70c9d7a Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Fri, 24 Mar 2017 17:46:42 +0100 Subject: VPP-669: ping: fix coverity check error 165075 + add ping testcase Fix the bug and add the unit test to start with ping test coverage Change-Id: Ibeacbed1f1660e677faa2dbb2ebe386216693e96 Signed-off-by: Andrew Yourtchenko --- src/vnet/ip/ping.c | 2 +- test/test_ping.py | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 test/test_ping.py (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index 4131bd71..4a7fdd6c 100644 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -835,7 +835,7 @@ ping_ip_address (vlib_main_t * vm, run_ping_ip46_address (vm, table_id, ping_ip4 ? &a4 : NULL, ping_ip6 ? &a6 : NULL, sw_if_index, ping_interval, - ping_repeat, ping_burst, data_len, verbose); + ping_repeat, data_len, ping_burst, verbose); done: return error; } diff --git a/test/test_ping.py b/test/test_ping.py new file mode 100644 index 00000000..4f3921e9 --- /dev/null +++ b/test/test_ping.py @@ -0,0 +1,118 @@ +import socket + +from scapy.layers.inet import IP, UDP, ICMP +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, GRE +from scapy.packet import Raw + +from framework import VppTestCase +from util import ppp + +""" TestPing is a subclass of VPPTestCase classes. + +Basic test for sanity check of the ping. + +""" + + +class TestPing(VppTestCase): + """ Ping Test Case """ + + @classmethod + def setUpClass(cls): + super(TestPing, cls).setUpClass() + try: + cls.create_pg_interfaces(range(2)) + cls.interfaces = list(cls.pg_interfaces) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.config_ip6() + i.disable_ipv6_ra() + i.resolve_arp() + i.resolve_ndp() + except Exception: + super(TestPing, cls).tearDownClass() + raise + + def tearDown(self): + super(TestPing, self).tearDown() + if not self.vpp_dead: + self.vapi.cli("show hardware") + + def test_ping_basic(self): + """ basic ping test """ + try: + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.logger.info(self.vapi.cli("show ip arp")) + self.logger.info(self.vapi.cli("show ip6 neighbors")) + + remote_ip4 = self.pg1.remote_ip4 + ping_cmd = "ping " + remote_ip4 + " interval 0.01 repeat 10" + ret = self.vapi.cli(ping_cmd) + self.logger.info(ret) + out = self.pg1.get_capture(10) + icmp_id = None + icmp_seq = 1 + for p in out: + ip = p[IP] + self.assertEqual(ip.version, 4) + self.assertEqual(ip.flags, 0) + self.assertEqual(ip.src, self.pg1.local_ip4) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(ip.proto, 1) + self.assertEqual(len(ip.options), 0) + self.assertGreaterEqual(ip.ttl, 254) + icmp = p[ICMP] + self.assertEqual(icmp.type, 8) + self.assertEqual(icmp.code, 0) + self.assertEqual(icmp.seq, icmp_seq) + icmp_seq = icmp_seq + 1 + if icmp_id is None: + icmp_id = icmp.id + else: + self.assertEqual(icmp.id, icmp_id) + finally: + self.vapi.cli("show error") + + def test_ping_burst(self): + """ burst ping test """ + try: + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.logger.info(self.vapi.cli("show ip arp")) + self.logger.info(self.vapi.cli("show ip6 neighbors")) + + remote_ip4 = self.pg1.remote_ip4 + ping_cmd = "ping " + remote_ip4 + " interval 0.01 burst 3" + ret = self.vapi.cli(ping_cmd) + self.logger.info(ret) + out = self.pg1.get_capture(3*5) + icmp_id = None + icmp_seq = 1 + count = 0 + for p in out: + ip = p[IP] + self.assertEqual(ip.version, 4) + self.assertEqual(ip.flags, 0) + self.assertEqual(ip.src, self.pg1.local_ip4) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(ip.proto, 1) + self.assertEqual(len(ip.options), 0) + self.assertGreaterEqual(ip.ttl, 254) + icmp = p[ICMP] + self.assertEqual(icmp.type, 8) + self.assertEqual(icmp.code, 0) + self.assertEqual(icmp.seq, icmp_seq) + count = count + 1 + if count >= 3: + icmp_seq = icmp_seq + 1 + count = 0 + if icmp_id is None: + icmp_id = icmp.id + else: + self.assertEqual(icmp.id, icmp_id) + finally: + self.vapi.cli("show error") -- cgit 1.2.3-korg From 92a838b14a1862ef07c631412069e968f303639b Mon Sep 17 00:00:00 2001 From: flyingeagle23 Date: Mon, 15 May 2017 16:57:20 +0800 Subject: ping command does not work when there is woker thread (VPP-844) Change-Id: Ic896b237a7d141243e1b7d6d4fbb2a120f44363e Signed-off-by: flyingeagle23 --- src/vnet/ip/ping.c | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 src/vnet/ip/ping.c (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c old mode 100644 new mode 100755 index 4a7fdd6c..5d04b3d6 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -890,6 +890,7 @@ VLIB_CLI_COMMAND (ping_command, static) = " [ipv4 | ipv6 ] [source ]" " [size ] [interval ] [repeat ] [table-id ]" " [verbose]", + .is_mp_safe = 1, }; /* *INDENT-ON* */ -- cgit 1.2.3-korg From 6e19b37757df334d58dbd6b7fa39f0e6e42f7d30 Mon Sep 17 00:00:00 2001 From: wenxian li Date: Sun, 4 Jun 2017 13:52:07 +0000 Subject: Fix typo for specifying table_id on ping. Change-Id: I76d399915c76ec68d8a401e11c14dc15c55291e4 Signed-off-by: wenxian li --- src/vnet/ip/ping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index 5d04b3d6..c664772f 100755 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -775,7 +775,7 @@ ping_ip_address (vlib_main_t * vm, } else if (unformat (input, "table-id")) { - if (!unformat (input, "du", &table_id)) + if (!unformat (input, "%u", &table_id)) { error = clib_error_return (0, -- cgit 1.2.3-korg From 03a6213fb5022d37ea92f974a1814db1c70bcbdf Mon Sep 17 00:00:00 2001 From: Mohammed Hawari Date: Tue, 18 Jul 2017 09:25:01 +0200 Subject: ping: fixing wrong value when there are worker threads - the echo_reply_node is now notifying the cli process on the main thread/vlib_main - the timestamp for the icmp reply is now acquired in the echo_reply_node and not in the cli process to avoid an off by 10ms error (see 【vpp-dev】delay is error in ping with multi worker thread) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I21d37002b0376b4f2ccab08d8f04c2f2944b9b39 Signed-off-by: Mohammed Hawari --- src/vnet/ip/ping.c | 29 ++++++++++++++++++++--------- src/vnet/ip/ping.h | 1 + 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index c664772f..eee2575a 100755 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** * @file @@ -53,8 +55,7 @@ format_icmp_echo_trace (u8 * s, va_list * va) */ static int -signal_ip46_icmp_reply_event (vlib_main_t * vm, - u8 event_type, vlib_buffer_t * b0) +signal_ip46_icmp_reply_event (u8 event_type, vlib_buffer_t * b0) { ping_main_t *pm = &ping_main; u16 net_icmp_id = 0; @@ -84,13 +85,19 @@ signal_ip46_icmp_reply_event (vlib_main_t * vm, return 0; ping_run_t *pr = vec_elt_at_index (pm->ping_runs, p[0]); + vlib_main_t *vm = vlib_mains[pr->cli_thread_index]; if (vlib_buffer_alloc (vm, &bi0_copy, 1) == 1) { - void *dst = vlib_buffer_get_current (vlib_get_buffer (vm, bi0_copy)); + 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. */ - + f64 nowts = vlib_time_now (vm); + /* Pass the timestamp to the cli_process thanks to the vnet_buffer unused metadata field */ + clib_memcpy (vnet_buffer + (vlib_get_buffer + (vm, bi0_copy))->unused, &nowts, sizeof (nowts)); vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy); return 1; } @@ -116,7 +123,7 @@ ip6_icmp_echo_reply_node_fn (vlib_main_t * vm, bi0 = from[0]; b0 = vlib_get_buffer (vm, bi0); - next0 = signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP6, b0) ? + next0 = signal_ip46_icmp_reply_event (PING_RESPONSE_IP6, b0) ? ICMP6_ECHO_REPLY_NEXT_DROP : ICMP6_ECHO_REPLY_NEXT_PUNT; if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) @@ -174,7 +181,7 @@ ip4_icmp_echo_reply_node_fn (vlib_main_t * vm, bi0 = from[0]; b0 = vlib_get_buffer (vm, bi0); - next0 = signal_ip46_icmp_reply_event (vm, PING_RESPONSE_IP4, b0) ? + next0 = signal_ip46_icmp_reply_event (PING_RESPONSE_IP4, b0) ? ICMP4_ECHO_REPLY_NEXT_DROP : ICMP4_ECHO_REPLY_NEXT_PUNT; if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) @@ -497,8 +504,9 @@ 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; - + f64 rtt = 0; + clib_memcpy (&rtt, vnet_buffer (b0)->unused, sizeof (rtt)); + rtt -= 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), @@ -513,7 +521,9 @@ 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; + f64 rtt = 0; + clib_memcpy (&rtt, vnet_buffer (b0)->unused, sizeof (rtt)); + rtt -= 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)); @@ -565,6 +575,7 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, pool_get (pm->ping_runs, pr); ping_run_index = pr - pm->ping_runs; pr->cli_process_id = curr_proc; + pr->cli_thread_index = vlib_get_thread_index (); 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++) diff --git a/src/vnet/ip/ping.h b/src/vnet/ip/ping.h index 0af9d587..b1b71f68 100644 --- a/src/vnet/ip/ping.h +++ b/src/vnet/ip/ping.h @@ -43,6 +43,7 @@ typedef struct ping_run_t u16 icmp_id; u16 curr_seq; uword cli_process_id; + uword cli_thread_index; } ping_run_t; typedef struct ping_main_t -- cgit 1.2.3-korg From 3600b67e4fbb3088f13cb75c322c5ef37e6e57a8 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 21 Aug 2017 22:37:39 +0200 Subject: ping: remove unnecesary x86 intrinsics #include This include file is present only on x86 so it breaks non-x86 builds. Change-Id: I6406b0b36f207b684535961545e95dbba707d267 Signed-off-by: Damjan Marion --- src/vnet/ip/ping.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index eee2575a..d250b796 100755 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -19,7 +19,6 @@ #include #include #include -#include /** * @file -- cgit 1.2.3-korg From 9d9265899e89c2c8db5b5b11a09fdf4034a2149c Mon Sep 17 00:00:00 2001 From: dongjuan Date: Thu, 31 Aug 2017 14:57:54 +0800 Subject: Fix vent_data memory leak in function run_ping_ip46_address (VPP-966) Change-Id: I05d0c8310aa86480a3d283f45e115284c4b4ebd5 Signed-off-by: dongjuan --- src/vnet/ip/ping.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index d250b796..c847e696 100755 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -646,6 +646,7 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, i = 1 + ping_repeat; break; } + vec_free(event_data); } } vlib_cli_output (vm, "\n"); -- cgit 1.2.3-korg From 69128d0209ba6108430dca9cc78ab36a9b1c793e Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Tue, 26 Sep 2017 10:54:34 -0400 Subject: Add thread-safe event signaller, use RPC where required Update ping code to use the new function Change-Id: Ieb753b23f8402cbe5667c22747896784c8ece937 Signed-off-by: Florin Coras Signed-off-by: Dave Barach --- src/vlib/node_funcs.h | 23 +++++++++++++++++++++++ src/vlib/threads.c | 24 +++++++++++++++++++++++- src/vlib/threads.h | 14 +++++++++++++- src/vlibmemory/memory_vlib.c | 13 ++++++++++++- src/vnet/ip/ping.c | 4 ++-- 5 files changed, 73 insertions(+), 5 deletions(-) (limited to 'src/vnet/ip/ping.c') diff --git a/src/vlib/node_funcs.h b/src/vlib/node_funcs.h index 3ae4e541..0734476c 100644 --- a/src/vlib/node_funcs.h +++ b/src/vlib/node_funcs.h @@ -965,6 +965,29 @@ vlib_process_signal_event_pointer (vlib_main_t * vm, d[0] = data; } +/** + * Signal event to process from any thread. + * + * When in doubt, use this. + */ +always_inline void +vlib_process_signal_event_mt (vlib_main_t * vm, + uword node_index, uword type_opaque, uword data) +{ + if (vlib_get_thread_index () != 0) + { + vlib_process_signal_event_mt_args_t args = { + .node_index = node_index, + .type_opaque = type_opaque, + .data = data, + }; + vlib_rpc_call_main_thread (vlib_process_signal_event_mt_helper, + (u8 *) & args, sizeof (args)); + } + else + vlib_process_signal_event (vm, node_index, type_opaque, data); +} + always_inline void vlib_process_signal_one_time_event (vlib_main_t * vm, uword node_index, diff --git a/src/vlib/threads.c b/src/vlib/threads.c index f9c7043c..be8daa64 100644 --- a/src/vlib/threads.c +++ b/src/vlib/threads.c @@ -1767,7 +1767,6 @@ vlib_frame_queue_main_init (u32 node_index, u32 frame_queue_nelts) return (fqm - tm->frame_queue_mains); } - int vlib_thread_cb_register (struct vlib_main_t *vm, vlib_thread_callbacks_t * cb) { @@ -1781,6 +1780,29 @@ vlib_thread_cb_register (struct vlib_main_t *vm, vlib_thread_callbacks_t * cb) return 0; } +void +vlib_process_signal_event_mt_helper (vlib_process_signal_event_mt_args_t * + args) +{ + ASSERT (vlib_get_thread_index () == 0); + vlib_process_signal_event (vlib_get_main (), args->node_index, + args->type_opaque, args->data); +} + +void *rpc_call_main_thread_cb_fn; + +void +vlib_rpc_call_main_thread (void *callback, u8 * args, u32 arg_size) +{ + if (rpc_call_main_thread_cb_fn) + { + void (*fp) (void *, u8 *, u32) = rpc_call_main_thread_cb_fn; + (*fp) (callback, args, arg_size); + } + else + clib_warning ("BUG: rpc_call_main_thread_cb_fn NULL!"); +} + clib_error_t * threads_init (vlib_main_t * vm) { diff --git a/src/vlib/threads.h b/src/vlib/threads.h index 72340ee1..8931584b 100644 --- a/src/vlib/threads.h +++ b/src/vlib/threads.h @@ -171,6 +171,13 @@ typedef struct frame_queue_nelt_counter_t *frame_queue_histogram; } vlib_frame_queue_main_t; +typedef struct +{ + uword node_index; + uword type_opaque; + uword data; +} vlib_process_signal_event_mt_args_t; + /* Called early, in thread 0's context */ clib_error_t *vlib_thread_init (vlib_main_t * vm); @@ -510,9 +517,14 @@ vlib_get_worker_handoff_queue_elt (u32 frame_queue_index, } u8 *vlib_thread_stack_init (uword thread_index); - int vlib_thread_cb_register (struct vlib_main_t *vm, vlib_thread_callbacks_t * cb); +extern void *rpc_call_main_thread_cb_fn; + +void +vlib_process_signal_event_mt_helper (vlib_process_signal_event_mt_args_t * + args); +void vlib_rpc_call_main_thread (void *function, u8 * args, u32 size); #endif /* included_vlib_threads_h */ diff --git a/src/vlibmemory/memory_vlib.c b/src/vlibmemory/memory_vlib.c index 77959e6d..d305ea61 100644 --- a/src/vlibmemory/memory_vlib.c +++ b/src/vlibmemory/memory_vlib.c @@ -1573,6 +1573,17 @@ _(RPC_CALL_REPLY,rpc_call_reply) #define foreach_plugin_trace_msg \ _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids) +/* + * Set the rpc callback at our earliest possible convenience. + * This avoids ordering issues between thread_init() -> start_workers and + * an init function which we could define here. If we ever intend to use + * vlib all by itself, we can't create a link-time dependency on + * an init function here and a typical "call foo_init first" + * guitar lick. + */ + +extern void *rpc_call_main_thread_cb_fn; + static clib_error_t * rpc_api_hookup (vlib_main_t * vm) { @@ -1599,7 +1610,7 @@ rpc_api_hookup (vlib_main_t * vm) /* No reason to halt the parade to create a trace record... */ am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1; - + rpc_call_main_thread_cb_fn = vl_api_rpc_call_main_thread; return 0; } diff --git a/src/vnet/ip/ping.c b/src/vnet/ip/ping.c index c847e696..0fa537f6 100755 --- a/src/vnet/ip/ping.c +++ b/src/vnet/ip/ping.c @@ -97,7 +97,7 @@ signal_ip46_icmp_reply_event (u8 event_type, vlib_buffer_t * b0) clib_memcpy (vnet_buffer (vlib_get_buffer (vm, bi0_copy))->unused, &nowts, sizeof (nowts)); - vlib_process_signal_event (vm, pr->cli_process_id, event_type, bi0_copy); + vlib_process_signal_event_mt (vm, pr->cli_process_id, event_type, bi0_copy); return 1; } @@ -646,7 +646,7 @@ run_ping_ip46_address (vlib_main_t * vm, u32 table_id, ip4_address_t * pa4, i = 1 + ping_repeat; break; } - vec_free(event_data); + vec_free (event_data); } } vlib_cli_output (vm, "\n"); -- cgit 1.2.3-korg