From ea8b126e31ab0f4e2c980be43cb423d50b7d7244 Mon Sep 17 00:00:00 2001 From: Gabriel Ganne Date: Sat, 17 Sep 2016 10:13:52 +0200 Subject: [PATCH 2/3] Add Port Mirroring feature port-mirroring is under /plugins connect l2-input-classify node to the port-mirroring node connect l2-output-classify node to the port-mirroring node next node depends on previous node. Author: Christophe Fontaine Author: Gabriel Ganne Signed-off-by: Gabriel Ganne Change-Id: Ia8b83615880ca274ee6b292e4fea0eb02d4d7aaa --- plugins/Makefile.am | 4 + plugins/configure.ac | 1 + plugins/portmirroring-plugin/Makefile.am | 59 +++++++ plugins/portmirroring-plugin/configure.ac | 16 ++ plugins/portmirroring-plugin/portmirroring/api.c | 131 +++++++++++++++ .../portmirroring-plugin/portmirroring/flowdata.h | 1 + .../portmirroring/port_mirroring.c | 134 ++++++++++++++++ .../portmirroring/port_mirroring.h | 67 ++++++++ .../portmirroring/port_mirroring_in_node.c | 178 +++++++++++++++++++++ .../portmirroring/port_mirroring_out_node.c | 168 +++++++++++++++++++ .../portmirroring/portmirroring.api | 21 +++ 11 files changed, 780 insertions(+) create mode 100644 plugins/portmirroring-plugin/Makefile.am create mode 100644 plugins/portmirroring-plugin/configure.ac create mode 100644 plugins/portmirroring-plugin/portmirroring/api.c create mode 120000 plugins/portmirroring-plugin/portmirroring/flowdata.h create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.c create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.h create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c create mode 100644 plugins/portmirroring-plugin/portmirroring/portmirroring.api diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d0a2e3c..7bd93a9 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -55,3 +55,7 @@ endif if ENABLE_flowtable_PLUGIN SUBDIRS += flowtable-plugin endif + +if ENABLE_portmirroring_PLUGIN +SUBDIRS += portmirroring-plugin +endif diff --git a/plugins/configure.ac b/plugins/configure.ac index cbb180f..5e30011 100644 --- a/plugins/configure.ac +++ b/plugins/configure.ac @@ -59,6 +59,7 @@ PLUGIN_ENABLED(snat) PLUGIN_ENABLED(ila) PLUGIN_ENABLED(lb) PLUGIN_ENABLED(flowtable) +PLUGIN_ENABLED(portmirroring) # Disabled plugins, require --enable-XXX-plugin PLUGIN_DISABLED(vcgn) diff --git a/plugins/portmirroring-plugin/Makefile.am b/plugins/portmirroring-plugin/Makefile.am new file mode 100644 index 0000000..77da992 --- /dev/null +++ b/plugins/portmirroring-plugin/Makefile.am @@ -0,0 +1,59 @@ +# Copyright (c) 2016 Cisco Systems, Inc. +# 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. + +AUTOMAKE_OPTIONS = foreign subdir-objects + +AM_CFLAGS = -Wall +AM_LDFLAGS = -module -shared -avoid-version + +vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins +vpppluginsdir = ${libdir}/vpp_plugins + +# vppapitestplugins_LTLIBRARIES = portmirroring_test_plugin.la +vppplugins_LTLIBRARIES = portmirroring_plugin.la + +portmirroring_plugin_la_SOURCES = \ + portmirroring/api.c \ + portmirroring/port_mirroring.c \ + portmirroring/port_mirroring_in_node.c \ + portmirroring/port_mirroring_out_node.c + +BUILT_SOURCES = portmirroring/portmirroring.api.h portmirroring/portmirroring.py + +SUFFIXES = .api.h .api + +%.api.h: %.api + mkdir -p `dirname $@` ; \ + $(CC) $(CPPFLAGS) -E -P -C -x c $^ \ + | vppapigen --input - --output $@ --show-name $@ + +%.py: %.api + $(info Creating Python binding for $@) + $(CC) $(CPPFLAGS) -E -P -C -x c $< \ + | vppapigen --input - --python - \ + | pyvppapigen.py --input - > $@ + + +pyapidir = ${prefix}/vpp_papi_plugins +pyapi_DATA = portmirroring/portmirroring.py + +noinst_HEADERS = portmirroring/port_mirroring.h portmirroring/portmirroring.api.h + +#portmirroring_test_plugin_la_SOURCES = \ +# portmirroring/portmirroring_test.c portmirroring/portmirroring_plugin.api.h + +# Remove *.la files +install-data-hook: + @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) + +# @(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES)) diff --git a/plugins/portmirroring-plugin/configure.ac b/plugins/portmirroring-plugin/configure.ac new file mode 100644 index 0000000..3af74c0 --- /dev/null +++ b/plugins/portmirroring-plugin/configure.ac @@ -0,0 +1,16 @@ +AC_INIT(pm_plugin, 1.0) +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) +AC_PREFIX_DEFAULT([/usr]) + +AC_PROG_LIBTOOL +AC_PROG_CC + +AC_ARG_WITH(dpdk, + AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]), + [with_dpdk=1], + [with_dpdk=0]) + +AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1") +AM_COND_IF(WITH_DPDK, CFLAGS+="-DDPDK=1") +AC_OUTPUT([Makefile]) diff --git a/plugins/portmirroring-plugin/portmirroring/api.c b/plugins/portmirroring-plugin/portmirroring/api.c new file mode 100644 index 0000000..696557b --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/api.c @@ -0,0 +1,131 @@ +/* + * 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 + +#include +#include +#include +#include + +#define vl_msg_id(n,h) n, +typedef enum { +#include + /* We'll want to know how many messages IDs we need... */ + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +/* Macro to finish up custom dump fns */ +#define FINISH \ + vec_add1 (s, 0); \ + vl_print (handle, (char *)s); \ + vec_free (s); \ + return handle; + +/* + * A handy macro to set up a message reply. + * Assumes that the following variables are available: + * mp - pointer to request message + * rmp - pointer to reply message type + * rv - return value + */ + +#define REPLY_MACRO(t) \ +do { \ + unix_shared_memory_queue_t * q = \ + vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = ntohs((t)+pmm->msg_id_base); \ + rmp->context = mp->context; \ + rmp->retval = ntohl(rv); \ + \ + vl_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +static void +vl_api_pm_conf_t_handler +(vl_api_pm_conf_t * mp) +{ + pm_main_t *pmm = &pm_main; + vl_api_pm_conf_reply_t * rmp; + int rv = 0; + + rv = pm_conf(mp->dst_interface, mp->from_node, mp->is_del); + + REPLY_MACRO (VL_API_PM_CONF_REPLY); +} + +static void *vl_api_pm_conf_t_print +(vl_api_pm_conf_t *mp, void * handle) +{ + u8 * s; + s = format (0, "SCRIPT: pm_conf "); + s = format (s, "%u ", mp->dst_interface); + s = format (s, "%u ", mp->from_node); + s = format (s, "%s ", (mp->is_del? "DELETE" : "ADD" )); + FINISH; +} + + +/* List of message types that this plugin understands */ +#define foreach_pm_plugin_api_msg \ +_(PM_CONF, pm_conf) \ + + +static clib_error_t * pm_api_init (vlib_main_t * vm) +{ + pm_main_t *pmm = &pm_main; + u8 *name = format (0, "portmirroring_%08x%c", api_version, 0); + pmm->msg_id_base = vl_msg_api_get_msg_ids + ((char *) name, VL_MSG_FIRST_AVAILABLE); + +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + pmm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_pm_plugin_api_msg; +#undef _ + + return 0; +} + +VLIB_INIT_FUNCTION (pm_api_init); + diff --git a/plugins/portmirroring-plugin/portmirroring/flowdata.h b/plugins/portmirroring-plugin/portmirroring/flowdata.h new file mode 120000 index 0000000..67f4f12 --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/flowdata.h @@ -0,0 +1 @@ +../../flowtable-plugin/flowtable/flowdata.h \ No newline at end of file diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c new file mode 100644 index 0000000..46b6e0f --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#if DPDK != 1 +#error "DPDK needed" +#endif + +#include +#include +#include + +int pm_conf(u8 dst_interface, u8 from_node, u8 is_del) +{ + pm_main_t *pm = &pm_main; + + if(is_del == 0) { + pm->sw_if_index = dst_interface; + pm->from_node = from_node; + if (from_node == PM_FROM_FLOWTABLE) { + pm->next_node_index = PM_IN_HIT_NEXT_ETHERNET_INPUT; + } else if (from_node == PM_FROM_CLASSSIFIER) { + pm->next_node_index = PM_IN_HIT_NEXT_L2_LEARN; + } else { + pm->next_node_index = PM_IN_HIT_NEXT_ERROR; + return -1; + } + } else { + pm->sw_if_index = ~0; + pm->from_node = ~0; + pm->next_node_index = ~0; + } + + return 0; +} + +static clib_error_t * +set_pm_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + pm_main_t *pm = &pm_main; + int enable_disable = 1; + int sw_if_index = ~0; + int from_node = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "from %U", unformat_vlib_node, + pm->vnet_main, &from_node)); + if (unformat (input, "to %U", unformat_vnet_sw_interface, + pm->vnet_main, &sw_if_index)); + else if (unformat (input, "del")) + enable_disable = 0; + else if (unformat (input, "disable")) + enable_disable = 0; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "interface required"); + if (from_node == ~0) + return clib_error_return (0, "previous node required"); + + + if (enable_disable) + { + pm->sw_if_index = sw_if_index; + pm->from_node = from_node; + return 0; + } + else + { + pm->sw_if_index = ~0; + pm->from_node = ~0; + } + + + return 0; +} + +VLIB_CLI_COMMAND (set_pm_command, static) = +{ + .path = "set pm", + .short_help = "set pm from <0|1> to [del]", + .function = set_pm_command_fn, +}; + + +static clib_error_t * +pm_init (vlib_main_t * vm) +{ + pm_main_t *pm = &pm_main; + + pm->sw_if_index = ~0; + pm->from_node = ~0; + + pm->pm_in_hit_node_index = pm_in_hit_node.index; + pm->pm_out_hit_node_index = pm_out_hit_node.index; + + /* pm-out previous node */ + u32 l2out_classify_idx = vlib_get_node_by_name(vm, (u8*) "l2-output-classify")->index; + vlib_node_add_next(vm, l2out_classify_idx, pm->pm_out_hit_node_index); + + /* pm-in & pm-out next node */ + pm->interface_output_node_index = vlib_get_node_by_name(vm, (u8*) "interface-output")->index; + + return 0; +} + +VLIB_INIT_FUNCTION (pm_init); + +clib_error_t * +vlib_plugin_register (vlib_main_t * vm, + vnet_plugin_handoff_t * h, + int from_early_init) +{ + clib_error_t *error = 0; + pm_main_t *pm = &pm_main; + pm->vnet_main = vnet_get_main(); + pm->vlib_main = vm; + return error; +} diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.h b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h new file mode 100644 index 0000000..8e9826f --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h @@ -0,0 +1,67 @@ +/* + * 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 __port_mirroring_h__ +#define __port_mirroring_h__ + +#include +#include + +enum { + PM_FROM_CLASSSIFIER = 0, + PM_FROM_FLOWTABLE = 1, + PM_FROM_MAX +}; + +typedef enum { + PM_IN_HIT_NEXT_ERROR, + PM_IN_HIT_NEXT_ETHERNET_INPUT, + PM_IN_HIT_NEXT_L2_LEARN, + PM_IN_HIT_N_NEXT, +} pm_in_hit_next_t; + +typedef struct +{ + /* mirror interface index */ + u32 sw_if_index; + u32 from_node; + + /* Hit node index */ + u32 pm_in_hit_node_index; + u32 pm_out_hit_node_index; + + u32 interface_output_node_index; + + /* depends on the previous node */ + u32 next_node_index; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + + /** + * API dynamically registered base ID. + */ + u16 msg_id_base; +} pm_main_t; + +pm_main_t pm_main; + +int pm_conf(u8 dst_interface, u8 from_node, u8 is_del); + +extern vlib_node_registration_t pm_in_hit_node; +extern vlib_node_registration_t pm_out_hit_node; + +#endif /* __port_mirroring_h__ */ diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c new file mode 100644 index 0000000..04b05df --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#if DPDK != 1 +#error "DPDK needed" +#endif + +#include +#include + +#include +#include + +#include "flowdata.h" +#include "port_mirroring.h" + +vlib_node_registration_t pm_in_hit_node; + +typedef struct { + u32 next_index; +} pm_in_hit_trace_t; + +/* packet trace format function */ +static u8 * +format_pm_in_hit_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 *); + pm_in_hit_trace_t * t = va_arg(*args, pm_in_hit_trace_t *); + + s = format(s, "PM_IN_HIT: next index %d", t->next_index); + + return s; +} + +vlib_node_registration_t pm_in_hit_node; + +#define foreach_pm_in_hit_error \ + _(HITS, "PM in packets processed") \ + _(NO_INTF_OUT, "No out interface configured") + +typedef enum { +#define _(sym, str) PM_IN_HIT_ERROR_ ## sym, + foreach_pm_in_hit_error +#undef _ + PM_IN_HIT_N_ERROR, +} pm_in_hit_error_t; + +static char * pm_in_hit_error_strings[] = { +#define _(sym, string) string, + foreach_pm_in_hit_error +#undef _ +}; + +static uword +pm_in_hit_node_fn(vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + u32 next_index; + vlib_frame_t * dup_frame = 0; + u32 * to_int_next = 0; + pm_main_t * pm = &pm_main; + + from = vlib_frame_vector_args(frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (pm->sw_if_index == ~0) { + vlib_node_increment_counter (vm, pm_in_hit_node.index, + PM_IN_HIT_ERROR_NO_INTF_OUT, + n_left_from); + } else { + /* The intercept frame... */ + dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index); + to_int_next = vlib_frame_vector_args(dup_frame); + } + + 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 > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + vlib_buffer_t * c0; + u32 next0 = pm->next_node_index; + + /* 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); + if (PREDICT_TRUE(to_int_next != 0)) + { + /* Make a copy */ + c0 = vlib_dpdk_clone_buffer(vm, b0); + + vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index; + + to_int_next[0] = vlib_get_buffer_index(vm, c0); + to_int_next++; + + } + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pm_in_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t)); + t->next_index = next0; + } + + /* restore opaque value which was used by the flowtable plugin */ + if (pm->from_node == PM_FROM_FLOWTABLE) { + flow_data_t flow_data; + clib_memcpy(&flow_data, b0->opaque, sizeof(flow_data)); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = flow_data.data.sw_if_index_current; + } + + /* 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); + } + + if (dup_frame) + { + dup_frame->n_vectors = frame->n_vectors; + vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame); + } + + vlib_node_increment_counter(vm, pm_in_hit_node.index, + PM_IN_HIT_ERROR_HITS, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE(pm_in_hit_node) = { + .function = pm_in_hit_node_fn, + .name = "pm-in-hit", + .vector_size = sizeof(u32), + .format_trace = format_pm_in_hit_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pm_in_hit_error_strings), + .error_strings = pm_in_hit_error_strings, + + .n_next_nodes = PM_IN_HIT_N_NEXT, + .next_nodes = { + [PM_IN_HIT_NEXT_ERROR] = "error-drop", + [PM_IN_HIT_NEXT_ETHERNET_INPUT] = "ethernet-input", + [PM_IN_HIT_NEXT_L2_LEARN] = "l2-learn", + } +}; diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c new file mode 100644 index 0000000..9eebb88 --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#if DPDK != 1 +#error "DPDK needed" +#endif + +#include +#include + +#include +#include + +vlib_node_registration_t pm_out_hit_node; + +typedef struct { + u32 next_index; +} pm_out_hit_trace_t; + +/* packet trace format function */ +static u8 * +format_pm_out_hit_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 *); + pm_out_hit_trace_t * t = va_arg(*args, pm_out_hit_trace_t *); + + s = format(s, "PM_OUT_HIT: next index %d", t->next_index); + + return s; +} + +#define foreach_pm_out_hit_error \ + _(HITS, "PM out packets processed") \ + _(NO_COLLECTOR, "No collector configured") + +typedef enum { +#define _(sym, str) PM_OUT_HIT_ERROR_ ## sym, + foreach_pm_out_hit_error +#undef _ + PM_OUT_HIT_N_ERROR, +} pm_out_hit_error_t; + +static char * pm_out_hit_error_strings[] = { +#define _(sym, string) string, + foreach_pm_out_hit_error +#undef _ +}; + +typedef enum { + PM_OUT_HIT_NEXT_IF_OUT, + PM_OUT_HIT_N_NEXT, +} pm_out_hit_next_t; + +static uword +pm_out_hit_node_fn(vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + pm_out_hit_next_t next_index; + vlib_frame_t * dup_frame = 0; + u32 * to_int_next = 0; + pm_main_t * pm = &pm_main; + + from = vlib_frame_vector_args(frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (pm->sw_if_index == ~0) { + vlib_node_increment_counter (vm, pm_out_hit_node.index, + PM_OUT_HIT_ERROR_NO_COLLECTOR, + n_left_from); + } else { + /* The intercept frame... */ + dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index); + to_int_next = vlib_frame_vector_args(dup_frame); + } + + 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 > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + vlib_buffer_t * c0; + u32 next0 = PM_OUT_HIT_NEXT_IF_OUT; + + /* 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); + if (PREDICT_TRUE(to_int_next != 0)) + { + /* Make an intercept copy */ + c0 = vlib_dpdk_clone_buffer(vm, b0); + + vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index; + + to_int_next[0] = vlib_get_buffer_index(vm, c0); + to_int_next++; + } + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + pm_out_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t)); + 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); + } + + if (dup_frame) + { + dup_frame->n_vectors = frame->n_vectors; + vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame); + } + + vlib_node_increment_counter(vm, pm_out_hit_node.index, + PM_OUT_HIT_ERROR_HITS, frame->n_vectors); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE(pm_out_hit_node) = { + .function = pm_out_hit_node_fn, + .name = "pm-out-hit", + .vector_size = sizeof(u32), + .format_trace = format_pm_out_hit_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(pm_out_hit_error_strings), + .error_strings = pm_out_hit_error_strings, + + .n_next_nodes = PM_OUT_HIT_N_NEXT, + + .next_nodes = { + [PM_OUT_HIT_NEXT_IF_OUT] = "interface-output", + } +}; diff --git a/plugins/portmirroring-plugin/portmirroring/portmirroring.api b/plugins/portmirroring-plugin/portmirroring/portmirroring.api new file mode 100644 index 0000000..fe86bb2 --- /dev/null +++ b/plugins/portmirroring-plugin/portmirroring/portmirroring.api @@ -0,0 +1,21 @@ +/** \brief Configure Port-Mirroring global parameters + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param dst_interface - name or id of interface to copy packets to + @param from_node - 0|1 (classifier|flowtable) + @param is_del - Whether we have to delete any port mirrring information +*/ +define pm_conf +{ + u32 client_index; + u32 context; + u8 dst_interface; + u8 from_node; + u8 is_del; +}; + +define pm_conf_reply { + u32 context; + i32 retval; +}; + -- 2.10.0.rc1.15.g5cb0d5a