diff options
Diffstat (limited to 'src/vnet/feature')
-rw-r--r-- | src/vnet/feature/feature.c | 463 | ||||
-rw-r--r-- | src/vnet/feature/feature.h | 382 | ||||
-rw-r--r-- | src/vnet/feature/registration.c | 301 |
3 files changed, 1146 insertions, 0 deletions
diff --git a/src/vnet/feature/feature.c b/src/vnet/feature/feature.c new file mode 100644 index 00000000000..032fe784ace --- /dev/null +++ b/src/vnet/feature/feature.c @@ -0,0 +1,463 @@ +/* + * 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/feature.h> + +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; + if (areg->arc_index_ptr) + *areg->arc_index_ptr = 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])) + { + 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) + { + vnet_feature_registration_t *next; + 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; + + next = freg->next; + freg->next = fm->next_feature_by_arc[arc_index]; + fm->next_feature_by_arc[arc_index] = freg; + + /* next */ + freg = next; + } + + areg = fm->next_arc; + 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; + } + + cm->end_feature_index = + vnet_get_feature_index (arc_index, areg->end_node); + + /* next */ + areg = areg->next; + arc_index++; + } + + return 0; +} + +VLIB_INIT_FUNCTION (vnet_feature_init); + +u8 +vnet_get_feature_arc_index (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; +} + +vnet_feature_registration_t * +vnet_get_feature_reg (const char *arc_name, const char *node_name) +{ + u8 arc_index; + + arc_index = vnet_get_feature_arc_index (arc_name); + if (arc_index == (u8) ~ 0) + return 0; + + vnet_feature_main_t *fm = &feature_main; + vnet_feature_registration_t *reg; + uword *p; + + p = hash_get_mem (fm->next_feature_by_name[arc_index], node_name); + if (p == 0) + return 0; + + reg = uword_to_pointer (p[0], vnet_feature_registration_t *); + return reg; +} + +u32 +vnet_get_feature_index (u8 arc, const char *s) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_registration_t *reg; + uword *p; + + if (s == 0) + return ~0; + + 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; +} + +int +vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index, + 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; + i16 feature_count; + int is_first_or_last; + u32 ci; + + if (arc_index == (u8) ~ 0) + return VNET_API_ERROR_INVALID_VALUE; + + if (feature_index == ~0) + return VNET_API_ERROR_INVALID_VALUE_2; + + cm = &fm->feature_config_mains[arc_index]; + vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0); + ci = cm->config_index_by_sw_if_index[sw_if_index]; + + vec_validate (fm->feature_count_by_sw_if_index[arc_index], sw_if_index); + feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index]; + + if (!enable_disable && feature_count < 1) + return 0; + + 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; + + /* update feature count */ + enable_disable = (enable_disable > 0); + feature_count += enable_disable ? 1 : -1; + is_first_or_last = (feature_count == enable_disable); + ASSERT (feature_count >= 0); + + if (is_first_or_last && cm->end_feature_index != ~0) + { + /*register end node */ + ci = (enable_disable + ? vnet_config_add_feature + : vnet_config_del_feature) + (vlib_get_main (), &cm->config_main, ci, cm->end_feature_index, 0, 0); + cm->config_index_by_sw_if_index[sw_if_index] = ci; + } + + fm->sw_if_index_has_features[arc_index] = + clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index, + (feature_count > 0)); + + fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count; + return 0; +} + +int +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) +{ + u32 feature_index; + u8 arc_index; + + arc_index = vnet_get_feature_arc_index (arc_name); + + if (arc_index == (u8) ~ 0) + return VNET_API_ERROR_INVALID_VALUE; + + feature_index = vnet_get_feature_index (arc_index, node_name); + + return vnet_feature_enable_disable_with_index (arc_index, feature_index, + sw_if_index, enable_disable, + feature_config, + n_feature_config_bytes); +} + + +/** 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); + freg = fm->next_feature_by_arc[areg->feature_arc_index]; + while (freg) + { + vlib_cli_output (vm, " %s\n", freg->node_name); + freg = freg->next; + } + + + /* 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); + } + } +} + +static clib_error_t * +set_interface_features_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + u8 *arc_name = 0; + u8 *feature_name = 0; + u32 sw_if_index = ~0; + u8 enable = 1; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + goto done; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (line_input, "%U %v", unformat_vnet_sw_interface, vnm, &sw_if_index, + &feature_name)) + ; + else if (unformat (line_input, "arc %v", &arc_name)) + ; + else if (unformat (line_input, "disable")) + enable = 0; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (sw_if_index == ~0) + { + error = clib_error_return (0, "Interface not specified..."); + goto done; + } + + vec_add1 (arc_name, 0); + vec_add1 (feature_name, 0); + + vnet_feature_registration_t *reg; + reg = + vnet_get_feature_reg ((const char *) arc_name, + (const char *) feature_name); + if (reg == 0) + { + error = clib_error_return (0, "Unknown feature..."); + goto done; + } + if (reg->enable_disable_cb) + error = reg->enable_disable_cb (sw_if_index, enable); + if (!error) + vnet_feature_enable_disable ((const char *) arc_name, + (const char *) feature_name, sw_if_index, + enable, 0, 0); + +done: + vec_free (feature_name); + vec_free (arc_name); + return error; +} + +/*? + * Set feature for given interface + * + * @cliexpar + * Example: + * @cliexcmd{set interface feature GigabitEthernet2/0/0 ip4_flow_classify arc ip4_unicast} + * @cliexend + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_feature_command, static) = { + .path = "set interface feature", + .short_help = "set interface feature <intfc> <feature_name> arc <arc_name>", + .function = set_interface_features_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/feature/feature.h b/src/vnet/feature/feature.h new file mode 100644 index 00000000000..b27aaf17804 --- /dev/null +++ b/src/vnet/feature/feature.h @@ -0,0 +1,382 @@ +/* + * 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 <vnet/vnet.h> +#include <vnet/api_errno.h> + +/** 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; + /** End node */ + char *end_node; + /* Feature arc index, assigned by init function */ + u8 feature_arc_index; + u8 *arc_index_ptr; +} vnet_feature_arc_registration_t; + +/* Enable feature callback. */ +typedef clib_error_t *(vnet_feature_enable_disable_function_t) + (u32 sw_if_index, int enable_disable); + +/** 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_ptr; + 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; + + /** Function to enable/disable feature **/ + vnet_feature_enable_disable_function_t *enable_disable_cb; +} vnet_feature_registration_t; + +typedef struct vnet_feature_config_main_t_ +{ + vnet_config_main_t config_main; + u32 *config_index_by_sw_if_index; + u32 end_feature_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; + + /** Feature arc index for device-input */ + u8 device_input_feature_arc_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, u8 arc, + u32 sw_if_index, int is_add); + +u32 vnet_get_feature_index (u8 arc, const char *s); +u8 vnet_get_feature_arc_index (const char *s); +vnet_feature_registration_t *vnet_get_feature_reg (const char *arc_name, + const char *node_name); + + +int +vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index, + u32 sw_if_index, int enable_disable, + void *feature_config, + u32 n_feature_config_bytes); + +int +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 inline vnet_feature_config_main_t * +vnet_get_feature_arc_config_main (u8 arc_index) +{ + vnet_feature_main_t *fm = &feature_main; + + if (arc_index == (u8) ~ 0) + return 0; + + return &fm->feature_config_mains[arc_index]; +} + +static_always_inline vnet_feature_config_main_t * +vnet_feature_get_config_main (u16 arc) +{ + vnet_feature_main_t *fm = &feature_main; + return &fm->feature_config_mains[arc]; +} + +static_always_inline int +vnet_have_features (u8 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_get_feature_config_index (u8 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_arc_start_with_data (u8 arc, u32 sw_if_index, u32 * next, + vlib_buffer_t * b, u32 n_data_bytes) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm; + cm = &fm->feature_config_mains[arc]; + + if (PREDICT_FALSE (vnet_have_features (arc, sw_if_index))) + { + b->feature_arc_index = arc; + b->current_config_index = + vec_elt (cm->config_index_by_sw_if_index, sw_if_index); + return vnet_get_config_data (&cm->config_main, &b->current_config_index, + next, n_data_bytes); + } + return 0; +} + +static_always_inline void +vnet_feature_arc_start (u8 arc, u32 sw_if_index, u32 * next0, + vlib_buffer_t * b0) +{ + vnet_feature_arc_start_with_data (arc, sw_if_index, next0, b0, 0); +} + +static_always_inline void * +vnet_feature_next_with_data (u32 sw_if_index, u32 * next0, + vlib_buffer_t * b0, u32 n_data_bytes) +{ + vnet_feature_main_t *fm = &feature_main; + u8 arc = b0->feature_arc_index; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc]; + + return vnet_get_config_data (&cm->config_main, + &b0->current_config_index, next0, + n_data_bytes); +} + +static_always_inline void +vnet_feature_next (u32 sw_if_index, u32 * next0, vlib_buffer_t * b0) +{ + vnet_feature_next_with_data (sw_if_index, next0, b0, 0); +} + +static_always_inline void +vnet_feature_start_device_input_x1 (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; + u8 feature_arc_index = fm->device_input_feature_arc_index; + cm = &fm->feature_config_mains[feature_arc_index]; + + if (PREDICT_FALSE + (clib_bitmap_get + (fm->sw_if_index_has_features[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->feature_arc_index = feature_arc_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_start_device_input_x2 (u32 sw_if_index, + u32 * next0, + u32 * next1, + vlib_buffer_t * b0, + vlib_buffer_t * b1, + u16 buffer_advanced0, + u16 buffer_advanced1) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm; + u8 feature_arc_index = fm->device_input_feature_arc_index; + cm = &fm->feature_config_mains[feature_arc_index]; + + if (PREDICT_FALSE + (clib_bitmap_get + (fm->sw_if_index_has_features[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 (b1)->device_input_feat.saved_next_index = *next1; + vnet_buffer (b0)->device_input_feat.buffer_advance = buffer_advanced0; + vnet_buffer (b1)->device_input_feat.buffer_advance = buffer_advanced1; + vlib_buffer_advance (b0, -buffer_advanced0); + vlib_buffer_advance (b1, -buffer_advanced1); + + b0->feature_arc_index = feature_arc_index; + b1->feature_arc_index = feature_arc_index; + b0->current_config_index = + vec_elt (cm->config_index_by_sw_if_index, sw_if_index); + b1->current_config_index = b0->current_config_index; + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + next0, /* # bytes of config data */ 0); + vnet_get_config_data (&cm->config_main, &b1->current_config_index, + next1, /* # bytes of config data */ 0); + } +} + +static_always_inline void +vnet_feature_start_device_input_x4 (u32 sw_if_index, + u32 * next0, + u32 * next1, + u32 * next2, + u32 * next3, + vlib_buffer_t * b0, + vlib_buffer_t * b1, + vlib_buffer_t * b2, + vlib_buffer_t * b3, + u16 buffer_advanced0, + u16 buffer_advanced1, + u16 buffer_advanced2, + u16 buffer_advanced3) +{ + vnet_feature_main_t *fm = &feature_main; + vnet_feature_config_main_t *cm; + u8 feature_arc_index = fm->device_input_feature_arc_index; + cm = &fm->feature_config_mains[feature_arc_index]; + + if (PREDICT_FALSE + (clib_bitmap_get + (fm->sw_if_index_has_features[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 (b1)->device_input_feat.saved_next_index = *next1; + vnet_buffer (b2)->device_input_feat.saved_next_index = *next2; + vnet_buffer (b3)->device_input_feat.saved_next_index = *next3; + + vnet_buffer (b0)->device_input_feat.buffer_advance = buffer_advanced0; + vnet_buffer (b1)->device_input_feat.buffer_advance = buffer_advanced1; + vnet_buffer (b2)->device_input_feat.buffer_advance = buffer_advanced2; + vnet_buffer (b3)->device_input_feat.buffer_advance = buffer_advanced3; + + vlib_buffer_advance (b0, -buffer_advanced0); + vlib_buffer_advance (b1, -buffer_advanced1); + vlib_buffer_advance (b2, -buffer_advanced2); + vlib_buffer_advance (b3, -buffer_advanced3); + + b0->feature_arc_index = feature_arc_index; + b1->feature_arc_index = feature_arc_index; + b2->feature_arc_index = feature_arc_index; + b3->feature_arc_index = feature_arc_index; + + b0->current_config_index = + vec_elt (cm->config_index_by_sw_if_index, sw_if_index); + b1->current_config_index = b0->current_config_index; + b2->current_config_index = b0->current_config_index; + b3->current_config_index = b0->current_config_index; + + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + next0, /* # bytes of config data */ 0); + vnet_get_config_data (&cm->config_main, &b1->current_config_index, + next1, /* # bytes of config data */ 0); + vnet_get_config_data (&cm->config_main, &b2->current_config_index, + next2, /* # bytes of config data */ 0); + vnet_get_config_data (&cm->config_main, &b3->current_config_index, + next3, /* # bytes of config data */ 0); + } +} + +#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/src/vnet/feature/registration.c b/src/vnet/feature/registration.c new file mode 100644 index 00000000000..1deeeef904c --- /dev/null +++ b/src/vnet/feature/registration.c @@ -0,0 +1,301 @@ +/* + * 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/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/mpls/mpls.h> + +/** + * @file + * @brief Feature Subgraph Ordering. + + 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. + + Use the topological sort result to set up vnet_config_main_t's for + use at runtime. + + Feature subgraph arcs are simple enough. They start at specific + fixed nodes, and end at specific fixed nodes. In between, a + per-interface current feature configuration dictates which + additional nodes each packet visits. Each so-called feature node + can [of course] drop any specific packet. + + See ip4_forward.c, ip6_forward.c in this directory to see the + current rx-unicast, rx-multicast, and tx feature subgraph arc + definitions. + + Let's say that we wish to add a new feature to the ip4 unicast + feature subgraph arc, which needs to run before @c ip4-lookup. In + either base code or a plugin, + <CODE><PRE> + \#include <vnet/feature/feature.h> + </PRE></CODE> + + and add the new feature as shown: + + <CODE><PRE> + VNET_FEATURE_INIT (ip4_lookup, static) = + { + .arch_name = "ip4-unicast", + .node_name = "my-ip4-unicast-feature", + .runs_before = VLIB_FEATURES ("ip4-lookup") + }; + </PRE></CODE> + + Here's the standard coding pattern to enable / disable + @c my-ip4-unicast-feature on an interface: + + <CODE><PRE> + + sw_if_index = <interface-handle> + vnet_feature_enable_disable ("ip4-unicast", "my-ip4-unicast-feature", + sw_if_index, 1 ); + </PRE></CODE> + + Here's how to obtain the correct next node index in packet + processing code, aka in the implementation of @c my-ip4-unicast-feature: + + <CODE><PRE> + vnet_feature_next (sw_if_index0, &next0, b0); + + </PRE></CODE> + + Nodes are free to drop or otherwise redirect packets. Packets + which "pass" should be enqueued via the next0 arc computed by + vnet_feature_next. +*/ + + +static int +comma_split (u8 * s, u8 ** a, u8 ** b) +{ + *a = s; + + while (*s && *s != ',') + s++; + + if (*s == ',') + *s = 0; + else + return 1; + + *b = (u8 *) (s + 1); + return 0; +} + +/** + * @brief Initialize a feature graph arc + * @param vm vlib main structure pointer + * @param vcm vnet config main structure pointer + * @param feature_start_nodes names of start-nodes which use this + * feature graph arc + * @param num_feature_start_nodes number of start-nodes + * @param first_reg first element in + * [an __attribute__((constructor)) function built, or + * otherwise created] singly-linked list of feature registrations + * @param [out] in_feature_nodes returned vector of + * topologically-sorted feature node names, for use in + * show commands + * @returns 0 on success, otherwise an error message. Errors + * are fatal since they invariably involve mistyped node-names, or + * genuinely missing node-names + */ +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 ***in_feature_nodes) +{ + uword *index_by_name; + uword *reg_by_index; + u8 **node_names = 0; + u8 *node_name; + char **these_constraints; + char *this_constraint_c; + u8 **constraints = 0; + u8 *constraint_tuple; + u8 *this_constraint; + u8 **orig, **closure; + uword *p; + int i, j, k; + u8 *a_name, *b_name; + int a_index, b_index; + int n_features; + u32 *result = 0; + vnet_feature_registration_t *this_reg = 0; + char **feature_nodes = 0; + hash_pair_t *hp; + u8 **keys_to_delete = 0; + + index_by_name = hash_create_string (0, sizeof (uword)); + reg_by_index = hash_create (0, sizeof (uword)); + + this_reg = first_reg; + + /* pass 1, collect feature node names, construct a before b pairs */ + while (this_reg) + { + node_name = format (0, "%s%c", this_reg->node_name, 0); + hash_set (reg_by_index, vec_len (node_names), (uword) this_reg); + + hash_set_mem (index_by_name, node_name, vec_len (node_names)); + + vec_add1 (node_names, node_name); + + these_constraints = this_reg->runs_before; + while (these_constraints && these_constraints[0]) + { + this_constraint_c = these_constraints[0]; + + constraint_tuple = format (0, "%s,%s%c", node_name, + this_constraint_c, 0); + vec_add1 (constraints, constraint_tuple); + these_constraints++; + } + + these_constraints = this_reg->runs_after; + while (these_constraints && these_constraints[0]) + { + this_constraint_c = these_constraints[0]; + + constraint_tuple = format (0, "%s,%s%c", + this_constraint_c, node_name, 0); + vec_add1 (constraints, constraint_tuple); + these_constraints++; + } + + this_reg = this_reg->next; + } + + n_features = vec_len (node_names); + orig = clib_ptclosure_alloc (n_features); + + for (i = 0; i < vec_len (constraints); i++) + { + this_constraint = constraints[i]; + + if (comma_split (this_constraint, &a_name, &b_name)) + return clib_error_return (0, "comma_split failed!"); + + p = hash_get_mem (index_by_name, a_name); + /* + * Note: the next two errors mean that something is + * b0rked. As in: if you code "A depends on B," and you forget + * to define a FEATURE_INIT macro for B, you lose. + * Nonexistent graph nodes are tolerated. + */ + if (p == 0) + return clib_error_return (0, "feature node '%s' not found", a_name); + a_index = p[0]; + + p = hash_get_mem (index_by_name, b_name); + if (p == 0) + return clib_error_return (0, "feature node '%s' not found", b_name); + b_index = p[0]; + + /* add a before b to the original set of constraints */ + orig[a_index][b_index] = 1; + vec_free (this_constraint); + } + + /* Compute the positive transitive closure of the original constraints */ + closure = clib_ptclosure (orig); + + /* Compute a partial order across feature nodes, if one exists. */ +again: + for (i = 0; i < n_features; i++) + { + for (j = 0; j < n_features; j++) + { + if (closure[i][j]) + goto item_constrained; + } + /* Item i can be output */ + vec_add1 (result, i); + { + for (k = 0; k < n_features; k++) + closure[k][i] = 0; + /* + * Add a "Magic" a before a constraint. + * This means we'll never output it again + */ + closure[i][i] = 1; + goto again; + } + item_constrained: + ; + } + + /* see if we got a partial order... */ + if (vec_len (result) != n_features) + return clib_error_return (0, "%d feature_init_cast no partial order!"); + + /* + * We win. + * Bind the index variables, and output the feature node name vector + * using the partial order we just computed. Result is in stack + * order, because the entry with the fewest constraints (e.g. none) + * is output first, etc. + */ + + for (i = n_features - 1; i >= 0; i--) + { + p = hash_get (reg_by_index, result[i]); + ASSERT (p != 0); + this_reg = (vnet_feature_registration_t *) p[0]; + if (this_reg->feature_index_ptr) + *this_reg->feature_index_ptr = n_features - (i + 1); + this_reg->feature_index = n_features - (i + 1); + vec_add1 (feature_nodes, this_reg->node_name); + } + + /* Set up the config infrastructure */ + vnet_config_init (vm, vcm, + feature_start_nodes, + num_feature_start_nodes, + feature_nodes, vec_len (feature_nodes)); + + /* Save a copy for show command */ + *in_feature_nodes = feature_nodes; + + /* Finally, clean up all the shit we allocated */ + /* *INDENT-OFF* */ + hash_foreach_pair (hp, index_by_name, + ({ + vec_add1 (keys_to_delete, (u8 *)hp->key); + })); + /* *INDENT-ON* */ + hash_free (index_by_name); + for (i = 0; i < vec_len (keys_to_delete); i++) + vec_free (keys_to_delete[i]); + vec_free (keys_to_delete); + hash_free (reg_by_index); + vec_free (result); + clib_ptclosure_free (orig); + clib_ptclosure_free (closure); + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |