Port NSH plugin to VPP 89/14489/6
authorHongjun Ni <hongjun.ni@intel.com>
Mon, 27 Aug 2018 12:27:43 +0000 (20:27 +0800)
committerDamjan Marion <dmarion@me.com>
Tue, 28 Aug 2018 06:04:57 +0000 (06:04 +0000)
Please refer to https://wiki.fd.io/view/NSH_SFC

Change-Id: Iba7e33e4dbb064c1527aaddbe8dce4b6b63a627a
Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
Signed-off-by: Keith Burns (alagalah) <alagalah@gmail.com>
22 files changed:
src/configure.ac
src/plugins/Makefile.am
src/plugins/ioam.am
src/plugins/nsh.am [new file with mode: 0644]
src/plugins/nsh/CMakeLists.txt [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export_thread.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_node.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/md2_ioam_transit.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.h [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_api.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_trace.c [new file with mode: 0644]
src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_util.h [new file with mode: 0644]
src/plugins/nsh/nsh.api [new file with mode: 0644]
src/plugins/nsh/nsh.c [new file with mode: 0644]
src/plugins/nsh/nsh.h [new file with mode: 0644]
src/plugins/nsh/nsh_error.def [new file with mode: 0644]
src/plugins/nsh/nsh_output.c [new file with mode: 0644]
src/plugins/nsh/nsh_packet.h [new file with mode: 0644]
src/plugins/nsh/nsh_pop.c [new file with mode: 0644]
src/plugins/nsh/nsh_test.c [new file with mode: 0644]

index f35e139..4c9c25d 100644 (file)
@@ -315,11 +315,12 @@ PLUGIN_ENABLED(l2e)
 PLUGIN_ENABLED(lacp)
 PLUGIN_ENABLED(lb)
 PLUGIN_ENABLED(mactime)
+PLUGIN_ENABLED(map)
 PLUGIN_ENABLED(marvell)
 PLUGIN_ENABLED(memif)
-PLUGIN_ENABLED(pppoe)
 PLUGIN_ENABLED(nat)
-PLUGIN_ENABLED(map)
+PLUGIN_ENABLED(nsh)
+PLUGIN_ENABLED(pppoe)
 PLUGIN_ENABLED(srv6ad)
 PLUGIN_ENABLED(srv6am)
 PLUGIN_ENABLED(srv6as)
index 12f8ddf..1000ee8 100644 (file)
@@ -87,6 +87,10 @@ if ENABLE_MACTIME_PLUGIN
 include mactime.am
 endif
 
+if ENABLE_MAP_PLUGIN
+include map.am
+endif
+
 if ENABLE_MARVELL_PLUGIN
 include marvell.am
 endif
@@ -95,6 +99,14 @@ if ENABLE_MEMIF_PLUGIN
 include memif.am
 endif
 
+if ENABLE_NAT_PLUGIN
+include nat.am
+endif
+
+if ENABLE_NSH_PLUGIN
+include nsh.am
+endif
+
 if ENABLE_PPPOE_PLUGIN
 include pppoe.am
 endif
@@ -111,14 +123,6 @@ if ENABLE_SRV6AS_PLUGIN
 include srv6_as.am
 endif
 
-if ENABLE_NAT_PLUGIN
-include nat.am
-endif
-
-if ENABLE_MAP_PLUGIN
-include map.am
-endif
-
 if ENABLE_STN_PLUGIN
 include stn.am
 endif
index 88439ad..6210958 100644 (file)
@@ -213,8 +213,8 @@ ioam_test_plugin_la_SOURCES = \
   ioam/ioam_test.c                                             \
   ioam/lib-pot/pot_test.c                                      \
   ioam/lib-pot/pot_plugin.api.h                                        \
-  ioam/lib-trace/trace_test.c                                  \
-  ioam/lib-trace/trace_plugin.api.h                            \
+  ioam/lib-trace/trace_test.c            \
+  ioam/lib-trace/trace_plugin.api.h   \
   ioam/export/ioam_export_test.c                               \
   ioam/export/ioam_export_plugin.api.h                         \
   ioam/lib-vxlan-gpe/vxlan_gpe_test.c                          \
diff --git a/src/plugins/nsh.am b/src/plugins/nsh.am
new file mode 100644 (file)
index 0000000..fb1d558
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (c) 2016 Intel 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.
+
+vppapitestplugins_LTLIBRARIES += nsh_test_plugin.la
+vppplugins_LTLIBRARIES += nsh_plugin.la
+
+nsh_plugin_la_SOURCES =            \
+    nsh/nsh.c               \
+       nsh/nsh_pop.c           \
+       nsh/nsh_output.c        \
+       nsh/nsh-md2-ioam/nsh_md2_ioam.c       \
+       nsh/nsh-md2-ioam/nsh_md2_ioam_api.c   \
+       nsh/nsh-md2-ioam/md2_ioam_transit.c   \
+       nsh/nsh-md2-ioam/nsh_md2_ioam_trace.c \
+       nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export.c   \
+       nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export_thread.c   \
+       nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_node.c
+
+BUILT_SOURCES +=            \
+       nsh/nsh_packet.h        \
+       nsh/nsh.api.h           \
+       nsh/nsh.api.json        \
+       nsh/nsh-md2-ioam/nsh_md2_ioam.h   \
+       nsh/nsh-md2-ioam/nsh_md2_ioam_util.h
+
+API_FILES += nsh/nsh.api
+
+nobase_apiinclude_HEADERS +=   \
+  nsh/nsh.api.h
+
+nsh_test_plugin_la_SOURCES = \
+  nsh/nsh_test.c            \
+  nsh/nsh_plugin.api.h
+
+# vi:syntax=automake
diff --git a/src/plugins/nsh/CMakeLists.txt b/src/plugins/nsh/CMakeLists.txt
new file mode 100644 (file)
index 0000000..866b370
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright (c) 2018 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.
+
+add_vpp_plugin(nsh
+  SOURCES
+  nsh.c
+  nsh_output.c
+  nsh_pop.c
+  nsh-md2-ioam/nsh_md2_ioam.c 
+  nsh-md2-ioam/nsh_md2_ioam_api.c 
+  nsh-md2-ioam/md2_ioam_transit.c
+  nsh-md2-ioam/nsh_md2_ioam_trace.c
+  nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export.c
+  nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export_thread.c
+  nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_node.c
+
+  API_FILES
+  nsh.api
+
+  API_TEST_SOURCES
+  nsh_test.c
+)
+
diff --git a/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export.c b/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export.c
new file mode 100644 (file)
index 0000000..cb2bb21
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+/*
+ *------------------------------------------------------------------
+ * nsh_md2_ioam_export.c - ioam export API / debug CLI handling
+ *------------------------------------------------------------------
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <ioam/export-common/ioam_export.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <nsh/nsh.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+
+
+ioam_export_main_t nsh_md2_ioam_export_main;
+
+
+extern vlib_node_registration_t nsh_md2_ioam_export_node;
+extern void nsh_md2_set_next_ioam_export_override (uword next);
+/* Action function shared between message handler and debug CLI */
+int
+nsh_md2_ioam_export_enable_disable (ioam_export_main_t * em,
+                                   u8 is_disable,
+                                   ip4_address_t * collector_address,
+                                   ip4_address_t * src_address)
+{
+  vlib_main_t *vm = em->vlib_main;
+  u32 node_index = nsh_md2_ioam_export_node.index;
+  vlib_node_t *nsh_input_node = NULL;
+
+  if (is_disable == 0)
+    {
+      if (em->my_hbh_slot == ~0)
+       {
+         /* Hook this export node to nsh-input */
+         nsh_input_node = vlib_get_node_by_name (vm, (u8 *) "nsh-input");
+         if (!nsh_input_node)
+           {
+             /* node does not exist give up */
+             return (-1);
+           }
+         em->my_hbh_slot =
+           vlib_node_add_next (vm, nsh_input_node->index, node_index);
+       }
+      if (1 == ioam_export_header_create (em, collector_address, src_address))
+       {
+         ioam_export_thread_buffer_init (em, vm);
+         nsh_md2_set_next_ioam_export_override (em->my_hbh_slot);
+         /* Turn on the export buffer check process */
+         vlib_process_signal_event (vm, em->export_process_node_index, 1, 0);
+
+       }
+      else
+       {
+         return (-2);
+       }
+    }
+  else
+    {
+      nsh_md2_set_next_ioam_export_override (0);       // VXLAN_GPE_DECAP_IOAM_V4_NEXT_POP
+      ioam_export_header_cleanup (em, collector_address, src_address);
+      ioam_export_thread_buffer_free (em);
+      /* Turn off the export buffer check process */
+      vlib_process_signal_event (vm, em->export_process_node_index, 2, 0);
+
+    }
+
+  return 0;
+}
+
+
+
+static clib_error_t *
+set_nsh_md2_ioam_export_ipfix_command_fn (vlib_main_t * vm,
+                                         unformat_input_t * input,
+                                         vlib_cli_command_t * cmd)
+{
+  ioam_export_main_t *em = &nsh_md2_ioam_export_main;
+  ip4_address_t collector, src;
+  u8 is_disable = 0;
+
+  collector.as_u32 = 0;
+  src.as_u32 = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "collector %U", unformat_ip4_address, &collector))
+       ;
+      else if (unformat (input, "src %U", unformat_ip4_address, &src))
+       ;
+      else if (unformat (input, "disable"))
+       is_disable = 1;
+      else
+       break;
+    }
+
+  if (collector.as_u32 == 0)
+    return clib_error_return (0, "collector address required");
+
+  if (src.as_u32 == 0)
+    return clib_error_return (0, "src address required");
+
+  em->ipfix_collector.as_u32 = collector.as_u32;
+  em->src_address.as_u32 = src.as_u32;
+
+  vlib_cli_output (vm, "Collector %U, src address %U",
+                  format_ip4_address, &em->ipfix_collector,
+                  format_ip4_address, &em->src_address);
+
+  /* Turn on the export timer process */
+  // vlib_process_signal_event (vm, flow_report_process_node.index,
+  //1, 0);
+  if (0 !=
+      nsh_md2_ioam_export_enable_disable (em, is_disable, &collector, &src))
+    {
+      return clib_error_return (0, "Unable to set ioam nsh-md2 export");
+    }
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_nsh_md2_ioam_ipfix_command, static) =
+{
+.path = "set nsh-md2-ioam export ipfix",
+.short_help = "set nsh-md2-ioam export ipfix collector <ip4-address> src <ip4-address>",
+.function = set_nsh_md2_ioam_export_ipfix_command_fn,
+};
+/* *INDENT-ON* */
+
+
+#define IPFIX_NSH_MD2_IOAM_EXPORT_ID 274       // TODO: Move this to ioam/ioam_export.h
+static clib_error_t *
+nsh_md2_ioam_export_init (vlib_main_t * vm)
+{
+  ioam_export_main_t *em = &nsh_md2_ioam_export_main;
+  clib_error_t *error = 0;
+
+  em->set_id = IPFIX_NSH_MD2_IOAM_EXPORT_ID;
+  em->unix_time_0 = (u32) time (0);    /* Store starting time */
+  em->vlib_time_0 = vlib_time_now (vm);
+
+  em->my_hbh_slot = ~0;
+  em->vlib_main = vm;
+  em->vnet_main = vnet_get_main ();
+  ioam_export_reset_next_node (em);
+
+  return error;
+}
+
+VLIB_INIT_FUNCTION (nsh_md2_ioam_export_init);
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export_thread.c b/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_export_thread.c
new file mode 100644 (file)
index 0000000..c5dd1bc
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+/*
+ * nsh_md2_ioam_export_thread.c
+ */
+#include <vnet/api_errno.h>
+#include <vppinfra/pool.h>
+#include <ioam/export-common/ioam_export.h>
+
+static vlib_node_registration_t nsh_md2_ioam_export_process_node;
+extern ioam_export_main_t nsh_md2_ioam_export_main;
+
+static uword
+nsh_md2_ioam_export_process (vlib_main_t * vm,
+                            vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+  return (ioam_export_process_common (&nsh_md2_ioam_export_main,
+                                     vm, rt, f,
+                                     nsh_md2_ioam_export_process_node.index));
+}
+
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nsh_md2_ioam_export_process_node, static) =
+{
+ .function = nsh_md2_ioam_export_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "nsh-md2-ioam-export-process",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_node.c b/src/plugins/nsh/nsh-md2-ioam/export-nsh-md2-ioam/nsh_md2_ioam_node.c
new file mode 100644 (file)
index 0000000..f2910c3
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2017 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 <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip.h>
+#include <nsh/nsh.h>
+#include <nsh/nsh_packet.h>
+#include <ioam/export-common/ioam_export.h>
+
+typedef struct
+{
+  u32 next_index;
+  u32 flow_label;
+} export_trace_t;
+
+extern ioam_export_main_t nsh_md2_ioam_export_main;
+vlib_node_registration_t export_node;
+/* packet trace format function */
+static u8 *
+format_export_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 *);
+  export_trace_t *t = va_arg (*args, export_trace_t *);
+
+  s = format (s, "EXPORT: flow_label %d, next index %d",
+             t->flow_label, t->next_index);
+  return s;
+}
+
+vlib_node_registration_t nsh_md2_ioam_export_node;
+
+#define foreach_export_error \
+_(RECORDED, "Packets recorded for export")
+
+typedef enum
+{
+#define _(sym,str) EXPORT_ERROR_##sym,
+  foreach_export_error
+#undef _
+    EXPORT_N_ERROR,
+} export_error_t;
+
+static char *export_error_strings[] = {
+#define _(sym,string) string,
+  foreach_export_error
+#undef _
+};
+
+typedef enum
+{
+  EXPORT_NEXT_NSH_MD2_IOAM_INPUT,
+  EXPORT_N_NEXT,
+} export_next_t;
+
+always_inline void
+copy3cachelines (void *dst, const void *src, size_t n)
+{
+
+  u64 *copy_dst, *copy_src;
+  int i;
+  copy_dst = (u64 *) dst;
+  copy_src = (u64 *) src;
+  if (PREDICT_FALSE (n < DEFAULT_EXPORT_SIZE))
+    {
+      for (i = 0; i < n / 64; i++)
+       {
+         copy_dst[0] = copy_src[0];
+         copy_dst[1] = copy_src[1];
+         copy_dst[2] = copy_src[2];
+         copy_dst[3] = copy_src[3];
+         copy_dst[4] = copy_src[4];
+         copy_dst[5] = copy_src[5];
+         copy_dst[6] = copy_src[6];
+         copy_dst[7] = copy_src[7];
+         copy_dst += 8;
+         copy_src += 8;
+       }
+      return;
+    }
+  for (i = 0; i < 3; i++)
+    {
+      copy_dst[0] = copy_src[0];
+      copy_dst[1] = copy_src[1];
+      copy_dst[2] = copy_src[2];
+      copy_dst[3] = copy_src[3];
+      copy_dst[4] = copy_src[4];
+      copy_dst[5] = copy_src[5];
+      copy_dst[6] = copy_src[6];
+      copy_dst[7] = copy_src[7];
+      copy_dst += 8;
+      copy_src += 8;
+    }
+}
+
+static void
+nsh_md2_ioam_export_fixup_func (vlib_buffer_t * export_buf,
+                               vlib_buffer_t * pak_buf)
+{
+  /* Todo: on implementing analyse */
+}
+
+static uword
+nsh_md2_ioam_export_node_fn (vlib_main_t * vm,
+                            vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  ioam_export_main_t *em = &nsh_md2_ioam_export_main;
+  ioam_export_node_common (em, vm, node, frame, ip4_header_t, length,
+                          ip_version_and_header_length,
+                          EXPORT_NEXT_NSH_MD2_IOAM_INPUT,
+                          nsh_md2_ioam_export_fixup_func);
+  return frame->n_vectors;
+}
+
+/*
+ * Node for iOAM export
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nsh_md2_ioam_export_node) =
+{
+  .function = nsh_md2_ioam_export_node_fn,
+  .name = "nsh-md2-ioam-export",
+  .vector_size = sizeof (u32),
+  .format_trace = format_export_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (export_error_strings),
+  .error_strings = export_error_strings,
+  .n_next_nodes = EXPORT_N_NEXT,
+    /* edit / add dispositions here */
+    .next_nodes =
+  {[EXPORT_NEXT_NSH_MD2_IOAM_INPUT] = "nsh-pop"},
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/md2_ioam_transit.c b/src/plugins/nsh/nsh-md2-ioam/md2_ioam_transit.c
new file mode 100644 (file)
index 0000000..9ea5447
--- /dev/null
@@ -0,0 +1,195 @@
+ /*
+  * Copyright (c) 2017 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 <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/udp/udp.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/lisp-gpe/lisp_gpe.h>
+#include <vnet/lisp-gpe/lisp_gpe_packet.h>
+#include <nsh/nsh.h>
+#include <nsh/nsh_packet.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam_util.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
+
+/* Statistics (not really errors) */
+#define foreach_nsh_md2_ioam_encap_transit_error    \
+_(ENCAPSULATED, "good packets encapsulated")
+
+static char *nsh_md2_ioam_encap_transit_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nsh_md2_ioam_encap_transit_error
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_ERROR_##sym,
+  foreach_nsh_md2_ioam_encap_transit_error
+#undef _
+    NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_N_ERROR,
+} nsh_md2_ioam_encap_transit_error_t;
+
+typedef enum
+{
+  NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_OUTPUT,
+  NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_DROP,
+  NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_N_NEXT
+} nsh_md2_ioam_encap_transit_next_t;
+
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (nsh_md2_ioam_encap_transit, static) =
+{
+  .arc_name = "ip4-output",
+  .node_name = "nsh-md2-ioam-encap-transit",
+  .runs_before = VNET_FEATURES ("adj-midchain-tx"),
+};
+/* *INDENT-ON* */
+
+
+static uword
+nsh_md2_ioam_encap_transit (vlib_main_t * vm,
+                           vlib_node_runtime_t * node,
+                           vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0 = NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_OUTPUT;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         ip4_header_t *ip0;
+         u32 iph_offset = 0;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         iph_offset = vnet_buffer (b0)->ip.save_rewrite_length;
+         ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0)
+                                 + iph_offset);
+
+         /* just forward non ipv4 packets */
+         if (PREDICT_FALSE
+             ((ip0->ip_version_and_header_length & 0xF0) == 0x40))
+           {
+             /* ipv4 packets */
+             udp_header_t *udp_hdr0 = (udp_header_t *) (ip0 + 1);
+             if (PREDICT_FALSE
+                 ((ip0->protocol == IP_PROTOCOL_UDP) &&
+                  (clib_net_to_host_u16 (udp_hdr0->dst_port) ==
+                   UDP_DST_PORT_lisp_gpe)))
+               {
+
+                 /* Check the iOAM header */
+                 lisp_gpe_header_t *lisp_gpe_hdr0 =
+                   (lisp_gpe_header_t *) (udp_hdr0 + 1);
+                 nsh_base_header_t *nsh_hdr =
+                   (nsh_base_header_t *) (lisp_gpe_hdr0 + 1);
+
+                 if (PREDICT_FALSE
+                     (lisp_gpe_hdr0->next_protocol ==
+                      LISP_GPE_NEXT_PROTO_NSH) && (nsh_hdr->md_type == 2))
+                   {
+                     uword *t = NULL;
+                     nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+                     fib_prefix_t key4;
+                     memset (&key4, 0, sizeof (key4));
+                     key4.fp_proto = FIB_PROTOCOL_IP4;
+                     key4.fp_addr.ip4.as_u32 = ip0->dst_address.as_u32;
+                     t = hash_get_mem (hm->dst_by_ip4, &key4);
+                     if (t)
+                       {
+                         vlib_buffer_advance (b0,
+                                              (word) (sizeof
+                                                      (ethernet_header_t)));
+                         nsh_md2_ioam_encap_decap_ioam_v4_one_inline (vm,
+                                                                      node,
+                                                                      b0,
+                                                                      &next0,
+                                                                      NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_DROP,
+                                                                      1
+                                                                      /* use_adj */
+                           );
+                         vlib_buffer_advance (b0,
+                                              -(word) (sizeof
+                                                       (ethernet_header_t)));
+                       }
+                   }
+               }
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return from_frame->n_vectors;
+}
+
+extern u8 *format_nsh_node_map_trace (u8 * s, va_list * args);
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nsh_md2_ioam_encap_transit_node) = {
+  .function = nsh_md2_ioam_encap_transit,
+  .name = "nsh-md2-ioam-encap-transit",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nsh_node_map_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(nsh_md2_ioam_encap_transit_error_strings),
+  .error_strings = nsh_md2_ioam_encap_transit_error_strings,
+
+  .n_next_nodes = NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_N_NEXT,
+
+  .next_nodes = {
+        [NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_OUTPUT] = "interface-output",
+        [NSH_MD2_IOAM_ENCAP_TRANSIT_IOAM_NEXT_DROP] = "error-drop",
+  },
+
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.c b/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.c
new file mode 100644 (file)
index 0000000..1fa1c55
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * nsh_md2_ioam.c - NSH iOAM functions for MD type 2
+ *
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <nsh/nsh.h>
+#include <nsh/nsh_packet.h>
+#include <vnet/ip/ip.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <nsh/nsh.api.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <nsh/nsh.api.h>
+#undef vl_endianfun
+
+nsh_md2_ioam_main_t nsh_md2_ioam_main;
+
+static void
+nsh_md2_ioam_set_clear_output_feature_on_intf (vlib_main_t * vm,
+                                                   u32 sw_if_index0,
+                                                   u8 is_add)
+{
+
+
+
+  vnet_feature_enable_disable ("ip4-output",
+                              "nsh-md2-ioam-encap-transit",
+                              sw_if_index0, is_add,
+                              0 /* void *feature_config */ ,
+                              0 /* u32 n_feature_config_bytes */ );
+  return;
+}
+
+void
+nsh_md2_ioam_clear_output_feature_on_all_intfs (vlib_main_t * vm)
+{
+  vnet_sw_interface_t *si = 0;
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_interface_main_t *im = &vnm->interface_main;
+
+  pool_foreach (si, im->sw_interfaces, (
+                                        {
+                                        nsh_md2_ioam_set_clear_output_feature_on_intf
+                                        (vm, si->sw_if_index, 0);
+                                        }));
+  return;
+}
+
+
+extern fib_forward_chain_type_t
+fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);
+
+int
+nsh_md2_ioam_enable_disable_for_dest (vlib_main_t * vm,
+                                          ip46_address_t dst_addr,
+                                          u32 outer_fib_index,
+                                          u8 is_ipv4, u8 is_add)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  u32 fib_index0 = 0;
+
+  fib_node_index_t fei = ~0;
+  u32 *sw_if_index0 = NULL;
+#if 0
+  fib_entry_t *fib_entry;
+  u32 adj_index0;
+  ip_adjacency_t *adj0;
+  load_balance_t *lb_m, *lb_b;
+  const dpo_id_t *dpo0, *dpo1;
+  u32 i, j, k;
+#endif
+  u32 *intf_list = NULL;
+  fib_prefix_t fib_prefix;
+
+  if (is_ipv4)
+    {
+      memset (&fib_prefix, 0, sizeof (fib_prefix_t));
+      fib_prefix.fp_len = 32;
+      fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
+#define  TRANSIT_UNIT_TEST_HACK 1
+#ifdef TRANSIT_UNIT_TEST_HACK
+      memset(&dst_addr, 0, sizeof(dst_addr));
+      dst_addr.ip4.as_u32 = clib_net_to_host_u32(0x14020102);
+#endif
+      fib_prefix.fp_addr = dst_addr;
+    }
+  else
+    {
+      return 0;
+    }
+
+  fei = fib_table_lookup (fib_index0, &fib_prefix);
+#if 0
+  fib_entry = fib_entry_get (fei);
+
+
+  if (!dpo_id_is_valid (&fib_entry->fe_lb))
+    {
+      return (-1);
+    }
+
+  lb_m = load_balance_get (fib_entry->fe_lb.dpoi_index);
+
+  for (i = 0; i < lb_m->lb_n_buckets; i++)
+    {
+      dpo0 = load_balance_get_bucket_i (lb_m, i);
+
+      if (dpo0->dpoi_type == DPO_LOAD_BALANCE ||
+         dpo0->dpoi_type == DPO_ADJACENCY)
+       {
+         if (dpo0->dpoi_type == DPO_ADJACENCY)
+           {
+             k = 1;
+           }
+         else
+           {
+             lb_b = load_balance_get (dpo0->dpoi_index);
+             k = lb_b->lb_n_buckets;
+           }
+
+         for (j = 0; j < k; j++)
+           {
+             if (dpo0->dpoi_type == DPO_ADJACENCY)
+               {
+                 dpo1 = dpo0;
+               }
+             else
+               {
+                 dpo1 = load_balance_get_bucket_i (lb_b, j);
+               }
+
+             if (dpo1->dpoi_type == DPO_ADJACENCY)
+               {
+                 adj_index0 = dpo1->dpoi_index;
+
+                 if (ADJ_INDEX_INVALID == adj_index0)
+                   {
+                     continue;
+                   }
+
+                 adj0 =
+                   ip_get_adjacency (&(ip4_main.lookup_main), adj_index0);
+                 sw_if_index0 = adj0->rewrite_header.sw_if_index;
+
+                 if (~0 == sw_if_index0)
+                   {
+                     continue;
+                   }
+
+
+                 if (is_add)
+                   {
+                     vnet_feature_enable_disable ("ip4-output",
+                                                  "nsh-md2-ioam-encap-transit",
+                                                  sw_if_index0, is_add, 0,
+                                                  /* void *feature_config */
+                                                  0    /* u32 n_feature_config_bytes */
+                       );
+
+                     vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
+                                              sw_if_index0, ~0);
+                     hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
+                   }
+                 else
+                   {
+                     hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
+                   }
+               }
+           }
+       }
+    }
+#else
+
+u32 fib_path_get_resolving_interface (fib_node_index_t path_index);
+    vec_add1(intf_list, fib_path_get_resolving_interface(fei));
+    vec_foreach(sw_if_index0, intf_list)
+    if (is_add)
+      {
+        vnet_feature_enable_disable ("ip4-output",
+                         "nsh-md2-ioam-encap-transit",
+                         *sw_if_index0, is_add, 0,
+                         /* void *feature_config */
+                         0    /* u32 n_feature_config_bytes */
+          );
+
+        vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
+                         *sw_if_index0, ~0);
+        hm->bool_ref_by_sw_if_index[*sw_if_index0] = 1;
+      }
+    else
+      {
+        hm->bool_ref_by_sw_if_index[*sw_if_index0] = ~0;
+      }
+
+#endif
+
+  if (is_ipv4)
+    {
+      uword *t = NULL;
+      nsh_md2_ioam_dest_tunnels_t *t1;
+      fib_prefix_t key4, *key4_copy;
+      hash_pair_t *hp;
+      memset (&key4, 0, sizeof (key4));
+      key4.fp_proto = FIB_PROTOCOL_IP4;
+      key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
+      t = hash_get_mem (hm->dst_by_ip4, &key4);
+      if (is_add)
+       {
+         if (t)
+           {
+             return 0;
+           }
+         pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
+         memset (t1, 0, sizeof (*t1));
+         t1->fp_proto = FIB_PROTOCOL_IP4;
+         t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
+         key4_copy = clib_mem_alloc (sizeof (*key4_copy));
+          memset(key4_copy, 0, sizeof(*key4_copy));
+         clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
+         hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
+         /*
+          * Attach to the FIB entry for the VxLAN-GPE destination
+          * and become its child. The dest route will invoke a callback
+          * when the fib entry changes, it can be used to
+          * re-program the output feature on the egress interface.
+          */
+
+         const fib_prefix_t tun_dst_pfx = {
+           .fp_len = 32,
+           .fp_proto = FIB_PROTOCOL_IP4,
+           .fp_addr = {.ip4 = t1->dst_addr.ip4,}
+         };
+
+         t1->fib_entry_index =
+           fib_table_entry_special_add (outer_fib_index,
+                                        &tun_dst_pfx,
+                                        FIB_SOURCE_RR,
+                                        FIB_ENTRY_FLAG_NONE);
+         t1->sibling_index =
+           fib_entry_child_add (t1->fib_entry_index,
+                                hm->fib_entry_type, t1 - hm->dst_tunnels);
+         t1->outer_fib_index = outer_fib_index;
+
+       }
+      else
+       {
+         if (!t)
+           {
+             return 0;
+           }
+         t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
+         hp = hash_get_pair (hm->dst_by_ip4, &key4);
+         key4_copy = (void *) (hp->key);
+         hash_unset_mem (hm->dst_by_ip4, &key4);
+         clib_mem_free (key4_copy);
+         pool_put (hm->dst_tunnels, t1);
+       }
+    }
+  else
+    {
+      // TBD for IPv6
+    }
+
+  return 0;
+}
+
+void
+nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  nsh_main_t *gm = &nsh_main;
+  nsh_md2_ioam_dest_tunnels_t *t;
+  u32 i;
+
+  if (pool_elts (hm->dst_tunnels) == 0)
+    return;
+
+  nsh_md2_ioam_clear_output_feature_on_all_intfs (gm->vlib_main);
+  i = vec_len (hm->bool_ref_by_sw_if_index);
+  vec_free (hm->bool_ref_by_sw_if_index);
+  vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
+  pool_foreach (t, hm->dst_tunnels, (
+                                     {
+                                     nsh_md2_ioam_enable_disable_for_dest
+                                     (gm->vlib_main,
+                                      t->dst_addr,
+                                      t->outer_fib_index,
+                                      (t->fp_proto == FIB_PROTOCOL_IP4), 1
+                                      /* is_add */
+                                     );
+                                     }
+               ));
+  return;
+}
+
+void
+nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  nsh_main_t *gm = &nsh_main;
+
+  u32 sw_if_index0 = 0;
+  for (sw_if_index0 = 0;
+       sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
+    {
+      if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
+       {
+         nsh_md2_ioam_set_clear_output_feature_on_intf
+           (gm->vlib_main, sw_if_index0, 0);
+       }
+    }
+
+  return;
+}
+
+
+
+
+clib_error_t *
+nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
+                                 int has_ppc_option)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+
+  hm->has_trace_option = has_trace_option;
+  hm->has_pot_option = has_pot_option;
+  hm->has_ppc_option = has_ppc_option;
+
+  if (hm->has_trace_option)
+    {
+      nsh_md2_ioam_trace_profile_setup ();
+    }
+  else if (!hm->has_trace_option)
+    {
+      nsh_md2_ioam_trace_profile_cleanup ();
+    }
+
+  return 0;
+}
+
+
+int nsh_md2_ioam_disable_for_dest
+  (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
+   u8 ipv4_set)
+{
+  nsh_md2_ioam_dest_tunnels_t *t;
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  nsh_main_t *gm = &nsh_main;
+
+  nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
+                                            dst_addr, outer_fib_index,
+                                            ipv4_set, 0);
+  if (pool_elts (hm->dst_tunnels) == 0)
+    {
+      nsh_md2_ioam_clear_output_feature_on_select_intfs ();
+      return 0;
+    }
+
+  pool_foreach (t, hm->dst_tunnels, (
+                                     {
+                                     nsh_md2_ioam_enable_disable_for_dest
+                                     (gm->vlib_main,
+                                      t->dst_addr,
+                                      t->outer_fib_index,
+                                      (t->fp_proto ==
+                                       FIB_PROTOCOL_IP4), 1 /* is_add */ );
+                                     }
+               ));
+  nsh_md2_ioam_clear_output_feature_on_select_intfs ();
+  return (0);
+
+}
+
+static clib_error_t *nsh_md2_ioam_set_transit_rewrite_command_fn
+  (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  nsh_main_t *gm = &nsh_main;
+  ip46_address_t dst_addr;
+  u8 dst_addr_set = 0;
+  u8 ipv4_set = 0;
+  u8 ipv6_set = 0;
+  u8 disable = 0;
+  clib_error_t *rv = 0;
+  u32 outer_fib_index = 0;
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
+       {
+         dst_addr_set = 1;
+         ipv4_set = 1;
+       }
+      else
+       if (unformat
+           (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
+       {
+         dst_addr_set = 1;
+         ipv6_set = 1;
+       }
+      else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
+       {
+       }
+
+      else if (unformat (input, "disable"))
+       disable = 1;
+      else
+       break;
+    }
+
+  if (dst_addr_set == 0)
+    return clib_error_return (0,
+                             "LISP-GPE Tunnel destination address not specified");
+  if (ipv4_set && ipv6_set)
+    return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+  if (!disable)
+    {
+      nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
+                                                dst_addr, outer_fib_index,
+                                                ipv4_set, 1);
+    }
+  else
+    {
+      nsh_md2_ioam_disable_for_dest
+       (vm, dst_addr, outer_fib_index, ipv4_set);
+    }
+  return rv;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (nsh_md2_ioam_set_transit_rewrite_cmd, static) = {
+  .path = "set nsh-md2-ioam-transit",
+  .short_help = "set nsh-ioam-lisp-gpe-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
+  .function = nsh_md2_ioam_set_transit_rewrite_command_fn,
+};
+
+/**
+ * Function definition to backwalk a FIB node
+ */
+static fib_node_back_walk_rc_t
+nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
+{
+  nsh_md2_ioam_refresh_output_feature_on_all_dest ();
+  return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t *
+nsh_md2_ioam_fib_node_get (fib_node_index_t index)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  return (&hm->node);
+}
+
+/**
+ * Function definition to inform the FIB node that its last lock has gone.
+ */
+static void
+nsh_md2_ioam_last_lock_gone (fib_node_t * node)
+{
+  ASSERT (0);
+}
+
+
+/*
+ * Virtual function table registered by MPLS GRE tunnels
+ * for participation in the FIB object graph.
+ */
+const static fib_node_vft_t nsh_md2_ioam_vft = {
+  .fnv_get = nsh_md2_ioam_fib_node_get,
+  .fnv_last_lock = nsh_md2_ioam_last_lock_gone,
+  .fnv_back_walk = nsh_md2_ioam_back_walk,
+};
+
+void
+nsh_md2_ioam_interface_init (void)
+{
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  hm->fib_entry_type = fib_node_register_new_type (&nsh_md2_ioam_vft);
+  return;
+}
+
diff --git a/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.h b/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.h
new file mode 100644 (file)
index 0000000..3d48fde
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017 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 __included_nsh_md2_ioam_h__
+#define __included_nsh_md2_ioam_h__
+
+#include <nsh/nsh.h>
+#include <nsh/nsh_packet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+
+
+typedef struct nsh_md2_ioam_sw_interface_
+{
+  u32 sw_if_index;
+} nsh_md2_ioam_sw_interface_t;
+
+typedef struct nsh_md2_ioam_dest_tunnels_
+{
+  ip46_address_t dst_addr;
+  u32 fp_proto;
+  u32 sibling_index;
+  fib_node_index_t fib_entry_index;
+  u32 outer_fib_index;
+} nsh_md2_ioam_dest_tunnels_t;
+
+typedef struct nsh_md2_ioam_main_
+{
+  /**
+   * Linkage into the FIB object graph
+   */
+  fib_node_t node;
+
+  /* time scale transform. Joy. */
+  u32 unix_time_0;
+  f64 vlib_time_0;
+
+
+  /* Trace option */
+  u8 has_trace_option;
+
+  /* Pot option */
+  u8 has_pot_option;
+
+#define PPC_NONE  0
+#define PPC_ENCAP 1
+#define PPC_DECAP 2
+  u8 has_ppc_option;
+
+#define TSP_SECONDS              0
+#define TSP_MILLISECONDS         1
+#define TSP_MICROSECONDS         2
+#define TSP_NANOSECONDS          3
+
+
+  /* API message ID base */
+  u16 msg_id_base;
+
+  /* Override to export for iOAM */
+  uword decap_v4_next_override;
+  uword decap_v6_next_override;
+
+  /* sequence of node graph for encap */
+  uword encap_v4_next_node;
+  uword encap_v6_next_node;
+
+  /* Software interfaces. */
+  nsh_md2_ioam_sw_interface_t *sw_interfaces;
+
+  /* hash ip4/ip6 -> list of destinations for doing transit iOAM operation */
+  nsh_md2_ioam_dest_tunnels_t *dst_tunnels;
+  uword *dst_by_ip4;
+  uword *dst_by_ip6;
+
+  /** per sw_if_index, to maintain bitmap */
+  u8 *bool_ref_by_sw_if_index;
+  fib_node_type_t fib_entry_type;
+
+
+} nsh_md2_ioam_main_t;
+extern nsh_md2_ioam_main_t nsh_md2_ioam_main;
+
+/*
+ * Primary h-b-h handler trace support
+ */
+typedef struct
+{
+  u32 next_index;
+  u32 trace_len;
+  u8 option_data[256];
+} ioam_trace_t;
+
+
+clib_error_t *nsh_md2_ioam_enable_disable (int has_trace_option,
+                                               int has_pot_option,
+                                               int has_ppc_option);
+
+
+
+int nsh_md2_ioam_trace_profile_setup (void);
+
+int nsh_md2_ioam_trace_profile_cleanup (void);
+extern void nsh_md2_ioam_interface_init (void);
+
+
+
+#endif
diff --git a/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_api.c b/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_api.c
new file mode 100644 (file)
index 0000000..9ed835b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+/*
+ *-----------------------------------------------------------------------
+ * nsh_md2_ioam_api.c - iOAM for NSH/LISP-GPE related APIs to create
+ *               and maintain profiles
+ *-----------------------------------------------------------------------
+ */
+
+#include <vnet/vnet.h>
+#include <vlib/unix/plugin.h>
+#include <vnet/plugin/plugin.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <nsh/nsh.api.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <nsh/nsh.api.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <nsh/nsh.api.h>
+#undef vl_printfun
+
+u8 *nsh_trace_main = NULL;
+static clib_error_t *
+nsh_md2_ioam_init (vlib_main_t * vm)
+{
+  nsh_md2_ioam_main_t *sm = &nsh_md2_ioam_main;
+  clib_error_t *error = 0;
+
+  nsh_trace_main =
+    (u8 *) vlib_get_plugin_symbol ("ioam_plugin.so", "trace_main");
+
+  if (!nsh_trace_main)
+    return error;
+
+  vec_new (nsh_md2_ioam_sw_interface_t, pool_elts (sm->sw_interfaces));
+  sm->dst_by_ip4 = hash_create_mem (0, sizeof (fib_prefix_t), sizeof (uword));
+
+  sm->dst_by_ip6 = hash_create_mem (0, sizeof (fib_prefix_t), sizeof (uword));
+
+  nsh_md2_ioam_interface_init ();
+
+  return error;
+}
+
+VLIB_INIT_FUNCTION (nsh_md2_ioam_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_trace.c b/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_trace.c
new file mode 100644 (file)
index 0000000..1baa236
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2017 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 <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+#include <ioam/lib-trace/trace_util.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+#include <nsh/nsh_packet.h>
+
+/* Timestamp precision multipliers for seconds, milliseconds, microseconds
+ * and nanoseconds respectively.
+ */
+static f64 trace_tsp_mul[4] = { 1, 1e3, 1e6, 1e9 };
+
+#define NSH_MD2_IOAM_TRACE_SIZE_DUMMY 20
+
+typedef union
+{
+  u64 as_u64;
+  u32 as_u32[2];
+} time_u64_t;
+
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED(struct {
+  u16 class;
+  u8 type;
+  u8 length;
+  u8 data_list_elts_left;
+  u16 ioam_trace_type;
+  u8 reserve;
+  u32 elts[0]; /* Variable type. So keep it generic */
+}) nsh_md2_ioam_trace_option_t;
+/* *INDENT-ON* */
+
+
+#define foreach_nsh_md2_ioam_trace_stats                               \
+  _(SUCCESS, "Pkts updated with TRACE records")                                        \
+  _(FAILED, "Errors in TRACE due to lack of TRACE records")
+
+static char *nsh_md2_ioam_trace_stats_strings[] = {
+#define _(sym,string) string,
+  foreach_nsh_md2_ioam_trace_stats
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) NSH_MD2_IOAM_TRACE_##sym,
+  foreach_nsh_md2_ioam_trace_stats
+#undef _
+    NSH_MD2_IOAM_TRACE_N_STATS,
+} nsh_md2_ioam_trace_stats_t;
+
+
+typedef struct
+{
+  /* stats */
+  u64 counters[ARRAY_LEN (nsh_md2_ioam_trace_stats_strings)];
+
+  /* convenience */
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+} nsh_md2_ioam_trace_main_t;
+
+nsh_md2_ioam_trace_main_t nsh_md2_ioam_trace_main;
+
+/*
+ * Find a trace profile
+ */
+
+extern u8 *nsh_trace_main;
+always_inline trace_profile *
+nsh_trace_profile_find (void)
+{
+  trace_main_t *sm = (trace_main_t *) nsh_trace_main;
+
+  return (&(sm->profile));
+}
+
+
+always_inline void
+nsh_md2_ioam_trace_stats_increment_counter (u32 counter_index, u64 increment)
+{
+  nsh_md2_ioam_trace_main_t *hm = &nsh_md2_ioam_trace_main;
+
+  hm->counters[counter_index] += increment;
+}
+
+
+static u8 *
+format_ioam_data_list_element (u8 * s, va_list * args)
+{
+  u32 *elt = va_arg (*args, u32 *);
+  u8 *trace_type_p = va_arg (*args, u8 *);
+  u8 trace_type = *trace_type_p;
+
+
+  if (trace_type & BIT_TTL_NODEID)
+    {
+      u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
+      s = format (s, "ttl 0x%x node id 0x%x ",
+                 ttl_node_id_host_byte_order >> 24,
+                 ttl_node_id_host_byte_order & 0x00FFFFFF);
+
+      elt++;
+    }
+
+  if (trace_type & BIT_ING_INTERFACE && trace_type & BIT_ING_INTERFACE)
+    {
+      u32 ingress_host_byte_order = clib_net_to_host_u32 (*elt);
+      s = format (s, "ingress 0x%x egress 0x%x ",
+                 ingress_host_byte_order >> 16,
+                 ingress_host_byte_order & 0xFFFF);
+      elt++;
+    }
+
+  if (trace_type & BIT_TIMESTAMP)
+    {
+      u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
+      s = format (s, "ts 0x%x \n", ts_in_host_byte_order);
+      elt++;
+    }
+
+  if (trace_type & BIT_APPDATA)
+    {
+      u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
+      s = format (s, "app 0x%x ", appdata_in_host_byte_order);
+      elt++;
+    }
+
+  return s;
+}
+
+
+
+int
+nsh_md2_ioam_trace_rewrite_handler (u8 * rewrite_string, u8 * rewrite_size)
+{
+  nsh_md2_ioam_trace_option_t *trace_option = NULL;
+  u8 trace_data_size = 0;
+  u8 trace_option_elts = 0;
+  trace_profile *profile = NULL;
+
+  profile = nsh_trace_profile_find ();
+
+  if (PREDICT_FALSE (!profile))
+    {
+      return (-1);
+    }
+
+  if (PREDICT_FALSE (!rewrite_string))
+    return -1;
+
+  trace_option_elts = profile->num_elts;
+  trace_data_size = fetch_trace_data_size (profile->trace_type);
+
+  trace_option = (nsh_md2_ioam_trace_option_t *) rewrite_string;
+  trace_option->class = clib_host_to_net_u16 (0x9);
+  trace_option->type = NSH_MD2_IOAM_OPTION_TYPE_TRACE;
+  trace_option->length = (trace_option_elts * trace_data_size) + 4;
+  trace_option->data_list_elts_left = trace_option_elts;
+  trace_option->ioam_trace_type =
+    clib_host_to_net_u16 (profile->trace_type & TRACE_TYPE_MASK);
+
+  *rewrite_size =
+    sizeof (nsh_md2_ioam_trace_option_t) +
+    (trace_option_elts * trace_data_size);
+
+  return 0;
+}
+
+
+int
+nsh_md2_ioam_trace_data_list_handler (vlib_buffer_t * b,
+                                     nsh_tlv_header_t * opt)
+{
+  u8 elt_index = 0;
+  nsh_md2_ioam_trace_option_t *trace =
+    (nsh_md2_ioam_trace_option_t *) ((u8 *) opt);
+  time_u64_t time_u64;
+  u32 *elt;
+  int rv = 0;
+  trace_profile *profile = NULL;
+  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
+  nsh_main_t *gm = &nsh_main;
+  u16 ioam_trace_type = 0;
+
+  profile = nsh_trace_profile_find ();
+
+  if (PREDICT_FALSE (!profile))
+    {
+      return (-1);
+    }
+
+
+  ioam_trace_type = profile->trace_type & TRACE_TYPE_MASK;
+  time_u64.as_u64 = 0;
+
+  if (PREDICT_TRUE (trace->data_list_elts_left))
+    {
+      trace->data_list_elts_left--;
+      /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes
+       * to skip to this node's location.
+       */
+      elt_index =
+       trace->data_list_elts_left *
+       fetch_trace_data_size (ioam_trace_type) / 4;
+      elt = &trace->elts[elt_index];
+      if (ioam_trace_type & BIT_TTL_NODEID)
+       {
+         ip4_header_t *ip0 = vlib_buffer_get_current (b);
+         *elt = clib_host_to_net_u32 (((ip0->ttl - 1) << 24) |
+                                      profile->node_id);
+         elt++;
+       }
+
+      if (ioam_trace_type & BIT_ING_INTERFACE)
+       {
+         u16 tx_if = vnet_buffer (b)->sw_if_index[VLIB_TX];
+
+         *elt =
+           (vnet_buffer (b)->sw_if_index[VLIB_RX] & 0xFFFF) << 16 | tx_if;
+         *elt = clib_host_to_net_u32 (*elt);
+         elt++;
+       }
+
+
+      if (ioam_trace_type & BIT_TIMESTAMP)
+       {
+         /* Send least significant 32 bits */
+         f64 time_f64 =
+           (f64) (((f64) hm->unix_time_0) +
+                  (vlib_time_now (gm->vlib_main) - hm->vlib_time_0));
+
+         time_u64.as_u64 = time_f64 * trace_tsp_mul[profile->trace_tsp];
+         *elt = clib_host_to_net_u32 (time_u64.as_u32[0]);
+         elt++;
+       }
+
+      if (ioam_trace_type & BIT_APPDATA)
+       {
+         /* $$$ set elt0->app_data */
+         *elt = clib_host_to_net_u32 (profile->app_data);
+         elt++;
+       }
+      nsh_md2_ioam_trace_stats_increment_counter
+       (NSH_MD2_IOAM_TRACE_SUCCESS, 1);
+    }
+  else
+    {
+      nsh_md2_ioam_trace_stats_increment_counter
+       (NSH_MD2_IOAM_TRACE_FAILED, 1);
+    }
+  return (rv);
+}
+
+
+
+u8 *
+nsh_md2_ioam_trace_data_list_trace_handler (u8 * s, nsh_tlv_header_t * opt)
+{
+  nsh_md2_ioam_trace_option_t *trace;
+  u8 trace_data_size_in_words = 0;
+  u32 *elt;
+  int elt_index = 0;
+  u16 ioam_trace_type = 0;
+
+  trace = (nsh_md2_ioam_trace_option_t *) ((u8 *) opt);
+  ioam_trace_type = clib_net_to_host_u16 (trace->ioam_trace_type);
+  trace_data_size_in_words = fetch_trace_data_size (ioam_trace_type) / 4;
+  elt = &trace->elts[0];
+  s =
+    format (s, "  Trace Type 0x%x , %d elts left\n", ioam_trace_type,
+           trace->data_list_elts_left);
+  while ((u8 *) elt < ((u8 *) (&trace->elts[0]) + trace->length - 4
+                      /* -2 accounts for ioam_trace_type,elts_left */ ))
+    {
+      s = format (s, "    [%d] %U\n", elt_index,
+                 format_ioam_data_list_element, elt, &ioam_trace_type);
+      elt_index++;
+      elt += trace_data_size_in_words;
+    }
+  return (s);
+}
+
+int
+nsh_md2_ioam_trace_swap_handler (vlib_buffer_t * b,
+                                nsh_tlv_header_t * old_opt,
+                                nsh_tlv_header_t * new_opt)
+{
+
+  clib_memcpy (new_opt, old_opt, new_opt->length + sizeof (nsh_tlv_header_t));
+  return nsh_md2_ioam_trace_data_list_handler (b, new_opt);
+}
+
+static clib_error_t *
+nsh_md2_ioam_show_ioam_trace_cmd_fn (vlib_main_t * vm,
+                                    unformat_input_t * input,
+                                    vlib_cli_command_t * cmd)
+{
+  nsh_md2_ioam_trace_main_t *hm = &nsh_md2_ioam_trace_main;
+  u8 *s = 0;
+  int i = 0;
+
+  for (i = 0; i < NSH_MD2_IOAM_TRACE_N_STATS; i++)
+    {
+      s = format (s, " %s - %lu\n", nsh_md2_ioam_trace_stats_strings[i],
+                 hm->counters[i]);
+    }
+
+  vlib_cli_output (vm, "%v", s);
+  vec_free (s);
+  return 0;
+}
+
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (nsh_md2_ioam_show_ioam_trace_cmd, static) = {
+  .path = "show ioam nsh-lisp-gpe trace",
+  .short_help = "iOAM trace statistics",
+  .function = nsh_md2_ioam_show_ioam_trace_cmd_fn,
+};
+/* *INDENT-ON* */
+
+
+int
+nsh_md2_ioam_trace_pop_handler (vlib_buffer_t * b, nsh_tlv_header_t * opt)
+{
+  return nsh_md2_ioam_trace_data_list_handler (b, opt);
+}
+
+static clib_error_t *
+nsh_md2_ioam_trace_init (vlib_main_t * vm)
+{
+  nsh_md2_ioam_trace_main_t *hm = &nsh_md2_ioam_trace_main;
+  nsh_md2_ioam_main_t *gm = &nsh_md2_ioam_main;
+  clib_error_t *error;
+
+  if ((error = vlib_call_init_function (vm, nsh_init)))
+    return (error);
+
+  if ((error = vlib_call_init_function (vm, nsh_md2_ioam_init)))
+    return (error);
+
+  hm->vlib_main = vm;
+  hm->vnet_main = vnet_get_main ();
+  gm->unix_time_0 = (u32) time (0);    /* Store starting time */
+  gm->vlib_time_0 = vlib_time_now (vm);
+
+  memset (hm->counters, 0, sizeof (hm->counters));
+
+  if (nsh_md2_register_option
+      (clib_host_to_net_u16 (0x9),
+       NSH_MD2_IOAM_OPTION_TYPE_TRACE,
+       NSH_MD2_IOAM_TRACE_SIZE_DUMMY,
+       nsh_md2_ioam_trace_rewrite_handler,
+       nsh_md2_ioam_trace_data_list_handler,
+       nsh_md2_ioam_trace_swap_handler,
+       nsh_md2_ioam_trace_pop_handler,
+       nsh_md2_ioam_trace_data_list_trace_handler) < 0)
+    return (clib_error_create
+           ("registration of NSH_MD2_IOAM_OPTION_TYPE_TRACE failed"));
+
+  return (0);
+}
+
+VLIB_INIT_FUNCTION (nsh_md2_ioam_trace_init);
+
+int
+nsh_md2_ioam_trace_profile_cleanup (void)
+{
+  nsh_main_t *hm = &nsh_main;
+
+  hm->options_size[NSH_MD2_IOAM_OPTION_TYPE_TRACE] = 0;
+
+  return 0;
+
+}
+
+static int
+nsh_md2_ioam_trace_get_sizeof_handler (u32 * result)
+{
+  u16 size = 0;
+  u8 trace_data_size = 0;
+  trace_profile *profile = NULL;
+
+  *result = 0;
+
+  profile = nsh_trace_profile_find ();
+
+  if (PREDICT_FALSE (!profile))
+    {
+      return (-1);
+    }
+
+  trace_data_size = fetch_trace_data_size (profile->trace_type);
+  if (PREDICT_FALSE (trace_data_size == 0))
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  if (PREDICT_FALSE (profile->num_elts * trace_data_size > 254))
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  size +=
+    sizeof (nsh_md2_ioam_trace_option_t) +
+    profile->num_elts * trace_data_size;
+  *result = size;
+
+  return 0;
+}
+
+
+int
+nsh_md2_ioam_trace_profile_setup (void)
+{
+  u32 trace_size = 0;
+  nsh_main_t *hm = &nsh_main;
+
+  trace_profile *profile = NULL;
+
+
+  profile = nsh_trace_profile_find ();
+
+  if (PREDICT_FALSE (!profile))
+    {
+      return (-1);
+    }
+
+
+  if (nsh_md2_ioam_trace_get_sizeof_handler (&trace_size) < 0)
+    return (-1);
+
+  hm->options_size[NSH_MD2_IOAM_OPTION_TYPE_TRACE] = trace_size;
+
+  return (0);
+}
+
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_util.h b/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam_util.h
new file mode 100644 (file)
index 0000000..8fbb3b7
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017 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 __included_nsh_md2_ioam_util_h__
+#define __included_nsh_md2_ioam_util_h__
+
+#include <vnet/lisp-gpe/lisp_gpe.h>
+#include <vnet/lisp-gpe/lisp_gpe_packet.h>
+#include <vnet/ip/ip.h>
+#include <nsh/nsh.h>
+#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>
+#include <nsh/nsh_packet.h>
+
+
+extern nsh_option_map_t *nsh_md2_lookup_option (u16 class, u8 type);
+
+
+typedef struct
+{
+  u8 trace_data[256];
+} nsh_transit_trace_t;
+
+always_inline void
+nsh_md2_ioam_encap_decap_ioam_v4_one_inline (vlib_main_t * vm,
+                                            vlib_node_runtime_t * node,
+                                            vlib_buffer_t * b0,
+                                            u32 * next0, u32 drop_node_val,
+                                            u8 use_adj)
+{
+  ip4_header_t *ip0;
+  udp_header_t *udp_hdr0;
+  lisp_gpe_header_t *lisp_gpe_hdr0;
+  nsh_base_header_t *nsh_hdr;
+  nsh_tlv_header_t *opt0;
+  nsh_tlv_header_t *limit0;
+  nsh_main_t *hm = &nsh_main;
+  nsh_option_map_t *nsh_option;
+
+  /* Populate the iOAM header */
+  ip0 = vlib_buffer_get_current (b0);
+  udp_hdr0 = (udp_header_t *) (ip0 + 1);
+  lisp_gpe_hdr0 = (lisp_gpe_header_t *) (udp_hdr0 + 1);
+  nsh_hdr = (nsh_base_header_t *) (lisp_gpe_hdr0 + 1);
+  opt0 = (nsh_tlv_header_t *) (nsh_hdr + 1);
+  limit0 =
+    (nsh_tlv_header_t *) ((u8 *) opt0 + (nsh_hdr->length * 4) -
+                         sizeof (nsh_base_header_t));
+
+  /*
+   * Basic validity checks
+   */
+  if ((nsh_hdr->length * 4) > clib_net_to_host_u16 (ip0->length))
+    {
+      *next0 = drop_node_val;
+      return;
+    }
+
+  if (nsh_hdr->md_type != 2)
+    {
+      *next0 = drop_node_val;
+      return;
+    }
+
+  /* Scan the set of h-b-h options, process ones that we understand */
+  while (opt0 < limit0)
+    {
+      u8 type0;
+      type0 = opt0->type;
+      switch (type0)
+       {
+       case 0:         /* Pad1 */
+         opt0 = (nsh_tlv_header_t *) ((u8 *) opt0) + 1;
+         continue;
+       case 1:         /* PadN */
+         break;
+       default:
+         nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+         if ((nsh_option != NULL) && (hm->options[nsh_option->option_id]))
+           {
+             if ((*hm->options[nsh_option->option_id]) (b0, opt0) < 0)
+               {
+                 *next0 = drop_node_val;
+                 return;
+               }
+           }
+         break;
+       }
+      opt0 =
+       (nsh_tlv_header_t *) (((u8 *) opt0) + opt0->length +
+                             sizeof (nsh_tlv_header_t));
+    }
+
+
+  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+    {
+      nsh_transit_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+      clib_memcpy (&(tr->trace_data), nsh_hdr, (nsh_hdr->length * 4));
+    }
+  return;
+}
+
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh.api b/src/plugins/nsh/nsh.api
new file mode 100644 (file)
index 0000000..6beed00
--- /dev/null
@@ -0,0 +1,123 @@
+
+
+/** \brief /** \brief Set or delete an NSH header entry keyed by NSP/NSI
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - add address if non-zero, else delete
+    @param nsp_nsi - Key for nsh_header_t entry to map to. : 24bit NSP 8bit NSI 
+    @param md_type - metadata type [1|2] - only MDType1 supported today
+    @param ver_o_c - version, O-bit and C-bit (see nsh_packet.h)
+    @param ttl - indicates the maximum SFF hops for an SFP
+    @param length - header length in n x 32bits, should be 6 for MDtype1
+    @param next_protocol - next protocol encapsulated behind NSH header: 1=Ethernet, 2=IP4, 3=IP6
+    @param c1 - 32bit Metadata type1 field (context1)
+    @param c2 - 32bit Metadata type1 field (context2)
+    @param c3 - 32bit Metadata type1 field (context3)
+    @param c4 - 32bit Metadata type1 field (context4)
+    @param tlvs - Metadata Type 2 only, Type Length Value metadata.
+*/
+define nsh_add_del_entry {
+    u32 client_index;
+    u32 context;
+    u8 is_add;
+    u32 nsp_nsi;
+    u8 md_type;
+    u8 ver_o_c;
+    u8 ttl;
+    u8 length;
+    u8 next_protocol;
+    u32 c1;
+    u32 c2;
+    u32 c3;
+    u32 c4;
+    u8 tlv_length;
+    u8 tlv[248];
+};
+
+/** \brief Reply from adding NSH entry (nsh_add_del_entry)
+    @param context - sender context, to match reply w/ request
+    @param retval - 0 means all ok
+*/
+define nsh_add_del_entry_reply {
+    u32 context;
+    i32 retval;
+    u32 entry_index;
+};
+
+
+define nsh_entry_dump {
+    u32 client_index;
+    u32 context;
+    u32 entry_index;
+};
+
+define nsh_entry_details {
+    u32 context;
+    u32 entry_index;
+    u32 nsp_nsi;
+    u8 md_type;
+    u8 ver_o_c;
+    u8 ttl;
+    u8 length;
+    u8 next_protocol;
+    u32 c1;
+    u32 c2;
+    u32 c3;
+    u32 c4;
+    u8 tlv_length;
+    u8 tlv[248];
+};
+
+/** \brief Set or delete a mapping from one NSH header to another and its egress (decap to inner packet, encap NSH with outer header)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - add address if non-zero, else delete
+    @param nsh_nsi - Key for nsh_header_t entry to map to. : 24bit NSP 8bit NSI
+    @param mapped_nsp_nsi - Key for nsh_header_t entry to map to. : 24bit NSP 8bit NSI this may be ~0
+       if next action is to decap to NSH next protocol
+       Note the following heuristic:
+                   - if nsp_nsi == mapped_nsp_nsi then use-case is like SFC SFF
+           - if nsp_nsi != mapped_nsp_nsi then use-case is like SFC SF
+       Note: these are heuristics. Rules about NSI decrement are out of scope
+    @param sw_if_index - index number of outer encap for NSH egress
+    @param next_node - explicitly which node to send to
+      Note the above parameters are instantiated by "encap-gre-intf <x>" means sw_if_index x, next_node gre-input
+*/
+define nsh_add_del_map {
+    u32 client_index;
+    u32 context;
+    u8 is_add;
+    u32 nsp_nsi;
+    u32 mapped_nsp_nsi;
+    u32 nsh_action;
+    u32 sw_if_index;
+    u32 rx_sw_if_index;
+    u32 next_node;
+};
+
+/** \brief Reply from adding NSH map (nsh_add_del_map)
+    @param context - sender context, to match reply w/ request
+    @param retval - 0 means all ok
+*/
+define nsh_add_del_map_reply {
+    u32 context;
+    i32 retval;
+    u32 map_index;
+};
+
+define nsh_map_dump {
+    u32 client_index;
+    u32 context;
+    u32 map_index;
+};
+
+define nsh_map_details {
+    u32 context;
+    u32 map_index;
+    u32 nsp_nsi;
+    u32 mapped_nsp_nsi;
+    u32 nsh_action;
+    u32 sw_if_index;
+    u32 rx_sw_if_index;
+    u32 next_node;
+};
diff --git a/src/plugins/nsh/nsh.c b/src/plugins/nsh/nsh.c
new file mode 100644 (file)
index 0000000..20e4603
--- /dev/null
@@ -0,0 +1,2545 @@
+/*
+ * nsh.c - nsh mapping
+ *
+ * Copyright (c) 2013 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <nsh/nsh.h>
+#include <vnet/gre/gre.h>
+#include <vnet/vxlan/vxlan.h>
+#include <vnet/vxlan-gpe/vxlan_gpe.h>
+#include <vnet/l2/l2_classify.h>
+#include <vnet/adj/adj.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vpp/app/version.h>
+
+/* define message IDs */
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include <nsh/nsh.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 <nsh/nsh.api.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <nsh/nsh.api.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <nsh/nsh.api.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <nsh/nsh.api.h>
+#undef vl_api_version
+
+#define vl_msg_name_crc_list
+#include <nsh/nsh.api.h>
+#undef vl_msg_name_crc_list
+
+/*
+ * 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)+nm->msg_id_base);               \
+    rmp->context = mp->context;                                 \
+    rmp->retval = ntohl(rv);                                    \
+                                                                \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+  } while(0);
+
+#define REPLY_MACRO2(t, body)                                   \
+  do {                                                          \
+    unix_shared_memory_queue_t * q;                             \
+    rv = vl_msg_api_pd_handler (mp, rv);                        \
+    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)+nm->msg_id_base);               \
+    rmp->context = mp->context;                                 \
+    rmp->retval = ntohl(rv);                                    \
+    do {body;} while (0);                                       \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+  } while(0);
+
+#define FINISH                                  \
+    vec_add1 (s, 0);                            \
+    vl_print (handle, (char *)s);               \
+    vec_free (s);                               \
+    return handle;
+
+/* List of message types that this plugin understands */
+
+#define foreach_nsh_plugin_api_msg             \
+  _(NSH_ADD_DEL_ENTRY, nsh_add_del_entry)      \
+  _(NSH_ENTRY_DUMP, nsh_entry_dump)             \
+  _(NSH_ADD_DEL_MAP, nsh_add_del_map)           \
+  _(NSH_MAP_DUMP, nsh_map_dump)
+
+ /* Uses network order's class and type to register */
+int
+nsh_md2_register_option (u16 class,
+                        u8 type,
+                        u8 option_size,
+                        int add_options (u8 * opt,
+                                         u8 * opt_size),
+                        int options (vlib_buffer_t * b,
+                                     nsh_tlv_header_t * opt),
+                        int swap_options (vlib_buffer_t * b,
+                                          nsh_tlv_header_t * old_opt,
+                                          nsh_tlv_header_t * new_opt),
+                        int pop_options (vlib_buffer_t * b,
+                                         nsh_tlv_header_t * opt),
+                        u8 * trace (u8 * s, nsh_tlv_header_t * opt))
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_option_map_by_key_t key, *key_copy;
+  uword *p;
+  nsh_option_map_t *nsh_option;
+
+  key.class = class;
+  key.type = type;
+  key.pad = 0;
+
+  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
+  /* Already registered */
+  if (p != 0)
+    {
+      return (-1);
+    }
+
+  pool_get_aligned (nm->nsh_option_mappings, nsh_option,
+                   CLIB_CACHE_LINE_BYTES);
+  memset (nsh_option, 0, sizeof (*nsh_option));
+  nsh_option->option_id = nsh_option - nm->nsh_option_mappings;
+
+  key_copy = clib_mem_alloc (sizeof (*key_copy));
+  clib_memcpy (key_copy, &key, sizeof (*key_copy));
+  hash_set_mem (nm->nsh_option_map_by_key, key_copy,
+               nsh_option - nm->nsh_option_mappings);
+
+  if (option_size > (MAX_NSH_OPTION_LEN + sizeof (nsh_tlv_header_t)))
+    {
+      return (-1);
+    }
+  nm->options_size[nsh_option->option_id] = option_size;
+  nm->add_options[nsh_option->option_id] = add_options;
+  nm->options[nsh_option->option_id] = options;
+  nm->swap_options[nsh_option->option_id] = swap_options;
+  nm->pop_options[nsh_option->option_id] = pop_options;
+  nm->trace[nsh_option->option_id] = trace;
+
+  return (0);
+}
+
+/* Uses network order's class and type to lookup */
+nsh_option_map_t *
+nsh_md2_lookup_option (u16 class, u8 type)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_option_map_by_key_t key;
+  uword *p;
+
+  key.class = class;
+  key.type = type;
+  key.pad = 0;
+
+  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
+  /* not registered */
+  if (p == 0)
+    {
+      return NULL;
+    }
+
+  return pool_elt_at_index (nm->nsh_option_mappings, p[0]);
+
+}
+
+/* Uses network order's class and type to unregister */
+int
+nsh_md2_unregister_option (u16 class,
+                          u8 type,
+                          int options (vlib_buffer_t * b,
+                                       nsh_tlv_header_t * opt),
+                          u8 * trace (u8 * s, nsh_tlv_header_t * opt))
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_option_map_by_key_t key, *key_copy;
+  uword *p;
+  hash_pair_t *hp;
+  nsh_option_map_t *nsh_option;
+
+  key.class = class;
+  key.type = type;
+  key.pad = 0;
+
+  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
+  /* not registered */
+  if (p == 0)
+    {
+      return (-1);
+    }
+
+  nsh_option = pool_elt_at_index (nm->nsh_option_mappings, p[0]);
+  nm->options[nsh_option->option_id] = NULL;
+  nm->add_options[nsh_option->option_id] = NULL;
+  nm->pop_options[nsh_option->option_id] = NULL;
+  nm->trace[nsh_option->option_id] = NULL;
+
+  hp = hash_get_pair (nm->nsh_option_map_by_key, &key);
+  key_copy = (void *) (hp->key);
+  hash_unset_mem (nm->nsh_option_map_by_key, &key_copy);
+  clib_mem_free (key_copy);
+
+  pool_put (nm->nsh_option_mappings, nsh_option);
+
+  return (0);
+}
+
+/* format from network order */
+u8 *
+format_nsh_header (u8 * s, va_list * args)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_md2_data_t *opt0;
+  nsh_md2_data_t *limit0;
+  nsh_option_map_t *nsh_option;
+  u8 option_len = 0;
+
+  u8 *header = va_arg (*args, u8 *);
+  nsh_base_header_t *nsh_base = (nsh_base_header_t *) header;
+  nsh_md1_data_t *nsh_md1 = (nsh_md1_data_t *) (nsh_base + 1);
+  nsh_md2_data_t *nsh_md2 = (nsh_md2_data_t *) (nsh_base + 1);
+  opt0 = (nsh_md2_data_t *) nsh_md2;
+  limit0 = (nsh_md2_data_t *) ((u8 *) nsh_md2 +
+                              ((nsh_base->length & NSH_LEN_MASK) * 4
+                               - sizeof (nsh_base_header_t)));
+
+  s = format (s, "nsh ver %d ", (nsh_base->ver_o_c >> 6));
+  if (nsh_base->ver_o_c & NSH_O_BIT)
+    s = format (s, "O-set ");
+
+  if (nsh_base->ver_o_c & NSH_C_BIT)
+    s = format (s, "C-set ");
+
+  s = format (s, "ttl %d ", (nsh_base->ver_o_c & NSH_TTL_H4_MASK) << 2 |
+             (nsh_base->length & NSH_TTL_L2_MASK) >> 6);
+
+  s = format (s, "len %d (%d bytes) md_type %d next_protocol %d\n",
+             (nsh_base->length & NSH_LEN_MASK),
+             (nsh_base->length & NSH_LEN_MASK) * 4,
+             nsh_base->md_type, nsh_base->next_protocol);
+
+  s = format (s, "  service path %d service index %d\n",
+             (clib_net_to_host_u32 (nsh_base->nsp_nsi) >> NSH_NSP_SHIFT) &
+             NSH_NSP_MASK,
+             clib_net_to_host_u32 (nsh_base->nsp_nsi) & NSH_NSI_MASK);
+
+  if (nsh_base->md_type == 1)
+    {
+      s = format (s, "  c1 %d c2 %d c3 %d c4 %d\n",
+                 clib_net_to_host_u32 (nsh_md1->c1),
+                 clib_net_to_host_u32 (nsh_md1->c2),
+                 clib_net_to_host_u32 (nsh_md1->c3),
+                 clib_net_to_host_u32 (nsh_md1->c4));
+    }
+  else if (nsh_base->md_type == 2)
+    {
+      s = format (s, "  Supported TLVs: \n");
+
+      /* Scan the set of variable metadata, network order */
+      while (opt0 < limit0)
+       {
+         nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+         if (nsh_option != NULL)
+           {
+             if (nm->trace[nsh_option->option_id] != NULL)
+               {
+                 s = (*nm->trace[nsh_option->option_id]) (s, opt0);
+               }
+             else
+               {
+                 s =
+                   format (s, "\n    untraced option %d length %d",
+                           opt0->type, opt0->length);
+               }
+           }
+         else
+           {
+             s =
+               format (s, "\n    unrecognized option %d length %d",
+                       opt0->type, opt0->length);
+           }
+
+         /* round to 4-byte */
+         option_len = ((opt0->length + 3) >> 2) << 2;
+         opt0 =
+           (nsh_md2_data_t *) (((u8 *) opt0) + sizeof (nsh_md2_data_t) +
+                               option_len);
+       }
+    }
+
+  return s;
+}
+
+static u8 *
+format_nsh_action (u8 * s, va_list * args)
+{
+  u32 nsh_action = va_arg (*args, u32);
+
+  switch (nsh_action)
+    {
+    case NSH_ACTION_SWAP:
+      return format (s, "swap");
+    case NSH_ACTION_PUSH:
+      return format (s, "push");
+    case NSH_ACTION_POP:
+      return format (s, "pop");
+    default:
+      return format (s, "unknown %d", nsh_action);
+    }
+  return s;
+}
+
+u8 *
+format_nsh_map (u8 * s, va_list * args)
+{
+  nsh_map_t *map = va_arg (*args, nsh_map_t *);
+
+  s = format (s, "nsh entry nsp: %d nsi: %d ",
+             (map->nsp_nsi >> NSH_NSP_SHIFT) & NSH_NSP_MASK,
+             map->nsp_nsi & NSH_NSI_MASK);
+  s = format (s, "maps to nsp: %d nsi: %d ",
+             (map->mapped_nsp_nsi >> NSH_NSP_SHIFT) & NSH_NSP_MASK,
+             map->mapped_nsp_nsi & NSH_NSI_MASK);
+
+  s = format (s, " nsh_action %U\n", format_nsh_action, map->nsh_action);
+
+  switch (map->next_node)
+    {
+    case NSH_NODE_NEXT_ENCAP_GRE4:
+      {
+       s = format (s, "encapped by GRE4 intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_GRE6:
+      {
+       s = format (s, "encapped by GRE6 intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_VXLANGPE:
+      {
+       s = format (s, "encapped by VXLAN GPE intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_VXLAN4:
+      {
+       s = format (s, "encapped by VXLAN4 intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_VXLAN6:
+      {
+       s = format (s, "encapped by VXLAN6 intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_DECAP_ETH_INPUT:
+      {
+       s = format (s, "encap-none");
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_LISP_GPE:
+      {
+       s = format (s, "encapped by LISP GPE intf: %d", map->sw_if_index);
+       break;
+      }
+    case NSH_NODE_NEXT_ENCAP_ETHERNET:
+      {
+       s = format (s, "encapped by Ethernet intf: %d", map->sw_if_index);
+       break;
+      }
+    default:
+      s = format (s, "only GRE and VXLANGPE support in this rev");
+    }
+
+  return s;
+}
+
+u8 *
+format_nsh_node_map_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 *);
+  nsh_input_trace_t *t = va_arg (*args, nsh_input_trace_t *);
+
+  s = format (s, "\n  %U", format_nsh_header, &(t->trace_data));
+
+  return s;
+}
+
+/**
+ * @brief Naming for NSH tunnel
+ *
+ * @param *s formatting string
+ * @param *args
+ *
+ * @return *s formatted string
+ *
+ */
+static u8 *
+format_nsh_name (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  return format (s, "nsh_tunnel%d", dev_instance);
+}
+
+/**
+ * @brief CLI function for NSH admin up/down
+ *
+ * @param *vnm
+ * @param nsh_hw_if
+ * @param flag
+ *
+ * @return *rc
+ *
+ */
+static clib_error_t *
+nsh_interface_admin_up_down (vnet_main_t * vnm, u32 nsh_hw_if, u32 flags)
+{
+  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+    vnet_hw_interface_set_flags (vnm, nsh_hw_if,
+                                VNET_HW_INTERFACE_FLAG_LINK_UP);
+  else
+    vnet_hw_interface_set_flags (vnm, nsh_hw_if, 0);
+
+  return 0;
+}
+
+static uword
+dummy_interface_tx (vlib_main_t * vm,
+                   vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  clib_warning ("you shouldn't be here, leaking buffers...");
+  return frame->n_vectors;
+}
+
+VNET_DEVICE_CLASS (nsh_device_class, static) =
+{
+.name = "NSH",.format_device_name = format_nsh_name,.tx_function =
+    dummy_interface_tx,.admin_up_down_function =
+    nsh_interface_admin_up_down,};
+
+/**
+ * @brief Formatting function for tracing VXLAN GPE with length
+ *
+ * @param *s
+ * @param *args
+ *
+ * @return *s
+ *
+ */
+static u8 *
+format_nsh_tunnel_with_length (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  s = format (s, "unimplemented dev %u", dev_instance);
+  return s;
+}
+
+VNET_HW_INTERFACE_CLASS (nsh_hw_class) =
+{
+.name = "NSH",.format_header =
+    format_nsh_tunnel_with_length,.build_rewrite =
+    default_build_rewrite,.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,};
+
+
+/**
+ * Action function to add or del an nsh map.
+ * Shared by both CLI and binary API
+ **/
+
+int
+nsh_add_del_map (nsh_add_del_map_args_t * a, u32 * map_indexp)
+{
+  nsh_main_t *nm = &nsh_main;
+  vnet_main_t *vnm = nm->vnet_main;
+  nsh_map_t *map = 0;
+  u32 key, *key_copy;
+  uword *entry;
+  hash_pair_t *hp;
+  u32 map_index = ~0;
+  vnet_hw_interface_t *hi;
+  u32 nsh_hw_if = ~0;
+  u32 nsh_sw_if = ~0;
+
+  /* net order, so data plane could use nsh header to lookup directly */
+  key = clib_host_to_net_u32 (a->map.nsp_nsi);
+
+  entry = hash_get_mem (nm->nsh_mapping_by_key, &key);
+
+  if (a->is_add)
+    {
+      /* adding an entry, must not already exist */
+      if (entry)
+       return -1;              //TODO API_ERROR_INVALID_VALUE;
+
+      pool_get_aligned (nm->nsh_mappings, map, CLIB_CACHE_LINE_BYTES);
+      memset (map, 0, sizeof (*map));
+
+      /* copy from arg structure */
+      map->nsp_nsi = a->map.nsp_nsi;
+      map->mapped_nsp_nsi = a->map.mapped_nsp_nsi;
+      map->nsh_action = a->map.nsh_action;
+      map->sw_if_index = a->map.sw_if_index;
+      map->rx_sw_if_index = a->map.rx_sw_if_index;
+      map->next_node = a->map.next_node;
+      map->adj_index = a->map.adj_index;
+
+
+      key_copy = clib_mem_alloc (sizeof (*key_copy));
+      clib_memcpy (key_copy, &key, sizeof (*key_copy));
+
+      hash_set_mem (nm->nsh_mapping_by_key, key_copy, map - nm->nsh_mappings);
+      map_index = map - nm->nsh_mappings;
+
+      if (vec_len (nm->free_nsh_tunnel_hw_if_indices) > 0)
+       {
+         nsh_hw_if = nm->free_nsh_tunnel_hw_if_indices
+           [vec_len (nm->free_nsh_tunnel_hw_if_indices) - 1];
+         _vec_len (nm->free_nsh_tunnel_hw_if_indices) -= 1;
+
+         hi = vnet_get_hw_interface (vnm, nsh_hw_if);
+         hi->dev_instance = map_index;
+         hi->hw_instance = hi->dev_instance;
+       }
+      else
+       {
+         nsh_hw_if = vnet_register_interface
+           (vnm, nsh_device_class.index, map_index, nsh_hw_class.index,
+            map_index);
+         hi = vnet_get_hw_interface (vnm, nsh_hw_if);
+         hi->output_node_index = nsh_aware_vnf_proxy_node.index;
+       }
+
+      map->nsh_hw_if = nsh_hw_if;
+      map->nsh_sw_if = nsh_sw_if = hi->sw_if_index;
+      vec_validate_init_empty (nm->tunnel_index_by_sw_if_index, nsh_sw_if,
+                              ~0);
+      nm->tunnel_index_by_sw_if_index[nsh_sw_if] = key;
+
+      vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
+                                  VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+    }
+  else
+    {
+      if (!entry)
+       return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
+
+      map = pool_elt_at_index (nm->nsh_mappings, entry[0]);
+
+      vnet_sw_interface_set_flags (vnm, map->nsh_sw_if,
+                                  VNET_SW_INTERFACE_FLAG_ADMIN_DOWN);
+      vec_add1 (nm->free_nsh_tunnel_hw_if_indices, map->nsh_sw_if);
+      nm->tunnel_index_by_sw_if_index[map->nsh_sw_if] = ~0;
+
+      hp = hash_get_pair (nm->nsh_mapping_by_key, &key);
+      key_copy = (void *) (hp->key);
+      hash_unset_mem (nm->nsh_mapping_by_key, &key);
+      clib_mem_free (key_copy);
+
+      pool_put (nm->nsh_mappings, map);
+    }
+
+  if (map_indexp)
+    *map_indexp = map_index;
+
+  return 0;
+}
+
+/**
+ * Action function to add or del an nsh-proxy-session.
+ * Shared by both CLI and binary API
+ **/
+
+int
+nsh_add_del_proxy_session (nsh_add_del_map_args_t * a)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_proxy_session_t *proxy = 0;
+  nsh_proxy_session_by_key_t key, *key_copy;
+  uword *entry;
+  hash_pair_t *hp;
+  u32 nsp = 0, nsi = 0;
+
+  memset (&key, 0, sizeof (key));
+  key.transport_type = a->map.next_node;
+  key.transport_index = a->map.sw_if_index;
+
+  entry = hash_get_mem (nm->nsh_proxy_session_by_key, &key);
+
+  if (a->is_add)
+    {
+      /* adding an entry, must not already exist */
+      if (entry)
+       return -1;              //TODO API_ERROR_INVALID_VALUE;
+
+      pool_get_aligned (nm->nsh_proxy_sessions, proxy, CLIB_CACHE_LINE_BYTES);
+      memset (proxy, 0, sizeof (*proxy));
+
+      /* Nsi needs to minus 1 within NSH-Proxy */
+      nsp = (a->map.nsp_nsi >> NSH_NSP_SHIFT) & NSH_NSP_MASK;
+      nsi = a->map.nsp_nsi & NSH_NSI_MASK;
+      if (nsi == 0)
+       return -1;
+
+      nsi = nsi - 1;
+      /* net order, so could use it to lookup nsh map table directly */
+      proxy->nsp_nsi = clib_host_to_net_u32 ((nsp << NSH_NSP_SHIFT) | nsi);
+
+      key_copy = clib_mem_alloc (sizeof (*key_copy));
+      clib_memcpy (key_copy, &key, sizeof (*key_copy));
+
+      hash_set_mem (nm->nsh_proxy_session_by_key, key_copy,
+                   proxy - nm->nsh_proxy_sessions);
+    }
+  else
+    {
+      if (!entry)
+       return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
+
+      proxy = pool_elt_at_index (nm->nsh_proxy_sessions, entry[0]);
+      hp = hash_get_pair (nm->nsh_proxy_session_by_key, &key);
+      key_copy = (void *) (hp->key);
+      hash_unset_mem (nm->nsh_proxy_session_by_key, &key);
+      clib_mem_free (key_copy);
+
+      pool_put (nm->nsh_proxy_sessions, proxy);
+    }
+
+  return 0;
+}
+
+/**
+ * CLI command for NSH map
+ */
+
+static uword
+unformat_nsh_action (unformat_input_t * input, va_list * args)
+{
+  u32 *result = va_arg (*args, u32 *);
+  u32 tmp;
+
+  if (unformat (input, "swap"))
+    *result = NSH_ACTION_SWAP;
+  else if (unformat (input, "push"))
+    *result = NSH_ACTION_PUSH;
+  else if (unformat (input, "pop"))
+    *result = NSH_ACTION_POP;
+  else if (unformat (input, "%d", &tmp))
+    *result = tmp;
+  else
+    return 0;
+
+  return 1;
+}
+
+static adj_index_t
+nsh_get_adj_by_sw_if_index (u32 sw_if_index)
+{
+  adj_index_t ai = ~0;
+
+  /* *INDENT-OFF* */
+  pool_foreach_index(ai, adj_pool,
+  ({
+      if (sw_if_index == adj_get_sw_if_index(ai))
+      {
+        return ai;
+      }
+  }));
+  /* *INDENT-ON* */
+
+  return ~0;
+}
+
+static clib_error_t *
+nsh_add_del_map_command_fn (vlib_main_t * vm,
+                           unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u8 is_add = 1;
+  u32 nsp, nsi, mapped_nsp, mapped_nsi, nsh_action;
+  int nsp_set = 0, nsi_set = 0, mapped_nsp_set = 0, mapped_nsi_set = 0;
+  int nsh_action_set = 0;
+  u32 next_node = ~0;
+  u32 adj_index = ~0;
+  u32 sw_if_index = ~0;                // temporary requirement to get this moved over to NSHSFC
+  u32 rx_sw_if_index = ~0;     // temporary requirement to get this moved over to NSHSFC
+  nsh_add_del_map_args_t _a, *a = &_a;
+  u32 map_index;
+  int rv;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "nsp %d", &nsp))
+       nsp_set = 1;
+      else if (unformat (line_input, "nsi %d", &nsi))
+       nsi_set = 1;
+      else if (unformat (line_input, "mapped-nsp %d", &mapped_nsp))
+       mapped_nsp_set = 1;
+      else if (unformat (line_input, "mapped-nsi %d", &mapped_nsi))
+       mapped_nsi_set = 1;
+      else if (unformat (line_input, "nsh_action %U", unformat_nsh_action,
+                        &nsh_action))
+       nsh_action_set = 1;
+      else if (unformat (line_input, "encap-gre4-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_GRE4;
+      else if (unformat (line_input, "encap-gre6-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_GRE6;
+      else if (unformat (line_input, "encap-vxlan-gpe-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_VXLANGPE;
+      else if (unformat (line_input, "encap-lisp-gpe-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_LISP_GPE;
+      else if (unformat (line_input, "encap-vxlan4-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_VXLAN4;
+      else if (unformat (line_input, "encap-vxlan6-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_VXLAN6;
+      else if (unformat (line_input, "encap-eth-intf %d", &sw_if_index))
+       {
+         next_node = NSH_NODE_NEXT_ENCAP_ETHERNET;
+         adj_index = nsh_get_adj_by_sw_if_index (sw_if_index);
+       }
+      else
+       if (unformat
+           (line_input, "encap-none %d %d", &sw_if_index, &rx_sw_if_index))
+       next_node = NSH_NODE_NEXT_DECAP_ETH_INPUT;
+      else
+       return clib_error_return (0, "parse error: '%U'",
+                                 format_unformat_error, line_input);
+    }
+
+  unformat_free (line_input);
+
+  if (nsp_set == 0 || nsi_set == 0)
+    return clib_error_return (0, "nsp nsi pair required. Key: for NSH entry");
+
+  if (mapped_nsp_set == 0 || mapped_nsi_set == 0)
+    return clib_error_return (0,
+                             "mapped-nsp mapped-nsi pair required. Key: for NSH entry");
+
+  if (nsh_action_set == 0)
+    return clib_error_return (0, "nsh_action required: swap|push|pop.");
+
+  if (next_node == ~0)
+    return clib_error_return (0,
+                             "must specific action: [encap-gre-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-lisp-gpe-intf <nn> | encap-none <tx_sw_if_index> <rx_sw_if_index>]");
+
+  memset (a, 0, sizeof (*a));
+
+  /* set args structure */
+  a->is_add = is_add;
+  a->map.nsp_nsi = (nsp << NSH_NSP_SHIFT) | nsi;
+  a->map.mapped_nsp_nsi = (mapped_nsp << NSH_NSP_SHIFT) | mapped_nsi;
+  a->map.nsh_action = nsh_action;
+  a->map.sw_if_index = sw_if_index;
+  a->map.rx_sw_if_index = rx_sw_if_index;
+  a->map.next_node = next_node;
+  a->map.adj_index = adj_index;
+
+  rv = nsh_add_del_map (a, &map_index);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    case -1:                   //TODO API_ERROR_INVALID_VALUE:
+      return clib_error_return (0,
+                               "mapping already exists. Remove it first.");
+
+    case -2:                   // TODO API_ERROR_NO_SUCH_ENTRY:
+      return clib_error_return (0, "mapping does not exist.");
+
+    default:
+      return clib_error_return (0, "nsh_add_del_map returned %d", rv);
+    }
+
+  if ((a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN4)
+      | (a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN6))
+    {
+      rv = nsh_add_del_proxy_session (a);
+
+      switch (rv)
+       {
+       case 0:
+         break;
+       case -1:                //TODO API_ERROR_INVALID_VALUE:
+         return clib_error_return (0,
+                                   "nsh-proxy-session already exists. Remove it first.");
+
+       case -2:                // TODO API_ERROR_NO_SUCH_ENTRY:
+         return clib_error_return (0, "nsh-proxy-session does not exist.");
+
+       default:
+         return clib_error_return
+           (0, "nsh_add_del_proxy_session() returned %d", rv);
+       }
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (create_nsh_map_command, static) =
+{
+.path = "create nsh map",.short_help =
+    "create nsh map nsp <nn> nsi <nn> [del] mapped-nsp <nn> mapped-nsi <nn> nsh_action [swap|push|pop] "
+    "[encap-gre4-intf <nn> | encap-gre4-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-lisp-gpe-intf <nn> "
+    " encap-vxlan4-intf <nn> | encap-vxlan6-intf <nn>| encap-eth-intf <nn> | encap-none]\n",.function
+    = nsh_add_del_map_command_fn,};
+
+/** API message handler */
+static void
+vl_api_nsh_add_del_map_t_handler (vl_api_nsh_add_del_map_t * mp)
+{
+  vl_api_nsh_add_del_map_reply_t *rmp;
+  nsh_main_t *nm = &nsh_main;
+  int rv;
+  nsh_add_del_map_args_t _a, *a = &_a;
+  u32 map_index = ~0;
+
+  a->is_add = mp->is_add;
+  a->map.nsp_nsi = ntohl (mp->nsp_nsi);
+  a->map.mapped_nsp_nsi = ntohl (mp->mapped_nsp_nsi);
+  a->map.nsh_action = ntohl (mp->nsh_action);
+  a->map.sw_if_index = ntohl (mp->sw_if_index);
+  a->map.rx_sw_if_index = ntohl (mp->rx_sw_if_index);
+  a->map.next_node = ntohl (mp->next_node);
+
+  rv = nsh_add_del_map (a, &map_index);
+
+  if ((a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN4)
+      | (a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN6))
+    {
+      rv = nsh_add_del_proxy_session (a);
+    }
+
+  REPLY_MACRO2 (VL_API_NSH_ADD_DEL_MAP_REPLY, (
+                                               {
+                                               rmp->map_index =
+                                               htonl (map_index);
+                                               }
+               ));
+}
+
+/**
+ * CLI command for showing the mapping between NSH entries
+ */
+static clib_error_t *
+show_nsh_map_command_fn (vlib_main_t * vm,
+                        unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_map_t *map;
+
+  if (pool_elts (nm->nsh_mappings) == 0)
+    vlib_cli_output (vm, "No nsh maps configured.");
+
+  pool_foreach (map, nm->nsh_mappings, (
+                                        {
+                                        vlib_cli_output (vm, "%U",
+                                                         format_nsh_map,
+                                                         map);
+                                        }
+               ));
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_nsh_map_command, static) =
+{
+.path = "show nsh map",.function = show_nsh_map_command_fn,};
+
+int
+nsh_header_rewrite (nsh_entry_t * nsh_entry)
+{
+  u8 *rw = 0;
+  int len = 0;
+  nsh_base_header_t *nsh_base;
+  nsh_md1_data_t *nsh_md1;
+  nsh_main_t *nm = &nsh_main;
+  nsh_md2_data_t *opt0;
+  nsh_md2_data_t *limit0;
+  nsh_md2_data_t *nsh_md2;
+  nsh_option_map_t _nsh_option, *nsh_option = &_nsh_option;
+  u8 old_option_size = 0;
+  u8 new_option_size = 0;
+
+  vec_free (nsh_entry->rewrite);
+  if (nsh_entry->nsh_base.md_type == 1)
+    {
+      len = sizeof (nsh_base_header_t) + sizeof (nsh_md1_data_t);
+    }
+  else if (nsh_entry->nsh_base.md_type == 2)
+    {
+      /* set to maxim, maybe dataplane will add more TLVs */
+      len = MAX_NSH_HEADER_LEN;
+    }
+  vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
+  memset (rw, 0, len);
+
+  nsh_base = (nsh_base_header_t *) rw;
+  nsh_base->ver_o_c = nsh_entry->nsh_base.ver_o_c;
+  nsh_base->length = nsh_entry->nsh_base.length;
+  nsh_base->md_type = nsh_entry->nsh_base.md_type;
+  nsh_base->next_protocol = nsh_entry->nsh_base.next_protocol;
+  nsh_base->nsp_nsi = clib_host_to_net_u32 (nsh_entry->nsh_base.nsp_nsi);
+
+  if (nsh_base->md_type == 1)
+    {
+      nsh_md1 = (nsh_md1_data_t *) (rw + sizeof (nsh_base_header_t));
+      nsh_md1->c1 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c1);
+      nsh_md1->c2 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c2);
+      nsh_md1->c3 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c3);
+      nsh_md1->c4 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c4);
+      nsh_entry->rewrite_size = 24;
+    }
+  else if (nsh_base->md_type == 2)
+    {
+      opt0 = (nsh_md2_data_t *) (nsh_entry->tlvs_data);
+      limit0 = (nsh_md2_data_t *) ((u8 *) opt0 + nsh_entry->tlvs_len);
+
+      nsh_md2 = (nsh_md2_data_t *) (rw + sizeof (nsh_base_header_t));
+      nsh_entry->rewrite_size = sizeof (nsh_base_header_t);
+
+      while (opt0 < limit0)
+       {
+         old_option_size = sizeof (nsh_md2_data_t) + opt0->length;
+         /* round to 4-byte */
+         old_option_size = ((old_option_size + 3) >> 2) << 2;
+
+         nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+         if (nsh_option == NULL)
+           {
+             goto next_tlv_md2;
+           }
+
+         if (nm->add_options[nsh_option->option_id] != NULL)
+           {
+             if (0 != nm->add_options[nsh_option->option_id] ((u8 *) nsh_md2,
+                                                              &new_option_size))
+               {
+                 goto next_tlv_md2;
+               }
+
+             /* round to 4-byte */
+             new_option_size = ((new_option_size + 3) >> 2) << 2;
+
+             nsh_entry->rewrite_size += new_option_size;
+             nsh_md2 =
+               (nsh_md2_data_t *) (((u8 *) nsh_md2) + new_option_size);
+             opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
+           }
+         else
+           {
+           next_tlv_md2:
+             opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
+           }
+
+       }
+    }
+
+  nsh_entry->rewrite = rw;
+  nsh_base->length = (nsh_base->length & NSH_TTL_L2_MASK) |
+    ((nsh_entry->rewrite_size >> 2) & NSH_LEN_MASK);
+
+  return 0;
+}
+
+
+/**
+ * Action function for adding an NSH entry
+ * nsh_add_del_entry_args_t *a: host order
+ */
+
+int
+nsh_add_del_entry (nsh_add_del_entry_args_t * a, u32 * entry_indexp)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_entry_t *nsh_entry = 0;
+  u32 key, *key_copy;
+  uword *entry_id;
+  hash_pair_t *hp;
+  u32 entry_index = ~0;
+  u8 tlvs_len = 0;
+  u8 *data = 0;
+
+  /* host order, because nsh map table stores nsp_nsi in host order */
+  key = a->nsh_entry.nsh_base.nsp_nsi;
+
+  entry_id = hash_get_mem (nm->nsh_entry_by_key, &key);
+
+  if (a->is_add)
+    {
+      /* adding an entry, must not already exist */
+      if (entry_id)
+       return -1;              // TODO VNET_API_ERROR_INVALID_VALUE;
+
+      pool_get_aligned (nm->nsh_entries, nsh_entry, CLIB_CACHE_LINE_BYTES);
+      memset (nsh_entry, 0, sizeof (*nsh_entry));
+
+      /* copy from arg structure */
+#define _(x) nsh_entry->nsh_base.x = a->nsh_entry.nsh_base.x;
+      foreach_copy_nsh_base_hdr_field;
+#undef _
+
+      if (a->nsh_entry.nsh_base.md_type == 1)
+       {
+         nsh_entry->md.md1_data.c1 = a->nsh_entry.md.md1_data.c1;
+         nsh_entry->md.md1_data.c2 = a->nsh_entry.md.md1_data.c2;
+         nsh_entry->md.md1_data.c3 = a->nsh_entry.md.md1_data.c3;
+         nsh_entry->md.md1_data.c4 = a->nsh_entry.md.md1_data.c4;
+       }
+      else if (a->nsh_entry.nsh_base.md_type == 2)
+       {
+         vec_free (nsh_entry->tlvs_data);
+         tlvs_len = a->nsh_entry.tlvs_len;
+         vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
+
+         clib_memcpy (data, a->nsh_entry.tlvs_data, tlvs_len);
+         nsh_entry->tlvs_data = data;
+         nsh_entry->tlvs_len = tlvs_len;
+         vec_free (a->nsh_entry.tlvs_data);
+       }
+
+      nsh_header_rewrite (nsh_entry);
+
+      key_copy = clib_mem_alloc (sizeof (*key_copy));
+      clib_memcpy (key_copy, &key, sizeof (*key_copy));
+
+      hash_set_mem (nm->nsh_entry_by_key, key_copy,
+                   nsh_entry - nm->nsh_entries);
+      entry_index = nsh_entry - nm->nsh_entries;
+    }
+  else
+    {
+      if (!entry_id)
+       return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
+
+      nsh_entry = pool_elt_at_index (nm->nsh_entries, entry_id[0]);
+      hp = hash_get_pair (nm->nsh_entry_by_key, &key);
+      key_copy = (void *) (hp->key);
+      hash_unset_mem (nm->nsh_entry_by_key, &key);
+      clib_mem_free (key_copy);
+
+      vec_free (nsh_entry->tlvs_data);
+      vec_free (nsh_entry->rewrite);
+      pool_put (nm->nsh_entries, nsh_entry);
+    }
+
+  if (entry_indexp)
+    *entry_indexp = entry_index;
+
+  return 0;
+}
+
+
+/**
+ * CLI command for adding NSH entry
+ */
+
+static clib_error_t *
+nsh_add_del_entry_command_fn (vlib_main_t * vm,
+                             unformat_input_t * input,
+                             vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u8 is_add = 1;
+  u8 ver_o_c = 0;
+  u8 ttl = 63;
+  u8 length = 0;
+  u8 md_type = 0;
+  u8 next_protocol = 1;                /* default: ip4 */
+  u32 nsp;
+  u8 nsp_set = 0;
+  u32 nsi;
+  u8 nsi_set = 0;
+  u32 nsp_nsi;
+  u32 c1 = 0;
+  u32 c2 = 0;
+  u32 c3 = 0;
+  u32 c4 = 0;
+  u8 *data = 0;
+  nsh_tlv_header_t tlv_header;
+  u8 cur_len = 0, tlvs_len = 0;
+  u8 *current;
+  nsh_main_t *nm = &nsh_main;
+  nsh_option_map_t _nsh_option, *nsh_option = &_nsh_option;
+  u8 option_size = 0;
+  u32 tmp;
+  int rv;
+  u32 entry_index;
+  nsh_add_del_entry_args_t _a, *a = &_a;
+  u8 has_ioam_trace_option = 0;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "version %d", &tmp))
+       ver_o_c |= (tmp & 3) << 6;
+      else if (unformat (line_input, "o-bit %d", &tmp))
+       ver_o_c |= (tmp & 1) << 5;
+      else if (unformat (line_input, "c-bit %d", &tmp))
+       ver_o_c |= (tmp & 1) << 4;
+      else if (unformat (line_input, "ttl %d", &ttl))
+       ver_o_c |= (ttl & NSH_LEN_MASK) >> 2;
+      else if (unformat (line_input, "md-type %d", &tmp))
+       md_type = tmp;
+      else if (unformat (line_input, "next-ip4"))
+       next_protocol = 1;
+      else if (unformat (line_input, "next-ip6"))
+       next_protocol = 2;
+      else if (unformat (line_input, "next-ethernet"))
+       next_protocol = 3;
+      else if (unformat (line_input, "c1 %d", &c1))
+       ;
+      else if (unformat (line_input, "c2 %d", &c2))
+       ;
+      else if (unformat (line_input, "c3 %d", &c3))
+       ;
+      else if (unformat (line_input, "c4 %d", &c4))
+       ;
+      else if (unformat (line_input, "nsp %d", &nsp))
+       nsp_set = 1;
+      else if (unformat (line_input, "nsi %d", &nsi))
+       nsi_set = 1;
+      else if (unformat (line_input, "tlv-ioam-trace"))
+       has_ioam_trace_option = 1;
+      else
+       return clib_error_return (0, "parse error: '%U'",
+                                 format_unformat_error, line_input);
+    }
+
+  unformat_free (line_input);
+
+  if (nsp_set == 0)
+    return clib_error_return (0, "nsp not specified");
+
+  if (nsi_set == 0)
+    return clib_error_return (0, "nsi not specified");
+
+  if (md_type == 1 && has_ioam_trace_option == 1)
+    return clib_error_return (0, "Invalid MD Type");
+
+  nsp_nsi = (nsp << 8) | nsi;
+
+  memset (a, 0, sizeof (*a));
+  a->is_add = is_add;
+
+  if (md_type == 1)
+    {
+      a->nsh_entry.md.md1_data.c1 = c1;
+      a->nsh_entry.md.md1_data.c2 = c2;
+      a->nsh_entry.md.md1_data.c3 = c3;
+      a->nsh_entry.md.md1_data.c4 = c4;
+      length = (sizeof (nsh_base_header_t) + sizeof (nsh_md1_data_t)) >> 2;
+    }
+  else if (md_type == 2)
+    {
+      length = sizeof (nsh_base_header_t) >> 2;
+
+      vec_free (a->nsh_entry.tlvs_data);
+      tlvs_len = (MAX_METADATA_LEN << 2);
+      vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
+      a->nsh_entry.tlvs_data = data;
+      current = data;
+
+      if (has_ioam_trace_option)
+       {
+         tlv_header.class = clib_host_to_net_u16 (NSH_MD2_IOAM_CLASS);
+         tlv_header.type = NSH_MD2_IOAM_OPTION_TYPE_TRACE;
+         /* Uses network order's class and type to lookup */
+         nsh_option =
+           nsh_md2_lookup_option (tlv_header.class, tlv_header.type);
+         if (nsh_option == NULL)
+           return clib_error_return (0, "iOAM Trace not registered");
+
+         if (nm->add_options[nsh_option->option_id] != NULL)
+           {
+             if (0 != nm->add_options[nsh_option->option_id] ((u8 *) current,
+                                                              &option_size))
+               {
+                 return clib_error_return (0, "Invalid MD Type");
+               }
+           }
+
+         nm->options_size[nsh_option->option_id] = option_size;
+         /* round to 4-byte */
+         option_size = (((option_size + 3) >> 2) << 2);
+
+         cur_len += option_size;
+         current = data + option_size;
+       }
+
+      /* Add more options' parsing */
+
+      a->nsh_entry.tlvs_len = cur_len;
+      length += (cur_len >> 2);
+    }
+  length = (length & NSH_LEN_MASK) | ((ttl & 0x3) << 6);
+
+#define _(x) a->nsh_entry.nsh_base.x = x;
+  foreach_copy_nsh_base_hdr_field;
+#undef _
+
+  rv = nsh_add_del_entry (a, &entry_index);
+
+  switch (rv)
+    {
+    case 0:
+      break;
+    default:
+      return clib_error_return (0, "nsh_add_del_entry returned %d", rv);
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (create_nsh_entry_command, static) =
+{
+.path = "create nsh entry",.short_help =
+    "create nsh entry {nsp <nn> nsi <nn>} [ttl <nn>] [md-type <nn>]"
+    "  [c1 <nn> c2 <nn> c3 <nn> c4 <nn>] [tlv-ioam-trace] [del]\n",.function
+    = nsh_add_del_entry_command_fn,};
+
+/** API message handler */
+static void vl_api_nsh_add_del_entry_t_handler
+  (vl_api_nsh_add_del_entry_t * mp)
+{
+  vl_api_nsh_add_del_entry_reply_t *rmp;
+  nsh_main_t *nm = &nsh_main;
+  int rv;
+  nsh_add_del_entry_args_t _a, *a = &_a;
+  u32 entry_index = ~0;
+  u8 tlvs_len = 0;
+  u8 *data = 0;
+
+  a->is_add = mp->is_add;
+  a->nsh_entry.nsh_base.ver_o_c =
+    (mp->ver_o_c & 0xF0) | ((mp->ttl & NSH_LEN_MASK) >> 2);
+  a->nsh_entry.nsh_base.length =
+    (mp->length & NSH_LEN_MASK) | ((mp->ttl & 0x3) << 6);
+  a->nsh_entry.nsh_base.md_type = mp->md_type;
+  a->nsh_entry.nsh_base.next_protocol = mp->next_protocol;
+  a->nsh_entry.nsh_base.nsp_nsi = ntohl (mp->nsp_nsi);
+  if (mp->md_type == 1)
+    {
+      a->nsh_entry.md.md1_data.c1 = ntohl (mp->c1);
+      a->nsh_entry.md.md1_data.c2 = ntohl (mp->c2);
+      a->nsh_entry.md.md1_data.c3 = ntohl (mp->c3);
+      a->nsh_entry.md.md1_data.c4 = ntohl (mp->c4);
+    }
+  else if (mp->md_type == 2)
+    {
+      tlvs_len = mp->tlv_length;
+      vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
+
+      clib_memcpy (data, mp->tlv, tlvs_len);
+      a->nsh_entry.tlvs_data = data;
+      a->nsh_entry.tlvs_len = tlvs_len;
+    }
+
+  rv = nsh_add_del_entry (a, &entry_index);
+
+  REPLY_MACRO2 (VL_API_NSH_ADD_DEL_ENTRY_REPLY, (
+                                                 {
+                                                 rmp->entry_index =
+                                                 htonl (entry_index);
+                                                 }
+               ));
+}
+
+static void send_nsh_entry_details
+  (nsh_entry_t * t, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_nsh_entry_details_t *rmp;
+  nsh_main_t *nm = &nsh_main;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+
+  rmp->_vl_msg_id = ntohs ((VL_API_NSH_ENTRY_DETAILS) + nm->msg_id_base);
+  rmp->ver_o_c = t->nsh_base.ver_o_c;
+  rmp->ttl = (t->nsh_base.ver_o_c & NSH_TTL_H4_MASK) << 2 |
+    (t->nsh_base.length & NSH_TTL_L2_MASK) >> 6;
+  rmp->length = t->nsh_base.length & NSH_LEN_MASK;
+  rmp->md_type = t->nsh_base.md_type;
+  rmp->next_protocol = t->nsh_base.next_protocol;
+  rmp->nsp_nsi = htonl (t->nsh_base.nsp_nsi);
+
+  if (t->nsh_base.md_type == 1)
+    {
+      rmp->tlv_length = 4;
+      rmp->c1 = htonl (t->md.md1_data.c1);
+      rmp->c2 = htonl (t->md.md1_data.c2);
+      rmp->c3 = htonl (t->md.md1_data.c3);
+      rmp->c4 = htonl (t->md.md1_data.c4);
+    }
+  else if (t->nsh_base.md_type == 2)
+    {
+      rmp->tlv_length = t->tlvs_len;
+      clib_memcpy (rmp->tlv, t->tlvs_data, t->tlvs_len);
+    }
+
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_nsh_entry_dump_t_handler (vl_api_nsh_entry_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  nsh_main_t *nm = &nsh_main;
+  nsh_entry_t *t;
+  u32 entry_index;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    {
+      return;
+    }
+
+  entry_index = ntohl (mp->entry_index);
+
+  if (~0 == entry_index)
+    {
+      pool_foreach (t, nm->nsh_entries, (
+                                         {
+                                         send_nsh_entry_details (t, q,
+                                                                 mp->context);
+                                         }
+                   ));
+    }
+  else
+    {
+      if (entry_index >= vec_len (nm->nsh_entries))
+       {
+         return;
+       }
+      t = &nm->nsh_entries[entry_index];
+      send_nsh_entry_details (t, q, mp->context);
+    }
+}
+
+static void send_nsh_map_details
+  (nsh_map_t * t, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_nsh_map_details_t *rmp;
+  nsh_main_t *nm = &nsh_main;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+
+  rmp->_vl_msg_id = ntohs ((VL_API_NSH_MAP_DETAILS) + nm->msg_id_base);
+  rmp->nsp_nsi = htonl (t->nsp_nsi);
+  rmp->mapped_nsp_nsi = htonl (t->mapped_nsp_nsi);
+  rmp->nsh_action = htonl (t->nsh_action);
+  rmp->sw_if_index = htonl (t->sw_if_index);
+  rmp->rx_sw_if_index = htonl (t->rx_sw_if_index);
+  rmp->next_node = htonl (t->next_node);
+
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_nsh_map_dump_t_handler (vl_api_nsh_map_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  nsh_main_t *nm = &nsh_main;
+  nsh_map_t *t;
+  u32 map_index;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    {
+      return;
+    }
+
+  map_index = ntohl (mp->map_index);
+
+  if (~0 == map_index)
+    {
+      pool_foreach (t, nm->nsh_mappings, (
+                                          {
+                                          send_nsh_map_details (t, q,
+                                                                mp->context);
+                                          }
+                   ));
+    }
+  else
+    {
+      if (map_index >= vec_len (nm->nsh_mappings))
+       {
+         return;
+       }
+      t = &nm->nsh_mappings[map_index];
+      send_nsh_map_details (t, q, mp->context);
+    }
+}
+
+static clib_error_t *
+show_nsh_entry_command_fn (vlib_main_t * vm,
+                          unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_entry_t *nsh_entry;
+
+  if (pool_elts (nm->nsh_entries) == 0)
+    vlib_cli_output (vm, "No nsh entries configured.");
+
+  pool_foreach (nsh_entry, nm->nsh_entries, (
+                                             {
+                                             vlib_cli_output (vm, "%U",
+                                                              format_nsh_header,
+                                                              nsh_entry->rewrite);
+                                             vlib_cli_output (vm,
+                                                              "  rewrite_size: %d bytes",
+                                                              nsh_entry->rewrite_size);
+                                             }
+               ));
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_nsh_entry_command, static) =
+{
+.path = "show nsh entry",.function = show_nsh_entry_command_fn,};
+
+
+/* Set up the API message handling tables */
+static clib_error_t *
+nsh_plugin_api_hookup (vlib_main_t * vm)
+{
+  nsh_main_t *nm __attribute__ ((unused)) = &nsh_main;
+#define _(N,n)                                                  \
+  vl_msg_api_set_handlers((VL_API_##N + nm->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_nsh_plugin_api_msg;
+#undef _
+
+  return 0;
+}
+
+static void
+setup_message_id_table (nsh_main_t * nm, api_main_t * am)
+{
+#define _(id,n,crc) \
+  vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + nm->msg_id_base);
+  foreach_vl_msg_name_crc_nsh;
+#undef _
+}
+
+always_inline void
+nsh_md2_encap (vlib_buffer_t * b, nsh_base_header_t * hdr,
+              nsh_entry_t * nsh_entry)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_base_header_t *nsh_base;
+  nsh_tlv_header_t *opt0;
+  nsh_tlv_header_t *limit0;
+  nsh_tlv_header_t *nsh_md2;
+  nsh_option_map_t *nsh_option;
+  u8 old_option_size = 0;
+  u8 new_option_size = 0;
+
+  /* Populate the NSH Header */
+  opt0 = (nsh_tlv_header_t *) (nsh_entry->tlvs_data);
+  limit0 = (nsh_tlv_header_t *) (nsh_entry->tlvs_data + nsh_entry->tlvs_len);
+
+  nsh_md2 = (nsh_tlv_header_t *) ((u8 *) hdr /*nsh_entry->rewrite */  +
+                                 sizeof (nsh_base_header_t));
+  nsh_entry->rewrite_size = sizeof (nsh_base_header_t);
+
+  /* Scan the set of variable metadata, process ones that we understand */
+  while (opt0 < limit0)
+    {
+      old_option_size = sizeof (nsh_tlv_header_t) + opt0->length;
+      /* round to 4-byte */
+      old_option_size = ((old_option_size + 3) >> 2) << 2;
+
+      nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+      if (nsh_option == NULL)
+       {
+         goto next_tlv_md2;
+       }
+
+      if (nm->options[nsh_option->option_id])
+       {
+         if ((*nm->options[nsh_option->option_id]) (b, nsh_md2))
+           {
+             goto next_tlv_md2;
+           }
+
+         /* option length may be varied */
+         new_option_size = sizeof (nsh_tlv_header_t) + nsh_md2->length;
+         /* round to 4-byte */
+         new_option_size = ((new_option_size + 3) >> 2) << 2;
+         nsh_entry->rewrite_size += new_option_size;
+
+         nsh_md2 = (nsh_tlv_header_t *) (((u8 *) nsh_md2) + new_option_size);
+         opt0 = (nsh_tlv_header_t *) (((u8 *) opt0) + old_option_size);
+
+       }
+      else
+       {
+       next_tlv_md2:
+         opt0 = (nsh_tlv_header_t *) (((u8 *) opt0) + old_option_size);
+       }
+    }
+
+  /* update nsh header's length */
+  nsh_base = (nsh_base_header_t *) nsh_entry->rewrite;
+  nsh_base->length = (nsh_base->length & NSH_TTL_L2_MASK) |
+    ((nsh_entry->rewrite_size >> 2) & NSH_LEN_MASK);
+  return;
+}
+
+always_inline void
+nsh_md2_swap (vlib_buffer_t * b,
+             nsh_base_header_t * hdr,
+             u32 header_len,
+             nsh_entry_t * nsh_entry, u32 * next, u32 drop_node_val)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_base_header_t *nsh_base;
+  nsh_tlv_header_t *opt0;
+  nsh_tlv_header_t *limit0;
+  nsh_tlv_header_t *nsh_md2;
+  nsh_option_map_t *nsh_option;
+  u8 old_option_size = 0;
+  u8 new_option_size = 0;
+
+  /* Populate the NSH Header */
+  opt0 = (nsh_md2_data_t *) (hdr + 1);
+  limit0 = (nsh_md2_data_t *) ((u8 *) hdr + header_len);
+
+  nsh_md2 =
+    (nsh_tlv_header_t *) (nsh_entry->rewrite + sizeof (nsh_base_header_t));
+  nsh_entry->rewrite_size = sizeof (nsh_base_header_t);
+
+  /* Scan the set of variable metadata, process ones that we understand */
+  while (opt0 < limit0)
+    {
+      old_option_size = sizeof (nsh_tlv_header_t) + opt0->length;
+      /* round to 4-byte */
+      old_option_size = ((old_option_size + 3) >> 2) << 2;
+
+      nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+      if (nsh_option == NULL)
+       {
+         goto next_tlv_md2;
+       }
+
+      if (nm->swap_options[nsh_option->option_id])
+       {
+         if ((*nm->swap_options[nsh_option->option_id]) (b, opt0, nsh_md2))
+           {
+             goto next_tlv_md2;
+           }
+
+         /* option length may be varied */
+         new_option_size = sizeof (nsh_tlv_header_t) + nsh_md2->length;
+         /* round to 4-byte */
+         new_option_size = ((new_option_size + 3) >> 2) << 2;
+         nsh_entry->rewrite_size += new_option_size;
+         nsh_md2 = (nsh_tlv_header_t *) (((u8 *) nsh_md2) + new_option_size);
+
+         opt0 = (nsh_tlv_header_t *) (((u8 *) opt0) + old_option_size);
+
+       }
+      else
+       {
+       next_tlv_md2:
+         opt0 = (nsh_tlv_header_t *) (((u8 *) opt0) + old_option_size);
+       }
+    }
+
+  /* update nsh header's length */
+  nsh_base = (nsh_base_header_t *) nsh_entry->rewrite;
+  nsh_base->length = (nsh_base->length & NSH_TTL_L2_MASK) |
+    ((nsh_entry->rewrite_size >> 2) & NSH_LEN_MASK);
+  return;
+}
+
+always_inline void
+nsh_md2_decap (vlib_buffer_t * b,
+              nsh_base_header_t * hdr,
+              u32 * header_len, u32 * next, u32 drop_node_val)
+{
+  nsh_main_t *nm = &nsh_main;
+  nsh_md2_data_t *opt0;
+  nsh_md2_data_t *limit0;
+  nsh_option_map_t *nsh_option;
+  u8 option_len = 0;
+
+  /* Populate the NSH Header */
+  opt0 = (nsh_md2_data_t *) (hdr + 1);
+  limit0 = (nsh_md2_data_t *) ((u8 *) hdr + *header_len);
+
+  /* Scan the set of variable metadata, process ones that we understand */
+  while (opt0 < limit0)
+    {
+      nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
+      if (nsh_option == NULL)
+       {
+         *next = drop_node_val;
+         return;
+       }
+
+      if (nm->pop_options[nsh_option->option_id])
+       {
+         if ((*nm->pop_options[nsh_option->option_id]) (b, opt0))
+           {
+             *next = drop_node_val;
+             return;
+           }
+       }
+      /* round to 4-byte */
+      option_len = ((opt0->length + 3) >> 2) << 2;
+      opt0 =
+       (nsh_md2_data_t *) (((u8 *) opt0) + sizeof (nsh_md2_data_t) +
+                           option_len);
+      *next =
+       (nm->decap_v4_next_override) ? (nm->decap_v4_next_override) : (*next);
+      *header_len = (nm->decap_v4_next_override) ? 0 : (*header_len);
+    }
+
+  return;
+}
+
+static uword
+nsh_input_map (vlib_main_t * vm,
+              vlib_node_runtime_t * node,
+              vlib_frame_t * from_frame, u32 node_type)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+  nsh_main_t *nm = &nsh_main;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         u32 bi0, bi1;
+         vlib_buffer_t *b0, *b1;
+         u32 next0 = NSH_NODE_NEXT_DROP, next1 = NSH_NODE_NEXT_DROP;
+         uword *entry0, *entry1;
+         nsh_base_header_t *hdr0 = 0, *hdr1 = 0;
+         u32 header_len0 = 0, header_len1 = 0;
+         u32 nsp_nsi0, nsp_nsi1;
+         u32 ttl0, ttl1;
+         u32 error0, error1;
+         nsh_map_t *map0 = 0, *map1 = 0;
+         nsh_entry_t *nsh_entry0 = 0, *nsh_entry1 = 0;
+         nsh_base_header_t *encap_hdr0 = 0, *encap_hdr1 = 0;
+         u32 encap_hdr_len0 = 0, encap_hdr_len1 = 0;
+         nsh_proxy_session_by_key_t key0, key1;
+         uword *p0, *p1;
+         nsh_proxy_session_t *proxy0, *proxy1;
+         u32 sw_if_index0 = 0, sw_if_index1 = 0;
+         ethernet_header_t dummy_eth0, dummy_eth1;
+
+         /* Prefetch next iteration. */
+         {
+           vlib_buffer_t *p2, *p3;
+
+           p2 = vlib_get_buffer (vm, from[2]);
+           p3 = vlib_get_buffer (vm, from[3]);
+
+           vlib_prefetch_buffer_header (p2, LOAD);
+           vlib_prefetch_buffer_header (p3, LOAD);
+
+           CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+           CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+         }
+
+         bi0 = from[0];
+         bi1 = from[1];
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+
+         error0 = 0;
+         error1 = 0;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+         hdr0 = vlib_buffer_get_current (b0);
+         if (node_type == NSH_INPUT_TYPE)
+           {
+             nsp_nsi0 = hdr0->nsp_nsi;
+             header_len0 = (hdr0->length & NSH_LEN_MASK) * 4;
+             ttl0 = (hdr0->ver_o_c & NSH_TTL_H4_MASK) << 2 |
+               (hdr0->length & NSH_TTL_L2_MASK) >> 6;
+             ttl0 = ttl0 - 1;
+             if (PREDICT_FALSE (ttl0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_INVALID_TTL;
+                 goto trace0;
+               }
+           }
+         else if (node_type == NSH_CLASSIFIER_TYPE)
+           {
+             nsp_nsi0 =
+               clib_host_to_net_u32 (vnet_buffer (b0)->
+                                     l2_classify.opaque_index);
+           }
+         else if (node_type == NSH_AWARE_VNF_PROXY_TYPE)
+           {
+             /* Push dummy Eth header */
+             memset (&dummy_eth0.dst_address[0], 0x11223344, 4);
+             memset (&dummy_eth0.dst_address[4], 0x5566, 2);
+             memset (&dummy_eth0.src_address[0], 0x778899aa, 4);
+             memset (&dummy_eth0.src_address[4], 0xbbcc, 2);
+             dummy_eth0.type = 0x0800;
+             vlib_buffer_advance (b0, -(word) sizeof (ethernet_header_t));
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, &dummy_eth0,
+                          (word) sizeof (ethernet_header_t));
+
+             sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+             nsp_nsi0 = nm->tunnel_index_by_sw_if_index[sw_if_index0];
+           }
+         else
+           {
+             memset (&key0, 0, sizeof (key0));
+             key0.transport_type = NSH_NODE_NEXT_ENCAP_VXLAN4;
+             key0.transport_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+             p0 = hash_get_mem (nm->nsh_proxy_session_by_key, &key0);
+             if (PREDICT_FALSE (p0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace0;
+               }
+
+             proxy0 = pool_elt_at_index (nm->nsh_proxy_sessions, p0[0]);
+             if (PREDICT_FALSE (proxy0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace0;
+               }
+             nsp_nsi0 = proxy0->nsp_nsi;
+           }
+
+         hdr1 = vlib_buffer_get_current (b1);
+         if (node_type == NSH_INPUT_TYPE)
+           {
+             nsp_nsi1 = hdr1->nsp_nsi;
+             header_len1 = (hdr1->length & NSH_LEN_MASK) * 4;
+             ttl1 = (hdr1->ver_o_c & NSH_TTL_H4_MASK) << 2 |
+               (hdr1->length & NSH_TTL_L2_MASK) >> 6;
+             ttl1 = ttl1 - 1;
+             if (PREDICT_FALSE (ttl1 == 0))
+               {
+                 error1 = NSH_NODE_ERROR_INVALID_TTL;
+                 goto trace1;
+               }
+           }
+         else if (node_type == NSH_CLASSIFIER_TYPE)
+           {
+             nsp_nsi1 =
+               clib_host_to_net_u32 (vnet_buffer (b1)->
+                                     l2_classify.opaque_index);
+           }
+         else if (node_type == NSH_AWARE_VNF_PROXY_TYPE)
+           {
+             /* Push dummy Eth header */
+             memset (&dummy_eth1.dst_address[0], 0x11223344, 4);
+             memset (&dummy_eth1.dst_address[4], 0x5566, 2);
+             memset (&dummy_eth1.src_address[0], 0x778899aa, 4);
+             memset (&dummy_eth1.src_address[4], 0xbbcc, 2);
+             dummy_eth1.type = 0x0800;
+             vlib_buffer_advance (b1, -(word) sizeof (ethernet_header_t));
+             hdr1 = vlib_buffer_get_current (b1);
+             clib_memcpy (hdr1, &dummy_eth1,
+                          (word) sizeof (ethernet_header_t));
+
+             sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
+             nsp_nsi1 = nm->tunnel_index_by_sw_if_index[sw_if_index1];
+           }
+         else
+           {
+             memset (&key1, 0, sizeof (key1));
+             key1.transport_type = NSH_NODE_NEXT_ENCAP_VXLAN4;
+             key1.transport_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
+             p1 = hash_get_mem (nm->nsh_proxy_session_by_key, &key1);
+             if (PREDICT_FALSE (p1 == 0))
+               {
+                 error1 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace1;
+               }
+
+             proxy1 = pool_elt_at_index (nm->nsh_proxy_sessions, p1[0]);
+             if (PREDICT_FALSE (proxy1 == 0))
+               {
+                 error1 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace1;
+               }
+             nsp_nsi1 = proxy1->nsp_nsi;
+           }
+
+         /* Process packet 0 */
+         entry0 = hash_get_mem (nm->nsh_mapping_by_key, &nsp_nsi0);
+         if (PREDICT_FALSE (entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace0;
+           }
+
+         /* Entry should point to a mapping ... */
+         map0 = pool_elt_at_index (nm->nsh_mappings, entry0[0]);
+         if (PREDICT_FALSE (map0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace0;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next0 = map0->next_node;
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = map0->adj_index;
+
+         if (PREDICT_FALSE (map0->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr0->md_type == 2))
+               {
+                 nsh_md2_decap (b0, hdr0, &header_len0, &next0,
+                                NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace0;
+                   }
+                 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+                   map0->rx_sw_if_index;
+               }
+
+             /* Pop NSH header */
+             vlib_buffer_advance (b0, (word) header_len0);
+             goto trace0;
+           }
+
+         entry0 = hash_get_mem (nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
+         if (PREDICT_FALSE (entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace0;
+           }
+
+         nsh_entry0 =
+           (nsh_entry_t *) pool_elt_at_index (nm->nsh_entries, entry0[0]);
+         encap_hdr0 = (nsh_base_header_t *) (nsh_entry0->rewrite);
+         /* rewrite_size should equal to (encap_hdr0->length * 4) */
+         encap_hdr_len0 = nsh_entry0->rewrite_size;
+
+         if (PREDICT_TRUE (map0->nsh_action == NSH_ACTION_SWAP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr0->md_type == 2))
+               {
+                 nsh_md2_swap (b0, hdr0, header_len0, nsh_entry0,
+                               &next0, NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace0;
+                   }
+               }
+
+             /* Pop old NSH header */
+             vlib_buffer_advance (b0, (word) header_len0);
+
+             /* After processing, md2's length may be varied */
+             encap_hdr_len0 = nsh_entry0->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b0, -(word) encap_hdr_len0);
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, encap_hdr0, (word) encap_hdr_len0);
+
+             goto trace0;
+           }
+
+         if (PREDICT_TRUE (map0->nsh_action == NSH_ACTION_PUSH))
+           {
+             /* After processing, md2's length may be varied */
+             encap_hdr_len0 = nsh_entry0->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b0, -(word) encap_hdr_len0);
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, encap_hdr0, (word) encap_hdr_len0);
+
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (nsh_entry0->nsh_base.md_type == 2))
+               {
+                 nsh_md2_encap (b0, hdr0, nsh_entry0);
+               }
+
+           }
+
+       trace0:b0->error = error0 ? node->errors[error0] : 0;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_input_trace_t *tr =
+               vlib_add_trace (vm, node, b0, sizeof (*tr));
+             clib_memcpy (&(tr->trace_data), hdr0,
+                          ((hdr0->length & NSH_LEN_MASK) * 4));
+           }
+
+         /* Process packet 1 */
+         entry1 = hash_get_mem (nm->nsh_mapping_by_key, &nsp_nsi1);
+         if (PREDICT_FALSE (entry1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace1;
+           }
+
+         /* Entry should point to a mapping ... */
+         map1 = pool_elt_at_index (nm->nsh_mappings, entry1[0]);
+         if (PREDICT_FALSE (map1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace1;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next1 = map1->next_node;
+         vnet_buffer (b1)->sw_if_index[VLIB_TX] = map1->sw_if_index;
+         vnet_buffer (b1)->ip.adj_index[VLIB_TX] = map1->adj_index;
+
+         if (PREDICT_FALSE (map1->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr1->md_type == 2))
+               {
+                 nsh_md2_decap (b1, hdr1, &header_len1, &next1,
+                                NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next1 == NSH_NODE_NEXT_DROP))
+                   {
+                     error1 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace1;
+                   }
+                 vnet_buffer (b1)->sw_if_index[VLIB_RX] =
+                   map0->rx_sw_if_index;
+               }
+
+             /* Pop NSH header */
+             vlib_buffer_advance (b1, (word) header_len1);
+             goto trace1;
+           }
+
+         entry1 = hash_get_mem (nm->nsh_entry_by_key, &map1->mapped_nsp_nsi);
+         if (PREDICT_FALSE (entry1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace1;
+           }
+
+         nsh_entry1 =
+           (nsh_entry_t *) pool_elt_at_index (nm->nsh_entries, entry1[0]);
+         encap_hdr1 = (nsh_base_header_t *) (nsh_entry1->rewrite);
+         /* rewrite_size should equal to (encap_hdr0->length * 4) */
+         encap_hdr_len1 = nsh_entry1->rewrite_size;
+
+         if (PREDICT_TRUE (map1->nsh_action == NSH_ACTION_SWAP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr1->md_type == 2))
+               {
+                 nsh_md2_swap (b1, hdr1, header_len1, nsh_entry1,
+                               &next1, NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next1 == NSH_NODE_NEXT_DROP))
+                   {
+                     error1 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace1;
+                   }
+               }
+
+             /* Pop old NSH header */
+             vlib_buffer_advance (b1, (word) header_len1);
+
+             /* After processing, md2's length may be varied */
+             encap_hdr_len1 = nsh_entry1->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b1, -(word) encap_hdr_len1);
+             hdr1 = vlib_buffer_get_current (b1);
+             clib_memcpy (hdr1, encap_hdr1, (word) encap_hdr_len1);
+
+             goto trace1;
+           }
+
+         if (PREDICT_FALSE (map1->nsh_action == NSH_ACTION_PUSH))
+           {
+             /* After processing, md2's length may be varied */
+             encap_hdr_len1 = nsh_entry1->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b1, -(word) encap_hdr_len1);
+             hdr1 = vlib_buffer_get_current (b1);
+             clib_memcpy (hdr1, encap_hdr1, (word) encap_hdr_len1);
+
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (nsh_entry1->nsh_base.md_type == 2))
+               {
+                 nsh_md2_encap (b1, hdr1, nsh_entry1);
+               }
+
+           }
+
+       trace1:b1->error = error1 ? node->errors[error1] : 0;
+
+         if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_input_trace_t *tr =
+               vlib_add_trace (vm, node, b1, sizeof (*tr));
+             clib_memcpy (&(tr->trace_data), hdr1,
+                          ((hdr1->length & NSH_LEN_MASK) * 4));
+           }
+
+         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 = 0;
+         vlib_buffer_t *b0 = NULL;
+         u32 next0 = NSH_NODE_NEXT_DROP;
+         uword *entry0;
+         nsh_base_header_t *hdr0 = 0;
+         u32 header_len0 = 0;
+         u32 nsp_nsi0;
+         u32 ttl0;
+         u32 error0;
+         nsh_map_t *map0 = 0;
+         nsh_entry_t *nsh_entry0 = 0;
+         nsh_base_header_t *encap_hdr0 = 0;
+         u32 encap_hdr_len0 = 0;
+         nsh_proxy_session_by_key_t key0;
+         uword *p0;
+         nsh_proxy_session_t *proxy0 = 0;
+         u32 sw_if_index0 = 0;
+         ethernet_header_t dummy_eth0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         error0 = 0;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         hdr0 = vlib_buffer_get_current (b0);
+
+         if (node_type == NSH_INPUT_TYPE)
+           {
+             nsp_nsi0 = hdr0->nsp_nsi;
+             header_len0 = (hdr0->length & NSH_LEN_MASK) * 4;
+             ttl0 = (hdr0->ver_o_c & NSH_TTL_H4_MASK) << 2 |
+               (hdr0->length & NSH_TTL_L2_MASK) >> 6;
+             ttl0 = ttl0 - 1;
+             if (PREDICT_FALSE (ttl0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_INVALID_TTL;
+                 goto trace00;
+               }
+           }
+         else if (node_type == NSH_CLASSIFIER_TYPE)
+           {
+             nsp_nsi0 =
+               clib_host_to_net_u32 (vnet_buffer (b0)->
+                                     l2_classify.opaque_index);
+           }
+         else if (node_type == NSH_AWARE_VNF_PROXY_TYPE)
+           {
+             /* Push dummy Eth header */
+             memset (&dummy_eth0.dst_address[0], 0x11223344, 4);
+             memset (&dummy_eth0.dst_address[4], 0x5566, 2);
+             memset (&dummy_eth0.src_address[0], 0x778899aa, 4);
+             memset (&dummy_eth0.src_address[4], 0xbbcc, 2);
+             dummy_eth0.type = 0x0800;
+             vlib_buffer_advance (b0, -(word) sizeof (ethernet_header_t));
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, &dummy_eth0,
+                          (word) sizeof (ethernet_header_t));
+
+             sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+             nsp_nsi0 = nm->tunnel_index_by_sw_if_index[sw_if_index0];
+           }
+         else
+           {
+             memset (&key0, 0, sizeof (key0));
+             key0.transport_type = NSH_NODE_NEXT_ENCAP_VXLAN4;
+             key0.transport_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+             p0 = hash_get_mem (nm->nsh_proxy_session_by_key, &key0);
+             if (PREDICT_FALSE (p0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace00;
+               }
+
+             proxy0 = pool_elt_at_index (nm->nsh_proxy_sessions, p0[0]);
+             if (PREDICT_FALSE (proxy0 == 0))
+               {
+                 error0 = NSH_NODE_ERROR_NO_PROXY;
+                 goto trace00;
+               }
+             nsp_nsi0 = proxy0->nsp_nsi;
+           }
+
+         entry0 = hash_get_mem (nm->nsh_mapping_by_key, &nsp_nsi0);
+
+         if (PREDICT_FALSE (entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace00;
+           }
+
+         /* Entry should point to a mapping ... */
+         map0 = pool_elt_at_index (nm->nsh_mappings, entry0[0]);
+
+         if (PREDICT_FALSE (map0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace00;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next0 = map0->next_node;
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = map0->adj_index;
+         vnet_buffer (b0)->sw_if_index[VLIB_RX] = map0->nsh_sw_if;
+
+         if (PREDICT_FALSE (map0->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr0->md_type == 2))
+               {
+                 nsh_md2_decap (b0, hdr0, &header_len0, &next0,
+                                NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace00;
+                   }
+                 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+                   map0->rx_sw_if_index;
+               }
+
+             /* Pop NSH header */
+             vlib_buffer_advance (b0, (word) header_len0);
+             goto trace00;
+           }
+
+         entry0 = hash_get_mem (nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
+         if (PREDICT_FALSE (entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace00;
+           }
+
+         nsh_entry0 =
+           (nsh_entry_t *) pool_elt_at_index (nm->nsh_entries, entry0[0]);
+         encap_hdr0 = (nsh_base_header_t *) (nsh_entry0->rewrite);
+         /* rewrite_size should equal to (encap_hdr0->length * 4) */
+         encap_hdr_len0 = nsh_entry0->rewrite_size;
+
+         if (PREDICT_TRUE (map0->nsh_action == NSH_ACTION_SWAP))
+           {
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (hdr0->md_type == 2))
+               {
+                 nsh_md2_swap (b0, hdr0, header_len0, nsh_entry0,
+                               &next0, NSH_NODE_NEXT_DROP);
+                 if (PREDICT_FALSE (next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace00;
+                   }
+               }
+
+             /* Pop old NSH header */
+             vlib_buffer_advance (b0, (word) header_len0);
+
+             /* After processing, md2's length may be varied */
+             encap_hdr_len0 = nsh_entry0->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b0, -(word) encap_hdr_len0);
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, encap_hdr0, (word) encap_hdr_len0);
+
+             goto trace00;
+           }
+
+         if (PREDICT_TRUE (map0->nsh_action == NSH_ACTION_PUSH))
+           {
+             /* After processing, md2's length may be varied */
+             encap_hdr_len0 = nsh_entry0->rewrite_size;
+             /* Push new NSH header */
+             vlib_buffer_advance (b0, -(word) encap_hdr_len0);
+             hdr0 = vlib_buffer_get_current (b0);
+             clib_memcpy (hdr0, encap_hdr0, (word) encap_hdr_len0);
+             /* Manipulate MD2 */
+             if (PREDICT_FALSE (nsh_entry0->nsh_base.md_type == 2))
+               {
+                 nsh_md2_encap (b0, hdr0, nsh_entry0);
+               }
+
+           }
+
+       trace00:b0->error = error0 ? node->errors[error0] : 0;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_input_trace_t *tr =
+               vlib_add_trace (vm, node, b0, sizeof (*tr));
+             clib_memcpy (&(tr->trace_data[0]), hdr0,
+                          ((hdr0->length & NSH_LEN_MASK) * 4));
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+
+    }
+
+  return from_frame->n_vectors;
+}
+
+/**
+ * @brief Graph processing dispatch function for NSH Input
+ *
+ * @node nsh_input
+ * @param *vm
+ * @param *node
+ * @param *from_frame
+ *
+ * @return from_frame->n_vectors
+ *
+ */
+static uword
+nsh_input (vlib_main_t * vm, vlib_node_runtime_t * node,
+          vlib_frame_t * from_frame)
+{
+  return nsh_input_map (vm, node, from_frame, NSH_INPUT_TYPE);
+}
+
+/**
+ * @brief Graph processing dispatch function for NSH-Proxy
+ *
+ * @node nsh_proxy
+ * @param *vm
+ * @param *node
+ * @param *from_frame
+ *
+ * @return from_frame->n_vectors
+ *
+ */
+static uword
+nsh_proxy (vlib_main_t * vm, vlib_node_runtime_t * node,
+          vlib_frame_t * from_frame)
+{
+  return nsh_input_map (vm, node, from_frame, NSH_PROXY_TYPE);
+}
+
+/**
+ * @brief Graph processing dispatch function for NSH Classifier
+ *
+ * @node nsh_classifier
+ * @param *vm
+ * @param *node
+ * @param *from_frame
+ *
+ * @return from_frame->n_vectors
+ *
+ */
+static uword
+nsh_classifier (vlib_main_t * vm, vlib_node_runtime_t * node,
+               vlib_frame_t * from_frame)
+{
+  return nsh_input_map (vm, node, from_frame, NSH_CLASSIFIER_TYPE);
+}
+
+/**
+ * @brief Graph processing dispatch function for NSH-AWARE-VNF-PROXY
+ *
+ * @node nsh_aware_vnf_proxy
+ * @param *vm
+ * @param *node
+ * @param *from_frame
+ *
+ * @return from_frame->n_vectors
+ *
+ */
+static uword
+nsh_aware_vnf_proxy (vlib_main_t * vm, vlib_node_runtime_t * node,
+                    vlib_frame_t * from_frame)
+{
+  return nsh_input_map (vm, node, from_frame, NSH_AWARE_VNF_PROXY_TYPE);
+}
+
+static char *nsh_node_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nsh_node_error
+#undef _
+};
+
+/* register nsh-input node */
+VLIB_REGISTER_NODE (nsh_input_node) =
+{
+  .function = nsh_input,.name = "nsh-input",.vector_size =
+    sizeof (u32),.format_trace = format_nsh_node_map_trace,.format_buffer =
+    format_nsh_header,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (nsh_node_error_strings),.error_strings =
+    nsh_node_error_strings,.n_next_nodes = NSH_NODE_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [NSH_NODE_NEXT_##s] = n,
+    foreach_nsh_node_next
+#undef _
+  }
+,};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_input_node, nsh_input);
+
+/* register nsh-proxy node */
+VLIB_REGISTER_NODE (nsh_proxy_node) =
+{
+  .function = nsh_proxy,.name = "nsh-proxy",.vector_size =
+    sizeof (u32),.format_trace = format_nsh_node_map_trace,.format_buffer =
+    format_nsh_header,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (nsh_node_error_strings),.error_strings =
+    nsh_node_error_strings,.n_next_nodes = NSH_NODE_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [NSH_NODE_NEXT_##s] = n,
+    foreach_nsh_node_next
+#undef _
+  }
+,};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_proxy_node, nsh_proxy);
+
+/* register nsh-classifier node */
+VLIB_REGISTER_NODE (nsh_classifier_node) =
+{
+  .function = nsh_classifier,.name = "nsh-classifier",.vector_size =
+    sizeof (u32),.format_trace = format_nsh_node_map_trace,.format_buffer =
+    format_nsh_header,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (nsh_node_error_strings),.error_strings =
+    nsh_node_error_strings,.n_next_nodes = NSH_NODE_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [NSH_NODE_NEXT_##s] = n,
+    foreach_nsh_node_next
+#undef _
+  }
+,};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_classifier_node, nsh_classifier);
+
+/* register nsh-aware-vnf-proxy node */
+VLIB_REGISTER_NODE (nsh_aware_vnf_proxy_node) =
+{
+  .function = nsh_aware_vnf_proxy,.name = "nsh-aware-vnf-proxy",.vector_size =
+    sizeof (u32),.format_trace = format_nsh_node_map_trace,.format_buffer =
+    format_nsh_header,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (nsh_node_error_strings),.error_strings =
+    nsh_node_error_strings,.n_next_nodes = NSH_NODE_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [NSH_NODE_NEXT_##s] = n,
+    foreach_nsh_node_next
+#undef _
+  }
+,};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_aware_vnf_proxy_node, nsh_aware_vnf_proxy);
+
+void
+nsh_md2_set_next_ioam_export_override (uword next)
+{
+  nsh_main_t *hm = &nsh_main;
+  hm->decap_v4_next_override = next;
+  return;
+}
+
+
+clib_error_t *
+nsh_init (vlib_main_t * vm)
+{
+  nsh_main_t *nm = &nsh_main;
+  clib_error_t *error = 0;
+  u8 *name;
+  uword next_node;
+
+  /* Init the main structures from VPP */
+  nm->vlib_main = vm;
+  nm->vnet_main = vnet_get_main ();
+
+  /* Various state maintenance mappings */
+  nm->nsh_mapping_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
+
+  nm->nsh_mapping_by_mapped_key
+    = hash_create_mem (0, sizeof (u32), sizeof (uword));
+
+  nm->nsh_entry_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
+
+  nm->nsh_proxy_session_by_key
+    =
+    hash_create_mem (0, sizeof (nsh_proxy_session_by_key_t), sizeof (uword));
+
+  nm->nsh_option_map_by_key
+    = hash_create_mem (0, sizeof (nsh_option_map_by_key_t), sizeof (uword));
+
+  name = format (0, "nsh_%08x%c", api_version, 0);
+
+  /* Set up the API */
+  nm->msg_id_base = vl_msg_api_get_msg_ids
+    ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+  error = nsh_plugin_api_hookup (vm);
+
+  /* Add our API messages to the global name_crc hash table */
+  setup_message_id_table (nm, &api_main);
+
+  /* Add dispositions to nodes that feed nsh-input */
+  //alagalah - validate we don't really need to use the node value
+  next_node =
+    vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
+                       nsh_input_node.index);
+  vlib_node_add_next (vm, vxlan4_gpe_input_node.index, nsh_proxy_node.index);
+  vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
+                     nsh_aware_vnf_proxy_node.index);
+  vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_NSH, next_node);
+
+  vlib_node_add_next (vm, vxlan6_gpe_input_node.index, nsh_input_node.index);
+  vlib_node_add_next (vm, vxlan6_gpe_input_node.index, nsh_proxy_node.index);
+  vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
+                     nsh_aware_vnf_proxy_node.index);
+
+  vlib_node_add_next (vm, gre4_input_node.index, nsh_input_node.index);
+  vlib_node_add_next (vm, gre4_input_node.index, nsh_proxy_node.index);
+  vlib_node_add_next (vm, gre4_input_node.index,
+                     nsh_aware_vnf_proxy_node.index);
+
+  vlib_node_add_next (vm, gre6_input_node.index, nsh_input_node.index);
+  vlib_node_add_next (vm, gre6_input_node.index, nsh_proxy_node.index);
+  vlib_node_add_next (vm, gre6_input_node.index,
+                     nsh_aware_vnf_proxy_node.index);
+
+  /* Add NSH-Proxy support */
+  vlib_node_add_next (vm, vxlan4_input_node.index, nsh_proxy_node.index);
+  vlib_node_add_next (vm, vxlan6_input_node.index, nsh_proxy_node.index);
+
+  /* Add NSH-Classifier support */
+  vlib_node_add_next (vm, ip4_classify_node.index, nsh_classifier_node.index);
+  vlib_node_add_next (vm, ip6_classify_node.index, nsh_classifier_node.index);
+  vlib_node_add_next (vm, l2_input_classify_node.index,
+                     nsh_classifier_node.index);
+
+  /* Add Ethernet+NSH support */
+  ethernet_register_input_type (vm, ETHERNET_TYPE_NSH, nsh_input_node.index);
+
+  vec_free (name);
+
+  return error;
+}
+
+VLIB_INIT_FUNCTION (nsh_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+    .version = VPP_BUILD_VER,
+    .description = "Network Service Header",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/nsh/nsh.h b/src/plugins/nsh/nsh.h
new file mode 100644 (file)
index 0000000..1b14567
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * 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 included_nsh_h
+#define included_nsh_h
+
+#include <vnet/vnet.h>
+#include <nsh/nsh_packet.h>
+#include <vnet/ip/ip4_packet.h>
+
+typedef struct {
+  u16 class;
+  u8 type;
+  u8 pad;
+} nsh_option_map_by_key_t;
+
+typedef struct {
+  u32 option_id;
+} nsh_option_map_t;
+
+#define MAX_METADATA_LEN 62
+/** Note:
+ * rewrite and rewrite_size used to support varied nsh header
+ */
+typedef struct {
+  /* Required for pool_get_aligned  */
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+  nsh_base_header_t nsh_base;
+  union {
+     nsh_md1_data_t md1_data;
+     nsh_md2_data_t md2_data;
+   } md;
+  u8 tlvs_len;    /* configured md2 metadata's length, unit: byte */
+  u8 * tlvs_data; /* configured md2 metadata, network order */
+
+  /** Rewrite string. network order
+   * contains base header and metadata */
+  u8 * rewrite;
+  u8  rewrite_size; /* unit: byte */
+} nsh_entry_t;
+
+typedef struct {
+  u8 is_add;
+  nsh_entry_t nsh_entry;
+} nsh_add_del_entry_args_t;
+
+typedef struct {
+  /* Required for pool_get_aligned  */
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+  /** Key for nsh_header_t entry: 24bit NSP 8bit NSI */
+  u32 nsp_nsi;
+  /** Key for nsh_header_t entry to map to. : 24bit NSP 8bit NSI
+   *  This may be ~0 if next action is to decap to NSH next protocol
+   *  Note the following heuristic:
+   *  if nsp_nsi == mapped_nsp_nsi then use-case is like SFC SFF
+   *  if nsp_nsi != mapped_nsp_nsi then use-case is like SFC SF
+   *  Note: these are heuristics. Rules about NSI decrement are out of scope
+   */
+  u32 mapped_nsp_nsi;
+  /* NSH Header action: swap, push and pop */
+  u32 nsh_action;
+
+  /** vnet intfc hw_if_index */
+  u32 nsh_hw_if;
+  /* vnet intfc sw_if_index */
+  u32 nsh_sw_if;
+
+  /* encap if index */
+  u32 sw_if_index;
+  u32 rx_sw_if_index;
+  u32 next_node;
+  u32 adj_index;
+} nsh_map_t;
+
+typedef struct {
+  u8 is_add;
+  nsh_map_t map;
+} nsh_add_del_map_args_t;
+
+typedef struct {
+  u32 transport_type; /* 1:vxlan; */
+  u32 transport_index; /* transport's sw_if_index */
+} nsh_proxy_session_by_key_t;
+
+typedef struct {
+  /* 24bit NSP 8bit NSI */
+  u32 nsp_nsi;
+} nsh_proxy_session_t;
+
+#define MAX_MD2_OPTIONS 256
+
+typedef struct {
+  /* API message ID base */
+  u16 msg_id_base;
+
+  /* vector of nsh_header entry instances */
+  nsh_entry_t *nsh_entries;
+
+  /* hash lookup nsh header by key: {u32: nsp_nsi} */
+  uword * nsh_entry_by_key;
+
+  /* vector of nsh_mappings */
+  nsh_map_t *nsh_mappings;
+
+  /* hash lookup nsh mapping by key: {u32: nsp_nsi} */
+  uword * nsh_mapping_by_key;
+  uword * nsh_mapping_by_mapped_key; // for use in NSHSFC
+
+  /* vector of nsh_proxy */
+  nsh_proxy_session_t *nsh_proxy_sessions;
+
+  /* hash lookup nsh_proxy by key */
+  uword * nsh_proxy_session_by_key;
+
+  /** Free vlib hw_if_indices */
+  u32 * free_nsh_tunnel_hw_if_indices;
+  /** Mapping from sw_if_index to tunnel index */
+  u32 * tunnel_index_by_sw_if_index;
+
+  /* vector of nsh_option_map */
+  nsh_option_map_t * nsh_option_mappings;
+  /* hash lookup nsh_option_map by key */
+  uword * nsh_option_map_by_key;
+
+  /* Array of function pointers to process MD-Type 2 handling routines */
+  /*
+   * For API or CLI configuration and construct the rewrite buffer, invokes add_options() function.
+   * In the encap node, i.e. when performing PUSH nsh header, invokes options() function.
+   * In the swap node, i.e. when performing SWAP nsh header, invokes swap_options() function.
+   * In the decap node, i.e. when performing POP nsh header, invokes pop_options() function.
+   */
+  u8 options_size[MAX_MD2_OPTIONS];  /* sum of header and metadata */
+  int (*add_options[MAX_MD2_OPTIONS]) (u8 * opt,
+                                          u8 * opt_size);
+  int (*options[MAX_MD2_OPTIONS]) (vlib_buffer_t * b,
+                                   nsh_tlv_header_t * opt);
+  int (*swap_options[MAX_MD2_OPTIONS]) (vlib_buffer_t * b,
+                                        nsh_tlv_header_t * old_opt,
+                                       nsh_tlv_header_t * new_opt);
+  int (*pop_options[MAX_MD2_OPTIONS]) (vlib_buffer_t * b,
+                                      nsh_tlv_header_t * opt);
+  u8 *(*trace[MAX_MD2_OPTIONS]) (u8 * s, nsh_tlv_header_t * opt);
+  uword decap_v4_next_override;
+
+  /* Feature arc indices */
+  u8 input_feature_arc_index;
+  u8 output_feature_arc_index;
+
+  /* convenience */
+  vlib_main_t * vlib_main;
+  vnet_main_t * vnet_main;
+} nsh_main_t;
+
+nsh_main_t nsh_main;
+
+extern vlib_node_registration_t nsh_aware_vnf_proxy_node;
+extern vlib_node_registration_t nsh_eth_output_node;
+
+typedef struct {
+   u8 trace_data[256];
+} nsh_input_trace_t;
+
+u8 * format_nsh_input_map_trace (u8 * s, va_list * args);
+u8 * format_nsh_header_with_length (u8 * s, va_list * args);
+
+/* Helper macros used in nsh.c and nsh_test.c */
+#define foreach_copy_nsh_base_hdr_field         \
+_(ver_o_c)                                     \
+_(length)                                      \
+_(md_type)                                     \
+_(next_protocol)                               \
+_(nsp_nsi)
+
+/* Statistics (not really errors) */
+#define foreach_nsh_node_error    \
+_(MAPPED, "NSH header found and mapped") \
+_(NO_MAPPING, "no mapping for nsh key") \
+_(NO_ENTRY, "no entry for nsh key") \
+_(NO_PROXY, "no proxy for transport key") \
+_(INVALID_NEXT_PROTOCOL, "invalid next protocol") \
+_(INVALID_OPTIONS, "invalid md2 options") \
+_(INVALID_TTL, "ttl equals zero") \
+
+typedef enum {
+#define _(sym,str) NSH_NODE_ERROR_##sym,
+  foreach_nsh_node_error
+#undef _
+  NSH_NODE_N_ERROR,
+
+} nsh_input_error_t;
+
+#define foreach_nsh_node_next        \
+  _(DROP, "error-drop")                        \
+  _(ENCAP_GRE4, "gre4-input" )         \
+  _(ENCAP_GRE6, "gre6-input" )         \
+  _(ENCAP_VXLANGPE, "vxlan-gpe-encap" ) \
+  _(ENCAP_VXLAN4, "vxlan4-encap" )  \
+  _(ENCAP_VXLAN6, "vxlan6-encap" )  \
+  _(DECAP_ETH_INPUT, "ethernet-input" ) \
+  _(ENCAP_LISP_GPE, "interface-output" )  \
+  _(ENCAP_ETHERNET, "nsh-eth-output")   \
+/*   _(DECAP_IP4_INPUT,  "ip4-input") \ */
+/*   _(DECAP_IP6_INPUT,  "ip6-input" ) \  */
+
+typedef enum {
+#define _(s,n) NSH_NODE_NEXT_##s,
+  foreach_nsh_node_next
+#undef _
+  NSH_NODE_N_NEXT,
+} nsh_node_next_t;
+
+typedef enum {
+  NSH_ACTION_SWAP,
+  NSH_ACTION_PUSH,
+  NSH_ACTION_POP,
+} nsh_action_type;
+
+typedef enum {
+  NSH_INPUT_TYPE,
+  NSH_PROXY_TYPE,
+  NSH_CLASSIFIER_TYPE,
+  NSH_AWARE_VNF_PROXY_TYPE,
+} nsh_entity_type;
+
+#define VNET_SW_INTERFACE_FLAG_ADMIN_DOWN 0
+
+/* md2 class and type definition */
+#define NSH_MD2_IOAM_CLASS 0x9
+#define NSH_MD2_IOAM_OPTION_TYPE_TRACE   0x3B
+#define NSH_MD2_IOAM_OPTION_TYPE_PROOF_OF_TRANSIT 0x3C
+
+#define NSH_MD2_IOAM_TRACE_DUMMY_LEN 0x8
+
+#define MAX_NSH_HEADER_LEN  256
+#define MAX_NSH_OPTION_LEN  128
+
+int
+nsh_md2_register_option (u16 class,
+                      u8 type,
+                      u8 option_size,
+                      int add_options (u8 * opt,
+                                       u8 * opt_size),
+                      int options(vlib_buffer_t * b,
+                                  nsh_tlv_header_t * opt),
+                      int swap_options (vlib_buffer_t * b,
+                                       nsh_tlv_header_t * old_opt,
+                                       nsh_tlv_header_t * new_opt),
+                      int pop_options (vlib_buffer_t * b,
+                                       nsh_tlv_header_t * opt),
+                      u8 * trace (u8 * s,
+                                  nsh_tlv_header_t * opt));
+
+typedef struct _nsh_main_dummy
+{
+  u8 output_feature_arc_index;
+} nsh_main_dummy_t;
+
+#endif /* included_nsh_h */
diff --git a/src/plugins/nsh/nsh_error.def b/src/plugins/nsh/nsh_error.def
new file mode 100644 (file)
index 0000000..c54e3b8
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+nsh_input_error (DECAPSULATED, "good packets decapsulated")
+nsh_input_error (NO_MAPPING, "no mapping for nsh key")
+nsh_input_error (INVALID_NEXT_PROTOCOL, "invalid next protocol")
\ No newline at end of file
diff --git a/src/plugins/nsh/nsh_output.c b/src/plugins/nsh/nsh_output.c
new file mode 100644 (file)
index 0000000..f0ac673
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * nsh_output.c: NSH Adj rewrite
+ *
+ * Copyright (c) 2017-2019 Intel 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/pg/pg.h>
+#include <vnet/ip/ip.h>
+#include <nsh/nsh.h>
+
+typedef struct {
+  /* Adjacency taken. */
+  u32 adj_index;
+  u32 flow_hash;
+
+  /* Packet data, possibly *after* rewrite. */
+  u8 packet_data[64 - 1*sizeof(u32)];
+} nsh_output_trace_t;
+
+#define foreach_nsh_output_next                \
+_(DROP, "error-drop")            \
+_(INTERFACE, "interface-output" )
+
+typedef enum {
+#define _(s,n) NSH_OUTPUT_NEXT_##s,
+  foreach_nsh_output_next
+#undef _
+  NSH_OUTPUT_N_NEXT,
+} nsh_output_next_t;
+
+static u8 *
+format_nsh_output_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 *);
+  nsh_output_trace_t * t = va_arg (*args, nsh_output_trace_t *);
+  uword indent = format_get_indent (s);
+
+  s = format (s, "adj-idx %d : %U flow hash: 0x%08x",
+              t->adj_index,
+              format_ip_adjacency, t->adj_index, FORMAT_IP_ADJACENCY_NONE,
+              t->flow_hash);
+  s = format (s, "\n%U%U",
+              format_white_space, indent,
+              format_ip_adjacency_packet_data,
+              t->adj_index, t->packet_data, sizeof (t->packet_data));
+  return s;
+}
+
+static inline uword
+nsh_output_inline (vlib_main_t * vm,
+                   vlib_node_runtime_t * node,
+                   vlib_frame_t * from_frame,
+                   int is_midchain)
+{
+  u32 n_left_from, next_index, * from, * to_next, thread_index;
+  vlib_node_runtime_t * error_node;
+  u32 n_left_to_next;
+  nsh_main_t *nm;
+
+  thread_index = vlib_get_thread_index();
+  error_node = vlib_node_get_runtime (vm, nsh_eth_output_node.index);
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+  next_index = node->cached_next_index;
+  nm = &nsh_main;
+
+  while (n_left_from > 0)
+    {
+      vlib_get_next_frame (vm, node, next_index,
+                           to_next, n_left_to_next);
+
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+        {
+          ip_adjacency_t * adj0;
+          nsh_base_header_t *hdr0;
+          ethernet_header_t * eth_hdr0;
+          vlib_buffer_t * p0;
+          u32 pi0, rw_len0, adj_index0, next0, error0;
+
+          ip_adjacency_t * adj1;
+          nsh_base_header_t *hdr1;
+          ethernet_header_t * eth_hdr1;
+          vlib_buffer_t * p1;
+          u32 pi1, rw_len1, adj_index1, next1, error1;
+
+          /* Prefetch next iteration. */
+          {
+            vlib_buffer_t * p2, * p3;
+
+            p2 = vlib_get_buffer (vm, from[2]);
+            p3 = vlib_get_buffer (vm, from[3]);
+
+            vlib_prefetch_buffer_header (p2, STORE);
+            vlib_prefetch_buffer_header (p3, STORE);
+
+            CLIB_PREFETCH (p2->data, sizeof (hdr0[0]), STORE);
+            CLIB_PREFETCH (p3->data, sizeof (hdr1[0]), STORE);
+          }
+
+          pi0 = to_next[0] = from[0];
+          pi1 = to_next[1] = from[1];
+
+          from += 2;
+          n_left_from -= 2;
+          to_next += 2;
+          n_left_to_next -= 2;
+
+          p0 = vlib_get_buffer (vm, pi0);
+          p1 = vlib_get_buffer (vm, pi1);
+
+          adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+          adj_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
+
+          adj0 = adj_get(adj_index0);
+          adj1 = adj_get(adj_index1);
+          hdr0 = vlib_buffer_get_current (p0);
+          hdr1 = vlib_buffer_get_current (p1);
+
+          /* Guess we are only writing on simple Ethernet header. */
+          vnet_rewrite_two_headers (adj0[0], adj1[0], hdr0, hdr1,
+                                   sizeof (ethernet_header_t));
+
+          eth_hdr0 = (ethernet_header_t*)((u8 *)hdr0-sizeof(ethernet_header_t));
+          eth_hdr0->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
+          eth_hdr1 = (ethernet_header_t*)((u8 *)hdr1-sizeof(ethernet_header_t));
+          eth_hdr1->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
+
+          /* Update packet buffer attributes/set output interface. */
+          rw_len0 = adj0[0].rewrite_header.data_bytes;
+          rw_len1 = adj1[0].rewrite_header.data_bytes;
+
+          /* Bump the adj counters for packet and bytes */
+          vlib_increment_combined_counter
+              (&adjacency_counters,
+               thread_index,
+               adj_index0,
+               1,
+               vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+          vlib_increment_combined_counter
+              (&adjacency_counters,
+               thread_index,
+               adj_index1,
+               1,
+               vlib_buffer_length_in_chain (vm, p1) + rw_len1);
+
+          /* Check MTU of outgoing interface. */
+          if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p0) <=
+                           adj0[0].rewrite_header.max_l3_packet_bytes))
+            {
+              p0->current_data -= rw_len0;
+              p0->current_length += rw_len0;
+
+              vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+              next0 = NSH_OUTPUT_NEXT_INTERFACE;
+              error0 = IP4_ERROR_NONE;
+
+              if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+                vnet_feature_arc_start (nm->output_feature_arc_index,
+                                        adj0[0].rewrite_header.sw_if_index,
+                                        &next0, p0);
+            }
+          else
+            {
+              error0 = IP4_ERROR_MTU_EXCEEDED;
+              next0 = NSH_OUTPUT_NEXT_DROP;
+            }
+          if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p1) <=
+                           adj1[0].rewrite_header.max_l3_packet_bytes))
+            {
+              p1->current_data -= rw_len1;
+              p1->current_length += rw_len1;
+
+              vnet_buffer (p1)->sw_if_index[VLIB_TX] =
+                  adj1[0].rewrite_header.sw_if_index;
+              next1 = NSH_OUTPUT_NEXT_INTERFACE;
+              error1 = IP4_ERROR_NONE;
+
+              if (PREDICT_FALSE(adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+                vnet_feature_arc_start (nm->output_feature_arc_index,
+                                        adj1[0].rewrite_header.sw_if_index,
+                                        &next1, p1);
+            }
+          else
+            {
+              error1 = IP4_ERROR_MTU_EXCEEDED;
+              next1 = NSH_OUTPUT_NEXT_DROP;
+            }
+          if (is_midchain)
+          {
+              adj0->sub_type.midchain.fixup_func
+                (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
+              adj1->sub_type.midchain.fixup_func
+                (vm, adj1, p1, adj1->sub_type.midchain.fixup_data);
+          }
+
+          p0->error = error_node->errors[error0];
+          p1->error = error_node->errors[error1];
+
+          if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              nsh_output_trace_t *tr = vlib_add_trace (vm, node,
+                                                        p0, sizeof (*tr));
+              tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
+              tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
+            }
+          if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              nsh_output_trace_t *tr = vlib_add_trace (vm, node,
+                                                        p1, sizeof (*tr));
+              tr->adj_index = vnet_buffer(p1)->ip.adj_index[VLIB_TX];
+              tr->flow_hash = vnet_buffer(p1)->ip.flow_hash;
+            }
+
+          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                           to_next, n_left_to_next,
+                                           pi0, pi1, next0, next1);
+        }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+        {
+          ip_adjacency_t * adj0;
+          nsh_base_header_t *hdr0;
+          ethernet_header_t * eth_hdr0;
+          vlib_buffer_t * p0;
+          u32 pi0, rw_len0, adj_index0, next0, error0;
+
+          pi0 = to_next[0] = from[0];
+
+          p0 = vlib_get_buffer (vm, pi0);
+
+          adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+
+          adj0 = adj_get(adj_index0);
+          hdr0 = vlib_buffer_get_current (p0);
+
+          /* Guess we are only writing on simple Ethernet header. */
+          vnet_rewrite_one_header (adj0[0], hdr0,
+                                   sizeof (ethernet_header_t));
+
+          eth_hdr0 = (ethernet_header_t*)((u8 *)hdr0-sizeof(ethernet_header_t));
+          eth_hdr0->type = clib_host_to_net_u16(ETHERNET_TYPE_NSH);
+
+          /* Update packet buffer attributes/set output interface. */
+          rw_len0 = adj0[0].rewrite_header.data_bytes;
+
+          vlib_increment_combined_counter
+              (&adjacency_counters,
+               thread_index,
+               adj_index0,
+               1,
+               vlib_buffer_length_in_chain (vm, p0) + rw_len0);
+
+          /* Check MTU of outgoing interface. */
+          if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p0) <=
+                           adj0[0].rewrite_header.max_l3_packet_bytes))
+            {
+              p0->current_data -= rw_len0;
+              p0->current_length += rw_len0;
+
+              vnet_buffer (p0)->sw_if_index[VLIB_TX] =
+                  adj0[0].rewrite_header.sw_if_index;
+              next0 = NSH_OUTPUT_NEXT_INTERFACE;
+              error0 = IP4_ERROR_NONE;
+
+              if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
+                vnet_feature_arc_start (nm->output_feature_arc_index,
+                                        adj0[0].rewrite_header.sw_if_index,
+                                        &next0, p0);
+            }
+          else
+            {
+              error0 = IP4_ERROR_MTU_EXCEEDED;
+              next0 = NSH_OUTPUT_NEXT_DROP;
+            }
+          if (is_midchain)
+          {
+              adj0->sub_type.midchain.fixup_func
+                (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
+          }
+
+          p0->error = error_node->errors[error0];
+
+          from += 1;
+          n_left_from -= 1;
+          to_next += 1;
+          n_left_to_next -= 1;
+
+          if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              nsh_output_trace_t *tr = vlib_add_trace (vm, node,
+                                                        p0, sizeof (*tr));
+              tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
+              tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
+            }
+
+          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                           to_next, n_left_to_next,
+                                           pi0, next0);
+        }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return from_frame->n_vectors;
+}
+
+typedef enum nsh_midchain_next_t_
+{
+    NSH_MIDCHAIN_NEXT_DROP,
+} nsh_midchain_next_t;
+
+static inline uword
+nsh_eth_output (vlib_main_t * vm,
+                vlib_node_runtime_t * node,
+                vlib_frame_t * from_frame)
+{
+    return (nsh_output_inline(vm, node, from_frame, /* is_midchain */ 0));
+}
+
+VLIB_REGISTER_NODE (nsh_eth_output_node) = {
+  .function = nsh_eth_output,
+  .name = "nsh-eth-output",
+  /* Takes a vector of packets. */
+  .vector_size = sizeof (u32),
+  .n_next_nodes = NSH_OUTPUT_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [NSH_OUTPUT_NEXT_##s] = n,
+    foreach_nsh_output_next
+#undef _
+  },
+
+  .format_trace = format_nsh_output_trace,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_eth_output_node, nsh_eth_output)
+
+static inline uword
+nsh_midchain (vlib_main_t * vm,
+               vlib_node_runtime_t * node,
+               vlib_frame_t * from_frame)
+{
+    return (nsh_output_inline(vm, node, from_frame, /* is_midchain */ 1));
+}
+
+VLIB_REGISTER_NODE (nsh_midchain_node) = {
+  .function = nsh_midchain,
+  .name = "nsh-midchain",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nsh_output_trace,
+  .n_next_nodes = 1,
+  .next_nodes = {
+      [NSH_MIDCHAIN_NEXT_DROP] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_midchain_node, nsh_midchain)
+
+/* Built-in nsh tx feature path definition */
+VNET_FEATURE_INIT (nsh_interface_output, static) = {
+  .arc_name = "nsh-eth-output",
+  .node_name = "interface-output",
+  .runs_before = 0, /* not before any other features */
+};
+
+/* Built-in ip4 tx feature path definition */
+/* *INDENT-OFF* */
+VNET_FEATURE_ARC_INIT (nsh_eth_output, static) =
+{
+  .arc_name  = "nsh-eth-output",
+  .start_nodes = VNET_FEATURES ("nsh-midchain"),
+};
+
+VNET_FEATURE_INIT (nsh_eth_tx_drop, static) =
+{
+  .arc_name = "nsh-eth-output",
+  .node_name = "error-drop",
+  .runs_before = 0,     /* not before any other features */
+};
+/* *INDENT-ON* */
+/**
+ * @brief Next index values from the NSH incomplete adj node
+ */
+#define foreach_nsh_adj_incomplete_next        \
+_(DROP, "error-drop")                   \
+_(IP4,  "ip4-arp")                      \
+_(IP6,  "ip6-discover-neighbor")
+
+typedef enum {
+#define _(s,n) NSH_ADJ_INCOMPLETE_NEXT_##s,
+  foreach_nsh_adj_incomplete_next
+#undef _
+  NSH_ADJ_INCOMPLETE_N_NEXT,
+} nsh_adj_incomplete_next_t;
+
+/**
+ * @brief A struct to hold tracing information for the NSH label imposition
+ * node.
+ */
+typedef struct nsh_adj_incomplete_trace_t_
+{
+    u32 next;
+} nsh_adj_incomplete_trace_t;
+
+
+/**
+ * @brief Graph node for incomplete NSH adjacency.
+ * This node will push traffic to either the v4-arp or v6-nd node
+ * based on the next-hop proto of the adj.
+ * We pay a cost for this 'routing' node, but an incomplete adj is the
+ * exception case.
+ */
+static inline uword
+nsh_adj_incomplete (vlib_main_t * vm,
+                     vlib_node_runtime_t * node,
+                     vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, * from, * to_next;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index,
+                           to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+        {
+          u32 pi0, next0, adj_index0;
+          ip_adjacency_t * adj0;
+          vlib_buffer_t * p0;
+
+          pi0 = to_next[0] = from[0];
+          p0 = vlib_get_buffer (vm, pi0);
+          from += 1;
+          n_left_from -= 1;
+          to_next += 1;
+          n_left_to_next -= 1;
+
+          adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+
+          adj0 = adj_get(adj_index0);
+
+          if (PREDICT_TRUE(FIB_PROTOCOL_IP4 == adj0->ia_nh_proto))
+          {
+              next0 = NSH_ADJ_INCOMPLETE_NEXT_IP4;
+          }
+          else
+          {
+              next0 = NSH_ADJ_INCOMPLETE_NEXT_IP6;
+          }
+
+          if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
+          {
+              nsh_adj_incomplete_trace_t *tr =
+                 vlib_add_trace (vm, node, p0, sizeof (*tr));
+              tr->next = next0;
+          }
+
+          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                           to_next, n_left_to_next,
+                                           pi0, next0);
+        }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return from_frame->n_vectors;
+}
+
+static u8 *
+format_nsh_adj_incomplete_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 *);
+    nsh_adj_incomplete_trace_t * t;
+    uword indent;
+
+    t = va_arg (*args, nsh_adj_incomplete_trace_t *);
+    indent = format_get_indent (s);
+
+    s = format (s, "%Unext:%d",
+                format_white_space, indent,
+                t->next);
+    return (s);
+}
+
+VLIB_REGISTER_NODE (nsh_adj_incomplete_node) = {
+  .function = nsh_adj_incomplete,
+  .name = "nsh-adj-incomplete",
+  .format_trace = format_nsh_adj_incomplete_trace,
+  /* Takes a vector of packets. */
+  .vector_size = sizeof (u32),
+  .n_next_nodes = NSH_ADJ_INCOMPLETE_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [NSH_ADJ_INCOMPLETE_NEXT_##s] = n,
+    foreach_nsh_adj_incomplete_next
+#undef _
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_adj_incomplete_node,
+                              nsh_adj_incomplete)
diff --git a/src/plugins/nsh/nsh_packet.h b/src/plugins/nsh/nsh_packet.h
new file mode 100644 (file)
index 0000000..9d6237a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 included_nsh_packet_h
+#define included_nsh_packet_h
+
+/*
+ * NSH packet format from draft-quinn-sfc-nsh-03.txt
+ *
+ * NSH Base Header
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |Ver|O|C|    TTL    |   Length  |    MD Type    | Next Protocol |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 
+ * 
+ * Base Header Field Descriptions:
+ * 
+ * Version: The version field is used to ensure backward compatibility
+ * going forward with future NSH updates.
+ * 
+ * O bit: Indicates that this packet is an operations and management
+ * (OAM) packet.  SFF and SFs nodes MUST examine the payload and take
+ * appropriate action (e.g. return status information).
+ * 
+ * OAM message specifics and handling details are outside the scope of
+ * this document.
+ * 
+ * C bit: Indicates that a critical metadata TLV is present (see section
+ * 7).  This bit acts as an indication for hardware implementers to
+ * decide how to handle the presence of a critical TLV without
+ * necessarily needing to parse all TLVs present.  The C bit MUST be set
+ * to 1 if one or more critical TLVs are present.
+ * 
+ * All other flag fields are reserved.
+ * 
+ * Length: total length, in 4 byte words, of the NSH header, including
+ * optional variable TLVs.  Length must be equal or greater than 6.
+ * 
+ * MD Type: indicates the format of NSH beyond the base header and the
+ * type of metadata being carried.  This typing is used to describe the
+ * use for the metadata.  A new registry will be requested from IANA for
+ * the MD Type.  NSH defines one type, type = 0x1 which indicates that
+ * the format of the header is as per this draft.
+ * 
+ * The format of the base header is invariant, and not described by MD
+ * Type.
+ * 
+ * Next Protocol: indicates the protocol type of the original packet.  A
+ * new IANA registry will be created for protocol type.
+ * 
+ * This draft defines the following Next Protocol values:
+ * 
+ * 0x1 : IPv4
+ * 0x2 : IPv6
+ * 0x3 : Ethernet
+ */
+
+typedef CLIB_PACKED(struct {
+  u8 ver_o_c; //TTL: high 4 bit
+  u8 length;  //TTL: low 2 bit
+  u8 md_type;
+  u8 next_protocol;
+  u32 nsp_nsi; // nsp 24 bits, nsi 8 bits
+}) nsh_base_header_t;
+
+typedef CLIB_PACKED(struct {
+  /* Context headers, always present */
+  u32 c1;
+  u32 c2;
+  u32 c3;
+  u32 c4;
+}) nsh_md1_data_t;
+
+typedef CLIB_PACKED(struct {
+  u16 class;
+  u8 type;
+  u8 length;
+}) nsh_tlv_header_t;
+
+typedef nsh_tlv_header_t nsh_md2_data_t;
+
+typedef CLIB_PACKED(struct {
+  nsh_base_header_t nsh_base;
+  union {
+     nsh_md1_data_t md1_data;
+     nsh_md2_data_t md2_data;
+   } md;
+}) nsh_header_t;
+
+#define NSH_VERSION (0<<6)
+#define NSH_O_BIT (1<<5)
+#define NSH_C_BIT (1<<4)
+
+#define NSH_TTL_H4_MASK 0xF
+#define NSH_TTL_L2_MASK 0xC0
+#define NSH_LEN_MASK 0x3F
+
+/* Network byte order shift / mask */
+#define NSH_NSI_MASK 0xFF
+#define NSH_NSP_MASK (0x00FFFFFF)
+#define NSH_NSP_SHIFT 8
+
+#endif /* included_nsh_packet_h */
diff --git a/src/plugins/nsh/nsh_pop.c b/src/plugins/nsh/nsh_pop.c
new file mode 100644 (file)
index 0000000..cd25bb9
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * nsh_pop.c - nsh POP only processing
+ *
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <nsh/nsh.h>
+#include <vnet/gre/gre.h>
+#include <vnet/vxlan/vxlan.h>
+#include <vnet/vxlan-gpe/vxlan_gpe.h>
+#include <vnet/l2/l2_classify.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+extern nsh_option_map_t * nsh_md2_lookup_option (u16 class, u8 type);
+
+extern u8 * format_nsh_header (u8 * s, va_list * args);
+extern u8 * format_nsh_node_map_trace (u8 * s, va_list * args);
+
+/* format from network order */
+u8 * format_nsh_pop_header (u8 * s, va_list * args)
+{
+  return format_nsh_header(s, args);
+}
+
+
+
+u8 * format_nsh_pop_node_map_trace (u8 * s, va_list * args)
+{
+  return format_nsh_node_map_trace(s, args);
+}
+
+
+static uword
+nsh_pop_inline (vlib_main_t * vm,
+               vlib_node_runtime_t * node,
+               vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+  nsh_main_t * nm = &nsh_main;
+
+  from = vlib_frame_vector_args(from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         u32 bi0, bi1;
+         vlib_buffer_t * b0, *b1;
+         u32 next0 = NSH_NODE_NEXT_DROP, next1 = NSH_NODE_NEXT_DROP;
+         uword * entry0, *entry1;
+         nsh_base_header_t * hdr0 = 0, *hdr1 = 0;
+         u32 header_len0 = 0, header_len1 = 0;
+         u32 nsp_nsi0, nsp_nsi1;
+         u32 error0, error1;
+         nsh_map_t * map0 = 0, *map1 = 0;
+
+         /* Prefetch next iteration. */
+         {
+           vlib_buffer_t * p2, *p3;
+
+           p2 = vlib_get_buffer(vm, from[2]);
+           p3 = vlib_get_buffer(vm, from[3]);
+
+           vlib_prefetch_buffer_header(p2, LOAD);
+           vlib_prefetch_buffer_header(p3, LOAD);
+
+           CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
+           CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
+         }
+
+         bi0 = from[0];
+         bi1 = from[1];
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+
+         error0 = 0;
+         error1 = 0;
+
+         b0 = vlib_get_buffer(vm, bi0);
+         b1 = vlib_get_buffer(vm, bi1);
+         hdr0 = vlib_buffer_get_current(b0);
+          nsp_nsi0 = hdr0->nsp_nsi;
+          header_len0 = hdr0->length * 4;
+
+          hdr1 = vlib_buffer_get_current(b1);
+         nsp_nsi1 = hdr1->nsp_nsi;
+         header_len1 = hdr1->length * 4;
+
+         /* Process packet 0 */
+         entry0 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi0);
+         if (PREDICT_FALSE(entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace0;
+           }
+
+         /* Entry should point to a mapping ...*/
+         map0 = pool_elt_at_index(nm->nsh_mappings, entry0[0]);
+         if (PREDICT_FALSE(map0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace0;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next0 = map0->next_node;
+         //vnet_buffer(b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
+
+         if(PREDICT_FALSE(map0->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+              if(PREDICT_FALSE(hdr0->md_type == 2))
+               {
+                 if (PREDICT_FALSE(next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace0;
+                   }
+                 //vnet_buffer(b0)->sw_if_index[VLIB_RX] = map0->sw_if_index;
+               }
+
+              /* Pop NSH header */
+             vlib_buffer_advance(b0, (word)header_len0);
+             goto trace0;
+           }
+
+         entry0 = hash_get_mem(nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
+         if (PREDICT_FALSE(entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace0;
+           }
+
+        trace0: b0->error = error0 ? node->errors[error0] : 0;
+
+          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+              nsh_input_trace_t *tr = vlib_add_trace(vm, node, b0, sizeof(*tr));
+              clib_memcpy ( &(tr->trace_data), hdr0, (hdr0->length*4) );
+            }
+
+         /* Process packet 1 */
+         entry1 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi1);
+         if (PREDICT_FALSE(entry1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace1;
+           }
+
+         /* Entry should point to a mapping ...*/
+         map1 = pool_elt_at_index(nm->nsh_mappings, entry1[0]);
+         if (PREDICT_FALSE(map1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace1;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next1 = map1->next_node;
+         //vnet_buffer(b1)->sw_if_index[VLIB_TX] = map1->sw_if_index;
+
+         if(PREDICT_FALSE(map1->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+              if(PREDICT_FALSE(hdr1->md_type == 2))
+               {
+                 if (PREDICT_FALSE(next1 == NSH_NODE_NEXT_DROP))
+                   {
+                     error1 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace1;
+                   }
+                 //vnet_buffer(b1)->sw_if_index[VLIB_RX] = map1->sw_if_index;
+               }
+
+              /* Pop NSH header */
+             vlib_buffer_advance(b1, (word)header_len1);
+             goto trace1;
+           }
+
+         entry1 = hash_get_mem(nm->nsh_entry_by_key, &map1->mapped_nsp_nsi);
+         if (PREDICT_FALSE(entry1 == 0))
+           {
+             error1 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace1;
+           }
+
+
+       trace1: b1->error = error1 ? node->errors[error1] : 0;
+
+         if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_input_trace_t *tr = vlib_add_trace(vm, node, b1, sizeof(*tr));
+             clib_memcpy ( &(tr->trace_data), hdr1, (hdr1->length*4) );
+           }
+
+         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 = 0;
+         vlib_buffer_t * b0 = NULL;
+         u32 next0 = NSH_NODE_NEXT_DROP;
+         uword * entry0;
+         nsh_base_header_t * hdr0 = 0;
+         u32 header_len0 = 0;
+         u32 nsp_nsi0;
+         u32 error0;
+         nsh_map_t * map0 = 0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         error0 = 0;
+
+         b0 = vlib_get_buffer(vm, bi0);
+         hdr0 = vlib_buffer_get_current(b0);
+
+          nsp_nsi0 = hdr0->nsp_nsi;
+          header_len0 = hdr0->length * 4;
+
+         entry0 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi0);
+
+         if (PREDICT_FALSE(entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace00;
+           }
+
+         /* Entry should point to a mapping ...*/
+         map0 = pool_elt_at_index(nm->nsh_mappings, entry0[0]);
+
+         if (PREDICT_FALSE(map0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_MAPPING;
+             goto trace00;
+           }
+
+         /* set up things for next node to transmit ie which node to handle it and where */
+         next0 = map0->next_node;
+         //vnet_buffer(b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
+
+         if(PREDICT_FALSE(map0->nsh_action == NSH_ACTION_POP))
+           {
+             /* Manipulate MD2 */
+              if(PREDICT_FALSE(hdr0->md_type == 2))
+               {
+                 if (PREDICT_FALSE(next0 == NSH_NODE_NEXT_DROP))
+                   {
+                     error0 = NSH_NODE_ERROR_INVALID_OPTIONS;
+                     goto trace00;
+                   }
+                 //vnet_buffer(b0)->sw_if_index[VLIB_RX] = map0->sw_if_index;
+               }
+
+              /* Pop NSH header */
+             vlib_buffer_advance(b0, (word)header_len0);
+             goto trace00;
+           }
+
+         entry0 = hash_get_mem(nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
+         if (PREDICT_FALSE(entry0 == 0))
+           {
+             error0 = NSH_NODE_ERROR_NO_ENTRY;
+             goto trace00;
+           }
+
+         trace00: b0->error = error0 ? node->errors[error0] : 0;
+
+         if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_input_trace_t *tr = vlib_add_trace(vm, node, b0, sizeof(*tr));
+             clib_memcpy ( &(tr->trace_data[0]), hdr0, (hdr0->length*4) );
+           }
+
+         vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
+                                         n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame(vm, node, next_index, n_left_to_next);
+
+    }
+
+  return from_frame->n_vectors;
+}
+
+/**
+ * @brief Graph processing dispatch function for NSH Input
+ *
+ * @node nsh_input
+ * @param *vm
+ * @param *node
+ * @param *from_frame
+ *
+ * @return from_frame->n_vectors
+ *
+ */
+static uword
+nsh_pop (vlib_main_t * vm, vlib_node_runtime_t * node,
+                  vlib_frame_t * from_frame)
+{
+  return nsh_pop_inline (vm, node, from_frame);
+}
+
+static char * nsh_pop_node_error_strings[] = {
+#define _(sym,string) string,
+  foreach_nsh_node_error
+#undef _
+};
+
+/* register nsh-input node */
+VLIB_REGISTER_NODE (nsh_pop_node) = {
+  .function = nsh_pop,
+  .name = "nsh-pop",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nsh_pop_node_map_trace,
+  .format_buffer = format_nsh_pop_header,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(nsh_pop_node_error_strings),
+  .error_strings = nsh_pop_node_error_strings,
+
+  .n_next_nodes = NSH_NODE_N_NEXT,
+
+  .next_nodes = {
+#define _(s,n) [NSH_NODE_NEXT_##s] = n,
+    foreach_nsh_node_next
+#undef _
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (nsh_pop_node, nsh_pop);
+
+
diff --git a/src/plugins/nsh/nsh_test.c b/src/plugins/nsh/nsh_test.c
new file mode 100644 (file)
index 0000000..396e84c
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * nsh.c - skeleton vpp-api-test plug-in
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+#include <nsh/nsh.h>
+
+uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* define message IDs */
+#define vl_msg_id(n,h) n,
+typedef enum {
+#include <nsh/nsh.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 <nsh/nsh.api.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <nsh/nsh.api.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <nsh/nsh.api.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <nsh/nsh.api.h>
+#undef vl_api_version
+
+#define vl_msg_name_crc_list
+#include <nsh/nsh.api.h>
+#undef vl_msg_name_crc_list
+
+
+typedef struct {
+    /* API message ID base */
+    u16 msg_id_base;
+    vat_main_t *vat_main;
+} nsh_test_main_t;
+
+nsh_test_main_t nsh_test_main;
+
+#define foreach_standard_reply_retval_handler   \
+_(nsh_add_del_entry_reply)                     \
+_(nsh_add_del_map_reply)                       \
+
+#define _(n)                                            \
+    static void vl_api_##n##_t_handler                  \
+    (vl_api_##n##_t * mp)                               \
+    {                                                   \
+        vat_main_t * vam = nsh_test_main.vat_main;   \
+        i32 retval = ntohl(mp->retval);                 \
+        if (vam->async_mode) {                          \
+            vam->async_errors += (retval < 0);          \
+        } else {                                        \
+            vam->retval = retval;                       \
+            vam->result_ready = 1;                      \
+        }                                               \
+    }
+foreach_standard_reply_retval_handler;
+#undef _
+
+/*
+ * Table of message reply handlers, must include boilerplate handlers
+ * we just generated
+ */
+#define foreach_vpe_api_reply_msg                                       \
+_(NSH_ADD_DEL_ENTRY_REPLY, nsh_add_del_entry_reply)                    \
+_(NSH_ENTRY_DETAILS, nsh_entry_details)                                 \
+_(NSH_ADD_DEL_MAP_REPLY, nsh_add_del_map_reply)                         \
+_(NSH_MAP_DETAILS, nsh_map_details)
+
+
+/* M: construct, but don't yet send a message */
+
+#define M(T,t)                                                  \
+do {                                                            \
+    vam->result_ready = 0;                                      \
+    mp = vl_msg_api_alloc(sizeof(*mp));                         \
+    memset (mp, 0, sizeof (*mp));                               \
+    mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base);      \
+    mp->client_index = vam->my_client_index;                    \
+} while(0);
+
+#define M2(T,t,n)                                               \
+do {                                                            \
+    vam->result_ready = 0;                                      \
+    mp = vl_msg_api_alloc(sizeof(*mp)+(n));                     \
+    memset (mp, 0, sizeof (*mp));                               \
+    mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base);      \
+    mp->client_index = vam->my_client_index;                    \
+} while(0);
+
+/* S: send a message */
+#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))
+
+/* W: wait for results, with timeout */
+#define W                                       \
+do {                                            \
+    timeout = vat_time_now (vam) + 1.0;         \
+                                                \
+    while (vat_time_now (vam) < timeout) {      \
+        if (vam->result_ready == 1) {           \
+            return (vam->retval);               \
+        }                                       \
+    }                                           \
+    return -99;                                 \
+} while(0);
+
+static int api_nsh_add_del_entry (vat_main_t * vam)
+{
+    nsh_test_main_t * sm = &nsh_test_main;
+    unformat_input_t * line_input = vam->input;
+    f64 timeout;
+    u8 is_add = 1;
+    u8 ver_o_c = 0;
+    u8 length = 0;
+    u8 md_type = 0;
+    u8 next_protocol = 1; /* default: ip4 */
+    u32 nsp;
+    u8 nsp_set = 0;
+    u32 nsi;
+    u8 nsi_set = 0;
+    u32 nsp_nsi;
+    u32 c1 = 0;
+    u32 c2 = 0;
+    u32 c3 = 0;
+    u32 c4 = 0;
+    u32 tmp;
+    vl_api_nsh_add_del_entry_t * mp;
+
+    while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
+      if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "version %d", &tmp))
+       ver_o_c |= (tmp & 3) << 6;
+      else if (unformat (line_input, "o-bit %d", &tmp))
+       ver_o_c |= (tmp & 1) << 5;
+      else if (unformat (line_input, "c-bit %d", &tmp))
+       ver_o_c |= (tmp & 1) << 4;
+      else if (unformat (line_input, "md-type %d", &tmp))
+       md_type = tmp;
+      else if (unformat(line_input, "next-ip4"))
+       next_protocol = 1;
+      else if (unformat(line_input, "next-ip6"))
+       next_protocol = 2;
+      else if (unformat(line_input, "next-ethernet"))
+       next_protocol = 3;
+      else if (unformat (line_input, "c1 %d", &c1))
+       ;
+      else if (unformat (line_input, "c2 %d", &c2))
+       ;
+      else if (unformat (line_input, "c3 %d", &c3))
+       ;
+      else if (unformat (line_input, "c4 %d", &c4))
+       ;
+      else if (unformat (line_input, "nsp %d", &nsp))
+       nsp_set = 1;
+      else if (unformat (line_input, "nsi %d", &nsi))
+       nsi_set = 1;
+      else
+       return -99; // PARSE ERROR;
+    }
+
+    unformat_free (line_input);
+
+    if (nsp_set == 0)
+      return -1; //TODO Error type for this cond: clib_error_return (0, "nsp not specified");
+
+    if (nsi_set == 0)
+      return -2; //TODO Error type for this cond:clib_error_return (0, "nsi not specified");
+
+    if (md_type == 1)
+      length = 6;
+    else if (md_type == 2)
+      length = 2;  /* base header length */
+
+    nsp_nsi = (nsp<<8) | nsi;
+
+    /* Construct the API message */
+    M(NSH_ADD_DEL_ENTRY, nsh_add_del_entry);
+    mp->is_add = is_add;
+
+#define _(x) mp->x = x;
+    foreach_copy_nsh_base_hdr_field;
+#undef _
+
+
+    /* send it... */
+    S;
+
+    /* Wait for a reply... */
+    W;
+}
+
+static void vl_api_nsh_entry_details_t_handler
+(vl_api_nsh_entry_details_t * mp)
+{
+    vat_main_t * vam = &vat_main;
+
+    fformat(vam->ofp, "%11d%11d%11d%11d%14d%14d%14d%14d%14d\n",
+            mp->ver_o_c,
+            mp->length,
+           mp->md_type,
+           mp->next_protocol,
+            ntohl(mp->nsp_nsi),
+           ntohl(mp->c1),
+           ntohl(mp->c2),
+           ntohl(mp->c3),
+            ntohl(mp->c4));
+}
+
+static int api_nsh_entry_dump (vat_main_t * vam)
+{
+    nsh_test_main_t * sm = &nsh_test_main;
+    vl_api_nsh_entry_dump_t *mp;
+    f64 timeout;
+
+    if (!vam->json_output) {
+        fformat(vam->ofp, "%11s%11s%15s%14s%14s%13s%13s%13s%13s\n",
+                "ver_o_c", "length", "md_type", "next_protocol",
+                "nsp_nsi", "c1", "c2", "c3", "c4");
+    }
+
+    /* Get list of nsh entries */
+    M(NSH_ENTRY_DUMP, nsh_entry_dump);
+
+    /* send it... */
+    S;
+
+    /* Wait for a reply... */
+    W;
+}
+
+static int api_nsh_add_del_map (vat_main_t * vam)
+{
+    nsh_test_main_t * sm = &nsh_test_main;
+    unformat_input_t * line_input = vam->input;
+    f64 timeout;
+    u8 is_add = 1;
+    u32 nsp, nsi, mapped_nsp, mapped_nsi;
+    int nsp_set = 0, nsi_set = 0, mapped_nsp_set = 0, mapped_nsi_set = 0;
+    u32 next_node = ~0;
+    u32 sw_if_index = ~0; // temporary requirement to get this moved over to NSHSFC
+    vl_api_nsh_add_del_map_t * mp;
+
+
+    while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
+      if (unformat (line_input, "del"))
+       is_add = 0;
+      else if (unformat (line_input, "nsp %d", &nsp))
+       nsp_set = 1;
+      else if (unformat (line_input, "nsi %d", &nsi))
+       nsi_set = 1;
+      else if (unformat (line_input, "mapped-nsp %d", &mapped_nsp))
+       mapped_nsp_set = 1;
+      else if (unformat (line_input, "mapped-nsi %d", &mapped_nsi))
+       mapped_nsi_set = 1;
+      else if (unformat (line_input, "encap-gre4-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_GRE4;
+      else if (unformat (line_input, "encap-gre6-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_GRE6;
+      else if (unformat (line_input, "encap-vxlan-gpe-intf %d", &sw_if_index))
+       next_node = NSH_NODE_NEXT_ENCAP_VXLANGPE;
+      else if (unformat (line_input, "encap-none"))
+       next_node = NSH_NODE_NEXT_DROP; // Once moved to NSHSFC see nsh.h:foreach_nsh_input_next to handle this case
+      else
+       return -99; //TODO clib_error_return (0, "parse error: '%U'",
+    }
+
+    unformat_free (line_input);
+
+    if (nsp_set == 0 || nsi_set == 0)
+      return -1; // TODO create return value: clib_error_return (0, "nsp nsi pair required. Key: for NSH entry");
+
+    if (mapped_nsp_set == 0 || mapped_nsi_set == 0)
+      return -2; // TODO create return valuee clib_error_return (0, "mapped-nsp mapped-nsi pair required. Key: for NSH entry");
+
+    if (next_node == ~0)
+      return -3; //TODO clib_error_return (0, "must specific action: [encap-gre-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-none]");
+
+
+    M(NSH_ADD_DEL_MAP, nsh_add_del_map);
+    /* set args structure */
+    mp->is_add = is_add;
+    mp->nsp_nsi = (nsp<< NSH_NSP_SHIFT) | nsi;
+    mp->mapped_nsp_nsi = (mapped_nsp<< NSH_NSP_SHIFT) | mapped_nsi;
+    mp->sw_if_index = sw_if_index;
+    mp->next_node = next_node;
+
+    /* send it... */
+    S;
+
+    /* Wait for a reply... */
+    W;
+
+
+}
+
+static void vl_api_nsh_map_details_t_handler
+(vl_api_nsh_map_details_t * mp)
+{
+    vat_main_t * vam = &vat_main;
+
+    fformat(vam->ofp, "%14d%14d%14d%14d\n",
+            ntohl(mp->nsp_nsi),
+           ntohl(mp->mapped_nsp_nsi),
+           ntohl(mp->sw_if_index),
+           ntohl(mp->next_node));
+}
+
+static int api_nsh_map_dump (vat_main_t * vam)
+{
+    nsh_test_main_t * sm = &nsh_test_main;
+    vl_api_nsh_map_dump_t *mp;
+    f64 timeout;
+
+    if (!vam->json_output) {
+        fformat(vam->ofp, "%16s%16s%13s%13s\n",
+                "nsp_nsi", "mapped_nsp_nsi", "sw_if_index", "next_node");
+    }
+
+    /* Get list of nsh entries */
+    M(NSH_MAP_DUMP, nsh_map_dump);
+
+    /* send it... */
+    S;
+
+    /* Wait for a reply... */
+    W;
+}
+
+/*
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg \
+_(nsh_add_del_entry, "{nsp <nn> nsi <nn>} c1 <nn> c2 <nn> c3 <nn> c4 <nn> [md-type <nn>] [tlv <xx>] [del]") \
+_(nsh_entry_dump, "")   \
+_(nsh_add_del_map, "nsp <nn> nsi <nn> [del] mapped-nsp <nn> mapped-nsi <nn> [encap-gre-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-none]")  \
+_(nsh_map_dump, "")
+
+void vat_api_hookup (vat_main_t *vam)
+{
+    nsh_test_main_t * sm = &nsh_test_main;
+    /* Hook up handlers for replies from the data plane plug-in */
+#define _(N,n)                                                  \
+    vl_msg_api_set_handlers((VL_API_##N + sm->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_vpe_api_reply_msg;
+#undef _
+
+    /* API messages we can send */
+#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
+    foreach_vpe_api_msg;
+#undef _
+
+    /* Help strings */
+#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
+    foreach_vpe_api_msg;
+#undef _
+}
+
+clib_error_t * vat_plugin_register (vat_main_t *vam)
+{
+  nsh_test_main_t * sm = &nsh_test_main;
+  u8 * name;
+
+  sm->vat_main = vam;
+
+  /* Ask the vpp engine for the first assigned message-id */
+  name = format (0, "nsh_%08x%c", api_version, 0);
+  sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
+
+  if (sm->msg_id_base != (u16) ~0)
+    vat_api_hookup (vam);
+
+  vec_free(name);
+
+  return 0;
+}