From 2231150b52b58c4114f8520cde8b26df2761e064 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 28 Oct 2016 20:30:15 +0200 Subject: [PATCH] feature: add new feature handling code and device-input features Signed-off-by: Damjan Marion Change-Id: I010ecde93863dbdad84b993cd3680a5446db59b5 --- vlib/vlib/node.c | 1 + vlib/vlib/node.h | 3 + vnet/Makefile.am | 14 +- vnet/vnet/buffer.h | 7 + vnet/vnet/devices/af_packet/node.c | 8 +- vnet/vnet/devices/dpdk/node.c | 4 + vnet/vnet/devices/feature.c | 53 ++++ vnet/vnet/devices/netmap/node.c | 8 +- vnet/vnet/devices/virtio/vhost-user.c | 6 + vnet/vnet/ethernet/ethernet.h | 2 +- vnet/vnet/ethernet/interface.c | 27 -- vnet/vnet/feature/feature.c | 337 +++++++++++++++++++++ vnet/vnet/feature/feature.h | 208 +++++++++++++ .../registration.c} | 254 +--------------- vnet/vnet/handoff.c | 7 +- vnet/vnet/interface_cli.c | 17 +- vnet/vnet/ip/feature_registration.h | 63 ---- vnet/vnet/ip/ip4.h | 2 +- vnet/vnet/ip/ip6.h | 2 +- vnet/vnet/ip/lookup.h | 2 +- vnet/vnet/l2/l2_patch.c | 11 +- vnet/vnet/mpls/mpls_features.c | 58 ---- vnet/vnet/mpls/node.c | 1 + vnet/vnet/unix/tuntap.c | 3 + 24 files changed, 690 insertions(+), 408 deletions(-) create mode 100644 vnet/vnet/devices/feature.c create mode 100644 vnet/vnet/feature/feature.c create mode 100644 vnet/vnet/feature/feature.h rename vnet/vnet/{ip/feature_registration.c => feature/registration.c} (54%) delete mode 100644 vnet/vnet/ip/feature_registration.h diff --git a/vlib/vlib/node.c b/vlib/vlib/node.c index 23f7ea0239e..118fabe749d 100644 --- a/vlib/vlib/node.c +++ b/vlib/vlib/node.c @@ -424,6 +424,7 @@ register_node (vlib_main_t * vm, vlib_node_registration_t * r) rt->n_next_nodes = r->n_next_nodes; rt->next_frame_index = vec_len (nm->next_frames); + rt->feature_arc_index = ~0; vec_resize (nm->next_frames, rt->n_next_nodes); for (i = 0; i < rt->n_next_nodes; i++) diff --git a/vlib/vlib/node.h b/vlib/vlib/node.h index b624e9d636d..251ee606d20 100644 --- a/vlib/vlib/node.h +++ b/vlib/vlib/node.h @@ -465,6 +465,9 @@ typedef struct vlib_node_runtime_t /* CPU this node runs on */ u16 cpu_index; + /* Index of feature arc in which the node participates */ + u16 feature_arc_index; + /* Function dependent node-runtime. */ u8 runtime_data[0]; } diff --git a/vnet/Makefile.am b/vnet/Makefile.am index f35ec1372db..96306dd3ccd 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -266,7 +266,6 @@ nobase_include_HEADERS += \ # Layer 3 protocol: IP v4/v6 ######################################## libvnet_la_SOURCES += \ - vnet/ip/feature_registration.c \ vnet/ip/format.c \ vnet/ip/icmp4.c \ vnet/ip/icmp6.c \ @@ -298,7 +297,6 @@ libvnet_la_SOURCES += \ vnet/ip/udp_pg.c nobase_include_HEADERS += \ - vnet/ip/feature_registration.h \ vnet/ip/format.h \ vnet/ip/icmp46_packet.h \ vnet/ip/icmp4.h \ @@ -718,6 +716,18 @@ nobase_include_HEADERS += \ vnet/devices/netmap/netmap.h +######################################## +# Driver feature graph arc support +######################################## + +libvnet_la_SOURCES += \ + vnet/devices/feature.c \ + vnet/feature/feature.c \ + vnet/feature/registration.c + +nobase_include_HEADERS += \ + vnet/feature/feature.h + ######################################## # Unix kernel related ######################################## diff --git a/vnet/vnet/buffer.h b/vnet/vnet/buffer.h index 7aaa6eed9ba..6385f19142c 100644 --- a/vnet/vnet/buffer.h +++ b/vnet/vnet/buffer.h @@ -319,6 +319,13 @@ typedef struct u16 overlay_afi; } lisp; + /* Driver rx feature */ + struct + { + u32 saved_next_index; /**< saved by drivers for short-cut */ + u16 buffer_advance; + } device_input_feat; + u32 unused[6]; }; } vnet_buffer_opaque_t; diff --git a/vnet/vnet/devices/af_packet/node.c b/vnet/vnet/devices/af_packet/node.c index 3a3807d8b10..f086b8da1df 100644 --- a/vnet/vnet/devices/af_packet/node.c +++ b/vnet/vnet/devices/af_packet/node.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -164,7 +165,7 @@ af_packet_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, tph = (struct tpacket2_hdr *) (block_start + rx_frame * frame_size); while ((tph->tp_status & TP_STATUS_USER) && (n_free_bufs > min_bufs)) { - vlib_buffer_t *b0, *first_b0 = 0; + vlib_buffer_t *b0 = 0, *first_b0 = 0; u32 next0 = next_index; u32 n_left_to_next; @@ -236,6 +237,11 @@ af_packet_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, tr->hw_if_index = apif->hw_if_index; clib_memcpy (&tr->tph, tph, sizeof (struct tpacket2_hdr)); } + + /* redirect if feature path enabled */ + vnet_feature_device_input_redirect_x1 (node, apif->sw_if_index, + &next0, b0, 0); + /* enque and take next packet */ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, first_bi0, next0); diff --git a/vnet/vnet/devices/dpdk/node.c b/vnet/vnet/devices/dpdk/node.c index fa651d81787..01a6094e589 100644 --- a/vnet/vnet/devices/dpdk/node.c +++ b/vnet/vnet/devices/dpdk/node.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "dpdk_priv.h" @@ -530,6 +531,9 @@ dpdk_device_input (dpdk_main_t * dm, */ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + /* Do we have any driver RX features configured on the interface? */ + vnet_feature_device_input_redirect_x1 (node, xd->vlib_sw_if_index, &next0, b0, l3_offset0); + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); diff --git a/vnet/vnet/devices/feature.c b/vnet/vnet/devices/feature.c new file mode 100644 index 00000000000..8de78178b26 --- /dev/null +++ b/vnet/vnet/devices/feature.c @@ -0,0 +1,53 @@ +/* + * 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 + +/* *INDENT-OFF* */ +VNET_FEATURE_ARC_INIT (device_input, static) = { + .arc_name = "device-input", +#if DPDK > 0 + .start_nodes = VNET_FEATURES ("dpdk-input", "vhost-user-input", "af-packet-input", "netmap-input"), +#else + .start_nodes = VNET_FEATURES ("vhost-user-input", "af-packet-input", "netmap-input"), +#endif +}; + +VNET_FEATURE_INIT (l2_patch, static) = { + .arc_name = "device-input", + .node_name = "l2-patch", + .runs_before = VNET_FEATURES ("ethernet-input"), +}; + +VNET_FEATURE_INIT (worker_handoff, static) = { + .arc_name = "device-input", + .node_name = "worker-handoff", + .runs_before = VNET_FEATURES ("ethernet-input"), +}; + +VNET_FEATURE_INIT (ethernet_input, static) = { + .arc_name = "device-input", + .node_name = "ethernet-input", + .runs_before = 0, /* not before any other features */ +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vnet/vnet/devices/netmap/node.c b/vnet/vnet/devices/netmap/node.c index 559db669aae..d13fa1bc7b5 100644 --- a/vnet/vnet/devices/netmap/node.c +++ b/vnet/vnet/devices/netmap/node.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -158,7 +159,7 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, while (r && n_left_to_next) { - vlib_buffer_t *b0, *first_b0 = 0; + vlib_buffer_t *b0 = 0, *first_b0 = 0; u32 offset = 0; u32 bi0 = 0, first_bi0 = 0, prev_bi0; u32 next_slot_index = (cur_slot_index + 1) % ring->num_slots; @@ -235,6 +236,11 @@ netmap_device_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node, memcpy (&tr->slot, slot, sizeof (struct netmap_slot)); } } + + /* redirect if feature path enabled */ + vnet_feature_device_input_redirect_x1 (node, nif->sw_if_index, + &next0, b0, 0); + /* enque and take next packet */ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, first_bi0, diff --git a/vnet/vnet/devices/virtio/vhost-user.c b/vnet/vnet/devices/virtio/vhost-user.c index 5916ced2570..bdac7c24861 100644 --- a/vnet/vnet/devices/virtio/vhost-user.c +++ b/vnet/vnet/devices/virtio/vhost-user.c @@ -36,6 +36,7 @@ #include #include +#include #include @@ -1290,6 +1291,11 @@ vhost_user_if_input (vlib_main_t * vm, to_next[0] = bi_head; to_next++; n_left_to_next--; + + /* redirect if feature path enabled */ + vnet_feature_device_input_redirect_x1 (node, vui->sw_if_index, + &next0, b_head, 0); + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi_head, next0); diff --git a/vnet/vnet/ethernet/ethernet.h b/vnet/vnet/ethernet/ethernet.h index 11bbd923a93..c31025b7840 100644 --- a/vnet/vnet/ethernet/ethernet.h +++ b/vnet/vnet/ethernet/ethernet.h @@ -43,7 +43,7 @@ #include #include #include -#include +#include always_inline u64 ethernet_mac_address_u64 (u8 * a) diff --git a/vnet/vnet/ethernet/interface.c b/vnet/vnet/ethernet/interface.c index 45d215d324f..a0dea6f52a2 100644 --- a/vnet/vnet/ethernet/interface.c +++ b/vnet/vnet/ethernet/interface.c @@ -701,33 +701,6 @@ VLIB_CLI_COMMAND (delete_sub_interface_command, static) = { }; /* *INDENT-ON* */ -static clib_error_t * -show_ethernet_interface_features_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - ethernet_main_t *em = ðernet_main; - u32 sw_if_index; - - if (!unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index)) - return clib_error_return (0, "Interface not specified..."); - - vlib_cli_output (vm, "Ethernet feature paths configured on %U...", - format_vnet_sw_if_index_name, vnm, sw_if_index); - - ip_interface_features_show (vm, "Ethernet", - em->feature_config_mains, sw_if_index); - - return 0; -} - -VLIB_CLI_COMMAND (show_ethernet_interface_features_command, static) = -{ -.path = "show ethernet interface features",.short_help = - "show ethernet interface features ",.function = - show_ethernet_interface_features_command_fn,}; - /* * fd.io coding-style-patch-verification: ON * diff --git a/vnet/vnet/feature/feature.c b/vnet/vnet/feature/feature.c new file mode 100644 index 00000000000..4cfe1faa20f --- /dev/null +++ b/vnet/vnet/feature/feature.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +vnet_feature_main_t feature_main; + +static clib_error_t * +vnet_feature_init (vlib_main_t * vm) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_registration_t *freg; + vnet_feature_arc_registration_t *areg; + u32 arc_index = 0; + + fm->arc_index_by_name = hash_create_string (0, sizeof (uword)); + areg = fm->next_arc; + + /* process feature arc registrations */ + while (areg) + { + char *s; + int i = 0; + areg->feature_arc_index = arc_index; + hash_set_mem (fm->arc_index_by_name, areg->arc_name, + pointer_to_uword (areg)); + + /* process start nodes */ + while ((s = areg->start_nodes[i])) + { + vlib_node_t *n; + vlib_node_runtime_t *rt; + n = vlib_get_node_by_name (vm, (u8 *) s); + + if (n == 0) + return clib_error_return (0, + "Unknown start node '%s' on feature arc '%s'", + s, areg->arc_name); + + rt = vlib_node_get_runtime (vm, n->index); + rt->feature_arc_index = arc_index; + i++; + } + areg->n_start_nodes = i; + + /* next */ + areg = areg->next; + arc_index++; + } + + vec_validate (fm->next_feature_by_arc, arc_index - 1); + vec_validate (fm->feature_nodes, arc_index - 1); + vec_validate (fm->feature_config_mains, arc_index - 1); + vec_validate (fm->next_feature_by_name, arc_index - 1); + vec_validate (fm->sw_if_index_has_features, arc_index - 1); + vec_validate (fm->feature_count_by_sw_if_index, arc_index - 1); + + freg = fm->next_feature; + while (freg) + { + vlib_node_t *n; + vlib_node_runtime_t *rt; + uword *p = hash_get_mem (fm->arc_index_by_name, freg->arc_name); + if (p == 0) + return clib_error_return (0, "Unknown feature arc '%s'", + freg->arc_name); + + areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *); + arc_index = areg->feature_arc_index; + + /* set feature arc index in node runtime */ + n = vlib_get_node_by_name (vm, (u8 *) freg->node_name); + if (n == 0) + return clib_error_return (0, "Unknown node '%s', freg->node_name"); + rt = vlib_node_get_runtime (vm, n->index); + rt->feature_arc_index = arc_index; + + vec_add1 (fm->next_feature_by_arc[arc_index], *freg); + + /* next */ + freg = freg->next; + } + + while (areg) + { + clib_error_t *error; + vnet_feature_config_main_t *cm; + vnet_config_main_t *vcm; + + arc_index = areg->feature_arc_index; + cm = &fm->feature_config_mains[arc_index]; + vcm = &cm->config_main; + if ((error = vnet_feature_arc_init (vm, vcm, + areg->start_nodes, + areg->n_start_nodes, + fm->next_feature_by_arc[arc_index], + &fm->feature_nodes[arc_index]))) + { + return error; + } + + fm->next_feature_by_name[arc_index] = + hash_create_string (0, sizeof (uword)); + freg = fm->next_feature_by_arc[arc_index]; + + while (freg) + { + hash_set_mem (fm->next_feature_by_name[arc_index], + freg->node_name, pointer_to_uword (freg)); + freg = freg->next; + } + + /* next */ + areg = areg->next; + arc_index++; + } + + return 0; +} + +VLIB_INIT_FUNCTION (vnet_feature_init); + +void +vnet_config_update_feature_count (vnet_feature_main_t * fm, u16 arc, + u32 sw_if_index, int is_add) +{ + uword bit_value; + + vec_validate (fm->feature_count_by_sw_if_index[arc], sw_if_index); + + fm->feature_count_by_sw_if_index[arc][sw_if_index] += is_add ? 1 : -1; + + ASSERT (fm->feature_count_by_sw_if_index[arc][sw_if_index] >= 0); + + bit_value = fm->feature_count_by_sw_if_index[arc][sw_if_index] > 0; + + fm->sw_if_index_has_features[arc] = + clib_bitmap_set (fm->sw_if_index_has_features[arc], sw_if_index, + bit_value); +} + +u16 +vnet_feature_arc_index_from_node_name (const char *s) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_arc_registration_t *reg; + uword *p; + + p = hash_get_mem (fm->arc_index_by_name, s); + if (p == 0) + return ~0; + + reg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *); + return reg->feature_arc_index; +} + +u32 +vnet_feature_index_from_node_name (u16 arc, const char *s) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_registration_t *reg; + uword *p; + + p = hash_get_mem (fm->next_feature_by_name[arc], s); + if (p == 0) + return ~0; + + reg = uword_to_pointer (p[0], vnet_feature_registration_t *); + return reg->feature_index_u32; +} + +void +vnet_feature_enable_disable (const char *arc_name, const char *node_name, + u32 sw_if_index, int enable_disable, + void *feature_config, u32 n_feature_config_bytes) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm; + u32 feature_index, ci; + u16 arc_index; + + arc_index = vnet_feature_arc_index_from_node_name (arc_name); + + if (arc_index == ~0) + return; + + cm = &fm->feature_config_mains[arc_index]; + vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0); + feature_index = vnet_feature_index_from_node_name (arc_index, node_name); + if (feature_index == ~0) + return; + ci = cm->config_index_by_sw_if_index[sw_if_index]; + + ci = (enable_disable + ? vnet_config_add_feature + : vnet_config_del_feature) + (vlib_get_main (), &cm->config_main, ci, feature_index, feature_config, + n_feature_config_bytes); + cm->config_index_by_sw_if_index[sw_if_index] = ci; + + vnet_config_update_feature_count (fm, arc_index, sw_if_index, + enable_disable); + +} + + +/** Display the set of available driver features. + Useful for verifying that expected features are present +*/ + +static clib_error_t * +show_features_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_arc_registration_t *areg; + vnet_feature_registration_t *freg; + + vlib_cli_output (vm, "Available feature paths"); + + areg = fm->next_arc; + while (areg) + { + vlib_cli_output (vm, "%s:", areg->arc_name); + vec_foreach (freg, fm->next_feature_by_arc[areg->feature_arc_index]) + { + vlib_cli_output (vm, " %s\n", freg->node_name); + } + + + /* next */ + areg = areg->next; + } + + return 0; +} + +/*? + * Display the set of available driver features + * + * @cliexpar + * Example: + * @cliexcmd{show ip features} + * @cliexend + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_features_command, static) = { + .path = "show features", + .short_help = "show features", + .function = show_features_command_fn, +}; +/* *INDENT-ON* */ + +/** Display the set of driver features configured on a specific interface + * Called by "show interface" handler + */ + +void +vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index) +{ + vnet_feature_main_t *fm = &feature_main; + u32 node_index, current_config_index; + u16 feature_arc; + vnet_feature_config_main_t *cm = fm->feature_config_mains; + vnet_feature_arc_registration_t *areg; + vnet_config_main_t *vcm; + vnet_config_t *cfg; + u32 cfg_index; + vnet_config_feature_t *feat; + vlib_node_t *n; + int i; + + vlib_cli_output (vm, "Driver feature paths configured on %U...", + format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index); + + areg = fm->next_arc; + while (areg) + { + feature_arc = areg->feature_arc_index; + vcm = &(cm[feature_arc].config_main); + + vlib_cli_output (vm, "\n%s:", areg->arc_name); + areg = areg->next; + + if (NULL == cm[feature_arc].config_index_by_sw_if_index || + vec_len (cm[feature_arc].config_index_by_sw_if_index) < sw_if_index) + { + vlib_cli_output (vm, " none configured"); + continue; + } + + current_config_index = + vec_elt (cm[feature_arc].config_index_by_sw_if_index, sw_if_index); + + if (current_config_index == ~0) + { + vlib_cli_output (vm, " none configured"); + continue; + } + + ASSERT (current_config_index + < vec_len (vcm->config_pool_index_by_user_index)); + + cfg_index = vcm->config_pool_index_by_user_index[current_config_index]; + cfg = pool_elt_at_index (vcm->config_pool, cfg_index); + + for (i = 0; i < vec_len (cfg->features); i++) + { + feat = cfg->features + i; + node_index = feat->node_index; + n = vlib_get_node (vm, node_index); + vlib_cli_output (vm, " %v", n->name); + } + } +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vnet/vnet/feature/feature.h b/vnet/vnet/feature/feature.h new file mode 100644 index 00000000000..1706dec5456 --- /dev/null +++ b/vnet/vnet/feature/feature.h @@ -0,0 +1,208 @@ +/* + * 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. + */ + +#ifndef included_features_h +#define included_features_h + +#include + +/** feature registration object */ +typedef struct _vnet_feature_arc_registration +{ + /** next registration in list of all registrations*/ + struct _vnet_feature_arc_registration *next; + /** Feature Arc name */ + char *arc_name; + /** Start nodes */ + char **start_nodes; + int n_start_nodes; + /* Feature arc index, assigned by init function */ + u16 feature_arc_index; +} vnet_feature_arc_registration_t; + +/** feature registration object */ +typedef struct _vnet_feature_registration +{ + /** next registration in list of all registrations*/ + struct _vnet_feature_registration *next; + /** Feature arc name */ + char *arc_name; + /** Graph node name */ + char *node_name; + /** Pointer to this feature index, filled in by vnet_feature_arc_init */ + u32 *feature_index; + u32 feature_index_u32; + /** Constraints of the form "this feature runs before X" */ + char **runs_before; + /** Constraints of the form "this feature runs after Y" */ + char **runs_after; +} vnet_feature_registration_t; + +typedef struct vnet_feature_config_main_t_ +{ + vnet_config_main_t config_main; + u32 *config_index_by_sw_if_index; +} vnet_feature_config_main_t; + +typedef struct +{ + /** feature arc configuration list */ + vnet_feature_arc_registration_t *next_arc; + uword **arc_index_by_name; + + /** feature path configuration lists */ + vnet_feature_registration_t *next_feature; + vnet_feature_registration_t **next_feature_by_arc; + uword **next_feature_by_name; + + /** feature config main objects */ + vnet_feature_config_main_t *feature_config_mains; + + /** Save partial order results for show command */ + char ***feature_nodes; + + /** bitmap of interfaces which have driver rx features configured */ + uword **sw_if_index_has_features; + + /** feature reference counts by interface */ + i16 **feature_count_by_sw_if_index; + + /** convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; +} vnet_feature_main_t; + +extern vnet_feature_main_t feature_main; + +#define VNET_FEATURE_ARC_INIT(x,...) \ + __VA_ARGS__ vnet_feature_arc_registration_t vnet_feat_arc_##x;\ +static void __vnet_add_feature_arc_registration_##x (void) \ + __attribute__((__constructor__)) ; \ +static void __vnet_add_feature_arc_registration_##x (void) \ +{ \ + vnet_feature_main_t * fm = &feature_main; \ + vnet_feat_arc_##x.next = fm->next_arc; \ + fm->next_arc = & vnet_feat_arc_##x; \ +} \ +__VA_ARGS__ vnet_feature_arc_registration_t vnet_feat_arc_##x + +#define VNET_FEATURE_INIT(x,...) \ + __VA_ARGS__ vnet_feature_registration_t vnet_feat_##x; \ +static void __vnet_add_feature_registration_##x (void) \ + __attribute__((__constructor__)) ; \ +static void __vnet_add_feature_registration_##x (void) \ +{ \ + vnet_feature_main_t * fm = &feature_main; \ + vnet_feat_##x.next = fm->next_feature; \ + fm->next_feature = & vnet_feat_##x; \ +} \ +__VA_ARGS__ vnet_feature_registration_t vnet_feat_##x + +void +vnet_config_update_feature_count (vnet_feature_main_t * fm, u16 arc, + u32 sw_if_index, int is_add); + +u32 vnet_feature_index_from_node_name (u16 type, const char *s); + +void +vnet_feature_enable_disable (const char *arc_name, const char *node_name, + u32 sw_if_index, int enable_disable, + void *feature_config, + u32 n_feature_config_bytes); + + +static_always_inline int +vnet_have_features (u32 arc, u32 sw_if_index) +{ + vnet_feature_main_t *fm = &feature_main; + return clib_bitmap_get (fm->sw_if_index_has_features[arc], sw_if_index); +} + +static_always_inline u32 +vnet_feature_get_config_index (u16 arc, u32 sw_if_index) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc]; + return vec_elt (cm->config_index_by_sw_if_index, sw_if_index); +} + +static_always_inline void +vnet_feature_redirect (u16 arc, u32 sw_if_index, u32 * next0, + vlib_buffer_t * b0) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc]; + + if (PREDICT_FALSE (vnet_have_features (arc, sw_if_index))) + { + b0->current_config_index = + vec_elt (cm->config_index_by_sw_if_index, sw_if_index); + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + next0, /* # bytes of config data */ 0); + } +} + +static_always_inline void +vnet_feature_device_input_redirect_x1 (vlib_node_runtime_t * node, + u32 sw_if_index, u32 * next0, + vlib_buffer_t * b0, + u16 buffer_advanced0) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm; + + ASSERT (node->feature_arc_index != ~(u16) 0); + cm = &fm->feature_config_mains[node->feature_arc_index]; + + if (PREDICT_FALSE + (clib_bitmap_get + (fm->sw_if_index_has_features[node->feature_arc_index], sw_if_index))) + { + /* + * Save next0 so that the last feature in the chain + * can skip ethernet-input if indicated... + */ + vnet_buffer (b0)->device_input_feat.saved_next_index = *next0; + vnet_buffer (b0)->device_input_feat.buffer_advance = buffer_advanced0; + vlib_buffer_advance (b0, -buffer_advanced0); + + b0->current_config_index = + vec_elt (cm->config_index_by_sw_if_index, sw_if_index); + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + next0, /* # bytes of config data */ 0); + } +} + +#define ORDER_CONSTRAINTS (char*[]) +#define VNET_FEATURES(...) (char*[]) { __VA_ARGS__, 0} + +clib_error_t *vnet_feature_arc_init (vlib_main_t * vm, + vnet_config_main_t * vcm, + char **feature_start_nodes, + int num_feature_start_nodes, + vnet_feature_registration_t * + first_reg, char ***feature_nodes); + +void vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index); + +#endif /* included_feature_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vnet/vnet/ip/feature_registration.c b/vnet/vnet/feature/registration.c similarity index 54% rename from vnet/vnet/ip/feature_registration.c rename to vnet/vnet/feature/registration.c index fd94bf1932c..64a82673b15 100644 --- a/vnet/vnet/ip/feature_registration.c +++ b/vnet/vnet/feature/registration.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Cisco and/or its affiliates. + * 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: @@ -19,9 +19,9 @@ /** * @file - * @brief IP Feature Subgraph Ordering. + * @brief Feature Subgraph Ordering. - Dynamically compute IP feature subgraph ordering by performing a + Dynamically compute feature subgraph ordering by performing a topological sort across a set of "feature A before feature B" and "feature C after feature B" constraints. @@ -42,17 +42,17 @@ feature subgraph arc, which needs to run before @c ip4-lookup. In either base code or a plugin,
-    \#include 
+    \#include 
     
and add the new feature as shown:
-    VNET_IP4_UNICAST_FEATURE_INIT (ip4_lookup, static) =
+    VNET_FEATURE_INIT (ip4_lookup, static) =
     {
+      .arch_name = "ip4-unicast",
       .node_name = "my-ip4-unicast-feature",
-      .runs_before = ORDER_CONSTRAINTS {"ip4-lookup", 0}
-      .feature_index = &my_feature_index,
+      .runs_before = VLIB_FEATURES ("ip4-lookup", 0)
     };
     
@@ -60,42 +60,18 @@ @c my-ip4-unicast-feature on an interface:
-    ip4_main_t *im = \&ip4_main;
-    ip_lookup_main_t *lm = &im->lookup_main;
-    vnet_feature_config_main_t *rx_cm =
-        &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
 
     sw_if_index = 
-    ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
-    ci = (is_add
-          ? vnet_config_add_feature
-          : vnet_config_del_feature)
-      (vm, &rx_cm->config_main,
-       ci,
-       my_feature_index,
-       0 / * &config struct if feature uses private config data * /,
-       0 / * sizeof config struct if feature uses private config data * /);
-    rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
-    
- - For tx features, add this line after setting -
-    tx_cm->config_index_by_sw_if_index = ci.
-    
- - This maintains a - per-interface "at least one TX feature enabled" bitmap: - -
-    vnet_config_update_tx_feature_count (lm, tx_cm, sw_if_index, is_add);
+    vnet_feature_enable_disable ("ip4-unicast", "my-ip4-unicast-feature",
+                                 sw_if_index, 1 );
     
Here's how to obtain the correct next node index in packet processing code, aka in the implementation of @c my-ip4-unicast-feature:
-    ip_lookup_main_t * lm = sm->ip4_lookup_main;
-    vnet_feature_config_main_t * cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
+    vnet_feature_main_t *fm = &feature_main;
+    vnet_feature_config_main_t * cm = &fm->feature_config_mains[VNET_FEAT_IP4_UNICAST];
 
     Call @c vnet_get_config_data to set next0, and to advance
     @c b0->current_config_index:
@@ -111,8 +87,6 @@
     vnet_get_config_data.
 */
 
-static const char *vnet_cast_names[] = VNET_CAST_NAMES;
-
 static int
 comma_split (u8 * s, u8 ** a, u8 ** b)
 {
@@ -292,7 +266,9 @@ again:
       p = hash_get (reg_by_index, result[i]);
       ASSERT (p != 0);
       this_reg = (vnet_feature_registration_t *) p[0];
-      *this_reg->feature_index = n_features - (i + 1);
+      if (this_reg->feature_index)
+	*this_reg->feature_index = n_features - (i + 1);
+      this_reg->feature_index_u32 = n_features - (i + 1);
       vec_add1 (feature_nodes, this_reg->node_name);
     }
 
@@ -323,208 +299,6 @@ again:
   return 0;
 }
 
-#define foreach_af_cast                                 \
-_(4, VNET_IP_RX_UNICAST_FEAT, "ip4 unicast")            \
-_(4, VNET_IP_RX_MULTICAST_FEAT, "ip4 multicast")        \
-_(4, VNET_IP_TX_FEAT, "ip4 output")                     \
-_(6, VNET_IP_RX_UNICAST_FEAT, "ip6 unicast")            \
-_(6, VNET_IP_RX_MULTICAST_FEAT, "ip6 multicast")        \
-_(6, VNET_IP_TX_FEAT, "ip6 output")
-
-/** Display the set of available ip features.
-    Useful for verifying that expected features are present
-*/
-
-static clib_error_t *
-show_ip_features_command_fn (vlib_main_t * vm,
-			     unformat_input_t * input,
-			     vlib_cli_command_t * cmd)
-{
-  ip4_main_t *im4 = &ip4_main;
-  ip6_main_t *im6 = &ip6_main;
-  int i;
-  char **features;
-
-  vlib_cli_output (vm, "Available IP feature nodes");
-
-#define _(a,c,s)                                        \
-  do {                                                  \
-    features = im##a->feature_nodes[c];                 \
-    vlib_cli_output (vm, "%s:", s);                     \
-    for (i = 0; i < vec_len(features); i++)             \
-      vlib_cli_output (vm, "  %s\n", features[i]);      \
-  } while(0);
-  foreach_af_cast;
-#undef _
-
-  return 0;
-}
-
-/*?
- * This command is used to display the set of available IP features.
- * This can be useful for verifying that expected features are present.
- *
- * @cliexpar
- * Example of how to display the set of available IP features:
- * @cliexstart{show ip features}
- * Available IP feature nodes
- * ip4 unicast:
- *   ip4-inacl
- *   ip4-source-check-via-rx
- *   ip4-source-check-via-any
- *   ip4-source-and-port-range-check-rx
- *   ip4-policer-classify
- *   ipsec-input-ip4
- *   vpath-input-ip4
- *   snat-in2out
- *   snat-out2in
- *   ip4-lookup
- * ip4 multicast:
- *   vpath-input-ip4
- *   ip4-lookup-multicast
- * ip4 output:
- *   ip4-source-and-port-range-check-tx
- *   interface-output
- * ip6 unicast:
- *   ip6-inacl
- *   ip6-policer-classify
- *   ipsec-input-ip6
- *   l2tp-decap
- *   vpath-input-ip6
- *   sir-to-ila
- *   ip6-lookup
- * ip6 multicast:
- *   vpath-input-ip6
- *   ip6-lookup
- * ip6 output:
- *   interface-output
- * @cliexend
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (show_ip_features_command, static) = {
-  .path = "show ip features",
-  .short_help = "show ip features",
-  .function = show_ip_features_command_fn,
-};
-/* *INDENT-ON* */
-
-/** Display the set of IP features configured on a specific interface
- */
-
-void
-ip_interface_features_show (vlib_main_t * vm,
-			    const char *pname,
-			    vnet_feature_config_main_t * cm, u32 sw_if_index)
-{
-  u32 node_index, current_config_index;
-  vnet_cast_t cast;
-  vnet_config_main_t *vcm;
-  vnet_config_t *cfg;
-  u32 cfg_index;
-  vnet_config_feature_t *feat;
-  vlib_node_t *n;
-  int i;
-
-  vlib_cli_output (vm, "%s feature paths configured on %U...",
-		   pname, format_vnet_sw_if_index_name,
-		   vnet_get_main (), sw_if_index);
-
-  for (cast = VNET_IP_RX_UNICAST_FEAT; cast < VNET_N_IP_FEAT; cast++)
-    {
-      vcm = &(cm[cast].config_main);
-
-      vlib_cli_output (vm, "\n%s %s:", pname, vnet_cast_names[cast]);
-
-      if (NULL == cm[cast].config_index_by_sw_if_index ||
-	  vec_len (cm[cast].config_index_by_sw_if_index) < sw_if_index)
-	{
-	  vlib_cli_output (vm, "none configured");
-	  continue;
-	}
-
-      current_config_index = vec_elt (cm[cast].config_index_by_sw_if_index,
-				      sw_if_index);
-
-      ASSERT (current_config_index
-	      < vec_len (vcm->config_pool_index_by_user_index));
-
-      cfg_index = vcm->config_pool_index_by_user_index[current_config_index];
-      cfg = pool_elt_at_index (vcm->config_pool, cfg_index);
-
-      for (i = 0; i < vec_len (cfg->features); i++)
-	{
-	  feat = cfg->features + i;
-	  node_index = feat->node_index;
-	  n = vlib_get_node (vm, node_index);
-	  vlib_cli_output (vm, "  %v", n->name);
-	}
-    }
-}
-
-static clib_error_t *
-show_ip_interface_features_command_fn (vlib_main_t * vm,
-				       unformat_input_t * input,
-				       vlib_cli_command_t * cmd)
-{
-  vnet_main_t *vnm = vnet_get_main ();
-  ip4_main_t *im4 = &ip4_main;
-  ip_lookup_main_t *lm4 = &im4->lookup_main;
-  ip6_main_t *im6 = &ip6_main;
-  ip_lookup_main_t *lm6 = &im6->lookup_main;
-
-  ip_lookup_main_t *lm;
-  u32 sw_if_index, af;
-
-  if (!unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
-    return clib_error_return (0, "Interface not specified...");
-
-  vlib_cli_output (vm, "IP feature paths configured on %U...",
-		   format_vnet_sw_if_index_name, vnm, sw_if_index);
-
-  for (af = 0; af < 2; af++)
-    {
-      if (af == 0)
-	lm = lm4;
-      else
-	lm = lm6;
-
-      ip_interface_features_show (vm, (af == 0) ? "ip4" : "ip6",
-				  lm->feature_config_mains, sw_if_index);
-    }
-
-  return 0;
-}
-
-/*?
- * This command is used to display the set of IP features configured
- * on a specific interface
- *
- * @cliexpar
- * Example of how to display the set of available IP features on an interface:
- * @cliexstart{show ip interface features GigabitEthernet2/0/0}
- * IP feature paths configured on GigabitEthernet2/0/0...
- * ipv4 unicast:
- *   ip4-lookup
- * ipv4 multicast:
- *   ip4-lookup-multicast
- * ipv4 multicast:
- *   interface-output
- * ipv6 unicast:
- *   ip6-lookup
- * ipv6 multicast:
- *   ip6-lookup
- * ipv6 multicast:
- *   interface-output
- * @cliexend
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (show_ip_interface_features_command, static) = {
-  .path = "show ip interface features",
-  .short_help = "show ip interface features ",
-  .function = show_ip_interface_features_command_fn,
-};
-/* *INDENT-ON* */
-
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/vnet/vnet/handoff.c b/vnet/vnet/handoff.c
index 32c665d0c99..5593aa74942 100644
--- a/vnet/vnet/handoff.c
+++ b/vnet/vnet/handoff.c
@@ -18,6 +18,7 @@
 #include 
 #include 
 #include 
+#include 
 
 typedef struct
 {
@@ -234,8 +235,7 @@ interface_handoff_enable_disable (vlib_main_t * vm, u32 sw_if_index,
   vnet_sw_interface_t *sw;
   vnet_main_t *vnm = vnet_get_main ();
   per_inteface_handoff_data_t *d;
-  int i, rv;
-  u32 node_index = enable_disable ? worker_handoff_node.index : ~0;
+  int i, rv = 0;
 
   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
@@ -264,7 +264,8 @@ interface_handoff_enable_disable (vlib_main_t * vm, u32 sw_if_index,
       /* *INDENT-ON* */
     }
 
-  rv = vnet_hw_interface_rx_redirect_to_node (vnm, sw_if_index, node_index);
+  vnet_feature_enable_disable ("device-input", "worker-handoff",
+			       sw_if_index, enable_disable, 0, 0);
   return rv;
 }
 
diff --git a/vnet/vnet/interface_cli.c b/vnet/vnet/interface_cli.c
index 05e650e252c..cd7a620b836 100644
--- a/vnet/vnet/interface_cli.c
+++ b/vnet/vnet/interface_cli.c
@@ -225,12 +225,12 @@ show_sw_interfaces (vlib_main_t * vm,
   vnet_main_t *vnm = vnet_get_main ();
   vnet_interface_main_t *im = &vnm->interface_main;
   vnet_sw_interface_t *si, *sorted_sis = 0;
+  u32 sw_if_index = ~(u32) 0;
   u8 show_addresses = 0;
+  u8 show_features = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
-      u32 sw_if_index;
-
       /* See if user wants to show specific interface */
       if (unformat
 	  (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
@@ -240,6 +240,8 @@ show_sw_interfaces (vlib_main_t * vm,
 	}
       else if (unformat (input, "address") || unformat (input, "addr"))
 	show_addresses = 1;
+      else if (unformat (input, "features") || unformat (input, "feat"))
+	show_features = 1;
       else
 	{
 	  error = clib_error_return (0, "unknown input `%U'",
@@ -248,6 +250,15 @@ show_sw_interfaces (vlib_main_t * vm,
 	}
     }
 
+  if (show_features)
+    {
+      if (sw_if_index == ~(u32) 0)
+	return clib_error_return (0, "Interface not specified...");
+
+      vnet_interface_features_show (vm, sw_if_index);
+      return 0;
+    }
+
   if (!show_addresses)
     vlib_cli_output (vm, "%U\n", format_vnet_sw_interface, vnm, 0);
 
@@ -390,7 +401,7 @@ done:
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_sw_interfaces_command, static) = {
   .path = "show interfaces",
-  .short_help = "show interfaces [address|addr] [  ...]",
+  .short_help = "show interfaces [address|addr|features|feat] [  ...]",
   .function = show_sw_interfaces,
 };
 /* *INDENT-ON* */
diff --git a/vnet/vnet/ip/feature_registration.h b/vnet/vnet/ip/feature_registration.h
deleted file mode 100644
index fcdd96b61bc..00000000000
--- a/vnet/vnet/ip/feature_registration.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef included_feature_registration_h
-#define included_feature_registration_h
-
-/** feature registration object */
-typedef struct _vnet_feature_registration
-{
-  /** next registration in list of all registrations*/
-  struct _vnet_feature_registration *next;
-  /** Graph node name */
-  char *node_name;
-  /** Pointer to this feature index, filled in by vnet_feature_arc_init */
-  u32 *feature_index;
-  /** Constraints of the form "this feature runs before X" */
-  char **runs_before;
-  /** Constraints of the form "this feature runs after Y" */
-  char **runs_after;
-} vnet_feature_registration_t;
-
-typedef struct vnet_feature_config_main_t_
-{
-  vnet_config_main_t config_main;
-  u32 *config_index_by_sw_if_index;
-} vnet_feature_config_main_t;
-
-/** Syntactic sugar, the c-compiler won't initialize registrations without it */
-#define ORDER_CONSTRAINTS (char*[])
-
-clib_error_t *vnet_feature_arc_init (vlib_main_t * vm,
-				     vnet_config_main_t * vcm,
-				     char **feature_start_nodes,
-				     int num_feature_start_nodes,
-				     vnet_feature_registration_t *
-				     first_reg, char ***feature_nodes);
-
-void ip_interface_features_show (vlib_main_t * vm,
-				 const char *pname,
-				 vnet_feature_config_main_t * cm,
-				 u32 sw_if_index);
-
-#endif /* included_feature_registration_h */
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/vnet/vnet/ip/ip4.h b/vnet/vnet/ip/ip4.h
index f00b6ca7968..f0806c08449 100644
--- a/vnet/vnet/ip/ip4.h
+++ b/vnet/vnet/ip/ip4.h
@@ -43,7 +43,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 
 typedef struct ip4_fib_t {
   /* Hash table for each prefix length mapping. */
diff --git a/vnet/vnet/ip/ip6.h b/vnet/vnet/ip/ip6.h
index 78546120a23..f6008d71ea1 100644
--- a/vnet/vnet/ip/ip6.h
+++ b/vnet/vnet/ip/ip6.h
@@ -46,7 +46,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
 #include 
 #include 
diff --git a/vnet/vnet/ip/lookup.h b/vnet/vnet/ip/lookup.h
index a1fbe6eb289..c1d8d204ef9 100644
--- a/vnet/vnet/ip/lookup.h
+++ b/vnet/vnet/ip/lookup.h
@@ -54,7 +54,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 
 /** @brief Common (IP4/IP6) next index stored in adjacency. */
 typedef enum {
diff --git a/vnet/vnet/l2/l2_patch.c b/vnet/vnet/l2/l2_patch.c
index ad05fa0d034..5e4691f45c7 100644
--- a/vnet/vnet/l2/l2_patch.c
+++ b/vnet/vnet/l2/l2_patch.c
@@ -16,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 typedef struct
@@ -283,18 +284,16 @@ VLIB_NODE_FUNCTION_MULTIARCH (l2_patch_node, l2_patch_node_fn)
       ethernet_set_flags (l2pm->vnet_main, rxhi->hw_if_index,
 			  ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
 
-      vnet_hw_interface_rx_redirect_to_node (l2pm->vnet_main,
-					     rxhi->hw_if_index,
-					     l2_patch_node.index);
+      vnet_feature_enable_disable ("device-input", "l2-patch",
+				   rxhi->hw_if_index, 1, 0, 0);
     }
   else
     {
       ethernet_set_flags (l2pm->vnet_main, rxhi->hw_if_index,
 			  0 /* disable promiscuous mode */ );
 
-      vnet_hw_interface_rx_redirect_to_node (l2pm->vnet_main,
-					     rxhi->hw_if_index,
-					     ~0 /* disable */ );
+      vnet_feature_enable_disable ("device-input", "l2-patch",
+				   rxhi->hw_if_index, 0, 0, 0);
       if (vec_len (l2pm->tx_next_by_rx_sw_if_index) > rx_sw_if_index)
 	{
 	  l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = ~0;
diff --git a/vnet/vnet/mpls/mpls_features.c b/vnet/vnet/mpls/mpls_features.c
index 10a58cfb09c..2f98867cf90 100644
--- a/vnet/vnet/mpls/mpls_features.c
+++ b/vnet/vnet/mpls/mpls_features.c
@@ -213,61 +213,3 @@ VNET_SW_INTERFACE_ADD_DEL_FUNCTION (mpls_sw_interface_add_del);
 _(VNET_IP_RX_UNICAST_FEAT, "mpls input")        \
 _(VNET_IP_TX_FEAT, "mpls output")               \
 
-static clib_error_t *
-show_mpls_features_command_fn (vlib_main_t * vm,
-                               unformat_input_t * input,
-                               vlib_cli_command_t * cmd)
-{
-  mpls_main_t * mm = &mpls_main;
-  int i;
-  char ** features;
-
-  vlib_cli_output (vm, "Available MPLS feature nodes");
-
-#define _(c,s)                                          \
-  do {                                                  \
-    features = mm->feature_nodes[c];                    \
-    vlib_cli_output (vm, "%s:", s);                     \
-    for (i = 0; i < vec_len(features); i++)             \
-      vlib_cli_output (vm, "  %s\n", features[i]);      \
-  } while(0);
-  foreach_af_cast;
-#undef _
-
-  return 0;
-}
-
-VLIB_CLI_COMMAND (show_ip_features_command, static) = {
-  .path = "show mpls features",
-  .short_help = "show mpls features",
-  .function = show_mpls_features_command_fn,
-};
-
-static clib_error_t *
-show_mpls_interface_features_command_fn (vlib_main_t * vm,
-                                         unformat_input_t * input,
-                                         vlib_cli_command_t * cmd)
-{
-  vnet_main_t * vnm = vnet_get_main();
-  u32 sw_if_index;
-
-  if (! unformat (input, "%U", unformat_vnet_sw_interface,
-                  vnm, &sw_if_index))
-    return clib_error_return (0, "Interface not specified...");
-
-  vlib_cli_output (vm, "MPLS feature paths configured on %U...",
-                   format_vnet_sw_if_index_name, vnm, sw_if_index);
-
-  ip_interface_features_show (vm, "MPLS",
-			      mpls_main.feature_config_mains,
-			      sw_if_index);
-
-  return 0;
-}
-
-VLIB_CLI_COMMAND (show_mpls_interface_features_command, static) = {
-  .path = "show mpls interface features",
-  .short_help = "show mpls interface features ",
-  .function = show_mpls_interface_features_command_fn,
-};
-
diff --git a/vnet/vnet/mpls/node.c b/vnet/vnet/mpls/node.c
index 7a4a302315a..c98b98e733b 100644
--- a/vnet/vnet/mpls/node.c
+++ b/vnet/vnet/mpls/node.c
@@ -18,6 +18,7 @@
 #include 
 #include 
 #include 
+#include 
 
 typedef struct {
   u32 next_index;
diff --git a/vnet/vnet/unix/tuntap.c b/vnet/vnet/unix/tuntap.c
index 89fd1dcfe81..e4d05446e36 100644
--- a/vnet/vnet/unix/tuntap.c
+++ b/vnet/vnet/unix/tuntap.c
@@ -47,6 +47,7 @@
 #include 
 
 #include 
+#include 
 
 #if DPDK == 1
 #include 
@@ -395,6 +396,8 @@ tuntap_rx (vlib_main_t * vm,
           next_index = TUNTAP_RX_NEXT_DROP;
       }
 
+    vnet_feature_device_input_redirect_x1 (node, tm->hw_if_index, &next_index, b, 0);
+
     vlib_set_next_frame_buffer (vm, node, next_index, bi);
 
     if (n_trace > 0)
-- 
2.16.6