aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/cop
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/cop')
-rw-r--r--src/vnet/cop/cop.c387
-rw-r--r--src/vnet/cop/cop.h89
-rw-r--r--src/vnet/cop/ip4_whitelist.c356
-rw-r--r--src/vnet/cop/ip6_whitelist.c298
-rw-r--r--src/vnet/cop/node1.c319
5 files changed, 1449 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,
+};
+
diff --git a/src/vnet/cop/cop.h b/src/vnet/cop/cop.h
new file mode 100644
index 00000000000..eb5f1dfd78e
--- /dev/null
+++ b/src/vnet/cop/cop.h
@@ -0,0 +1,89 @@
+/*
+ * 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 __vnet_cop_h__
+#define __vnet_cop_h__
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+
+#include <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+
+typedef enum {
+ VNET_COP_IP4,
+ VNET_COP_IP6,
+ VNET_COP_DEFAULT,
+ VNET_N_COPS,
+} vnet_cop_t;
+
+typedef enum {
+ /* First check src address against whitelist */
+ IP4_RX_COP_WHITELIST,
+ IP6_RX_COP_WHITELIST,
+ DEFAULT_RX_COP_WHITELIST,
+
+ /* Pkts not otherwise dropped go to xxx-input */
+ IP4_RX_COP_INPUT,
+ IP6_RX_COP_INPUT,
+ DEFAULT_RX_COP_INPUT,
+
+ /* Going, going, gone... */
+ RX_COP_DROP,
+
+ COP_RX_N_FEATURES,
+} cop_feature_type_t;
+
+typedef struct {
+ vnet_config_main_t config_main;
+ u32 * config_index_by_sw_if_index;
+} cop_config_main_t;
+
+typedef struct {
+ u32 fib_index;
+} cop_config_data_t;
+
+typedef struct {
+ cop_config_main_t cop_config_mains[VNET_N_COPS];
+
+ /* convenience */
+ vlib_main_t * vlib_main;
+ vnet_main_t * vnet_main;
+} cop_main_t;
+
+cop_main_t cop_main;
+
+extern vlib_node_registration_t cop_input_node;
+
+int cop_interface_enable_disable (u32 sw_if_index, int enable_disable);
+
+typedef struct {
+ u32 sw_if_index;
+ u8 ip4;
+ u8 ip6;
+ u8 default_cop;
+ u32 fib_id;
+} cop_whitelist_enable_disable_args_t;
+
+int cop_whitelist_enable_disable (cop_whitelist_enable_disable_args_t *a);
+
+#endif /* __vnet_cop_h__ */
diff --git a/src/vnet/cop/ip4_whitelist.c b/src/vnet/cop/ip4_whitelist.c
new file mode 100644
index 00000000000..d5121e72980
--- /dev/null
+++ b/src/vnet/cop/ip4_whitelist.c
@@ -0,0 +1,356 @@
+/*
+ * 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>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/dpo/load_balance.h>
+
+typedef struct {
+ u32 next_index;
+ u32 sw_if_index;
+} ip4_cop_whitelist_trace_t;
+
+/* packet trace format function */
+static u8 * format_ip4_cop_whitelist_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 *);
+ ip4_cop_whitelist_trace_t * t = va_arg (*args, ip4_cop_whitelist_trace_t *);
+
+ s = format (s, "IP4_COP_WHITELIST: sw_if_index %d, next index %d",
+ t->sw_if_index, t->next_index);
+ return s;
+}
+
+vlib_node_registration_t ip4_cop_whitelist_node;
+
+#define foreach_ip4_cop_whitelist_error \
+_(DROPPED, "ip4 cop whitelist packets dropped")
+
+typedef enum {
+#define _(sym,str) IP4_COP_WHITELIST_ERROR_##sym,
+ foreach_ip4_cop_whitelist_error
+#undef _
+ IP4_COP_WHITELIST_N_ERROR,
+} ip4_cop_whitelist_error_t;
+
+static char * ip4_cop_whitelist_error_strings[] = {
+#define _(sym,string) string,
+ foreach_ip4_cop_whitelist_error
+#undef _
+};
+
+static uword
+ip4_cop_whitelist_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, * from, * to_next;
+ cop_feature_type_t next_index;
+ cop_main_t *cm = &cop_main;
+ vlib_combined_counter_main_t * vcm = &load_balance_main.lbm_via_counters;
+ u32 cpu_index = vm->cpu_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index,
+ to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 bi0, bi1;
+ vlib_buffer_t * b0, * b1;
+ u32 next0, next1;
+ u32 sw_if_index0, sw_if_index1;
+ ip4_header_t * ip0, * ip1;
+ cop_config_main_t * ccm0, * ccm1;
+ cop_config_data_t * c0, * c1;
+ ip4_fib_mtrie_t * mtrie0, * mtrie1;
+ ip4_fib_mtrie_leaf_t leaf0, leaf1;
+ u32 lb_index0, lb_index1;
+ const load_balance_t * lb0, *lb1;
+ const dpo_id_t *dpo0, *dpo1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t * p2, * p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+ ip0 = vlib_buffer_get_current (b0);
+
+ ccm0 = cm->cop_config_mains + VNET_COP_IP4;
+
+ c0 = vnet_get_config_data
+ (&ccm0->config_main,
+ &vnet_buffer (b0)->cop.current_config_index,
+ &next0,
+ sizeof (c0[0]));
+
+ mtrie0 = &ip4_fib_get (c0->fib_index)->mtrie;
+
+ leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 0);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 1);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 2);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 3);
+
+ lb_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+
+ ASSERT (lb_index0
+ == ip4_fib_table_lookup_lb (ip4_fib_get(c0->fib_index),
+ &ip0->src_address));
+ lb0 = load_balance_get (lb_index0);
+ dpo0 = load_balance_get_bucket_i(lb0, 0);
+
+ if (PREDICT_FALSE(dpo0->dpoi_type != DPO_RECEIVE))
+ {
+ b0->error = node->errors[IP4_COP_WHITELIST_ERROR_DROPPED];
+ next0 = RX_COP_DROP;
+ }
+
+ b1 = vlib_get_buffer (vm, bi1);
+ sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+
+ ip1 = vlib_buffer_get_current (b1);
+
+ ccm1 = cm->cop_config_mains + VNET_COP_IP4;
+
+ c1 = vnet_get_config_data
+ (&ccm1->config_main,
+ &vnet_buffer (b1)->cop.current_config_index,
+ &next1,
+ sizeof (c1[0]));
+ mtrie1 = &ip4_fib_get (c1->fib_index)->mtrie;
+
+ leaf1 = IP4_FIB_MTRIE_LEAF_ROOT;
+
+ leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
+ &ip1->src_address, 0);
+
+ leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
+ &ip1->src_address, 1);
+
+ leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
+ &ip1->src_address, 2);
+
+ leaf1 = ip4_fib_mtrie_lookup_step (mtrie1, leaf1,
+ &ip1->src_address, 3);
+
+ lb_index1 = ip4_fib_mtrie_leaf_get_adj_index (leaf1);
+ ASSERT (lb_index1
+ == ip4_fib_table_lookup_lb (ip4_fib_get(c1->fib_index),
+ &ip1->src_address));
+ lb1 = load_balance_get (lb_index1);
+ dpo1 = load_balance_get_bucket_i(lb1, 0);
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index0, 1,
+ vlib_buffer_length_in_chain (vm, b0)
+ + sizeof(ethernet_header_t));
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index1, 1,
+ vlib_buffer_length_in_chain (vm, b1)
+ + sizeof(ethernet_header_t));
+
+
+ if (PREDICT_FALSE(dpo1->dpoi_type != DPO_RECEIVE))
+ {
+ b1->error = node->errors[IP4_COP_WHITELIST_ERROR_DROPPED];
+ next1 = RX_COP_DROP;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip4_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b1->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip4_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->sw_if_index = sw_if_index1;
+ t->next_index = next1;
+ }
+
+ /* verify speculative enqueues, maybe switch current next frame */
+ 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)
+ {
+ u32 bi0;
+ vlib_buffer_t * b0;
+ u32 next0;
+ u32 sw_if_index0;
+ ip4_header_t * ip0;
+ cop_config_main_t *ccm0;
+ cop_config_data_t *c0;
+ ip4_fib_mtrie_t * mtrie0;
+ ip4_fib_mtrie_leaf_t leaf0;
+ u32 lb_index0;
+ const load_balance_t * lb0;
+ const dpo_id_t *dpo0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+ ip0 = vlib_buffer_get_current (b0);
+
+ ccm0 = cm->cop_config_mains + VNET_COP_IP4;
+
+ c0 = vnet_get_config_data
+ (&ccm0->config_main,
+ &vnet_buffer (b0)->cop.current_config_index,
+ &next0,
+ sizeof (c0[0]));
+
+ mtrie0 = &ip4_fib_get (c0->fib_index)->mtrie;
+
+ leaf0 = IP4_FIB_MTRIE_LEAF_ROOT;
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 0);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 1);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 2);
+
+ leaf0 = ip4_fib_mtrie_lookup_step (mtrie0, leaf0,
+ &ip0->src_address, 3);
+
+ lb_index0 = ip4_fib_mtrie_leaf_get_adj_index (leaf0);
+
+ ASSERT (lb_index0
+ == ip4_fib_table_lookup_lb (ip4_fib_get(c0->fib_index),
+ &ip0->src_address));
+
+ lb0 = load_balance_get (lb_index0);
+ dpo0 = load_balance_get_bucket_i(lb0, 0);
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index0, 1,
+ vlib_buffer_length_in_chain (vm, b0)
+ + sizeof(ethernet_header_t));
+
+ if (PREDICT_FALSE(dpo0->dpoi_type != DPO_RECEIVE))
+ {
+ b0->error = node->errors[IP4_COP_WHITELIST_ERROR_DROPPED];
+ next0 = RX_COP_DROP;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip4_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ 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;
+}
+
+VLIB_REGISTER_NODE (ip4_cop_whitelist_node) = {
+ .function = ip4_cop_whitelist_node_fn,
+ .name = "ip4-cop-whitelist",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip4_cop_whitelist_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(ip4_cop_whitelist_error_strings),
+ .error_strings = ip4_cop_whitelist_error_strings,
+
+ .n_next_nodes = COP_RX_N_FEATURES,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [IP4_RX_COP_WHITELIST] = "ip4-cop-whitelist",
+ [IP6_RX_COP_WHITELIST] = "ip6-cop-whitelist",
+ [DEFAULT_RX_COP_WHITELIST] = "default-cop-whitelist",
+ [IP4_RX_COP_INPUT] = "ip4-input",
+ [IP6_RX_COP_INPUT] = "ip6-input",
+ [DEFAULT_RX_COP_INPUT] = "ethernet-input",
+ [RX_COP_DROP] = "error-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_cop_whitelist_node, ip4_cop_whitelist_node_fn)
+
+static clib_error_t *
+ip4_whitelist_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip4_whitelist_init);
diff --git a/src/vnet/cop/ip6_whitelist.c b/src/vnet/cop/ip6_whitelist.c
new file mode 100644
index 00000000000..c2e16ccfe54
--- /dev/null
+++ b/src/vnet/cop/ip6_whitelist.c
@@ -0,0 +1,298 @@
+/*
+ * 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>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/dpo/load_balance.h>
+
+typedef struct {
+ u32 next_index;
+ u32 sw_if_index;
+} ip6_cop_whitelist_trace_t;
+
+/* packet trace format function */
+static u8 * format_ip6_cop_whitelist_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 *);
+ ip6_cop_whitelist_trace_t * t = va_arg (*args, ip6_cop_whitelist_trace_t *);
+
+ s = format (s, "IP6_COP_WHITELIST: sw_if_index %d, next index %d",
+ t->sw_if_index, t->next_index);
+ return s;
+}
+
+vlib_node_registration_t ip6_cop_whitelist_node;
+
+#define foreach_ip6_cop_whitelist_error \
+_(DROPPED, "ip6 cop whitelist packets dropped")
+
+typedef enum {
+#define _(sym,str) IP6_COP_WHITELIST_ERROR_##sym,
+ foreach_ip6_cop_whitelist_error
+#undef _
+ IP6_COP_WHITELIST_N_ERROR,
+} ip6_cop_whitelist_error_t;
+
+static char * ip6_cop_whitelist_error_strings[] = {
+#define _(sym,string) string,
+ foreach_ip6_cop_whitelist_error
+#undef _
+};
+
+static uword
+ip6_cop_whitelist_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, * from, * to_next;
+ cop_feature_type_t next_index;
+ cop_main_t *cm = &cop_main;
+ ip6_main_t * im6 = &ip6_main;
+ vlib_combined_counter_main_t * vcm = &load_balance_main.lbm_via_counters;
+ u32 cpu_index = vm->cpu_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index,
+ to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 bi0, bi1;
+ vlib_buffer_t * b0, * b1;
+ u32 next0, next1;
+ u32 sw_if_index0, sw_if_index1;
+ ip6_header_t * ip0, * ip1;
+ cop_config_main_t * ccm0, * ccm1;
+ cop_config_data_t * c0, * c1;
+ u32 lb_index0, lb_index1;
+ const load_balance_t * lb0, *lb1;
+ const dpo_id_t *dpo0, *dpo1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t * p2, * p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+ ip0 = vlib_buffer_get_current (b0);
+
+ ccm0 = cm->cop_config_mains + VNET_COP_IP6;
+
+ c0 = vnet_get_config_data
+ (&ccm0->config_main,
+ &vnet_buffer (b0)->cop.current_config_index,
+ &next0,
+ sizeof (c0[0]));
+
+ lb_index0 = ip6_fib_table_fwding_lookup (im6, c0->fib_index,
+ &ip0->src_address);
+ lb0 = load_balance_get (lb_index0);
+ dpo0 = load_balance_get_bucket_i(lb0, 0);
+
+ if (PREDICT_FALSE(dpo0->dpoi_type != DPO_RECEIVE))
+ {
+ b0->error = node->errors[IP6_COP_WHITELIST_ERROR_DROPPED];
+ next0 = RX_COP_DROP;
+ }
+
+ b1 = vlib_get_buffer (vm, bi1);
+ sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+
+ ip1 = vlib_buffer_get_current (b1);
+
+ ccm1 = cm->cop_config_mains + VNET_COP_IP6;
+
+ c1 = vnet_get_config_data
+ (&ccm1->config_main,
+ &vnet_buffer (b1)->cop.current_config_index,
+ &next1,
+ sizeof (c1[0]));
+
+ lb_index1 = ip6_fib_table_fwding_lookup (im6, c1->fib_index,
+ &ip1->src_address);
+
+ lb1 = load_balance_get (lb_index1);
+ dpo1 = load_balance_get_bucket_i(lb1, 0);
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index0, 1,
+ vlib_buffer_length_in_chain (vm, b0)
+ + sizeof(ethernet_header_t));
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index1, 1,
+ vlib_buffer_length_in_chain (vm, b1)
+ + sizeof(ethernet_header_t));
+
+ if (PREDICT_FALSE(dpo1->dpoi_type != DPO_RECEIVE))
+ {
+ b1->error = node->errors[IP6_COP_WHITELIST_ERROR_DROPPED];
+ next1 = RX_COP_DROP;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip6_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b1->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip6_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->sw_if_index = sw_if_index1;
+ t->next_index = next1;
+ }
+
+ /* verify speculative enqueues, maybe switch current next frame */
+ 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)
+ {
+ u32 bi0;
+ vlib_buffer_t * b0;
+ u32 next0;
+ u32 sw_if_index0;
+ ip6_header_t * ip0;
+ cop_config_main_t *ccm0;
+ cop_config_data_t *c0;
+ u32 lb_index0;
+ const load_balance_t * lb0;
+ const dpo_id_t *dpo0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+ ip0 = vlib_buffer_get_current (b0);
+
+ ccm0 = cm->cop_config_mains + VNET_COP_IP6;
+
+ c0 = vnet_get_config_data
+ (&ccm0->config_main,
+ &vnet_buffer (b0)->cop.current_config_index,
+ &next0,
+ sizeof (c0[0]));
+
+ lb_index0 = ip6_fib_table_fwding_lookup (im6, c0->fib_index,
+ &ip0->src_address);
+
+ lb0 = load_balance_get (lb_index0);
+ dpo0 = load_balance_get_bucket_i(lb0, 0);
+
+ vlib_increment_combined_counter
+ (vcm, cpu_index, lb_index0, 1,
+ vlib_buffer_length_in_chain (vm, b0)
+ + sizeof(ethernet_header_t));
+
+ if (PREDICT_FALSE(dpo0->dpoi_type != DPO_RECEIVE))
+ {
+ b0->error = node->errors[IP6_COP_WHITELIST_ERROR_DROPPED];
+ next0 = RX_COP_DROP;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ ip6_cop_whitelist_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ 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;
+}
+
+VLIB_REGISTER_NODE (ip6_cop_whitelist_node) = {
+ .function = ip6_cop_whitelist_node_fn,
+ .name = "ip6-cop-whitelist",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip6_cop_whitelist_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(ip6_cop_whitelist_error_strings),
+ .error_strings = ip6_cop_whitelist_error_strings,
+
+ .n_next_nodes = COP_RX_N_FEATURES,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [IP4_RX_COP_WHITELIST] = "ip4-cop-whitelist",
+ [IP6_RX_COP_WHITELIST] = "ip6-cop-whitelist",
+ [DEFAULT_RX_COP_WHITELIST] = "default-cop-whitelist",
+ [IP4_RX_COP_INPUT] = "ip4-input",
+ [IP6_RX_COP_INPUT] = "ip6-input",
+ [DEFAULT_RX_COP_INPUT] = "ethernet-input",
+ [RX_COP_DROP] = "error-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_cop_whitelist_node, ip6_cop_whitelist_node_fn)
+
+static clib_error_t *
+ip6_whitelist_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip6_whitelist_init);
diff --git a/src/vnet/cop/node1.c b/src/vnet/cop/node1.c
new file mode 100644
index 00000000000..b448b531039
--- /dev/null
+++ b/src/vnet/cop/node1.c
@@ -0,0 +1,319 @@
+/*
+ * 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>
+
+typedef struct {
+ u32 next_index;
+ u32 sw_if_index;
+} cop_input_trace_t;
+
+/* packet trace format function */
+static u8 * format_cop_input_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 *);
+ cop_input_trace_t * t = va_arg (*args, cop_input_trace_t *);
+
+ s = format (s, "COP_INPUT: sw_if_index %d, next index %d",
+ t->sw_if_index, t->next_index);
+ return s;
+}
+
+vlib_node_registration_t cop_input_node;
+
+#define foreach_cop_input_error \
+_(PROCESSED, "COP input packets processed")
+
+typedef enum {
+#define _(sym,str) COP_INPUT_ERROR_##sym,
+ foreach_cop_input_error
+#undef _
+ COP_INPUT_N_ERROR,
+} cop_input_error_t;
+
+static char * cop_input_error_strings[] = {
+#define _(sym,string) string,
+ foreach_cop_input_error
+#undef _
+};
+
+static uword
+cop_input_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from, * from, * to_next;
+ cop_feature_type_t next_index;
+ cop_main_t *cm = &cop_main;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index,
+ to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 bi0, bi1;
+ vlib_buffer_t * b0, * b1;
+ u32 next0, next1;
+ u32 sw_if_index0, sw_if_index1;
+ ethernet_header_t * en0, * en1;
+ cop_config_main_t * ccm0, * ccm1;
+ u32 advance0, advance1;
+ int proto0, proto1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t * p2, * p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ en0 = vlib_buffer_get_current (b0);
+ en1 = vlib_buffer_get_current (b1);
+
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+ sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+
+ proto0 = VNET_COP_DEFAULT;
+ proto1 = VNET_COP_DEFAULT;
+ advance0 = 0;
+ advance1 = 0;
+
+ if (en0->type == clib_host_to_net_u16(ETHERNET_TYPE_IP4))
+ {
+ proto0 = VNET_COP_IP4;
+ advance0 = sizeof(ethernet_header_t);
+ }
+ else if (en0->type == clib_host_to_net_u16(ETHERNET_TYPE_IP6))
+ {
+ proto0 = VNET_COP_IP6;
+ advance0 = sizeof(ethernet_header_t);
+ }
+
+ if (en1->type == clib_host_to_net_u16(ETHERNET_TYPE_IP4))
+ {
+ proto1 = VNET_COP_IP4;
+ advance1 = sizeof(ethernet_header_t);
+ }
+ else if (en1->type == clib_host_to_net_u16(ETHERNET_TYPE_IP6))
+ {
+ proto1 = VNET_COP_IP6;
+ advance1 = sizeof(ethernet_header_t);
+ }
+
+ ccm0 = cm->cop_config_mains + proto0;
+ ccm1 = cm->cop_config_mains + proto1;
+ vnet_buffer(b0)->cop.current_config_index =
+ ccm0->config_index_by_sw_if_index [sw_if_index0];
+
+ vnet_buffer(b1)->cop.current_config_index =
+ ccm1->config_index_by_sw_if_index [sw_if_index1];
+
+ vlib_buffer_advance (b0, advance0);
+ vlib_buffer_advance (b1, advance1);
+
+ vnet_get_config_data (&ccm0->config_main,
+ &vnet_buffer(b0)->cop.current_config_index,
+ &next0, 0 /* bytes of config data */);
+
+ vnet_get_config_data (&ccm1->config_main,
+ &vnet_buffer(b1)->cop.current_config_index,
+ &next1, 0 /* bytes of config data */);
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ cop_input_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b1->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ cop_input_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->sw_if_index = sw_if_index1;
+ t->next_index = next1;
+ }
+ /* verify speculative enqueues, maybe switch current next frame */
+ 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)
+ {
+ u32 bi0;
+ vlib_buffer_t * b0;
+ u32 next0;
+ u32 sw_if_index0;
+ ethernet_header_t *en0;
+ cop_config_main_t *ccm0;
+ u32 advance0;
+ int proto0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ /*
+ * Direct from the driver, we should be at offset 0
+ * aka at &b0->data[0]
+ */
+ ASSERT (b0->current_data == 0);
+
+ en0 = vlib_buffer_get_current (b0);
+
+ sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+
+ proto0 = VNET_COP_DEFAULT;
+ advance0 = 0;
+
+ if (en0->type == clib_host_to_net_u16(ETHERNET_TYPE_IP4))
+ {
+ proto0 = VNET_COP_IP4;
+ advance0 = sizeof(ethernet_header_t);
+ }
+ else if (en0->type == clib_host_to_net_u16(ETHERNET_TYPE_IP6))
+ {
+ proto0 = VNET_COP_IP6;
+ advance0 = sizeof(ethernet_header_t);
+ }
+
+ ccm0 = cm->cop_config_mains + proto0;
+ vnet_buffer(b0)->cop.current_config_index =
+ ccm0->config_index_by_sw_if_index [sw_if_index0];
+
+ vlib_buffer_advance (b0, advance0);
+
+ vnet_get_config_data (&ccm0->config_main,
+ &vnet_buffer(b0)->cop.current_config_index,
+ &next0, 0 /* bytes of config data */);
+
+ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ cop_input_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->sw_if_index = sw_if_index0;
+ t->next_index = next0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ 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);
+ }
+ vlib_node_increment_counter (vm, cop_input_node.index,
+ COP_INPUT_ERROR_PROCESSED, frame->n_vectors);
+ return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (cop_input_node) = {
+ .function = cop_input_node_fn,
+ .name = "cop-input",
+ .vector_size = sizeof (u32),
+ .format_trace = format_cop_input_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(cop_input_error_strings),
+ .error_strings = cop_input_error_strings,
+
+ .n_next_nodes = COP_RX_N_FEATURES,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [IP4_RX_COP_WHITELIST] = "ip4-cop-whitelist",
+ [IP6_RX_COP_WHITELIST] = "ip6-cop-whitelist",
+ [DEFAULT_RX_COP_WHITELIST] = "default-cop-whitelist",
+ [IP4_RX_COP_INPUT] = "ip4-input",
+ [IP6_RX_COP_INPUT] = "ip6-input",
+ [DEFAULT_RX_COP_INPUT] = "ethernet-input",
+ [RX_COP_DROP] = "error-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (cop_input_node, cop_input_node_fn)
+
+#define foreach_cop_stub \
+_(default-cop-whitelist, default_cop_whitelist)
+
+#define _(n,f) \
+ \
+static uword \
+f##_node_fn (vlib_main_t * vm, \
+ vlib_node_runtime_t * node, \
+ vlib_frame_t * frame) \
+{ \
+ clib_warning ("BUG: stub function called"); \
+ return 0; \
+} \
+ \
+VLIB_REGISTER_NODE (f##_input_node) = { \
+ .function = f##_node_fn, \
+ .name = #n, \
+ .vector_size = sizeof (u32), \
+ .type = VLIB_NODE_TYPE_INTERNAL, \
+ \
+ .n_errors = 0, \
+ .error_strings = 0, \
+ \
+ .n_next_nodes = 0, \
+};
+
+foreach_cop_stub;
+
+
+
+
+
+