From: Neale Ranns Date: Mon, 31 Jul 2017 09:30:50 +0000 (-0700) Subject: punt and drop features: X-Git-Tag: v18.04-rc0~493 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=d91c1dbdb31f80db7d967f2f57c43d0a81d65297 punt and drop features: - new IPv4 and IPv6 feature arcs on the punt and drop nodes - new features: - redirect punted traffic to an interface and nexthop - police punted traffic. Change-Id: I53be8bf4e06545add8a3619e462de5ffedd0a95c Signed-off-by: Neale Ranns --- diff --git a/src/vat/api_format.c b/src/vat/api_format.c index ba1a791afec..7cd4b22df9a 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -18349,10 +18349,10 @@ api_policer_add_del (vat_main_t * vam) clib_memcpy (mp->name, name, vec_len (name)); vec_free (name); mp->is_add = is_add; - mp->cir = cir; - mp->eir = eir; - mp->cb = cb; - mp->eb = eb; + mp->cir = ntohl (cir); + mp->eir = ntohl (eir); + mp->cb = clib_net_to_host_u64 (cb); + mp->eb = clib_net_to_host_u64 (eb); mp->rate_type = rate_type; mp->round_type = round_type; mp->type = type; diff --git a/src/vnet.am b/src/vnet.am index 15b3467c772..ab0978074b6 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -333,6 +333,7 @@ libvnet_la_SOURCES += \ vnet/ip/ip46_cli.c \ vnet/ip/ip4_format.c \ vnet/ip/ip4_forward.c \ + vnet/ip/ip4_punt_drop.c \ vnet/ip/ip4_input.c \ vnet/ip/ip4_mtrie.c \ vnet/ip/ip4_pg.c \ @@ -340,6 +341,7 @@ libvnet_la_SOURCES += \ vnet/ip/ip4_source_check.c \ vnet/ip/ip6_format.c \ vnet/ip/ip6_forward.c \ + vnet/ip/ip6_punt_drop.c \ vnet/ip/ip6_hop_by_hop.c \ vnet/ip/ip6_input.c \ vnet/ip/ip6_neighbor.c \ diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index 69ab7011943..e302b1e7a44 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -547,6 +547,42 @@ define mfib_signal_details u8 ip_packet_data[256]; }; +/** \brief IP punt policer + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 to add neighbor, 0 to delete + @param is_ipv6 - 1 for IPv6 neighbor, 0 for IPv4 + @param policer_index - Index of policer to use +*/ +autoreply define ip_punt_police +{ + u32 client_index; + u32 context; + u32 policer_index; + u8 is_add; + u8 is_ip6; +}; + +/** \brief IP punt redirect + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 to add neighbor, 0 to delete + @param is_ipv6 - 1 for IPv6 neighbor, 0 for IPv4 + @param tx_sw_if_index - the TX interface to which traffic shoulde be + redirected. + @param nh - The next-hop to redirect the traffic to. +*/ +autoreply define ip_punt_redirect +{ + u32 client_index; + u32 context; + u32 rx_sw_if_index; + u32 tx_sw_if_index; + u8 is_add; + u8 is_ip6; + u8 nh[16]; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/ip/ip4.h b/src/vnet/ip/ip4.h index af0e6b9a8a5..4c5cc054c4a 100644 --- a/src/vnet/ip/ip4.h +++ b/src/vnet/ip/ip4.h @@ -281,6 +281,12 @@ int vnet_set_ip4_flow_hash (u32 table_id, int vnet_set_ip4_classify_intfc (vlib_main_t * vm, u32 sw_if_index, u32 table_index); +void ip4_punt_policer_add_del (u8 is_add, u32 policer_index); + +void ip4_punt_redirect_add (u32 rx_sw_if_index, + u32 tx_sw_if_index, ip46_address_t * nh); +void ip4_punt_redirect_del (u32 rx_sw_if_index); + /* Compute flow hash. We'll use it to select which adjacency to use for this flow. And other things. */ always_inline u32 diff --git a/src/vnet/ip/ip4_error.h b/src/vnet/ip/ip4_error.h index 95d12ec22d5..d2775636539 100644 --- a/src/vnet/ip/ip4_error.h +++ b/src/vnet/ip/ip4_error.h @@ -57,8 +57,8 @@ _ (MTU_EXCEEDED, "ip4 MTU exceeded and DF set") \ _ (DST_LOOKUP_MISS, "ip4 destination lookup miss") \ _ (SRC_LOOKUP_MISS, "ip4 source lookup miss") \ - _ (ADJACENCY_DROP, "ip4 adjacency drop") \ - _ (ADJACENCY_PUNT, "ip4 adjacency punt") \ + _ (DROP, "ip4 drop") \ + _ (PUNT, "ip4 punt") \ \ /* Errors signalled by ip4-local. */ \ _ (UNKNOWN_PROTOCOL, "unknown ip protocol") \ diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c index 3aebb181fce..daffae410cc 100755 --- a/src/vnet/ip/ip4_forward.c +++ b/src/vnet/ip/ip4_forward.c @@ -60,12 +60,6 @@ * This file contains the source code for IPv4 forwarding. */ -void -ip4_forward_next_trace (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - vlib_rx_or_tx_t which_adj_index); - always_inline uword ip4_lookup_inline (vlib_main_t * vm, vlib_node_runtime_t * node, @@ -1387,68 +1381,6 @@ ip4_forward_next_trace (vlib_main_t * vm, } } -static uword -ip4_drop_or_punt (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, ip4_error_t error_code) -{ - u32 *buffers = vlib_frame_vector_args (frame); - uword n_packets = frame->n_vectors; - - vlib_error_drop_buffers (vm, node, buffers, - /* stride */ 1, - n_packets, - /* next */ 0, - ip4_input_node.index, error_code); - - if (node->flags & VLIB_NODE_FLAG_TRACE) - ip4_forward_next_trace (vm, node, frame, VLIB_TX); - - return n_packets; -} - -static uword -ip4_drop (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return ip4_drop_or_punt (vm, node, frame, IP4_ERROR_ADJACENCY_DROP); -} - -static uword -ip4_punt (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return ip4_drop_or_punt (vm, node, frame, IP4_ERROR_ADJACENCY_PUNT); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ip4_drop_node, static) = -{ - .function = ip4_drop, - .name = "ip4-drop", - .vector_size = sizeof (u32), - .format_trace = format_ip4_forward_next_trace, - .n_next_nodes = 1, - .next_nodes = { - [0] = "error-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (ip4_drop_node, ip4_drop); - -VLIB_REGISTER_NODE (ip4_punt_node, static) = -{ - .function = ip4_punt, - .name = "ip4-punt", - .vector_size = sizeof (u32), - .format_trace = format_ip4_forward_next_trace, - .n_next_nodes = 1, - .next_nodes = { - [0] = "error-punt", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (ip4_punt_node, ip4_punt); -/* *INDENT-ON */ - /* Compute TCP/UDP/ICMP4 checksum in software. */ u16 ip4_tcp_udp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, @@ -1483,11 +1415,12 @@ ip4_tcp_udp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, n_bytes_left = n_this_buffer = payload_length_host_byte_order; data_this_buffer = (void *) ip0 + ip_header_length; - n_ip_bytes_this_buffer = p0->current_length - (((u8 *) ip0 - p0->data) - p0->current_data); + n_ip_bytes_this_buffer = + p0->current_length - (((u8 *) ip0 - p0->data) - p0->current_data); if (n_this_buffer + ip_header_length > n_ip_bytes_this_buffer) { n_this_buffer = n_ip_bytes_this_buffer > ip_header_length ? - n_ip_bytes_this_buffer - ip_header_length : 0; + n_ip_bytes_this_buffer - ip_header_length : 0; } while (1) { @@ -1870,8 +1803,8 @@ VLIB_REGISTER_NODE (ip4_local_node) = .n_next_nodes = IP_LOCAL_N_NEXT, .next_nodes = { - [IP_LOCAL_NEXT_DROP] = "error-drop", - [IP_LOCAL_NEXT_PUNT] = "error-punt", + [IP_LOCAL_NEXT_DROP] = "ip4-drop", + [IP_LOCAL_NEXT_PUNT] = "ip4-punt", [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip4-udp-lookup", [IP_LOCAL_NEXT_ICMP] = "ip4-icmp-input", }, diff --git a/src/vnet/ip/ip4_punt_drop.c b/src/vnet/ip/ip4_punt_drop.c new file mode 100644 index 00000000000..72f36f33560 --- /dev/null +++ b/src/vnet/ip/ip4_punt_drop.c @@ -0,0 +1,515 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +/* *INDENT-OFF* */ +VNET_FEATURE_ARC_INIT (ip4_punt) = +{ + .arc_name = "ip4-punt", + .start_nodes = VNET_FEATURES ("ip4-punt"), +}; + +VNET_FEATURE_ARC_INIT (ip4_drop) = +{ + .arc_name = "ip4-drop", + .start_nodes = VNET_FEATURES ("ip4-drop"), +}; +/* *INDENT-ON* */ + +u8 * +format_ip_punt_policer_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 *); + ip_punt_policer_trace_t *t = va_arg (*args, ip_punt_policer_trace_t *); + + s = format (s, "policer_index %d next %d", t->policer_index, t->next); + return s; +} + +ip_punt_policer_t ip4_punt_policer_cfg = { + .policer_index = ~0, +}; + +static char *ip4_punt_policer_error_strings[] = { +#define _(sym,string) string, + foreach_ip_punt_policer_error +#undef _ +}; + +static uword +ip4_punt_policer (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip_punt_policer (vm, node, frame, + vnet_feat_arc_ip4_punt.feature_arc_index, + ip4_punt_policer_cfg.policer_index)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_punt_policer_node, static) = { + .function = ip4_punt_policer, + .name = "ip4-punt-policer", + .vector_size = sizeof (u32), + .n_next_nodes = IP_PUNT_POLICER_N_NEXT, + .format_trace = format_ip_punt_policer_trace, + .n_errors = ARRAY_LEN(ip4_punt_policer_error_strings), + .error_strings = ip4_punt_policer_error_strings, + + .next_nodes = { + [IP_PUNT_POLICER_NEXT_DROP] = "ip4-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_punt_policer_node, + ip4_punt_policer); + +VNET_FEATURE_INIT (ip4_punt_policer_node, static) = { + .arc_name = "ip4-punt", + .node_name = "ip4-punt-policer", + .runs_before = VNET_FEATURES("ip4-punt-redirect"), +}; +/* *INDENT-ON* */ + +u8 * +format_ip_punt_redirect_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 *); + ip_punt_redirect_trace_t *t = va_arg (*args, ip_punt_redirect_trace_t *); + vnet_main_t *vnm = vnet_get_main (); + vnet_sw_interface_t *si; + + si = vnet_get_sw_interface_safe (vnm, t->redirect.tx_sw_if_index); + + if (NULL != si) + s = format (s, "via %U on %U using adj:%d", + format_ip46_address, &t->redirect.nh, IP46_TYPE_ANY, + format_vnet_sw_interface_name, vnm, si, + t->redirect.adj_index); + else + s = format (s, "via %U on %d using adj:%d", + format_ip46_address, &t->redirect.nh, IP46_TYPE_ANY, + t->redirect.tx_sw_if_index, t->redirect.adj_index); + + return s; +} + +/* *INDENT-OFF* */ +ip_punt_redirect_t ip4_punt_redirect_cfg = { + .any_rx_sw_if_index = { + .tx_sw_if_index = ~0, + }, +}; +/* *INDENT-ON* */ + + +#define foreach_ip4_punt_redirect_error \ +_(DROP, "ip4 punt redirect drop") + +typedef enum +{ +#define _(sym,str) IP4_PUNT_REDIRECT_ERROR_##sym, + foreach_ip4_punt_redirect_error +#undef _ + IP4_PUNT_REDIRECT_N_ERROR, +} ip4_punt_redirect_error_t; + +static char *ip4_punt_redirect_error_strings[] = { +#define _(sym,string) string, + foreach_ip4_punt_redirect_error +#undef _ +}; + +static uword +ip4_punt_redirect (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip_punt_redirect (vm, node, frame, + vnet_feat_arc_ip4_punt.feature_arc_index, + &ip4_punt_redirect_cfg)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_punt_redirect_node, static) = { + .function = ip4_punt_redirect, + .name = "ip4-punt-redirect", + .vector_size = sizeof (u32), + .n_next_nodes = IP_PUNT_REDIRECT_N_NEXT, + .format_trace = format_ip_punt_redirect_trace, + .n_errors = ARRAY_LEN(ip4_punt_redirect_error_strings), + .error_strings = ip4_punt_redirect_error_strings, + + /* edit / add dispositions here */ + .next_nodes = { + [IP_PUNT_REDIRECT_NEXT_DROP] = "ip4-drop", + [IP_PUNT_REDIRECT_NEXT_TX] = "ip4-rewrite", + [IP_PUNT_REDIRECT_NEXT_ARP] = "ip4-arp", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_punt_redirect_node, + ip4_punt_redirect); + +VNET_FEATURE_INIT (ip4_punt_redirect_node, static) = { + .arc_name = "ip4-punt", + .node_name = "ip4-punt-redirect", + .runs_before = VNET_FEATURES("error-punt"), +}; +/* *INDENT-ON* */ + +static uword +ip4_drop (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip4_forward_next_trace (vm, node, frame, VLIB_TX); + + return ip_drop_or_punt (vm, node, frame, + vnet_feat_arc_ip4_drop.feature_arc_index); + +} + +static uword +ip4_punt (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip4_forward_next_trace (vm, node, frame, VLIB_TX); + + return ip_drop_or_punt (vm, node, frame, + vnet_feat_arc_ip4_punt.feature_arc_index); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_drop_node, static) = +{ + .function = ip4_drop, + .name = "ip4-drop", + .vector_size = sizeof (u32), + .format_trace = format_ip4_forward_next_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_drop_node, ip4_drop); + +VLIB_REGISTER_NODE (ip4_punt_node, static) = +{ + .function = ip4_punt, + .name = "ip4-punt", + .vector_size = sizeof (u32), + .format_trace = format_ip4_forward_next_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-punt", + }, +}; + +VNET_FEATURE_INIT (ip4_punt_end_of_arc, static) = { + .arc_name = "ip4-punt", + .node_name = "error-punt", + .runs_before = 0, /* not before any other features */ +}; + +VNET_FEATURE_INIT (ip4_drop_end_of_arc, static) = { + .arc_name = "ip4-drop", + .node_name = "error-drop", + .runs_before = 0, /* not before any other features */ +}; +/* *INDENT-ON */ + +void +ip4_punt_policer_add_del (u8 is_add, u32 policer_index) +{ + ip4_punt_policer_cfg.policer_index = policer_index; + + vnet_feature_enable_disable ("ip4-punt", "ip4-punt-policer", + 0, is_add, 0, 0); +} + +static clib_error_t * +ip4_punt_police_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 policer_index; + u8 is_add = 1; + + policer_index = ~0; + + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%d", &policer_index)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (is_add && ~0 == policer_index) + { + error = clib_error_return (0, "expected policer index `%U'", + format_unformat_error, line_input); + goto done; + } + if (!is_add) + policer_index = ~0; + + ip4_punt_policer_add_del(is_add, policer_index); + +done: + unformat_free (line_input); + return (error); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt policer } + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip4_punt_policer_command, static) = +{ + .path = "ip punt policer", + .function = ip4_punt_police_cmd, + .short_help = "ip punt policer [add|del] ", +}; +/* *INDENT-ON* */ + +/* + * an uninitalised rx-redirect strcut used to pad the vector + */ +ip_punt_redirect_rx_t uninit_rx_redirect = { + .tx_sw_if_index = ~0, +}; + +void +ip_punt_redirect_add (ip_punt_redirect_t * cfg, + u32 rx_sw_if_index, + ip_punt_redirect_rx_t * redirect, + fib_protocol_t fproto, vnet_link_t linkt) +{ + ip_punt_redirect_rx_t *new; + + if (~0 == rx_sw_if_index) + { + cfg->any_rx_sw_if_index = *redirect; + new = &cfg->any_rx_sw_if_index; + } + else + { + vec_validate_init_empty (cfg->redirect_by_rx_sw_if_index, + rx_sw_if_index, uninit_rx_redirect); + cfg->redirect_by_rx_sw_if_index[rx_sw_if_index] = *redirect; + new = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index]; + } + + new->adj_index = adj_nbr_add_or_lock (fproto, linkt, + &redirect->nh, + redirect->tx_sw_if_index); +} + +void +ip_punt_redirect_del (ip_punt_redirect_t * cfg, u32 rx_sw_if_index) +{ + ip_punt_redirect_rx_t *old; + + if (~0 == rx_sw_if_index) + { + old = &cfg->any_rx_sw_if_index; + } + else + { + old = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index]; + } + + adj_unlock (old->adj_index); + *old = uninit_rx_redirect; +} + +void +ip4_punt_redirect_add (u32 rx_sw_if_index, + u32 tx_sw_if_index, ip46_address_t * nh) +{ + ip_punt_redirect_rx_t rx = { + .tx_sw_if_index = tx_sw_if_index, + .nh = *nh, + }; + + ip_punt_redirect_add (&ip4_punt_redirect_cfg, + rx_sw_if_index, &rx, FIB_PROTOCOL_IP4, VNET_LINK_IP4); + + vnet_feature_enable_disable ("ip4-punt", "ip4-punt-redirect", 0, 1, 0, 0); +} + +void +ip4_punt_redirect_del (u32 rx_sw_if_index) +{ + vnet_feature_enable_disable ("ip4-punt", "ip4-punt-redirect", 0, 0, 0, 0); + + ip_punt_redirect_del (&ip4_punt_redirect_cfg, rx_sw_if_index); +} + +static clib_error_t * +ip4_punt_redirect_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 rx_sw_if_index; + u32 tx_sw_if_index; + ip46_address_t nh; + vnet_main_t *vnm; + u8 is_add; + + is_add = 1; + vnm = vnet_get_main (); + + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "rx all")) + rx_sw_if_index = ~0; + else if (unformat (line_input, "rx %U", + unformat_vnet_sw_interface, vnm, &rx_sw_if_index)) + ; + else if (unformat (line_input, "via %U %U", + unformat_ip4_address, + &nh.ip4, + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + ; + else if (unformat (line_input, "via %U", + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + memset (&nh, 0, sizeof (nh)); + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (is_add) + ip4_punt_redirect_add (rx_sw_if_index, tx_sw_if_index, &nh); + else + ip4_punt_redirect_del (rx_sw_if_index); + +done: + unformat_free (line_input); + return (error); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt policer} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip4_punt_redirect_command, static) = +{ + .path = "ip punt redirect", + .function = ip4_punt_redirect_cmd, + .short_help = "ip punt redirect [add|del] rx [|all] via [] ", +}; +/* *INDENT-ON* */ + +u8 * +format_ip_punt_redirect (u8 * s, va_list * args) +{ + ip_punt_redirect_t *cfg = va_arg (*args, ip_punt_redirect_t *); + ip_punt_redirect_rx_t *rx; + u32 rx_sw_if_index; + vnet_main_t *vnm = vnet_get_main (); + + vec_foreach_index (rx_sw_if_index, cfg->redirect_by_rx_sw_if_index) + { + rx = &cfg->redirect_by_rx_sw_if_index[rx_sw_if_index]; + if (~0 != rx->tx_sw_if_index) + { + s = format (s, " rx %U redirect via %U %U\n", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, rx_sw_if_index), + format_ip46_address, &rx->nh, IP46_TYPE_ANY, + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, rx->tx_sw_if_index)); + } + } + if (~0 != cfg->any_rx_sw_if_index.tx_sw_if_index) + { + s = format (s, " rx all redirect via %U %U\n", + format_ip46_address, &cfg->any_rx_sw_if_index.nh, + IP46_TYPE_ANY, format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, + cfg-> + any_rx_sw_if_index.tx_sw_if_index)); + } + + return (s); +} + +static clib_error_t * +ip4_punt_redirect_show_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "%U", format_ip_punt_redirect, &ip4_punt_redirect_cfg); + + return (NULL); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt redierect} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_ip4_punt_redirect_command, static) = +{ + .path = "show ip punt redirect", + .function = ip4_punt_redirect_show_cmd, + .short_help = "show ip punt redirect [add|del] rx [|all] via [] ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h index 8aef53a9dcb..033ecfc43b8 100644 --- a/src/vnet/ip/ip6.h +++ b/src/vnet/ip/ip6.h @@ -230,6 +230,11 @@ extern vlib_node_registration_t ip6_discover_neighbor_node; extern vlib_node_registration_t ip6_glean_node; extern vlib_node_registration_t ip6_midchain_node; +extern void ip6_forward_next_trace (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + vlib_rx_or_tx_t which_adj_index); + always_inline uword ip6_destination_matches_route (const ip6_main_t * im, const ip6_address_t * key, @@ -394,6 +399,11 @@ u8 *format_ip6_forward_next_trace (u8 * s, va_list * args); u32 ip6_tcp_udp_icmp_validate_checksum (vlib_main_t * vm, vlib_buffer_t * p0); +void ip6_punt_policer_add_del (u8 is_add, u32 policer_index); +void ip6_punt_redirect_add (u32 rx_sw_if_index, + u32 tx_sw_if_index, ip46_address_t * nh); +void ip6_punt_redirect_del (u32 rx_sw_if_index); + int vnet_set_ip6_classify_intfc (vlib_main_t * vm, u32 sw_if_index, u32 table_index); extern vlib_node_registration_t ip6_lookup_node; diff --git a/src/vnet/ip/ip6_error.h b/src/vnet/ip/ip6_error.h index a2807169123..7bb4b77b5a3 100644 --- a/src/vnet/ip/ip6_error.h +++ b/src/vnet/ip/ip6_error.h @@ -54,8 +54,8 @@ _ (MTU_EXCEEDED, "ip6 MTU exceeded") \ _ (DST_LOOKUP_MISS, "ip6 destination lookup miss") \ _ (SRC_LOOKUP_MISS, "ip6 source lookup miss") \ - _ (ADJACENCY_DROP, "ip6 adjacency drop") \ - _ (ADJACENCY_PUNT, "ip6 adjacency punt") \ + _ (DROP, "ip6 drop") \ + _ (PUNT, "ip6 punt") \ \ /* Errors signalled by ip6-local. */ \ _ (UNKNOWN_PROTOCOL, "unknown ip protocol") \ diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index 4f9ad85432f..f54b433485f 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -61,11 +61,6 @@ * This file contains the source code for IPv6 forwarding. */ -void -ip6_forward_next_trace (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - vlib_rx_or_tx_t which_adj_index); always_inline uword ip6_lookup_inline (vlib_main_t * vm, @@ -1133,70 +1128,6 @@ ip6_forward_next_trace (vlib_main_t * vm, } } -static uword -ip6_drop_or_punt (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, ip6_error_t error_code) -{ - u32 *buffers = vlib_frame_vector_args (frame); - uword n_packets = frame->n_vectors; - - vlib_error_drop_buffers (vm, node, buffers, - /* stride */ 1, - n_packets, - /* next */ 0, - ip6_input_node.index, error_code); - - if (node->flags & VLIB_NODE_FLAG_TRACE) - ip6_forward_next_trace (vm, node, frame, VLIB_TX); - - return n_packets; -} - -static uword -ip6_drop (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return ip6_drop_or_punt (vm, node, frame, IP6_ERROR_ADJACENCY_DROP); -} - -static uword -ip6_punt (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return ip6_drop_or_punt (vm, node, frame, IP6_ERROR_ADJACENCY_PUNT); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ip6_drop_node, static) = -{ - .function = ip6_drop, - .name = "ip6-drop", - .vector_size = sizeof (u32), - .format_trace = format_ip6_forward_next_trace, - .n_next_nodes = 1, - .next_nodes = - { - [0] = "error-drop",}, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FUNCTION_MULTIARCH (ip6_drop_node, ip6_drop); - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ip6_punt_node, static) = -{ - .function = ip6_punt, - .name = "ip6-punt", - .vector_size = sizeof (u32), - .format_trace = format_ip6_forward_next_trace, - .n_next_nodes = 1, - .next_nodes = - { - [0] = "error-punt",}, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_node, ip6_punt); - /* Compute TCP/UDP/ICMP6 checksum in software. */ u16 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, @@ -1649,8 +1580,8 @@ VLIB_REGISTER_NODE (ip6_local_node, static) = .n_next_nodes = IP_LOCAL_N_NEXT, .next_nodes = { - [IP_LOCAL_NEXT_DROP] = "error-drop", - [IP_LOCAL_NEXT_PUNT] = "error-punt", + [IP_LOCAL_NEXT_DROP] = "ip6-drop", + [IP_LOCAL_NEXT_PUNT] = "ip6-punt", [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip6-udp-lookup", [IP_LOCAL_NEXT_ICMP] = "ip6-icmp-input", }, diff --git a/src/vnet/ip/ip6_punt_drop.c b/src/vnet/ip/ip6_punt_drop.c new file mode 100644 index 00000000000..a1f0ebe6901 --- /dev/null +++ b/src/vnet/ip/ip6_punt_drop.c @@ -0,0 +1,396 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +/* *INDENT-OFF* */ +VNET_FEATURE_ARC_INIT (ip6_punt) = +{ + .arc_name = "ip6-punt", + .start_nodes = VNET_FEATURES ("ip6-punt"), +}; + +VNET_FEATURE_ARC_INIT (ip6_drop) = +{ + .arc_name = "ip6-drop", + .start_nodes = VNET_FEATURES ("ip6-drop"), +}; +/* *INDENT-ON* */ + +ip_punt_policer_t ip6_punt_policer_cfg; + +static char *ip6_punt_policer_error_strings[] = { +#define _(sym,string) string, + foreach_ip_punt_policer_error +#undef _ +}; + +static uword +ip6_punt_policer (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip_punt_policer (vm, node, frame, + vnet_feat_arc_ip6_punt.feature_arc_index, + ip6_punt_policer_cfg.policer_index)); +} + + +/* *INDENT-OFF* */ + +VLIB_REGISTER_NODE (ip6_punt_policer_node, static) = { + .function = ip6_punt_policer, + .name = "ip6-punt-policer", + .vector_size = sizeof (u32), + .n_next_nodes = IP_PUNT_POLICER_N_NEXT, + .format_trace = format_ip_punt_policer_trace, + .n_errors = ARRAY_LEN(ip6_punt_policer_error_strings), + .error_strings = ip6_punt_policer_error_strings, + + /* edit / add dispositions here */ + .next_nodes = { + [IP_PUNT_POLICER_NEXT_DROP] = "ip6-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_policer_node, + ip6_punt_policer); + +VNET_FEATURE_INIT (ip6_punt_policer_node, static) = { + .arc_name = "ip6-punt", + .node_name = "ip6-punt-policer", + .runs_before = VNET_FEATURES("ip6-punt-redirect") +}; +/* *INDENT-ON* */ + +static uword +ip6_drop (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip6_forward_next_trace (vm, node, frame, VLIB_TX); + + return ip_drop_or_punt (vm, node, frame, + vnet_feat_arc_ip6_drop.feature_arc_index); + +} + +static uword +ip6_punt (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip6_forward_next_trace (vm, node, frame, VLIB_TX); + + return ip_drop_or_punt (vm, node, frame, + vnet_feat_arc_ip6_punt.feature_arc_index); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_drop_node, static) = +{ + .function = ip6_drop, + .name = "ip6-drop", + .vector_size = sizeof (u32), + .format_trace = format_ip6_forward_next_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_drop_node, ip6_drop); + +VLIB_REGISTER_NODE (ip6_punt_node, static) = +{ + .function = ip6_punt, + .name = "ip6-punt", + .vector_size = sizeof (u32), + .format_trace = format_ip6_forward_next_trace, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-punt", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_node, ip6_punt); + +VNET_FEATURE_INIT (ip6_punt_end_of_arc, static) = { + .arc_name = "ip6-punt", + .node_name = "error-punt", + .runs_before = 0, /* not before any other features */ +}; + +VNET_FEATURE_INIT (ip6_drop_end_of_arc, static) = { + .arc_name = "ip6-drop", + .node_name = "error-drop", + .runs_before = 0, /* not before any other features */ +}; +/* *INDENT-ON */ + +void +ip6_punt_policer_add_del (u8 is_add, u32 policer_index) +{ + ip6_punt_policer_cfg.policer_index = policer_index; + + vnet_feature_enable_disable ("ip6-punt", "ip6-punt-policer", + 0, is_add, 0, 0); +} + +static clib_error_t * +ip6_punt_police_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 policer_index; + u8 is_add = 1; + + policer_index = ~0; + + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%d", &policer_index)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (is_add && ~0 == policer_index) + { + error = clib_error_return (0, "expected policer index `%U'", + format_unformat_error, line_input); + goto done; + } + if (!is_add) + policer_index = ~0; + + ip6_punt_policer_add_del(is_add, policer_index); + +done: + unformat_free (line_input); + return (error); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt policer } + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip6_punt_policer_command, static) = +{ + .path = "ip6 punt policer", + .function = ip6_punt_police_cmd, + .short_help = "ip6 punt policer [add|del] ", +}; +/* *INDENT-ON* */ + +ip_punt_redirect_t ip6_punt_redirect_cfg = { + .any_rx_sw_if_index = { + .tx_sw_if_index = ~0, + } + , +}; + +#define foreach_ip6_punt_redirect_error \ +_(DROP, "ip6 punt redirect drop") + +typedef enum +{ +#define _(sym,str) IP6_PUNT_REDIRECT_ERROR_##sym, + foreach_ip6_punt_redirect_error +#undef _ + IP6_PUNT_REDIRECT_N_ERROR, +} ip6_punt_redirect_error_t; + +static char *ip6_punt_redirect_error_strings[] = { +#define _(sym,string) string, + foreach_ip6_punt_redirect_error +#undef _ +}; + +static uword +ip6_punt_redirect (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip_punt_redirect (vm, node, frame, + vnet_feat_arc_ip6_punt.feature_arc_index, + &ip6_punt_redirect_cfg)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_punt_redirect_node, static) = { + .function = ip6_punt_redirect, + .name = "ip6-punt-redirect", + .vector_size = sizeof (u32), + .n_next_nodes = IP_PUNT_REDIRECT_N_NEXT, + .format_trace = format_ip_punt_redirect_trace, + .n_errors = ARRAY_LEN(ip6_punt_redirect_error_strings), + .error_strings = ip6_punt_redirect_error_strings, + + /* edit / add dispositions here */ + .next_nodes = { + [IP_PUNT_REDIRECT_NEXT_DROP] = "ip6-drop", + [IP_PUNT_REDIRECT_NEXT_TX] = "ip6-rewrite", + [IP_PUNT_REDIRECT_NEXT_ARP] = "ip6-discover-neighbor", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_redirect_node, + ip6_punt_redirect); + +VNET_FEATURE_INIT (ip6_punt_redirect_node, static) = { + .arc_name = "ip6-punt", + .node_name = "ip6-punt-redirect", + .runs_before = VNET_FEATURES("error-punt") +}; +/* *INDENT-ON* */ + +void +ip6_punt_redirect_add (u32 rx_sw_if_index, + u32 tx_sw_if_index, ip46_address_t * nh) +{ + ip_punt_redirect_rx_t rx = { + .tx_sw_if_index = tx_sw_if_index, + .nh = *nh, + }; + + ip_punt_redirect_add (&ip6_punt_redirect_cfg, + rx_sw_if_index, &rx, FIB_PROTOCOL_IP6, VNET_LINK_IP6); + + vnet_feature_enable_disable ("ip6-punt", "ip6-punt-redirect", 0, 1, 0, 0); +} + +void +ip6_punt_redirect_del (u32 rx_sw_if_index) +{ + vnet_feature_enable_disable ("ip6-punt", "ip6-punt-redirect", 0, 0, 0, 0); + + ip_punt_redirect_del (&ip6_punt_redirect_cfg, rx_sw_if_index); +} + +static clib_error_t * +ip6_punt_redirect_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 rx_sw_if_index; + u32 tx_sw_if_index; + ip46_address_t nh; + vnet_main_t *vnm; + u8 is_add; + + is_add = 1; + vnm = vnet_get_main (); + + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "rx all")) + rx_sw_if_index = ~0; + else if (unformat (line_input, "rx %U", + unformat_vnet_sw_interface, vnm, &rx_sw_if_index)) + ; + else if (unformat (line_input, "via %U %U", + unformat_ip6_address, + &nh.ip6, + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + ; + else if (unformat (line_input, "via %U", + unformat_vnet_sw_interface, vnm, &tx_sw_if_index)) + memset (&nh, 0, sizeof (nh)); + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (is_add) + ip6_punt_redirect_add (rx_sw_if_index, tx_sw_if_index, &nh); + else + ip6_punt_redirect_del (rx_sw_if_index); + +done: + unformat_free (line_input); + return (error); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt policer } + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip6_punt_redirect_command, static) = +{ + .path = "ip6 punt redirect", + .function = ip6_punt_redirect_cmd, + .short_help = "ip6 punt redirect [add|del] rx [|all] via [] ", +}; +/* *INDENT-ON* */ + +static clib_error_t * +ip6_punt_redirect_show_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "%U", format_ip_punt_redirect, &ip6_punt_redirect_cfg); + + return (NULL); +} + +/*? + * + * @cliexpar + * @cliexcmd{set ip punt policer } + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_ip6_punt_redirect_command, static) = +{ + .path = "show ip6 punt redirect", + .function = ip6_punt_redirect_show_cmd, + .short_help = "show ip6 punt redirect [add|del] rx [|all] via [] ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index 4d4daac8536..d9ec4b47998 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -70,6 +70,8 @@ _(IP_DUMP, ip_dump) \ _(IP_NEIGHBOR_ADD_DEL, ip_neighbor_add_del) \ _(IP_ADD_DEL_ROUTE, ip_add_del_route) \ _(IP_TABLE_ADD_DEL, ip_table_add_del) \ +_(IP_PUNT_POLICE, ip_punt_police) \ +_(IP_PUNT_REDIRECT, ip_punt_redirect) \ _(SET_IP_FLOW_HASH,set_ip_flow_hash) \ _(SW_INTERFACE_IP6ND_RA_CONFIG, sw_interface_ip6nd_ra_config) \ _(SW_INTERFACE_IP6ND_RA_PREFIX, sw_interface_ip6nd_ra_prefix) \ @@ -647,6 +649,64 @@ vl_api_ip6_mfib_dump_t_handler (vl_api_ip6_mfib_dump_t * mp) vec_free (api_rpaths); } +static void +vl_api_ip_punt_police_t_handler (vl_api_ip_punt_police_t * mp, + vlib_main_t * vm) +{ + vl_api_ip_punt_police_reply_t *rmp; + int rv = 0; + + if (mp->is_ip6) + ip6_punt_policer_add_del (mp->is_add, ntohl (mp->policer_index)); + else + ip4_punt_policer_add_del (mp->is_add, ntohl (mp->policer_index)); + + REPLY_MACRO (VL_API_IP_PUNT_POLICE_REPLY); +} + +static void +vl_api_ip_punt_redirect_t_handler (vl_api_ip_punt_redirect_t * mp, + vlib_main_t * vm) +{ + vl_api_ip_punt_redirect_reply_t *rmp; + int rv = 0; + + if (mp->is_add) + { + ip46_address_t nh; + + memset (&nh, 0, sizeof (nh)); + + if (mp->is_ip6) + { + memcpy (&nh.ip6, mp->nh, sizeof (nh.ip6)); + + ip6_punt_redirect_add (ntohl (mp->rx_sw_if_index), + ntohl (mp->tx_sw_if_index), &nh); + } + else + { + memcpy (&nh.ip4, mp->nh, sizeof (nh.ip4)); + + ip4_punt_redirect_add (ntohl (mp->rx_sw_if_index), + ntohl (mp->tx_sw_if_index), &nh); + } + } + else + { + if (mp->is_ip6) + { + ip6_punt_redirect_del (ntohl (mp->rx_sw_if_index)); + } + else + { + ip4_punt_redirect_del (ntohl (mp->rx_sw_if_index)); + } + } + + REPLY_MACRO (VL_API_IP_PUNT_REDIRECT_REPLY); +} + static void vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t * mp, vlib_main_t * vm) diff --git a/src/vnet/ip/ip_punt_drop.h b/src/vnet/ip/ip_punt_drop.h new file mode 100644 index 00000000000..7ba65e1a6e4 --- /dev/null +++ b/src/vnet/ip/ip_punt_drop.h @@ -0,0 +1,467 @@ +/* + * 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 __IP_PUNT_DROP_H__ +#define __IP_PUNT_DROP_H__ + +#include +#include +#include + +/** + * IP4 punt policer configuration + * we police the punt rate to prevent overloading the host + */ +typedef struct ip_punt_policer_t_ +{ + u32 policer_index; +} ip_punt_policer_t; + +typedef enum ip_punt_policer_next_t_ +{ + IP_PUNT_POLICER_NEXT_DROP, + IP_PUNT_POLICER_N_NEXT, +} ip_punt_policer_next_t; + +typedef struct ip_punt_policer_trace_t_ +{ + u32 policer_index; + u32 next; +} ip_punt_policer_trace_t; + +#define foreach_ip_punt_policer_error \ +_(DROP, "ip punt policer drop") + +typedef enum +{ +#define _(sym,str) IP_PUNT_POLICER_ERROR_##sym, + foreach_ip_punt_policer_error +#undef _ + IP4_PUNT_POLICER_N_ERROR, +} ip_punt_policer_error_t; + +extern u8 *format_ip_punt_policer_trace (u8 * s, va_list * args); + +/** + * IP punt policing node function + */ +always_inline uword +ip_punt_policer (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 arc_index, u32 policer_index) +{ + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + u64 time_in_policer_periods; + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; + + time_in_policer_periods = + clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t *b0, *b1; + u32 next0, next1; + u8 act0, act1; + u32 bi0, bi1; + + next0 = next1 = 0; + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + vnet_get_config_data (&cm->config_main, + &b0->current_config_index, &next0, 0); + vnet_get_config_data (&cm->config_main, + &b1->current_config_index, &next1, 0); + + act0 = vnet_policer_police (vm, b0, + policer_index, + time_in_policer_periods, + POLICE_CONFORM); + act1 = vnet_policer_police (vm, b1, + policer_index, + time_in_policer_periods, + POLICE_CONFORM); + + if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP)) + { + next0 = IP_PUNT_POLICER_NEXT_DROP; + b0->error = node->errors[IP_PUNT_POLICER_ERROR_DROP]; + } + if (PREDICT_FALSE (act1 == SSE2_QOS_ACTION_DROP)) + { + next1 = IP_PUNT_POLICER_NEXT_DROP; + b1->error = node->errors[IP_PUNT_POLICER_ERROR_DROP]; + } + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ip_punt_policer_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next = next0; + t->policer_index = policer_index; + } + if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) + { + ip_punt_policer_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->next = next1; + t->policer_index = policer_index; + } + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, + n_left_to_next, + bi0, bi1, next0, next1); + } + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 next0; + u32 bi0; + u8 act0; + + next0 = 0; + bi0 = to_next[0] = from[0]; + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + vnet_get_config_data (&cm->config_main, + &b0->current_config_index, &next0, 0); + + act0 = vnet_policer_police (vm, b0, + policer_index, + time_in_policer_periods, + POLICE_CONFORM); + if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP)) + { + next0 = IP_PUNT_POLICER_NEXT_DROP; + b0->error = node->errors[IP_PUNT_POLICER_ERROR_DROP]; + } + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ip_punt_policer_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next = next0; + t->policer_index = policer_index; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +/** + * IP4 punt redirect per-rx interface configuration + * redirect punted traffic to another location. + */ +typedef struct ip_punt_redirect_rx_t_ +{ + /** + * The next-hop to send redirected packets to + */ + ip46_address_t nh; + + /** + * the TX interface to send redirected packets + */ + u32 tx_sw_if_index; + + /** + * redirect forwarding adjacency + */ + adj_index_t adj_index; +} ip_punt_redirect_rx_t; + +/** + * IP punt redirect configuration + */ +typedef struct ip_punt_redirect_t_ +{ + /** + * any RX interface redirect + */ + ip_punt_redirect_rx_t any_rx_sw_if_index; + + /** + * per-RX interface configuration + */ + ip_punt_redirect_rx_t *redirect_by_rx_sw_if_index; +} ip_punt_redirect_t; + +/** + * IP punt redirect next nodes + */ +typedef enum ip_punt_redirect_next_t_ +{ + IP_PUNT_REDIRECT_NEXT_DROP, + IP_PUNT_REDIRECT_NEXT_TX, + IP_PUNT_REDIRECT_NEXT_ARP, + IP_PUNT_REDIRECT_N_NEXT, +} ip_punt_redirect_next_t; + +/** + * IP Punt redirect trace + */ +typedef struct ip4_punt_redirect_trace_t_ +{ + ip_punt_redirect_rx_t redirect; + u32 next; +} ip_punt_redirect_trace_t; + +/** + * Add a punt redirect entry + */ +extern void ip_punt_redirect_add (ip_punt_redirect_t * cfg, + u32 rx_sw_if_index, + ip_punt_redirect_rx_t * redirect, + fib_protocol_t fproto, vnet_link_t linkt); +extern void ip_punt_redirect_del (ip_punt_redirect_t * cfg, + u32 rx_sw_if_index); +extern u8 *format_ip_punt_redirect (u8 * s, va_list * args); + +extern u8 *format_ip_punt_redirect_trace (u8 * s, va_list * args); + +always_inline u32 +ip_punt_redirect_tx_via_adj (vlib_buffer_t * b0, adj_index_t ai) +{ + ip_adjacency_t *adj = adj_get (ai); + u32 next0; + + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai; + + switch (adj->lookup_next_index) + { + case IP_LOOKUP_NEXT_ARP: + next0 = IP_PUNT_REDIRECT_NEXT_ARP; + break; + case IP_LOOKUP_NEXT_REWRITE: + next0 = IP_PUNT_REDIRECT_NEXT_TX; + break; + default: + next0 = IP_PUNT_REDIRECT_NEXT_DROP; + break; + } + + return (next0); +} + +always_inline uword +ip_punt_redirect (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + u8 arc_index, ip_punt_redirect_t * redirect) +{ + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 rx_sw_if_index0; + ip_punt_redirect_rx_t *rrx0; + vlib_buffer_t *b0; + u32 next0; + u32 bi0; + + rrx0 = NULL; + next0 = 0; + bi0 = to_next[0] = from[0]; + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + vnet_get_config_data (&cm->config_main, + &b0->current_config_index, &next0, 0); + + rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + if (vec_len (redirect->redirect_by_rx_sw_if_index) > + rx_sw_if_index0) + { + rrx0 = &redirect->redirect_by_rx_sw_if_index[rx_sw_if_index0]; + if (~0 != rrx0->tx_sw_if_index) + { + next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index); + } + else if (~0 != redirect->any_rx_sw_if_index.tx_sw_if_index) + { + rrx0 = &redirect->any_rx_sw_if_index; + next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index); + } + } + else if (~0 != redirect->any_rx_sw_if_index.tx_sw_if_index) + { + rrx0 = &redirect->any_rx_sw_if_index; + next0 = ip_punt_redirect_tx_via_adj (b0, rrx0->adj_index); + } + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + ip_punt_redirect_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next = next0; + if (rrx0) + t->redirect = *rrx0; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +always_inline uword +ip_drop_or_punt (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 arc_index) +{ + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 8 && n_left_to_next >= 4) + { + vlib_buffer_t *b0, *b1, *b2, *b3; + u32 next0, next1, next2, next3; + u32 bi0, bi1, bi2, bi3; + + next0 = next1 = next2 = next3 = 0; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p4, *p5, *p6, *p7; + + p4 = vlib_get_buffer (vm, from[4]); + p5 = vlib_get_buffer (vm, from[5]); + p6 = vlib_get_buffer (vm, from[6]); + p7 = vlib_get_buffer (vm, from[7]); + + vlib_prefetch_buffer_header (p4, LOAD); + vlib_prefetch_buffer_header (p5, LOAD); + vlib_prefetch_buffer_header (p6, LOAD); + vlib_prefetch_buffer_header (p7, LOAD); + } + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + bi2 = to_next[2] = from[2]; + bi3 = to_next[3] = from[3]; + + from += 4; + n_left_from -= 4; + to_next += 4; + n_left_to_next -= 4; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + b2 = vlib_get_buffer (vm, bi2); + b3 = vlib_get_buffer (vm, bi3); + + /* punt and drop features are not associated with a given interface + * so the special index 0 is used */ + vnet_feature_arc_start (arc_index, 0, &next0, b0); + vnet_feature_arc_start (arc_index, 0, &next1, b1); + vnet_feature_arc_start (arc_index, 0, &next2, b2); + vnet_feature_arc_start (arc_index, 0, &next3, b3); + + vlib_validate_buffer_enqueue_x4 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, bi2, bi3, + next0, next1, next2, next3); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 next0; + u32 bi0; + + next0 = 0; + bi0 = to_next[0] = from[0]; + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + vnet_feature_arc_start (arc_index, 0, &next0, b0); + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/policer/node_funcs.c b/src/vnet/policer/node_funcs.c index fd031d02eea..25cb4201674 100644 --- a/src/vnet/policer/node_funcs.c +++ b/src/vnet/policer/node_funcs.c @@ -18,14 +18,11 @@ #include #include #include +#include #include #include #include -#define IP4_NON_DSCP_BITS 0x03 -#define IP4_DSCP_SHIFT 2 -#define IP6_NON_DSCP_BITS 0xf03fffff -#define IP6_DSCP_SHIFT 22 /* Dispatch functions meant to be instantiated elsewhere */ @@ -67,60 +64,6 @@ static char *vnet_policer_error_strings[] = { #undef _ }; -static_always_inline void -vnet_policer_mark (vlib_buffer_t * b, u8 dscp) -{ - ethernet_header_t *eh; - ip4_header_t *ip4h; - ip6_header_t *ip6h; - u16 type; - - eh = (ethernet_header_t *) b->data; - type = clib_net_to_host_u16 (eh->type); - - if (PREDICT_TRUE (type == ETHERNET_TYPE_IP4)) - { - ip4h = (ip4_header_t *) & (b->data[sizeof (ethernet_header_t)]);; - ip4h->tos &= IP4_NON_DSCP_BITS; - ip4h->tos |= dscp << IP4_DSCP_SHIFT; - ip4h->checksum = ip4_header_checksum (ip4h); - } - else - { - if (PREDICT_TRUE (type == ETHERNET_TYPE_IP6)) - { - ip6h = (ip6_header_t *) & (b->data[sizeof (ethernet_header_t)]); - ip6h->ip_version_traffic_class_and_flow_label &= - clib_host_to_net_u32 (IP6_NON_DSCP_BITS); - ip6h->ip_version_traffic_class_and_flow_label |= - clib_host_to_net_u32 (dscp << IP6_DSCP_SHIFT); - } - } -} - -static_always_inline - u8 vnet_policer_police (vlib_main_t * vm, - vlib_buffer_t * b, - u32 policer_index, - u64 time_in_policer_periods, - policer_result_e packet_color) -{ - u8 act; - u32 len; - u32 col; - policer_read_response_type_st *pol; - vnet_policer_main_t *pm = &vnet_policer_main; - - len = vlib_buffer_length_in_chain (vm, b); - pol = &pm->policers[policer_index]; - col = vnet_police_packet (pol, len, packet_color, time_in_policer_periods); - act = pol->action[col]; - if (PREDICT_TRUE (act == SSE2_QOS_ACTION_MARK_AND_TRANSMIT)) - vnet_policer_mark (b, pol->mark_dscp[col]); - - return act; -} - static inline uword vnet_policer_inline (vlib_main_t * vm, vlib_node_runtime_t * node, diff --git a/src/vnet/policer/police_inlines.h b/src/vnet/policer/police_inlines.h new file mode 100644 index 00000000000..64386e6f1bf --- /dev/null +++ b/src/vnet/policer/police_inlines.h @@ -0,0 +1,89 @@ +/* + * 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 __POLICE_INLINES_H__ +#define __POLICE_INLINES_H__ + +#include +#include +#include + +#define IP4_NON_DSCP_BITS 0x03 +#define IP4_DSCP_SHIFT 2 +#define IP6_NON_DSCP_BITS 0xf03fffff +#define IP6_DSCP_SHIFT 22 + +static_always_inline void +vnet_policer_mark (vlib_buffer_t * b, u8 dscp) +{ + ethernet_header_t *eh; + ip4_header_t *ip4h; + ip6_header_t *ip6h; + u16 type; + + eh = (ethernet_header_t *) b->data; + type = clib_net_to_host_u16 (eh->type); + + if (PREDICT_TRUE (type == ETHERNET_TYPE_IP4)) + { + ip4h = (ip4_header_t *) & (b->data[sizeof (ethernet_header_t)]);; + ip4h->tos &= IP4_NON_DSCP_BITS; + ip4h->tos |= dscp << IP4_DSCP_SHIFT; + ip4h->checksum = ip4_header_checksum (ip4h); + } + else + { + if (PREDICT_TRUE (type == ETHERNET_TYPE_IP6)) + { + ip6h = (ip6_header_t *) & (b->data[sizeof (ethernet_header_t)]); + ip6h->ip_version_traffic_class_and_flow_label &= + clib_host_to_net_u32 (IP6_NON_DSCP_BITS); + ip6h->ip_version_traffic_class_and_flow_label |= + clib_host_to_net_u32 (dscp << IP6_DSCP_SHIFT); + } + } +} + +static_always_inline u8 +vnet_policer_police (vlib_main_t * vm, + vlib_buffer_t * b, + u32 policer_index, + u64 time_in_policer_periods, + policer_result_e packet_color) +{ + u8 act; + u32 len; + u32 col; + policer_read_response_type_st *pol; + vnet_policer_main_t *pm = &vnet_policer_main; + + len = vlib_buffer_length_in_chain (vm, b); + pol = &pm->policers[policer_index]; + col = vnet_police_packet (pol, len, packet_color, time_in_policer_periods); + act = pol->action[col]; + if (PREDICT_TRUE (act == SSE2_QOS_ACTION_MARK_AND_TRANSMIT)) + vnet_policer_mark (b, pol->mark_dscp[col]); + + return act; +} + +#endif // __POLICE_INLINES_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/policer/policer_api.c b/src/vnet/policer/policer_api.c index 67fb9a4e2b9..3dc2cdd6a0a 100644 --- a/src/vnet/policer/policer_api.c +++ b/src/vnet/policer/policer_api.c @@ -63,10 +63,10 @@ vl_api_policer_add_del_t_handler (vl_api_policer_add_del_t * mp) cfg.rfc = mp->type; cfg.rnd_type = mp->round_type; cfg.rate_type = mp->rate_type; - cfg.rb.kbps.cir_kbps = mp->cir; - cfg.rb.kbps.eir_kbps = mp->eir; - cfg.rb.kbps.cb_bytes = mp->cb; - cfg.rb.kbps.eb_bytes = mp->eb; + cfg.rb.kbps.cir_kbps = ntohl (mp->cir); + cfg.rb.kbps.eir_kbps = ntohl (mp->eir); + cfg.rb.kbps.cb_bytes = clib_net_to_host_u64 (mp->cb); + cfg.rb.kbps.eb_bytes = clib_net_to_host_u64 (mp->eb); cfg.conform_action.action_type = mp->conform_action_type; cfg.conform_action.dscp = mp->conform_dscp; cfg.exceed_action.action_type = mp->exceed_action_type; diff --git a/test/test_ip4.py b/test/test_ip4.py index 55d16735a01..5bd50ce3ea4 100644 --- a/test/test_ip4.py +++ b/test/test_ip4.py @@ -11,7 +11,7 @@ from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \ from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q, ARP -from scapy.layers.inet import IP, UDP, ICMP, icmptypes, icmpcodes +from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes from util import ppp from scapy.contrib.mpls import MPLS @@ -1009,5 +1009,115 @@ class TestIPVlan0(VppTestCase): self.send_and_expect(self.pg0, pkts, self.pg1) +class TestIPPunt(VppTestCase): + """ IPv4 Punt Police/Redirect """ + + def setUp(self): + super(TestIPPunt, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def tearDown(self): + super(TestIPPunt, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip4() + i.admin_down() + + def send_and_expect(self, input, pkts, output): + self.vapi.cli("clear trace") + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = output.get_capture(len(pkts)) + return rx + + def send_and_assert_no_replies(self, intf, pkts, remark): + self.vapi.cli("clear trace") + intf.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + for i in self.pg_interfaces: + i.get_capture(0) + i.assert_nothing_captured(remark=remark) + + def test_ip_punt(self): + """ IP punt police and redirect """ + + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / + TCP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + pkts = p * 1025 + + # + # Configure a punt redirect via pg1. + # + nh_addr = socket.inet_pton(socket.AF_INET, + self.pg1.remote_ip4) + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr) + + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # add a policer + # + policer = self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0, + rate_type=1) + self.vapi.ip_punt_police(policer.policer_index) + + self.vapi.cli("clear trace") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # the number of packet recieved should be greater than 0, + # but not equal to the number sent, since some were policed + # + rx = self.pg1._get_capture(1) + self.assertTrue(len(rx) > 0) + self.assertTrue(len(rx) < len(pkts)) + + # + # remove the poilcer. back to full rx + # + self.vapi.ip_punt_police(policer.policer_index, is_add=0) + self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0, + rate_type=1, is_add=0) + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # remove the redirect. expect full drop. + # + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr, + is_add=0) + self.send_and_assert_no_replies(self.pg0, pkts, + "IP no punt config") + + # + # Add a redirect that is not input port selective + # + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr) + self.send_and_expect(self.pg0, pkts, self.pg1) + + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr, + is_add=0) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_ip6.py b/test/test_ip6.py index aad3713c4c0..dbe87465f7b 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -13,7 +13,7 @@ from vpp_neighbor import find_nbr, VppNeighbor from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q -from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \ +from scapy.layers.inet6 import IPv6, UDP, TCP, ICMPv6ND_NS, ICMPv6ND_RS, \ ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \ ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types @@ -1506,5 +1506,117 @@ class TestIP6LoadBalance(VppTestCase): self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3) +class TestIP6Punt(VppTestCase): + """ IPv6 Punt Police/Redirect """ + + def setUp(self): + super(TestIP6Punt, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIP6Punt, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def send_and_expect(self, input, pkts, output): + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = output.get_capture(len(pkts)) + return rx + + def send_and_assert_no_replies(self, intf, pkts, remark): + self.vapi.cli("clear trace") + intf.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + for i in self.pg_interfaces: + i.get_capture(0) + i.assert_nothing_captured(remark=remark) + + def test_ip_punt(self): + """ IP6 punt police and redirect """ + + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / + TCP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + pkts = p * 1025 + + # + # Configure a punt redirect via pg1. + # + nh_addr = inet_pton(AF_INET6, + self.pg1.remote_ip6) + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr, + is_ip6=1) + + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # add a policer + # + policer = self.vapi.policer_add_del("ip6-punt", 400, 0, 10, 0, + rate_type=1) + self.vapi.ip_punt_police(policer.policer_index, is_ip6=1) + + self.vapi.cli("clear trace") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # the number of packet recieved should be greater than 0, + # but not equal to the number sent, since some were policed + # + rx = self.pg1._get_capture(1) + self.assertTrue(len(rx) > 0) + self.assertTrue(len(rx) < len(pkts)) + + # + # remove the poilcer. back to full rx + # + self.vapi.ip_punt_police(policer.policer_index, is_add=0, is_ip6=1) + self.vapi.policer_add_del("ip6-punt", 400, 0, 10, 0, + rate_type=1, is_add=0) + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # remove the redirect. expect full drop. + # + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr, + is_add=0, + is_ip6=1) + self.send_and_assert_no_replies(self.pg0, pkts, + "IP no punt config") + + # + # Add a redirect that is not input port selective + # + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr, + is_ip6=1) + self.send_and_expect(self.pg0, pkts, self.pg1) + + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr, + is_add=0, + is_ip6=1) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index c3127f84022..3d6a3184bb8 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2422,3 +2422,60 @@ class VppPapiProvider(object): return self.api( self.papi.macip_acl_dump, {'acl_index': acl_index}) + + def policer_add_del(self, + name, + cir, + eir, + cb, + eb, + is_add=1, + rate_type=0, + round_type=0, + ptype=0, + color_aware=0, + conform_action_type=1, + conform_dscp=0, + exceed_action_type=0, + exceed_dscp=0, + violate_action_type=0, + violate_dscp=0): + return self.api(self.papi.policer_add_del, + {'name': name, + 'cir': cir, + 'eir': eir, + 'cb': cb, + 'eb': eb, + 'is_add': is_add, + 'rate_type': rate_type, + 'round_type': round_type, + 'type': ptype, + 'color_aware': color_aware, + 'conform_action_type': conform_action_type, + 'conform_dscp': conform_dscp, + 'exceed_action_type': exceed_action_type, + 'exceed_dscp': exceed_dscp, + 'violate_action_type': violate_action_type, + 'violate_dscp': violate_dscp}) + + def ip_punt_police(self, + policer_index, + is_ip6=0, + is_add=1): + return self.api(self.papi.ip_punt_police, + {'policer_index': policer_index, + 'is_add': is_add, + 'is_ip6': is_ip6}) + + def ip_punt_redirect(self, + rx_sw_if_index, + tx_sw_if_index, + nh, + is_ip6=0, + is_add=1): + return self.api(self.papi.ip_punt_redirect, + {'rx_sw_if_index': rx_sw_if_index, + 'tx_sw_if_index': tx_sw_if_index, + 'nh': nh, + 'is_add': is_add, + 'is_ip6': is_ip6})