span: add feature (rx only) (VPP-185)

Change-Id: I0f7cbf06b5a5acd745d13c9f5c761ea18132107b
Signed-off-by: marek <mazavods@gmail.com>
Signed-off-by: Damjan Marion <damarion@cisco.com>
Signed-off-by: Pavel Kotucek <pkotucek@cisco.com>
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/vnet/Makefile.am b/vnet/Makefile.am
index fef928a..3ffcca8 100644
--- a/vnet/Makefile.am
+++ b/vnet/Makefile.am
@@ -632,6 +632,17 @@
   vnet/lawful-intercept/lawful_intercept.h
 
 ########################################
+# SPAN (port mirroring)
+########################################
+
+libvnet_la_SOURCES +=				\
+  vnet/span/span.c	\
+  vnet/span/node.c
+
+nobase_include_HEADERS += 			\
+  vnet/span/span.h
+
+########################################
 # Packet generator
 ########################################
 
diff --git a/vnet/vnet/devices/devices.c b/vnet/vnet/devices/devices.c
index 928b0b4..3eef95b 100644
--- a/vnet/vnet/devices/devices.c
+++ b/vnet/vnet/devices/devices.c
@@ -52,6 +52,12 @@
   .runs_before = VNET_FEATURES ("ethernet-input"),
 };
 
+VNET_FEATURE_INIT (span_input, static) = {
+  .arc_name = "device-input",
+  .node_name = "span-input",
+  .runs_before = VNET_FEATURES ("ethernet-input"),
+};
+
 VNET_FEATURE_INIT (ethernet_input, static) = {
   .arc_name = "device-input",
   .node_name = "ethernet-input",
diff --git a/vnet/vnet/feature/feature.c b/vnet/vnet/feature/feature.c
index 1f46285..c8cde36 100644
--- a/vnet/vnet/feature/feature.c
+++ b/vnet/vnet/feature/feature.c
@@ -187,6 +187,11 @@
   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
   ci = cm->config_index_by_sw_if_index[sw_if_index];
 
+  vec_validate (fm->feature_count_by_sw_if_index[arc_index], sw_if_index);
+  if (!enable_disable
+      && fm->feature_count_by_sw_if_index[arc_index][sw_if_index] < 1)
+    return 0;
+
   ci = (enable_disable
 	? vnet_config_add_feature
 	: vnet_config_del_feature)
diff --git a/vnet/vnet/feature/feature.h b/vnet/vnet/feature/feature.h
index b026cdf..667c5e3 100644
--- a/vnet/vnet/feature/feature.h
+++ b/vnet/vnet/feature/feature.h
@@ -145,6 +145,13 @@
   return &fm->feature_config_mains[arc_index];
 }
 
+static_always_inline vnet_feature_config_main_t *
+vnet_feature_get_config_main (u16 arc)
+{
+  vnet_feature_main_t *fm = &feature_main;
+  return &fm->feature_config_mains[arc];
+}
+
 static_always_inline int
 vnet_have_features (u8 arc, u32 sw_if_index)
 {
diff --git a/vnet/vnet/span/node.c b/vnet/vnet/span/node.c
new file mode 100644
index 0000000..2012283
--- /dev/null
+++ b/vnet/vnet/span/node.c
@@ -0,0 +1,260 @@
+/*
+ * 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/error.h>
+
+#include <vnet/span/span.h>
+
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+vlib_node_registration_t span_node;
+
+/* packet trace format function */
+u8 *
+format_span_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 *);
+  span_trace_t *t = va_arg (*args, span_trace_t *);
+
+  vnet_main_t *vnm = &vnet_main;
+  s = format (s, "SPAN: mirrored %U -> %U",
+	      format_vnet_sw_if_index_name, vnm, t->src_sw_if_index,
+	      format_vnet_sw_if_index_name, vnm, t->mirror_sw_if_index);
+
+  return s;
+}
+
+#define foreach_span_error                      \
+_(HITS, "SPAN incomming packets processed")
+
+typedef enum
+{
+#define _(sym,str) SPAN_ERROR_##sym,
+  foreach_span_error
+#undef _
+    SPAN_N_ERROR,
+} span_error_t;
+
+static char *span_error_strings[] = {
+#define _(sym,string) string,
+  foreach_span_error
+#undef _
+};
+
+static uword
+span_node_fn (vlib_main_t * vm,
+	      vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  span_main_t *sm = &span_main;
+  vnet_main_t *vnm = &vnet_main;
+  u32 n_left_from, *from, *to_next, *to_mirror_next = 0;
+  u32 n_span_packets = 0;
+  u32 next_index, mirror_sw_if_index0, mirror_sw_if_index1;
+  u32 last_mirror_sw_if_index = ~0;
+  vlib_frame_t *mirror_frame = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  /* TODO dual loop */
+  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;
+	  u32 bi1;
+	  vlib_buffer_t *b0, *c0;
+	  vlib_buffer_t *b1, *c1;
+	  u32 sw_if_index0;
+	  u32 next0 = 0;	//SPAN_NEXT_ORIG_ETHERNET;
+	  u32 sw_if_index1;
+	  u32 next1 = 1;	//SPAN_NEXT_ORIG_ETHERNET;
+
+	  /* speculatively enqueue b0, b1 to the current next frame */
+	  to_next[0] = bi0 = from[0];
+	  to_next[1] = bi1 = from[1];
+	  to_next += 2;
+	  n_left_to_next -= 2;
+	  from += 2;
+	  n_left_from -= 2;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+	  mirror_sw_if_index0 = sm->dst_by_src_sw_if_index[sw_if_index0];
+	  b1 = vlib_get_buffer (vm, bi1);
+	  sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+	  mirror_sw_if_index1 = sm->dst_by_src_sw_if_index[sw_if_index1];
+
+	  /* get frame to mirror interface */
+	  if (PREDICT_FALSE
+	      ((last_mirror_sw_if_index != mirror_sw_if_index0)
+	       || mirror_frame == 0))
+	    {
+	      if (mirror_frame)
+		vnet_put_frame_to_sw_interface (vnm, last_mirror_sw_if_index,
+						mirror_frame);
+	      last_mirror_sw_if_index = mirror_sw_if_index0;
+	      mirror_frame =
+		vnet_get_frame_to_sw_interface (vnm, mirror_sw_if_index0);
+	      to_mirror_next = vlib_frame_vector_args (mirror_frame);
+	    }
+	  /* get frame to mirror interface */
+	  if (PREDICT_FALSE
+	      ((last_mirror_sw_if_index != mirror_sw_if_index1)
+	       || mirror_frame == 0))
+	    {
+	      if (mirror_frame)
+		vnet_put_frame_to_sw_interface (vnm, last_mirror_sw_if_index,
+						mirror_frame);
+	      last_mirror_sw_if_index = mirror_sw_if_index1;
+	      mirror_frame =
+		vnet_get_frame_to_sw_interface (vnm, mirror_sw_if_index0);
+	      to_mirror_next = vlib_frame_vector_args (mirror_frame);
+	    }
+	  c0 = vlib_buffer_copy (vm, b0);
+	  vnet_buffer (c0)->sw_if_index[VLIB_TX] = mirror_sw_if_index0;
+	  to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
+	  to_mirror_next += 1;
+	  mirror_frame->n_vectors++;
+
+	  vnet_feature_next (sw_if_index0, &next0, b0);
+
+	  c1 = vlib_buffer_copy (vm, b1);
+	  vnet_buffer (c1)->sw_if_index[VLIB_TX] = mirror_sw_if_index1;
+	  to_mirror_next[0] = vlib_get_buffer_index (vm, c1);
+	  to_mirror_next += 1;
+	  mirror_frame->n_vectors++;
+
+	  vnet_feature_next (sw_if_index1, &next1, b1);
+
+	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      span_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+	      t->src_sw_if_index = sw_if_index0;
+	      t->mirror_sw_if_index =
+		sm->dst_by_src_sw_if_index[sw_if_index0];
+	    }
+
+	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      span_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t));
+	      t->src_sw_if_index = sw_if_index1;
+	      t->mirror_sw_if_index =
+		sm->dst_by_src_sw_if_index[sw_if_index1];
+	    }
+	  /* verify speculative enqueue, 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, *c0;
+	  u32 sw_if_index0;
+	  u32 next0 = 0;	//SPAN_NEXT_ORIG_ETHERNET;
+
+	  /* speculatively enqueue b0 to the current next frame */
+	  to_next[0] = bi0 = from[0];
+	  to_next += 1;
+	  n_left_to_next -= 1;
+	  from += 1;
+	  n_left_from -= 1;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+	  mirror_sw_if_index0 = sm->dst_by_src_sw_if_index[sw_if_index0];
+
+	  /* get frame to mirror interface */
+	  if (PREDICT_FALSE
+	      ((last_mirror_sw_if_index != mirror_sw_if_index0)
+	       || mirror_frame == 0))
+	    {
+	      if (mirror_frame)
+		vnet_put_frame_to_sw_interface (vnm, last_mirror_sw_if_index,
+						mirror_frame);
+	      last_mirror_sw_if_index = mirror_sw_if_index0;
+	      mirror_frame =
+		vnet_get_frame_to_sw_interface (vnm, mirror_sw_if_index0);
+	      to_mirror_next = vlib_frame_vector_args (mirror_frame);
+	    }
+	  c0 = vlib_buffer_copy (vm, b0);
+	  vnet_buffer (c0)->sw_if_index[VLIB_TX] = mirror_sw_if_index0;
+	  to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
+	  to_mirror_next += 1;
+	  mirror_frame->n_vectors++;
+
+	  vnet_feature_next (sw_if_index0, &next0, b0);
+
+	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      span_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+	      t->src_sw_if_index = sw_if_index0;
+	      t->mirror_sw_if_index =
+		sm->dst_by_src_sw_if_index[sw_if_index0];
+	    }
+	  /* 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);
+    }
+
+  vnet_put_frame_to_sw_interface (vnm, last_mirror_sw_if_index, mirror_frame);
+  vlib_node_increment_counter (vm, span_node.index, SPAN_ERROR_HITS,
+			       n_span_packets);
+
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (span_node) = {
+  .function = span_node_fn,
+  .name = "span-input",
+  .vector_size = sizeof (u32),
+  .format_trace = format_span_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(span_error_strings),
+  .error_strings = span_error_strings,
+
+  .n_next_nodes = 0,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
+
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (span_node, span_node_fn)
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/span/span.c b/vnet/vnet/span/span.c
new file mode 100644
index 0000000..de43af0
--- /dev/null
+++ b/vnet/vnet/span/span.c
@@ -0,0 +1,139 @@
+/*
+ * 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 <vlib/vlib.h>
+#include <vppinfra/error.h>
+#include <vnet/feature/feature.h>
+
+#include <vnet/span/span.h>
+
+int
+span_add_delete_entry (vlib_main_t * vm,
+		       u32 src_sw_if_index, u32 dst_sw_if_index, u8 is_add)
+{
+  span_main_t *sm = &span_main;
+
+  if ((src_sw_if_index == ~0) || (dst_sw_if_index == ~0 && is_add)
+      || (src_sw_if_index == dst_sw_if_index))
+    return VNET_API_ERROR_INVALID_INTERFACE;
+
+  vnet_sw_interface_t *sw =
+    vnet_get_sw_interface (sm->vnet_main, src_sw_if_index);
+
+  vec_validate_aligned (sm->dst_by_src_sw_if_index, sw->hw_if_index,
+			CLIB_CACHE_LINE_BYTES);
+  sm->dst_by_src_sw_if_index[sw->hw_if_index] = is_add ? dst_sw_if_index : 0;
+  vnet_feature_enable_disable ("device-input", "span-input",
+			       sw->hw_if_index, is_add, 0, 0);
+  return 0;
+}
+
+static clib_error_t *
+set_interface_span_command_fn (vlib_main_t * vm,
+			       unformat_input_t * input,
+			       vlib_cli_command_t * cmd)
+{
+  span_main_t *sm = &span_main;
+  u32 src_sw_if_index = ~0;
+  u32 dst_sw_if_index = ~0;
+  u8 is_add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface,
+		    sm->vnet_main, &src_sw_if_index))
+	;
+      else if (unformat (input, "destination %U", unformat_vnet_sw_interface,
+			 sm->vnet_main, &dst_sw_if_index))
+	;
+      else if (unformat (input, "disable"))
+	is_add = 0;
+      else
+	break;
+    }
+
+  int rv =
+    span_add_delete_entry (vm, src_sw_if_index, dst_sw_if_index, is_add);
+  if (rv == VNET_API_ERROR_INVALID_INTERFACE)
+    return clib_error_return (0, "Invalid interface");
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_interface_span_command, static) = {
+  .path = "set interface span",
+  .short_help = "set interface span <if-name> [disable | destination <if-name>]",
+  .function = set_interface_span_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+show_interfaces_span_command_fn (vlib_main_t * vm,
+				 unformat_input_t * input,
+				 vlib_cli_command_t * cmd)
+{
+
+  span_main_t *sm = &span_main;
+  vnet_main_t *vnm = &vnet_main;
+  u32 src_sw_if_index = 0, *dst_sw_if_index;
+  u8 header = 1;
+
+  vec_foreach (dst_sw_if_index, sm->dst_by_src_sw_if_index)
+  {
+    if (*dst_sw_if_index > 0)	// && *dst_sw_if_index != ~0)
+      {
+	if (header)
+	  {
+	    vlib_cli_output (vm,
+			     "SPAN source interface to destination interface table");
+	    header = 0;
+	  }
+	vlib_cli_output (vm, "%32U => %-32U",
+			 format_vnet_sw_if_index_name, vnm, src_sw_if_index,
+			 format_vnet_sw_if_index_name, vnm, *dst_sw_if_index);
+      }
+    src_sw_if_index++;
+  }
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_interfaces_span_command, static) = {
+  .path = "show interfaces span",
+  .short_help = "Shows SPAN mirror table",
+  .function = show_interfaces_span_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+span_init (vlib_main_t * vm)
+{
+  span_main_t *sm = &span_main;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main ();
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (span_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/span/span.h b/vnet/vnet/span/span.h
new file mode 100644
index 0000000..751bebf
--- /dev/null
+++ b/vnet/vnet/span/span.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __span_h__
+#define __span_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+
+typedef struct
+{
+  /* destination interface index by source interface index */
+  u32 *dst_by_src_sw_if_index;
+
+  /* convenience */
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+} span_main_t;
+
+span_main_t span_main;
+
+typedef struct
+{
+  u32 src_sw_if_index;		/* mirrored interface index */
+  u32 mirror_sw_if_index;	/* output interface index */
+} span_trace_t;
+
+#endif /* __span_h__ */
+
+int
+span_add_delete_entry (vlib_main_t * vm, u32 src_sw_if_index,
+		       u32 dst_sw_if_index, u8 is_add);
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/span/span.md b/vnet/vnet/span/span.md
new file mode 100644
index 0000000..ee3f814
--- /dev/null
+++ b/vnet/vnet/span/span.md
@@ -0,0 +1,65 @@
+# VPP SPAN implementation
+
+This is a memo intended to contain documentation of the VPP SPAN implementation.
+Everything that is not directly obvious should come here.
+
+
+## Switched Port Analyzer (SPAN)
+Port mirroring is used on a network switch to send a copy of network packets seen on one switch port to a network monitoring connection on another switch port.
+Can be used by network engineers or administrators to measure performnce, analyze and debug data or diagnose errors on a network.
+
+### RX traffic node
+There is one static node to mirror incomming packets.
+* span-input: Creates a copy of incomming buffer due to incomming buffers can be reused internally.
+
+Chaining: dpdk-input -> span-input ->
+* original buffer is sent to ethernet-input for processing
+* buffer copy is sent to interface-output
+
+### Configuration
+SPAN supports the following CLI configuration commands:
+
+#### Enable/Disable SPAN (CLI)
+	set interface span <if-name> [disable | destination <if-name>]
+
+<if-name>: mirrored interface name
+destination <if-name>: monitoring interface name
+disable: delete mirroring
+
+#### Enable/Disabl SPAN (API)
+SPAN supports the following API configuration command:
+	sw_interface_span_enable_disable src GigabitEthernet0/8/0 dst GigabitEthernet0/9/0
+	sw_interface_span_enable_disable src_sw_if_index 1 dst_sw_if_index 2
+
+src/src_sw_if_index: mirrored interface name
+dst/dst_sw_if_index: monitoring interface name
+
+#### Remove SPAN entry (API)
+SPAN supports the following API configuration command:
+	sw_interface_span_enable_disable src_sw_if_index 1 dst_sw_if_index 2 disable
+
+src_sw_if_index: mirrored interface name
+dst_sw_if_index: monitoring interface name
+
+### Configuration example
+
+Mirror all packets on interface GigabitEthernet0/10/0 to interface GigabitEthernet0/11/0.
+
+Configure IPv4 addresses on mirrored interface:
+set interface ip address GigabitEthernet0/10/0 192.168.1.13/24
+set interface state GigabitEthernet0/10/0 up
+
+Configure IPv4 addresses on monitoring interface:
+set interface ip address GigabitEthernet0/11/0 192.168.2.13/24
+set interface state GigabitEthernet0/11/0 up
+
+Configure SPAN
+set span src GigabitEthernet0/10/0 dst GigabitEthernet0/11/0
+
+### Operational data
+
+Active SPAN mirroring CLI show command:
+    show interfaces span
+
+Active SPAN mirroring API dump command:
+    sw_interface_span_dump