0002-Add-Port-Mirroring-feature.patch revision 55fd743f
1From ea8b126e31ab0f4e2c980be43cb423d50b7d7244 Mon Sep 17 00:00:00 2001
2From: Gabriel Ganne <gabriel.ganne@qosmos.com>
3Date: Sat, 17 Sep 2016 10:13:52 +0200
4Subject: [PATCH 2/3] Add Port Mirroring feature
5
6port-mirroring is under /plugins
7connect l2-input-classify node to the port-mirroring node
8connect l2-output-classify node to the port-mirroring node
9
10next node depends on previous node.
11
12Author: Christophe Fontaine <christophe.fontaine@qosmos.com>
13Author: Gabriel Ganne <gabriel.ganne@qosmos.com>
14
15Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com>
16Change-Id: Ia8b83615880ca274ee6b292e4fea0eb02d4d7aaa
17---
18 plugins/Makefile.am                                |   4 +
19 plugins/configure.ac                               |   1 +
20 plugins/portmirroring-plugin/Makefile.am           |  59 +++++++
21 plugins/portmirroring-plugin/configure.ac          |  16 ++
22 plugins/portmirroring-plugin/portmirroring/api.c   | 131 +++++++++++++++
23 .../portmirroring-plugin/portmirroring/flowdata.h  |   1 +
24 .../portmirroring/port_mirroring.c                 | 134 ++++++++++++++++
25 .../portmirroring/port_mirroring.h                 |  67 ++++++++
26 .../portmirroring/port_mirroring_in_node.c         | 178 +++++++++++++++++++++
27 .../portmirroring/port_mirroring_out_node.c        | 168 +++++++++++++++++++
28 .../portmirroring/portmirroring.api                |  21 +++
29 11 files changed, 780 insertions(+)
30 create mode 100644 plugins/portmirroring-plugin/Makefile.am
31 create mode 100644 plugins/portmirroring-plugin/configure.ac
32 create mode 100644 plugins/portmirroring-plugin/portmirroring/api.c
33 create mode 120000 plugins/portmirroring-plugin/portmirroring/flowdata.h
34 create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.c
35 create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.h
36 create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c
37 create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c
38 create mode 100644 plugins/portmirroring-plugin/portmirroring/portmirroring.api
39
40diff --git a/plugins/Makefile.am b/plugins/Makefile.am
41index d0a2e3c..7bd93a9 100644
42--- a/plugins/Makefile.am
43+++ b/plugins/Makefile.am
44@@ -55,3 +55,7 @@ endif
45 if ENABLE_flowtable_PLUGIN
46 SUBDIRS += flowtable-plugin
47 endif
48+
49+if ENABLE_portmirroring_PLUGIN
50+SUBDIRS += portmirroring-plugin
51+endif
52diff --git a/plugins/configure.ac b/plugins/configure.ac
53index cbb180f..5e30011 100644
54--- a/plugins/configure.ac
55+++ b/plugins/configure.ac
56@@ -59,6 +59,7 @@ PLUGIN_ENABLED(snat)
57 PLUGIN_ENABLED(ila)
58 PLUGIN_ENABLED(lb)
59 PLUGIN_ENABLED(flowtable)
60+PLUGIN_ENABLED(portmirroring)
61 
62 # Disabled plugins, require --enable-XXX-plugin
63 PLUGIN_DISABLED(vcgn)
64diff --git a/plugins/portmirroring-plugin/Makefile.am b/plugins/portmirroring-plugin/Makefile.am
65new file mode 100644
66index 0000000..77da992
67--- /dev/null
68+++ b/plugins/portmirroring-plugin/Makefile.am
69@@ -0,0 +1,59 @@
70+# Copyright (c) 2016 Cisco Systems, Inc.
71+# Licensed under the Apache License, Version 2.0 (the "License");
72+# you may not use this file except in compliance with the License.
73+# You may obtain a copy of the License at:
74+#
75+#     http://www.apache.org/licenses/LICENSE-2.0
76+#
77+# Unless required by applicable law or agreed to in writing, software
78+# distributed under the License is distributed on an "AS IS" BASIS,
79+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
80+# See the License for the specific language governing permissions and
81+# limitations under the License.
82+
83+AUTOMAKE_OPTIONS = foreign subdir-objects
84+
85+AM_CFLAGS = -Wall
86+AM_LDFLAGS = -module -shared -avoid-version
87+
88+vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins
89+vpppluginsdir = ${libdir}/vpp_plugins
90+
91+# vppapitestplugins_LTLIBRARIES = portmirroring_test_plugin.la
92+vppplugins_LTLIBRARIES = portmirroring_plugin.la
93+
94+portmirroring_plugin_la_SOURCES = \
95+        portmirroring/api.c \
96+        portmirroring/port_mirroring.c \
97+		portmirroring/port_mirroring_in_node.c \
98+		portmirroring/port_mirroring_out_node.c
99+
100+BUILT_SOURCES = portmirroring/portmirroring.api.h portmirroring/portmirroring.py
101+
102+SUFFIXES = .api.h .api
103+
104+%.api.h: %.api
105+	mkdir -p `dirname $@` ; \
106+	$(CC) $(CPPFLAGS) -E -P -C -x c $^ \
107+	| vppapigen --input - --output $@ --show-name $@
108+
109+%.py: %.api
110+	$(info Creating Python binding for $@)
111+	$(CC) $(CPPFLAGS) -E -P -C -x c $<              \
112+	| vppapigen --input - --python -                \
113+	| pyvppapigen.py --input - > $@
114+
115+
116+pyapidir = ${prefix}/vpp_papi_plugins
117+pyapi_DATA = portmirroring/portmirroring.py
118+
119+noinst_HEADERS = portmirroring/port_mirroring.h portmirroring/portmirroring.api.h
120+
121+#portmirroring_test_plugin_la_SOURCES = \
122+#  portmirroring/portmirroring_test.c portmirroring/portmirroring_plugin.api.h
123+
124+# Remove *.la files
125+install-data-hook:
126+	@(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES))
127+
128+#	@(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES))
129diff --git a/plugins/portmirroring-plugin/configure.ac b/plugins/portmirroring-plugin/configure.ac
130new file mode 100644
131index 0000000..3af74c0
132--- /dev/null
133+++ b/plugins/portmirroring-plugin/configure.ac
134@@ -0,0 +1,16 @@
135+AC_INIT(pm_plugin, 1.0)
136+AM_INIT_AUTOMAKE
137+AM_SILENT_RULES([yes])
138+AC_PREFIX_DEFAULT([/usr])
139+
140+AC_PROG_LIBTOOL
141+AC_PROG_CC
142+
143+AC_ARG_WITH(dpdk,
144+            AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]),
145+            [with_dpdk=1],
146+            [with_dpdk=0])
147+
148+AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
149+AM_COND_IF(WITH_DPDK, CFLAGS+="-DDPDK=1")
150+AC_OUTPUT([Makefile])
151diff --git a/plugins/portmirroring-plugin/portmirroring/api.c b/plugins/portmirroring-plugin/portmirroring/api.c
152new file mode 100644
153index 0000000..696557b
154--- /dev/null
155+++ b/plugins/portmirroring-plugin/portmirroring/api.c
156@@ -0,0 +1,131 @@
157+/*
158+ * Copyright (c) 2016 Cisco and/or its affiliates.
159+ * Licensed under the Apache License, Version 2.0 (the "License");
160+ * you may not use this file except in compliance with the License.
161+ * You may obtain a copy of the License at:
162+ *
163+ *     http://www.apache.org/licenses/LICENSE-2.0
164+ *
165+ * Unless required by applicable law or agreed to in writing, software
166+ * distributed under the License is distributed on an "AS IS" BASIS,
167+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
168+ * See the License for the specific language governing permissions and
169+ * limitations under the License.
170+ */
171+
172+#include <portmirroring/port_mirroring.h>
173+
174+#include <vppinfra/byte_order.h>
175+#include <vlibapi/api.h>
176+#include <vlibmemory/api.h>
177+#include <vlibsocket/api.h>
178+
179+#define vl_msg_id(n,h) n,
180+typedef enum {
181+#include <portmirroring/portmirroring.api.h>
182+    /* We'll want to know how many messages IDs we need... */
183+    VL_MSG_FIRST_AVAILABLE,
184+} vl_msg_id_t;
185+#undef vl_msg_id
186+
187+
188+/* define message structures */
189+#define vl_typedefs
190+#include <portmirroring/portmirroring.api.h>
191+#undef vl_typedefs
192+
193+/* define generated endian-swappers */
194+#define vl_endianfun
195+#include <portmirroring/portmirroring.api.h>
196+#undef vl_endianfun
197+
198+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
199+
200+/* Get the API version number */
201+#define vl_api_version(n,v) static u32 api_version=(v);
202+#include <portmirroring/portmirroring.api.h>
203+#undef vl_api_version
204+
205+/* Macro to finish up custom dump fns */
206+#define FINISH                                  \
207+    vec_add1 (s, 0);                            \
208+    vl_print (handle, (char *)s);               \
209+    vec_free (s);                               \
210+    return handle;
211+
212+/*
213+ * A handy macro to set up a message reply.
214+ * Assumes that the following variables are available:
215+ * mp - pointer to request message
216+ * rmp - pointer to reply message type
217+ * rv - return value
218+ */
219+
220+#define REPLY_MACRO(t)                                          \
221+do {                                                            \
222+    unix_shared_memory_queue_t * q =                            \
223+    vl_api_client_index_to_input_queue (mp->client_index);      \
224+    if (!q)                                                     \
225+        return;                                                 \
226+                                                                \
227+    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
228+    rmp->_vl_msg_id = ntohs((t)+pmm->msg_id_base);               \
229+    rmp->context = mp->context;                                 \
230+    rmp->retval = ntohl(rv);                                    \
231+                                                                \
232+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
233+} while(0);
234+
235+static void
236+vl_api_pm_conf_t_handler
237+(vl_api_pm_conf_t * mp)
238+{
239+  pm_main_t *pmm = &pm_main;
240+  vl_api_pm_conf_reply_t * rmp;
241+  int rv = 0;
242+
243+  rv = pm_conf(mp->dst_interface, mp->from_node, mp->is_del);
244+
245+  REPLY_MACRO (VL_API_PM_CONF_REPLY);
246+}
247+
248+static void *vl_api_pm_conf_t_print
249+(vl_api_pm_conf_t *mp, void * handle)
250+{
251+  u8 * s;
252+  s = format (0, "SCRIPT: pm_conf ");
253+  s = format (s, "%u ", mp->dst_interface);
254+  s = format (s, "%u ", mp->from_node);
255+  s = format (s, "%s ", (mp->is_del? "DELETE" : "ADD" ));
256+  FINISH;
257+}
258+
259+
260+/* List of message types that this plugin understands */
261+#define foreach_pm_plugin_api_msg            \
262+_(PM_CONF, pm_conf)                          \
263+
264+
265+static clib_error_t * pm_api_init (vlib_main_t * vm)
266+{
267+  pm_main_t *pmm = &pm_main;
268+  u8 *name = format (0, "portmirroring_%08x%c", api_version, 0);
269+  pmm->msg_id_base = vl_msg_api_get_msg_ids
270+      ((char *) name, VL_MSG_FIRST_AVAILABLE);
271+
272+#define _(N,n)                                                  \
273+    vl_msg_api_set_handlers((VL_API_##N + pmm->msg_id_base),     \
274+                           #n,                  \
275+                           vl_api_##n##_t_handler,              \
276+                           vl_noop_handler,                     \
277+                           vl_api_##n##_t_endian,               \
278+                           vl_api_##n##_t_print,                \
279+                           sizeof(vl_api_##n##_t), 1);
280+  foreach_pm_plugin_api_msg;
281+#undef _
282+
283+  return 0;
284+}
285+
286+VLIB_INIT_FUNCTION (pm_api_init);
287+
288diff --git a/plugins/portmirroring-plugin/portmirroring/flowdata.h b/plugins/portmirroring-plugin/portmirroring/flowdata.h
289new file mode 120000
290index 0000000..67f4f12
291--- /dev/null
292+++ b/plugins/portmirroring-plugin/portmirroring/flowdata.h
293@@ -0,0 +1 @@
294+../../flowtable-plugin/flowtable/flowdata.h
295\ No newline at end of file
296diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c
297new file mode 100644
298index 0000000..46b6e0f
299--- /dev/null
300+++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c
301@@ -0,0 +1,134 @@
302+/*
303+ * Copyright (c) 2015 Cisco and/or its affiliates.
304+ * Licensed under the Apache License, Version 2.0 (the "License");
305+ * you may not use this file except in compliance with the License.
306+ * You may obtain a copy of the License at:
307+ *
308+ *     http://www.apache.org/licenses/LICENSE-2.0
309+ *
310+ * Unless required by applicable law or agreed to in writing, software
311+ * distributed under the License is distributed on an "AS IS" BASIS,
312+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
313+ * See the License for the specific language governing permissions and
314+ * limitations under the License.
315+ */
316+
317+#if DPDK != 1
318+#error "DPDK needed"
319+#endif
320+
321+#include <vnet/plugin/plugin.h>
322+#include <vnet/api_errno.h>
323+#include <portmirroring/port_mirroring.h>
324+
325+int pm_conf(u8 dst_interface, u8 from_node, u8 is_del)
326+{
327+    pm_main_t *pm = &pm_main;
328+
329+    if(is_del == 0) {
330+        pm->sw_if_index = dst_interface;
331+        pm->from_node = from_node;
332+        if (from_node == PM_FROM_FLOWTABLE) {
333+            pm->next_node_index = PM_IN_HIT_NEXT_ETHERNET_INPUT;
334+        } else if (from_node == PM_FROM_CLASSSIFIER) {
335+            pm->next_node_index = PM_IN_HIT_NEXT_L2_LEARN;
336+        } else {
337+             pm->next_node_index = PM_IN_HIT_NEXT_ERROR;
338+            return -1;
339+        }
340+    } else {
341+        pm->sw_if_index = ~0;
342+        pm->from_node = ~0;
343+        pm->next_node_index = ~0;
344+    }
345+
346+    return 0;
347+}
348+
349+static clib_error_t *
350+set_pm_command_fn (vlib_main_t * vm,
351+        unformat_input_t * input, vlib_cli_command_t * cmd)
352+{
353+    pm_main_t *pm = &pm_main;
354+    int enable_disable = 1;
355+    int sw_if_index = ~0;
356+    int from_node = ~0;
357+
358+    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
359+    {
360+        if (unformat (input, "from %U", unformat_vlib_node,
361+                    pm->vnet_main, &from_node));
362+        if (unformat (input, "to %U", unformat_vnet_sw_interface,
363+                    pm->vnet_main, &sw_if_index));
364+        else if (unformat (input, "del"))
365+            enable_disable = 0;
366+        else if (unformat (input, "disable"))
367+            enable_disable = 0;
368+        else
369+            break;
370+    }
371+
372+    if (sw_if_index == ~0)
373+        return clib_error_return (0, "interface required");
374+    if (from_node == ~0)
375+        return clib_error_return (0, "previous node required");
376+
377+
378+    if (enable_disable)
379+    {
380+        pm->sw_if_index = sw_if_index;
381+        pm->from_node = from_node;
382+        return 0;
383+    }
384+    else
385+    {
386+        pm->sw_if_index = ~0;
387+        pm->from_node = ~0;
388+    }
389+
390+
391+    return 0;
392+}
393+
394+VLIB_CLI_COMMAND (set_pm_command, static) =
395+{
396+    .path = "set pm",
397+    .short_help = "set pm from <0|1> to <intfc> [del]",
398+    .function = set_pm_command_fn,
399+};
400+
401+
402+static clib_error_t *
403+pm_init (vlib_main_t * vm)
404+{
405+    pm_main_t *pm = &pm_main;
406+
407+    pm->sw_if_index = ~0;
408+    pm->from_node = ~0;
409+
410+    pm->pm_in_hit_node_index = pm_in_hit_node.index;
411+    pm->pm_out_hit_node_index = pm_out_hit_node.index;
412+
413+    /* pm-out previous node */
414+    u32 l2out_classify_idx =  vlib_get_node_by_name(vm, (u8*) "l2-output-classify")->index;
415+    vlib_node_add_next(vm, l2out_classify_idx, pm->pm_out_hit_node_index);
416+
417+    /* pm-in & pm-out next node */
418+    pm->interface_output_node_index = vlib_get_node_by_name(vm, (u8*) "interface-output")->index;
419+
420+    return 0;
421+}
422+
423+VLIB_INIT_FUNCTION (pm_init);
424+
425+clib_error_t *
426+vlib_plugin_register (vlib_main_t * vm,
427+                      vnet_plugin_handoff_t * h,
428+                      int from_early_init)
429+{
430+  clib_error_t *error = 0;
431+  pm_main_t *pm = &pm_main;
432+  pm->vnet_main = vnet_get_main();
433+  pm->vlib_main = vm;
434+  return error;
435+}
436diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.h b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h
437new file mode 100644
438index 0000000..8e9826f
439--- /dev/null
440+++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h
441@@ -0,0 +1,67 @@
442+/*
443+ * Copyright (c) 2015 Cisco and/or its affiliates.
444+ * Licensed under the Apache License, Version 2.0 (the "License");
445+ * you may not use this file except in compliance with the License.
446+ * You may obtain a copy of the License at:
447+ *
448+ *     http://www.apache.org/licenses/LICENSE-2.0
449+ *
450+ * Unless required by applicable law or agreed to in writing, software
451+ * distributed under the License is distributed on an "AS IS" BASIS,
452+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
453+ * See the License for the specific language governing permissions and
454+ * limitations under the License.
455+ */
456+
457+#ifndef __port_mirroring_h__
458+#define __port_mirroring_h__
459+
460+#include <vnet/vnet.h>
461+#include <vnet/ip/ip.h>
462+
463+enum {
464+    PM_FROM_CLASSSIFIER = 0,
465+    PM_FROM_FLOWTABLE = 1,
466+    PM_FROM_MAX
467+};
468+
469+typedef enum {
470+    PM_IN_HIT_NEXT_ERROR,
471+    PM_IN_HIT_NEXT_ETHERNET_INPUT,
472+    PM_IN_HIT_NEXT_L2_LEARN,
473+    PM_IN_HIT_N_NEXT,
474+} pm_in_hit_next_t;
475+
476+typedef struct
477+{
478+  /* mirror interface index */
479+  u32 sw_if_index;
480+  u32 from_node;
481+
482+  /* Hit node index */
483+  u32 pm_in_hit_node_index;
484+  u32 pm_out_hit_node_index;
485+
486+  u32 interface_output_node_index;
487+
488+  /* depends on the previous node */
489+  u32 next_node_index;
490+
491+  /* convenience */
492+  vlib_main_t *vlib_main;
493+  vnet_main_t *vnet_main;
494+
495+  /**
496+   * API dynamically registered base ID.
497+   */
498+  u16 msg_id_base;
499+} pm_main_t;
500+
501+pm_main_t pm_main;
502+
503+int pm_conf(u8 dst_interface, u8 from_node, u8 is_del);
504+
505+extern vlib_node_registration_t pm_in_hit_node;
506+extern vlib_node_registration_t pm_out_hit_node;
507+
508+#endif /* __port_mirroring_h__ */
509diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c
510new file mode 100644
511index 0000000..04b05df
512--- /dev/null
513+++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c
514@@ -0,0 +1,178 @@
515+/*
516+ * Copyright (c) 2015 Cisco and/or its affiliates.
517+ * Licensed under the Apache License, Version 2.0 (the "License");
518+ * you may not use this file except in compliance with the License.
519+ * You may obtain a copy of the License at:
520+ *
521+ *     http://www.apache.org/licenses/LICENSE-2.0
522+ *
523+ * Unless required by applicable law or agreed to in writing, software
524+ * distributed under the License is distributed on an "AS IS" BASIS,
525+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
526+ * See the License for the specific language governing permissions and
527+ * limitations under the License.
528+ */
529+
530+#include <vlib/vlib.h>
531+#include <vnet/vnet.h>
532+#include <vppinfra/error.h>
533+
534+#if DPDK != 1
535+#error "DPDK needed"
536+#endif
537+
538+#include <portmirroring/port_mirroring.h>
539+#include <vnet/dpdk_replication.h>
540+
541+#include <vppinfra/error.h>
542+#include <vppinfra/elog.h>
543+
544+#include "flowdata.h"
545+#include "port_mirroring.h"
546+
547+vlib_node_registration_t pm_in_hit_node;
548+
549+typedef struct {
550+    u32 next_index;
551+} pm_in_hit_trace_t;
552+
553+/* packet trace format function */
554+static u8 *
555+format_pm_in_hit_trace(u8 * s, va_list * args)
556+{
557+    CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
558+    CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
559+    pm_in_hit_trace_t * t = va_arg(*args, pm_in_hit_trace_t *);
560+
561+    s = format(s, "PM_IN_HIT: next index %d", t->next_index);
562+
563+    return s;
564+}
565+
566+vlib_node_registration_t pm_in_hit_node;
567+
568+#define foreach_pm_in_hit_error                    \
569+    _(HITS, "PM in packets processed")                 \
570+    _(NO_INTF_OUT, "No out interface configured")
571+
572+typedef enum {
573+#define _(sym, str) PM_IN_HIT_ERROR_ ## sym,
574+    foreach_pm_in_hit_error
575+#undef _
576+    PM_IN_HIT_N_ERROR,
577+} pm_in_hit_error_t;
578+
579+static char * pm_in_hit_error_strings[] = {
580+#define _(sym, string) string,
581+    foreach_pm_in_hit_error
582+#undef _
583+};
584+
585+static uword
586+pm_in_hit_node_fn(vlib_main_t * vm,
587+    vlib_node_runtime_t * node, vlib_frame_t * frame)
588+{
589+    u32 n_left_from, * from, * to_next;
590+    u32 next_index;
591+    vlib_frame_t * dup_frame = 0;
592+    u32 * to_int_next = 0;
593+    pm_main_t * pm = &pm_main;
594+
595+    from = vlib_frame_vector_args(frame);
596+    n_left_from = frame->n_vectors;
597+    next_index = node->cached_next_index;
598+
599+    if (pm->sw_if_index == ~0) {
600+        vlib_node_increment_counter (vm, pm_in_hit_node.index,
601+                PM_IN_HIT_ERROR_NO_INTF_OUT,
602+                n_left_from);
603+    } else {
604+        /* The intercept frame... */
605+        dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index);
606+        to_int_next = vlib_frame_vector_args(dup_frame);
607+    }
608+
609+    while (n_left_from > 0)
610+    {
611+        u32 n_left_to_next;
612+
613+        vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
614+
615+        while (n_left_from > 0 && n_left_to_next > 0)
616+        {
617+            u32 bi0;
618+            vlib_buffer_t * b0;
619+            vlib_buffer_t * c0;
620+            u32 next0 = pm->next_node_index;
621+
622+            /* speculatively enqueue b0 to the current next frame */
623+            bi0 = from[0];
624+            to_next[0] = bi0;
625+            from += 1;
626+            to_next += 1;
627+            n_left_from -= 1;
628+            n_left_to_next -= 1;
629+
630+            b0 = vlib_get_buffer(vm, bi0);
631+            if (PREDICT_TRUE(to_int_next != 0))
632+            {
633+                /* Make a copy */
634+                c0 = vlib_dpdk_clone_buffer(vm, b0);
635+
636+                vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index;
637+
638+                to_int_next[0] = vlib_get_buffer_index(vm, c0);
639+                to_int_next++;
640+
641+            }
642+
643+            if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
644+                && (b0->flags & VLIB_BUFFER_IS_TRACED)))
645+            {
646+                pm_in_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t));
647+                t->next_index = next0;
648+            }
649+
650+            /* restore opaque value which was used by the flowtable plugin */
651+            if (pm->from_node == PM_FROM_FLOWTABLE) {
652+                flow_data_t flow_data;
653+                clib_memcpy(&flow_data, b0->opaque, sizeof(flow_data));
654+                vnet_buffer (b0)->sw_if_index[VLIB_RX] = flow_data.data.sw_if_index_current;
655+            }
656+
657+            /* verify speculative enqueue, maybe switch current next frame */
658+            vlib_validate_buffer_enqueue_x1(vm, node, next_index,
659+                to_next, n_left_to_next, bi0, next0);
660+        }
661+
662+        vlib_put_next_frame(vm, node, next_index, n_left_to_next);
663+    }
664+
665+    if (dup_frame)
666+    {
667+        dup_frame->n_vectors = frame->n_vectors;
668+        vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame);
669+    }
670+
671+    vlib_node_increment_counter(vm, pm_in_hit_node.index,
672+        PM_IN_HIT_ERROR_HITS, frame->n_vectors);
673+    return frame->n_vectors;
674+}
675+
676+VLIB_REGISTER_NODE(pm_in_hit_node) = {
677+    .function = pm_in_hit_node_fn,
678+    .name = "pm-in-hit",
679+    .vector_size = sizeof(u32),
680+    .format_trace = format_pm_in_hit_trace,
681+    .type = VLIB_NODE_TYPE_INTERNAL,
682+
683+    .n_errors = ARRAY_LEN(pm_in_hit_error_strings),
684+    .error_strings = pm_in_hit_error_strings,
685+
686+    .n_next_nodes = PM_IN_HIT_N_NEXT,
687+    .next_nodes = {
688+        [PM_IN_HIT_NEXT_ERROR] = "error-drop",
689+        [PM_IN_HIT_NEXT_ETHERNET_INPUT] = "ethernet-input",
690+        [PM_IN_HIT_NEXT_L2_LEARN] = "l2-learn",
691+    }
692+};
693diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c
694new file mode 100644
695index 0000000..9eebb88
696--- /dev/null
697+++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c
698@@ -0,0 +1,168 @@
699+/*
700+ * Copyright (c) 2015 Cisco and/or its affiliates.
701+ * Licensed under the Apache License, Version 2.0 (the "License");
702+ * you may not use this file except in compliance with the License.
703+ * You may obtain a copy of the License at:
704+ *
705+ *     http://www.apache.org/licenses/LICENSE-2.0
706+ *
707+ * Unless required by applicable law or agreed to in writing, software
708+ * distributed under the License is distributed on an "AS IS" BASIS,
709+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
710+ * See the License for the specific language governing permissions and
711+ * limitations under the License.
712+ */
713+
714+#include <vlib/vlib.h>
715+#include <vnet/vnet.h>
716+#include <vppinfra/error.h>
717+
718+#if DPDK != 1
719+#error "DPDK needed"
720+#endif
721+
722+#include <portmirroring/port_mirroring.h>
723+#include <vnet/dpdk_replication.h>
724+
725+#include <vppinfra/error.h>
726+#include <vppinfra/elog.h>
727+
728+vlib_node_registration_t pm_out_hit_node;
729+
730+typedef struct {
731+    u32 next_index;
732+} pm_out_hit_trace_t;
733+
734+/* packet trace format function */
735+static u8 *
736+format_pm_out_hit_trace(u8 * s, va_list * args)
737+{
738+    CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
739+    CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
740+    pm_out_hit_trace_t * t = va_arg(*args, pm_out_hit_trace_t *);
741+
742+    s = format(s, "PM_OUT_HIT: next index %d", t->next_index);
743+
744+    return s;
745+}
746+
747+#define foreach_pm_out_hit_error                    \
748+    _(HITS, "PM out packets processed")                 \
749+    _(NO_COLLECTOR, "No collector configured")
750+
751+typedef enum {
752+#define _(sym, str) PM_OUT_HIT_ERROR_ ## sym,
753+    foreach_pm_out_hit_error
754+#undef _
755+    PM_OUT_HIT_N_ERROR,
756+} pm_out_hit_error_t;
757+
758+static char * pm_out_hit_error_strings[] = {
759+#define _(sym, string) string,
760+    foreach_pm_out_hit_error
761+#undef _
762+};
763+
764+typedef enum {
765+    PM_OUT_HIT_NEXT_IF_OUT,
766+    PM_OUT_HIT_N_NEXT,
767+} pm_out_hit_next_t;
768+
769+static uword
770+pm_out_hit_node_fn(vlib_main_t * vm,
771+    vlib_node_runtime_t * node, vlib_frame_t * frame)
772+{
773+    u32 n_left_from, * from, * to_next;
774+    pm_out_hit_next_t next_index;
775+    vlib_frame_t * dup_frame = 0;
776+    u32 * to_int_next = 0;
777+    pm_main_t * pm = &pm_main;
778+
779+    from = vlib_frame_vector_args(frame);
780+    n_left_from = frame->n_vectors;
781+    next_index = node->cached_next_index;
782+
783+    if (pm->sw_if_index == ~0) {
784+        vlib_node_increment_counter (vm, pm_out_hit_node.index,
785+                PM_OUT_HIT_ERROR_NO_COLLECTOR,
786+                n_left_from);
787+    } else {
788+        /* The intercept frame... */
789+        dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index);
790+        to_int_next = vlib_frame_vector_args(dup_frame);
791+    }
792+
793+    while (n_left_from > 0)
794+    {
795+        u32 n_left_to_next;
796+
797+        vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
798+
799+        while (n_left_from > 0 && n_left_to_next > 0)
800+        {
801+            u32 bi0;
802+            vlib_buffer_t * b0;
803+            vlib_buffer_t * c0;
804+            u32 next0 = PM_OUT_HIT_NEXT_IF_OUT;
805+
806+            /* speculatively enqueue b0 to the current next frame */
807+            bi0 = from[0];
808+            to_next[0] = bi0;
809+            from += 1;
810+            to_next += 1;
811+            n_left_from -= 1;
812+            n_left_to_next -= 1;
813+
814+            b0 = vlib_get_buffer(vm, bi0);
815+            if (PREDICT_TRUE(to_int_next != 0))
816+            {
817+                /* Make an intercept copy */
818+                c0 = vlib_dpdk_clone_buffer(vm, b0);
819+
820+                vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index;
821+
822+                to_int_next[0] = vlib_get_buffer_index(vm, c0);
823+                to_int_next++;
824+            }
825+
826+            if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
827+                && (b0->flags & VLIB_BUFFER_IS_TRACED)))
828+            {
829+                pm_out_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t));
830+                t->next_index = next0;
831+            }
832+            /* verify speculative enqueue, maybe switch current next frame */
833+            vlib_validate_buffer_enqueue_x1(vm, node, next_index,
834+                to_next, n_left_to_next, bi0, next0);
835+        }
836+
837+        vlib_put_next_frame(vm, node, next_index, n_left_to_next);
838+    }
839+
840+    if (dup_frame)
841+    {
842+        dup_frame->n_vectors = frame->n_vectors;
843+        vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame);
844+    }
845+
846+    vlib_node_increment_counter(vm, pm_out_hit_node.index,
847+        PM_OUT_HIT_ERROR_HITS, frame->n_vectors);
848+    return frame->n_vectors;
849+}
850+
851+VLIB_REGISTER_NODE(pm_out_hit_node) = {
852+    .function = pm_out_hit_node_fn,
853+    .name = "pm-out-hit",
854+    .vector_size = sizeof(u32),
855+    .format_trace = format_pm_out_hit_trace,
856+    .type = VLIB_NODE_TYPE_INTERNAL,
857+
858+    .n_errors = ARRAY_LEN(pm_out_hit_error_strings),
859+    .error_strings = pm_out_hit_error_strings,
860+
861+    .n_next_nodes = PM_OUT_HIT_N_NEXT,
862+
863+    .next_nodes = {
864+        [PM_OUT_HIT_NEXT_IF_OUT] = "interface-output",
865+    }
866+};
867diff --git a/plugins/portmirroring-plugin/portmirroring/portmirroring.api b/plugins/portmirroring-plugin/portmirroring/portmirroring.api
868new file mode 100644
869index 0000000..fe86bb2
870--- /dev/null
871+++ b/plugins/portmirroring-plugin/portmirroring/portmirroring.api
872@@ -0,0 +1,21 @@
873+/** \brief Configure Port-Mirroring global parameters
874+    @param client_index - opaque cookie to identify the sender
875+    @param context - sender context, to match reply w/ request
876+    @param dst_interface - name or id of interface to copy packets to
877+    @param from_node - 0|1 (classifier|flowtable)
878+    @param is_del - Whether we have to delete any port mirrring information
879+*/
880+define pm_conf
881+{
882+  u32 client_index;
883+  u32 context;
884+  u8 dst_interface;
885+  u8 from_node;
886+  u8 is_del;
887+};
888+
889+define pm_conf_reply {
890+  u32 context;
891+  i32 retval;
892+};
893+
894-- 
8952.10.0.rc1.15.g5cb0d5a
896
897