/* * Copyright (c) 2017 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 #include #include #include #include #include #include #include #include typedef enum { UDP_PING_NEXT_DROP, UDP_PING_NEXT_PUNT, UDP_PING_NEXT_UDP_LOOKUP, UDP_PING_NEXT_ICMP, UDP_PING_NEXT_IP6_LOOKUP, UDP_PING_NEXT_IP6_DROP, UDP_PING_N_NEXT, } udp_ping_next_t; #define foreach_udp_ping_error \ _(BADHBH, "Malformed hop-by-hop header") typedef enum { #define _(sym,str) UDP_PING_ERROR_##sym, foreach_udp_ping_error #undef _ UDP_PING_N_ERROR, } udp_ping_error_t; static char *udp_ping_error_strings[] = { #define _(sym,string) string, foreach_udp_ping_error #undef _ }; udp_ping_main_t udp_ping_main; uword udp_ping_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f); extern int ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, ip6_hop_by_hop_option_t * opt); typedef struct { ip6_address_t src; ip6_address_t dst; u16 src_port; u16 dst_port; u16 handle; u16 next_index; u8 msg_type; } udp_ping_trace_t; /* packet trace format function */ static u8 * format_udp_ping_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); udp_ping_trace_t *t = va_arg (*args, udp_ping_trace_t *); s = format (s, "udp-ping-local: src %U, dst %U, src_port %u, dst_port %u " "handle %u, next_index %u, msg_type %u", format_ip6_address, &t->src, format_ip6_address, &t->dst, t->src_port, t->dst_port, t->handle, t->next_index, t->msg_type); return s; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (udp_ping_node, static) = { .function = udp_ping_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "udp-ping-process", }; /* *INDENT-ON* */ void udp_ping_calculate_timer_interval (void) { int i; ip46_udp_ping_flow *flow = NULL; u16 min_interval = 0x1e9; for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++) { if (pool_is_free_index (udp_ping_main.ip46_flow, i)) continue; flow = pool_elt_at_index (udp_ping_main.ip46_flow, i); if (min_interval > flow->udp_data.interval) min_interval = flow->udp_data.interval; } if (udp_ping_main.timer_interval != min_interval) { udp_ping_main.timer_interval = min_interval; vlib_process_signal_event (udp_ping_main.vlib_main, udp_ping_node.index, EVENT_SIG_RECHECK, 0); } } void ip46_udp_ping_set_flow (ip46_address_t src, ip46_address_t dst, u16 start_src_port, u16 end_src_port, u16 start_dst_port, u16 end_dst_port, u16 interval, u8 fault_det, u8 is_disable) { u8 found = 0; ip46_udp_ping_flow *flow = NULL; int i; for (i = 0; i < vec_len (udp_ping_main.ip46_flow); i++) { if (pool_is_free_index (udp_ping_main.ip46_flow, i)) continue; flow = pool_elt_at_index (udp_ping_main.ip46_flow, i); if ((0 == udp_ping_compare_flow (src, dst, start_src_port, end_src_port, start_dst_port, end_dst_port, flow))) { found = 1; break; } } if (found) { u16 cur_interval; if (is_disable) { cur_interval = flow->udp_data.interval; udp_ping_free_flow_data (flow); pool_put_index (udp_ping_main.ip46_flow, i); if (udp_ping_main.timer_interval == interval) udp_ping_calculate_timer_interval (); return; } cur_interval = flow->udp_data.interval; flow->udp_data.interval = interval; if (udp_ping_main.timer_interval > interval) { udp_ping_main.timer_interval = interval; vlib_process_signal_event (udp_ping_main.vlib_main, udp_ping_node.index, EVENT_SIG_RECHECK, 0); } else if (udp_ping_main.timer_interval == cur_interval) udp_ping_calculate_timer_interval (); return; } /* Delete operation and item not found */ if (is_disable) return; /* Alloc new session */ pool_get_aligned (udp_ping_main.ip46_flow, flow, CLIB_CACHE_LINE_BYTES); udp_ping_populate_flow (src, dst, start_src_port, end_src_port, start_dst_port, end_dst_port, interval, fault_det, flow); udp_ping_create_rewrite (flow, (flow - udp_ping_main.ip46_flow)); if (udp_ping_main.timer_interval > interval) { udp_ping_main.timer_interval = interval; vlib_process_signal_event (udp_ping_main.vlib_main, udp_ping_node.index, EVENT_SIG_RECHECK, 0); } return; } uword unformat_port_range (unformat_input_t * input, va_list * args) { u16 *start_port, *end_port; uword c; u8 colon_present = 0; start_port = va_arg (*args, u16 *); end_port = va_arg (*args, u16 *); *start_port = *end_port = 0; /* Get start port */ while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { switch (c) { case '0' ... '9': *start_port = ((*start_port) * 10) + (c - '0'); break; case ':': colon_present = 1; break; default: return 0; } if (colon_present) break; } if (!colon_present) return 0; /* Get end port */ while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { switch (c) { case '0' ... '9': *end_port = ((*end_port) * 10) + (c - '0'); break; default: return 1; } } if (end_port < start_port) return 0; return 1; } static clib_error_t * set_udp_ping_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ip46_address_t dst, src; u16 start_src_port, end_src_port; u16 start_dst_port, end_dst_port; u32 interval; u8 is_disable = 0; u8 fault_det = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "src %U", unformat_ip46_address, &src, IP46_TYPE_ANY)) ; else if (unformat (input, "src-port-range %U", unformat_port_range, &start_src_port, &end_src_port)) ; else if (unformat (input, "dst %U", unformat_ip46_address, &dst, IP46_TYPE_ANY)) ; else if (unformat (input, "dst-port-range %U", unformat_port_range, &start_dst_port, &end_dst_port)) ; else if (unformat (input, "interval %d", &interval)) ; else if (unformat (input, "fault-detect")) fault_det = 1; else if (unformat (input, "disable")) is_disable = 1; else break; } ip46_udp_ping_set_flow (src, dst, start_src_port, end_src_port, start_dst_port, end_dst_port, (u16) interval, fault_det, is_disable); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_udp_ping_command, static) = { .path = "set udp-ping", .short_help = "set udp-ping src src-port-range \ dst dst-port-range \ interval