diff options
Diffstat (limited to 'src/vnet/cop/cop.c')
-rw-r--r-- | src/vnet/cop/cop.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/vnet/cop/cop.c b/src/vnet/cop/cop.c new file mode 100644 index 00000000000..465d6c97a2a --- /dev/null +++ b/src/vnet/cop/cop.c @@ -0,0 +1,387 @@ +/* + * 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/cop/cop.h> + +cop_main_t cop_main; + +static clib_error_t * +cop_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) +{ + cop_main_t * cm = &cop_main; + cop_config_data_t _data, *data = &_data; + vlib_main_t * vm = cm->vlib_main; + vnet_hw_interface_t * hi = vnet_get_sup_hw_interface (vnm, sw_if_index);; + cop_config_main_t * ccm; + int address_family; + u32 ci, default_next; + + memset (data, 0, sizeof(*data)); + + /* + * Ignore local interface, pg interfaces. $$$ need a #define for the + * first "real" interface. The answer is 5 at the moment. + */ + if (hi->dev_class_index == vnet_local_interface_device_class.index) + return 0; + + for (address_family = VNET_COP_IP4; address_family < VNET_N_COPS; + address_family++) + { + ccm = &cm->cop_config_mains[address_family]; + + /* + * Once-only code to initialize the per-address-family + * cop feature subgraphs. + * Since the (single) start-node, cop-input, must be able + * to push pkts into three separate subgraphs, we + * use a unified cop_feature_type_t enumeration. + */ + + if (!(ccm->config_main.node_index_by_feature_index)) + { + switch (address_family) + { + case VNET_COP_IP4: + { + static char * start_nodes[] = { "cop-input" }; + static char * feature_nodes[] = { + [IP4_RX_COP_WHITELIST] = "ip4-cop-whitelist", + [IP4_RX_COP_INPUT] = "ip4-input", + }; + + vnet_config_init (vm, &ccm->config_main, + start_nodes, ARRAY_LEN(start_nodes), + feature_nodes, ARRAY_LEN(feature_nodes)); + } + break; + case VNET_COP_IP6: + { + static char * start_nodes[] = { "cop-input" }; + static char * feature_nodes[] = { + [IP6_RX_COP_WHITELIST] = "ip6-cop-whitelist", + [IP6_RX_COP_INPUT] = "ip6-input", + }; + vnet_config_init (vm, &ccm->config_main, + start_nodes, ARRAY_LEN(start_nodes), + feature_nodes, ARRAY_LEN(feature_nodes)); + } + break; + + case VNET_COP_DEFAULT: + { + static char * start_nodes[] = { "cop-input" }; + static char * feature_nodes[] = { + [DEFAULT_RX_COP_WHITELIST] = "default-cop-whitelist", + [DEFAULT_RX_COP_INPUT] = "ethernet-input", + }; + vnet_config_init (vm, &ccm->config_main, + start_nodes, ARRAY_LEN(start_nodes), + feature_nodes, ARRAY_LEN(feature_nodes)); + } + break; + + default: + clib_warning ("bug"); + break; + } + } + vec_validate_init_empty (ccm->config_index_by_sw_if_index, sw_if_index, + ~0); + + ci = ccm->config_index_by_sw_if_index[sw_if_index]; + + /* Create a sensible initial config: send pkts to xxx-input */ + if (address_family == VNET_COP_IP4) + default_next = IP4_RX_COP_INPUT; + else if (address_family == VNET_COP_IP6) + default_next = IP6_RX_COP_INPUT; + else + default_next = DEFAULT_RX_COP_INPUT; + + if (is_add) + ci = vnet_config_add_feature (vm, &ccm->config_main, + ci, + default_next, + data, sizeof(*data)); + else + ci = vnet_config_del_feature (vm, &ccm->config_main, + ci, + default_next, + data, sizeof(*data)); + + ccm->config_index_by_sw_if_index[sw_if_index] = ci; + } + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (cop_sw_interface_add_del); + +static clib_error_t * +cop_init (vlib_main_t *vm) +{ + cop_main_t * cm = &cop_main; + clib_error_t * error; + + if ((error = vlib_call_init_function (vm, ip4_whitelist_init))) + return error; + + if ((error = vlib_call_init_function (vm, ip6_whitelist_init))) + return error; + + cm->vlib_main = vm; + cm->vnet_main = vnet_get_main(); + + return 0; +} + +VLIB_INIT_FUNCTION (cop_init); + +int cop_interface_enable_disable (u32 sw_if_index, int enable_disable) +{ + cop_main_t * cm = &cop_main; + vnet_sw_interface_t * sw; + int rv; + u32 node_index = enable_disable ? cop_input_node.index : ~0; + + /* Not a physical port? */ + sw = vnet_get_sw_interface (cm->vnet_main, sw_if_index); + if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + /* + * Redirect pkts from the driver to the cop node. + * Returns VNET_API_ERROR_UNIMPLEMENTED if the h/w driver + * doesn't implement the API. + * + * Node_index = ~0 => shut off redirection + */ + rv = vnet_hw_interface_rx_redirect_to_node (cm->vnet_main, sw_if_index, + node_index); + return rv; +} + +static clib_error_t * +cop_enable_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + cop_main_t * cm = &cop_main; + u32 sw_if_index = ~0; + int enable_disable = 1; + + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "disable")) + enable_disable = 0; + else if (unformat (input, "%U", unformat_vnet_sw_interface, + cm->vnet_main, &sw_if_index)) + ; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "Please specify an interface..."); + + rv = cop_interface_enable_disable (sw_if_index, enable_disable); + + switch(rv) { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return + (0, "Invalid interface, only works on physical ports"); + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Device driver doesn't support redirection"); + break; + + default: + return clib_error_return (0, "cop_interface_enable_disable returned %d", + rv); + } + return 0; +} + +VLIB_CLI_COMMAND (cop_interface_command, static) = { + .path = "cop interface", + .short_help = + "cop interface <interface-name> [disable]", + .function = cop_enable_disable_command_fn, +}; + + +int cop_whitelist_enable_disable (cop_whitelist_enable_disable_args_t *a) +{ + cop_main_t * cm = &cop_main; + vlib_main_t * vm = cm->vlib_main; + ip4_main_t * im4 = &ip4_main; + ip6_main_t * im6 = &ip6_main; + int address_family; + int is_add; + cop_config_main_t * ccm; + u32 next_to_add_del = 0; + uword * p; + u32 fib_index = 0; + u32 ci; + cop_config_data_t _data, *data=&_data; + + /* + * Enable / disable whitelist processing on the specified interface + */ + + for (address_family = VNET_COP_IP4; address_family < VNET_N_COPS; + address_family++) + { + ccm = &cm->cop_config_mains[address_family]; + + switch(address_family) + { + case VNET_COP_IP4: + is_add = (a->ip4 != 0); + next_to_add_del = IP4_RX_COP_WHITELIST; + /* configured opaque data must match, or no supper */ + p = hash_get (im4->fib_index_by_table_id, a->fib_id); + if (p) + fib_index = p[0]; + else + { + if (is_add) + return VNET_API_ERROR_NO_SUCH_FIB; + else + continue; + } + break; + + case VNET_COP_IP6: + is_add = (a->ip6 != 0); + next_to_add_del = IP6_RX_COP_WHITELIST; + p = hash_get (im6->fib_index_by_table_id, a->fib_id); + if (p) + fib_index = p[0]; + else + { + if (is_add) + return VNET_API_ERROR_NO_SUCH_FIB; + else + continue; + } + break; + + case VNET_COP_DEFAULT: + is_add = (a->default_cop != 0); + next_to_add_del = DEFAULT_RX_COP_WHITELIST; + break; + + default: + clib_warning ("BUG"); + } + + ci = ccm->config_index_by_sw_if_index[a->sw_if_index]; + data->fib_index = fib_index; + + if (is_add) + ci = vnet_config_add_feature (vm, &ccm->config_main, + ci, + next_to_add_del, + data, sizeof (*data)); + else + ci = vnet_config_del_feature (vm, &ccm->config_main, + ci, + next_to_add_del, + data, sizeof (*data)); + + ccm->config_index_by_sw_if_index[a->sw_if_index] = ci; + } + return 0; +} + +static clib_error_t * +cop_whitelist_enable_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + cop_main_t * cm = &cop_main; + u32 sw_if_index = ~0; + u8 ip4 = 0; + u8 ip6 = 0; + u8 default_cop = 0; + u32 fib_id = 0; + int rv; + cop_whitelist_enable_disable_args_t _a, * a = &_a; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "ip4")) + ip4 = 1; + else if (unformat (input, "ip6")) + ip6 = 1; + else if (unformat (input, "default")) + default_cop = 1; + else if (unformat (input, "%U", unformat_vnet_sw_interface, + cm->vnet_main, &sw_if_index)) + ; + else if (unformat (input, "fib-id %d", &fib_id)) + ; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "Please specify an interface..."); + + a->sw_if_index = sw_if_index; + a->ip4 = ip4; + a->ip6 = ip6; + a->default_cop = default_cop; + a->fib_id = fib_id; + + rv = cop_whitelist_enable_disable (a); + + switch(rv) { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return + (0, "Invalid interface, only works on physical ports"); + break; + + case VNET_API_ERROR_NO_SUCH_FIB: + return clib_error_return + (0, "Invalid fib"); + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Device driver doesn't support redirection"); + break; + + default: + return clib_error_return (0, "cop_whitelist_enable_disable returned %d", + rv); + } + + return 0; +} + +VLIB_CLI_COMMAND (cop_whitelist_command, static) = { + .path = "cop whitelist", + .short_help = + "cop whitelist <interface-name> [ip4][ip6][default][fib-id <NN>][disable]", + .function = cop_whitelist_enable_disable_command_fn, +}; + |