aboutsummaryrefslogtreecommitdiffstats
path: root/flowtable/0002-Add-Port-Mirroring-feature.patch
diff options
context:
space:
mode:
Diffstat (limited to 'flowtable/0002-Add-Port-Mirroring-feature.patch')
-rw-r--r--flowtable/0002-Add-Port-Mirroring-feature.patch896
1 files changed, 896 insertions, 0 deletions
diff --git a/flowtable/0002-Add-Port-Mirroring-feature.patch b/flowtable/0002-Add-Port-Mirroring-feature.patch
new file mode 100644
index 0000000..270a487
--- /dev/null
+++ b/flowtable/0002-Add-Port-Mirroring-feature.patch
@@ -0,0 +1,896 @@
+From ea8b126e31ab0f4e2c980be43cb423d50b7d7244 Mon Sep 17 00:00:00 2001
+From: Gabriel Ganne <gabriel.ganne@qosmos.com>
+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 <christophe.fontaine@qosmos.com>
+Author: Gabriel Ganne <gabriel.ganne@qosmos.com>
+
+Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com>
+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 <portmirroring/port_mirroring.h>
++
++#include <vppinfra/byte_order.h>
++#include <vlibapi/api.h>
++#include <vlibmemory/api.h>
++#include <vlibsocket/api.h>
++
++#define vl_msg_id(n,h) n,
++typedef enum {
++#include <portmirroring/portmirroring.api.h>
++ /* 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 <portmirroring/portmirroring.api.h>
++#undef vl_typedefs
++
++/* define generated endian-swappers */
++#define vl_endianfun
++#include <portmirroring/portmirroring.api.h>
++#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 <portmirroring/portmirroring.api.h>
++#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 <vnet/plugin/plugin.h>
++#include <vnet/api_errno.h>
++#include <portmirroring/port_mirroring.h>
++
++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 <intfc> [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 <vnet/vnet.h>
++#include <vnet/ip/ip.h>
++
++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 <vlib/vlib.h>
++#include <vnet/vnet.h>
++#include <vppinfra/error.h>
++
++#if DPDK != 1
++#error "DPDK needed"
++#endif
++
++#include <portmirroring/port_mirroring.h>
++#include <vnet/dpdk_replication.h>
++
++#include <vppinfra/error.h>
++#include <vppinfra/elog.h>
++
++#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 <vlib/vlib.h>
++#include <vnet/vnet.h>
++#include <vppinfra/error.h>
++
++#if DPDK != 1
++#error "DPDK needed"
++#endif
++
++#include <portmirroring/port_mirroring.h>
++#include <vnet/dpdk_replication.h>
++
++#include <vppinfra/error.h>
++#include <vppinfra/elog.h>
++
++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
+