Simple ip4 NAT plugin 66/1766/12
authorDave Barach <dave@barachs.net>
Sun, 26 Jun 2016 14:42:08 +0000 (10:42 -0400)
committerKeith Burns <alagalah@gmail.com>
Fri, 1 Jul 2016 18:20:13 +0000 (18:20 +0000)
Change-Id: Iffe77bf2a05ced41474540ff54a842101aad7c41
Signed-off-by: Dave Barach <dave@barachs.net>
14 files changed:
build-data/platforms/vpp.mk
plugins/Makefile.am
plugins/configure.ac
plugins/snat-plugin/Makefile.am [new file with mode: 0644]
plugins/snat-plugin/configure.ac [new file with mode: 0644]
plugins/snat-plugin/snat/in2out.c [new file with mode: 0644]
plugins/snat-plugin/snat/out2in.c [new file with mode: 0644]
plugins/snat-plugin/snat/snat.api [new file with mode: 0644]
plugins/snat-plugin/snat/snat.c [new file with mode: 0644]
plugins/snat-plugin/snat/snat.h [new file with mode: 0644]
plugins/snat-plugin/snat/snat_all_api_h.h [new file with mode: 0644]
plugins/snat-plugin/snat/snat_msg_enum.h [new file with mode: 0644]
plugins/snat-plugin/snat/snat_test.c [new file with mode: 0644]
vpp/Makefile.am

index 9026ad3..45f74d3 100644 (file)
@@ -38,6 +38,10 @@ vnet_configure_args_vpp = --with-dpdk
 # Set these parameters carefully. The vlib_buffer_t is 128 bytes, i.e.
 vlib_configure_args_vpp = --with-pre-data=128
 
+# Enable plugins here, and no place else, via multiple --enable-XXX-plugin
+# stanzas.
+plugins_configure_args_vpp = --with-dpdk --enable-sixrd-plugin 
+
 # DPDK configuration parameters
 # vpp_uses_external_dpdk = yes
 # vpp_dpdk_inc_dir = /usr/include/dpdk
index 1fcc18e..83dc70b 100644 (file)
@@ -29,3 +29,7 @@ endif
 if ENABLE_VCGN_PLUGIN
 SUBDIRS += vcgn-plugin
 endif
+
+if ENABLE_SNAT_PLUGIN
+SUBDIRS += snat-plugin
+endif
index 40c0bab..8a5fdbc 100644 (file)
@@ -17,10 +17,30 @@ AC_ARG_WITH(plugin-toolkit,
             [with_plugin_toolkit=${prefix}/include],
             [with_plugin_toolkit=.])
 
+AC_ARG_WITH(dpdk,
+            AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]),
+            [with_dpdk=1],
+            [with_dpdk=0])
+
 AC_SUBST(TOOLKIT_INCLUDE,[${with_plugin_toolkit}])
 AM_CONDITIONAL(WITH_PLUGIN_TOOLKIT, test "$with_plugin_toolkit" != ".")
 AM_CONDITIONAL(ENABLE_TESTS, test "$enable_tests" = "1")
 
+AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
+AC_SUBST(DPDK,["-DDPDK=${with_dpdk}"])
+
+
+#
+# Please DO NOT, UNDER ANY CIRCUMSTANCES enable or disable
+# plugins by "clever" manipulation of the arguments to AC_ARG_ENABLE
+#
+# Instead, please configure the default set of plugins in 
+# .../build-data/platforms/<platform>.mk, by adding --enable-XXX-plugin
+# stanzas to plugins_configure_args_<platform> 
+
+# The following per-plugin boilerplate is begging for an additional
+# macro, but the first 10 tries at making one didn't work. Another day.
+
 #
 # Sample plugin
 #
@@ -40,8 +60,8 @@ AM_CONDITIONAL(ENABLE_SAMPLE_PLUGIN, test "$enable_sample_plugin" = "1")
 #
 AC_ARG_ENABLE(sixrd_plugin,
               AC_HELP_STRING([--enable-sixrd-plugin], [Build sixrd plugin]),
-              [],
-              [enable_sixrd_plugin=1])
+              [enable_sixrd_plugin=1],
+              [enable_sixrd_plugin=0])
 
 if test "x$enable_sixrd_plugin" = x1; then
    AC_CONFIG_SUBDIRS([sixrd-plugin])
@@ -54,8 +74,8 @@ AM_CONDITIONAL(ENABLE_SIXRD_PLUGIN, test "$enable_sixrd_plugin" = "1")
 #
 AC_ARG_ENABLE(ioam_plugin,
               AC_HELP_STRING([--enable-ioam-plugin], [Build ioam plugin]),
-              [],
-              [enable_ioam_plugin=1])
+              [enable_ioam_plugin=1],
+              [enable_ioam_plugin=0])
 
 if test "x$enable_ioam_plugin" = x1; then
    AC_CONFIG_SUBDIRS([ioam-plugin])
@@ -77,4 +97,19 @@ fi
 
 AM_CONDITIONAL(ENABLE_VCGN_PLUGIN, test "$enable_vcgn_plugin" = "1")
 
+#
+# SNAT plugin
+#
+AC_ARG_ENABLE(snat_plugin,
+              AC_HELP_STRING([--enable-snat-plugin], [Build snat plugin]),
+              [enable_snat_plugin=1],
+              [enable_snat_plugin=0])
+
+if test "x$enable_snat_plugin" = x1; then
+   AC_CONFIG_SUBDIRS([snat-plugin])
+fi
+
+AM_CONDITIONAL(ENABLE_SNAT_PLUGIN, test "$enable_snat_plugin" = "1")
+
+
 AC_OUTPUT([Makefile])
diff --git a/plugins/snat-plugin/Makefile.am b/plugins/snat-plugin/Makefile.am
new file mode 100644 (file)
index 0000000..11b0839
--- /dev/null
@@ -0,0 +1,55 @@
+
+# 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.
+
+AUTOMAKE_OPTIONS = foreign subdir-objects
+
+AM_CFLAGS = -Wall -I@TOOLKIT_INCLUDE@ @DPDK@
+
+lib_LTLIBRARIES = snat_plugin.la snat_test_plugin.la
+
+snat_plugin_la_SOURCES = snat/snat.c           \
+        snat/in2out.c                          \
+        snat/out2in.c                          \
+       snat/snat_plugin.api.h 
+
+snat_plugin_la_LDFLAGS = -module
+
+BUILT_SOURCES = snat/snat.api.h
+
+SUFFIXES = .api.h .api
+
+%.api.h: %.api
+       mkdir -p `dirname $@` ; \
+       $(CC) $(CPPFLAGS) -E -P -C -x c $^ \
+       | vppapigen --input - --output $@ --show-name $@
+
+nobase_include_HEADERS =                       \
+  snat/snat_all_api_h.h                        \
+  snat/snat_msg_enum.h                 \
+  snat/snat.api.h
+
+snat_test_plugin_la_SOURCES = \
+  snat/snat_test.c snat/snat_plugin.api.h
+snat_test_plugin_la_LDFLAGS = -module
+
+if WITH_PLUGIN_TOOLKIT
+install-data-hook:
+       mkdir /usr/lib/vpp_plugins || true
+       mkdir /usr/lib/vpp_api_test_plugins || true
+       cp -L $(prefix)/lib/snat_plugin.so /usr/lib/vpp_plugins
+       cp -L $(prefix)/lib/snat_test_plugin.so \
+               /usr/lib/vpp_api_test_plugins
+       rm -f $(prefix)/lib/snat_plugin.*
+       rm -f $(prefix)/lib/snat_test_plugin.*
+endif
diff --git a/plugins/snat-plugin/configure.ac b/plugins/snat-plugin/configure.ac
new file mode 100644 (file)
index 0000000..e43437d
--- /dev/null
@@ -0,0 +1,27 @@
+
+AC_INIT(snat_plugin, 1.0)
+AM_INIT_AUTOMAKE
+
+AC_PROG_LIBTOOL
+AM_PROG_AS
+AC_PROG_CC
+AM_PROG_CC_C_O
+
+AC_ARG_WITH(dpdk,
+            AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]),
+            [with_dpdk=1],
+            [with_dpdk=0])
+
+AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
+AC_SUBST(DPDK,["-DDPDK=${with_dpdk}"])
+
+AC_ARG_WITH(plugin-toolkit,
+            AC_HELP_STRING([--with-plugin-toolkit],
+            [build using the vpp toolkit]),
+            [with_plugin_toolkit=${prefix}/include],
+            [with_plugin_toolkit=.])
+
+AC_SUBST(TOOLKIT_INCLUDE,[${with_plugin_toolkit}])
+AM_CONDITIONAL(WITH_PLUGIN_TOOLKIT, test "$with_plugin_toolkit" != ".")
+
+AC_OUTPUT([Makefile])
diff --git a/plugins/snat-plugin/snat/in2out.c b/plugins/snat-plugin/snat/in2out.c
new file mode 100644 (file)
index 0000000..3f7df91
--- /dev/null
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <snat/snat.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+vlib_node_registration_t snat_in2out_node, snat_in2out_slowpath_node;
+
+typedef struct {
+  u32 sw_if_index;
+  u32 next_index;
+  u32 session_index;
+  u32 is_slow_path;
+} snat_in2out_trace_t;
+
+/* packet trace format function */
+static u8 * format_snat_in2out_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 *);
+  snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *);
+  char * tag;
+
+  tag = t->is_slow_path ? "SNAT_IN2OUT_SLOW_PATH" : "SNAT_IN2OUT_FAST_PATH";
+  
+  s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag,
+              t->sw_if_index, t->next_index, t->session_index);
+
+  return s;
+}
+
+vlib_node_registration_t snat_in2out_node;
+
+#define foreach_snat_in2out_error                       \
+_(UNSUPPORTED_PROTOCOL, "Unsupported protocol")         \
+_(IN2OUT_PACKETS, "Good in2out packets processed")      \
+_(OUT_OF_PORTS, "Out of ports")                         \
+_(BAD_OUTSIDE_FIB, "Outside VRF ID not found")          \
+_(BAD_ICMP_TYPE, "icmp type not echo-request")
+  
+typedef enum {
+#define _(sym,str) SNAT_IN2OUT_ERROR_##sym,
+  foreach_snat_in2out_error
+#undef _
+  SNAT_IN2OUT_N_ERROR,
+} snat_in2out_error_t;
+
+static char * snat_in2out_error_strings[] = {
+#define _(sym,string) string,
+  foreach_snat_in2out_error
+#undef _
+};
+
+typedef enum {
+  SNAT_IN2OUT_NEXT_LOOKUP,
+  SNAT_IN2OUT_NEXT_DROP,
+  SNAT_IN2OUT_NEXT_SLOW_PATH,
+  SNAT_IN2OUT_N_NEXT,
+} snat_in2out_next_t;
+
+
+static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
+                      ip4_header_t * ip0,
+                      u32 rx_fib_index0,
+                      snat_session_key_t * key0,
+                      snat_session_t ** sessionp,
+                      vlib_node_runtime_t * node,
+                      u32 next0)
+{
+  snat_user_t *u;
+  snat_user_key_t user_key;
+  snat_session_t *s;
+  clib_bihash_kv_8_8_t kv0, value0;
+  u32 oldest_per_user_translation_list_index;
+  dlist_elt_t * oldest_per_user_translation_list_elt;
+  dlist_elt_t * per_user_translation_list_elt;
+  dlist_elt_t * per_user_list_head_elt;
+  u32 session_index;
+  snat_session_key_t key1;
+  u32 address_index;
+  u32 outside_fib_index;
+  uword * p;
+
+  p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
+  if (! p)
+    {
+      b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_OUTSIDE_FIB];
+      return SNAT_IN2OUT_NEXT_DROP;
+    }
+  outside_fib_index = p[0];
+
+  user_key.addr = ip0->src_address;
+  user_key.fib_index = rx_fib_index0;
+  kv0.key = user_key.as_u64;
+  
+  /* Ever heard of the "user" = src ip4 address before? */
+  if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0))
+    {
+      /* no, make a new one */
+      pool_get (sm->users, u);
+      memset (u, 0, sizeof (*u));
+      u->addr = ip0->src_address;
+
+      pool_get (sm->list_pool, per_user_list_head_elt);
+
+      u->sessions_per_user_list_head_index = per_user_list_head_elt -
+        sm->list_pool;
+
+      clib_dlist_init (sm->list_pool, u->sessions_per_user_list_head_index);
+      
+      kv0.value = u - sm->users;
+      
+      /* add user */
+      clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */);
+    }
+  else
+    {
+      u = pool_elt_at_index (sm->users, value0.value);
+    }
+
+  /* Over quota? Recycle the least recently used translation */
+  if (u->nsessions >= sm->max_translations_per_user)
+    {
+      /* Remove the oldest translation */
+      oldest_per_user_translation_list_index = 
+        clib_dlist_remove_head 
+        (sm->list_pool, u->sessions_per_user_list_head_index);
+
+      ASSERT (oldest_per_user_translation_list_index != ~0);
+
+      /* add it back to the end of the LRU list */
+      clib_dlist_addtail (sm->list_pool, u->sessions_per_user_list_head_index,
+                          oldest_per_user_translation_list_index);
+
+      /* Get the list element */
+      oldest_per_user_translation_list_elt = 
+        pool_elt_at_index (sm->list_pool, 
+                           oldest_per_user_translation_list_index);
+      
+      /* Get the session index from the list element */
+      session_index = oldest_per_user_translation_list_elt->value;
+
+      /* Get the session */
+      s = pool_elt_at_index (sm->sessions, session_index);
+
+      /* Remove in2out, out2in keys */
+      kv0.key = s->in2out.as_u64;
+      if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 0 /* is_add */))
+          clib_warning ("in2out key delete failed");
+      kv0.key = s->out2in.as_u64;
+      if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */))
+          clib_warning ("out2in key delete failed");
+
+      snat_free_outside_address_and_port 
+        (sm, &s->out2in, s->outside_address_index);
+      s->outside_address_index = ~0;
+
+      if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+        {
+          ASSERT(0);
+
+          b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
+          return SNAT_IN2OUT_NEXT_DROP;
+        }
+      s->outside_address_index = address_index;
+    }
+  else
+    {
+      if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+        {
+          ASSERT(0);
+
+          b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
+          return SNAT_IN2OUT_NEXT_DROP;
+        }
+
+      /* Create a new session */
+      pool_get (sm->sessions, s);
+      memset (s, 0, sizeof (*s));
+      
+      s->outside_address_index = address_index;
+
+      /* Create list elts */
+      pool_get (sm->list_pool, per_user_translation_list_elt);
+      clib_dlist_init (sm->list_pool, per_user_translation_list_elt -
+                       sm->list_pool);
+      
+      per_user_translation_list_elt->value = s - sm->sessions;
+      s->per_user_index = per_user_translation_list_elt - sm->list_pool;
+      s->per_user_list_head_index = u->sessions_per_user_list_head_index;
+      
+      clib_dlist_addtail (sm->list_pool, s->per_user_list_head_index,
+                          per_user_translation_list_elt - sm->list_pool);
+      u->nsessions++;
+    }
+  
+  s->in2out = *key0;
+  s->out2in = key1;
+  s->out2in.protocol = key0->protocol;
+  s->out2in.fib_index = outside_fib_index;
+  *sessionp = s;
+
+  /* Add to translation hashes */
+  kv0.key = s->in2out.as_u64;
+  kv0.value = s - sm->sessions;
+  if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */))
+      clib_warning ("in2out key add failed");
+  
+  kv0.key = s->out2in.as_u64;
+  kv0.value = s - sm->sessions;
+  
+  if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */))
+      clib_warning ("out2in key add failed");
+
+  return next0;
+}
+                      
+static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
+                                         vlib_buffer_t * b0,
+                                         ip4_header_t * ip0,
+                                         icmp46_header_t * icmp0,
+                                         u32 sw_if_index0,
+                                         u32 rx_fib_index0,
+                                         vlib_node_runtime_t * node,
+                                         u32 next0,
+                                         f64 now)
+{
+  snat_session_key_t key0;
+  icmp_echo_header_t *echo0;
+  clib_bihash_kv_8_8_t kv0, value0;
+  snat_session_t * s0;
+  u32 new_addr0, old_addr0;
+  u16 old_id0, new_id0;
+  ip_csum_t sum0;
+  snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+
+  if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request))
+    {
+      b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE];
+      return SNAT_IN2OUT_NEXT_DROP;
+    }
+  
+  echo0 = (icmp_echo_header_t *)(icmp0+1);
+
+  key0.addr = ip0->src_address;
+  key0.port = echo0->identifier;
+  key0.protocol = SNAT_PROTOCOL_ICMP;
+  key0.fib_index = rx_fib_index0;
+  
+  kv0.key = key0.as_u64;
+  
+  if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0))
+    {
+      ip4_address_t * first_int_addr;
+
+      if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+        {
+          first_int_addr = 
+            ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+                                         0 /* just want the address */);
+          rt->cached_sw_if_index = sw_if_index0;
+          rt->cached_ip4_address = first_int_addr->as_u32;
+        }
+      
+      /* Don't NAT packet aimed at the intfc address */
+      if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+                                rt->cached_ip4_address))
+        return next0;
+      
+      next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
+                         &s0, node, next0);
+      
+      if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
+        return next0;
+    }
+  else
+    s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+  old_addr0 = ip0->src_address.as_u32;
+  ip0->src_address = s0->out2in.addr;
+  new_addr0 = ip0->src_address.as_u32;
+  vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+  
+  sum0 = ip0->checksum;
+  sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                         ip4_header_t,
+                         src_address /* changed member */);
+  ip0->checksum = ip_csum_fold (sum0);
+  
+  old_id0 = echo0->identifier;
+  new_id0 = s0->out2in.port;
+  echo0->identifier = new_id0;
+
+  sum0 = icmp0->checksum;
+  sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
+                         identifier);
+  icmp0->checksum = ip_csum_fold (sum0);
+
+  /* Accounting, per-user LRU list maintenance */
+  s0->last_heard = now;
+  s0->total_pkts++;
+  s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
+  clib_dlist_remove (sm->list_pool, s0->per_user_index);
+  clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                      s0->per_user_index);
+
+  return next0;
+}
+
+static inline uword
+snat_in2out_node_fn_inline (vlib_main_t * vm,
+                            vlib_node_runtime_t * node,
+                            vlib_frame_t * frame, int is_slow_path)
+{
+  u32 n_left_from, * from, * to_next;
+  snat_in2out_next_t next_index;
+  u32 pkts_processed = 0;
+  snat_main_t * sm = &snat_main;
+  snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+  f64 now = vlib_time_now (vm);
+  u32 stats_node_index;
+
+  stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index :
+    snat_in2out_node.index;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_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, next1;
+          u32 sw_if_index0, sw_if_index1;
+          ip4_header_t * ip0, * ip1;
+          ip_csum_t sum0, sum1;
+          u32 new_addr0, old_addr0, new_addr1, old_addr1;
+          u16 old_port0, new_port0, old_port1, new_port1;
+          udp_header_t * udp0, * udp1;
+          tcp_header_t * tcp0, * tcp1;
+          icmp46_header_t * icmp0, * icmp1;
+          snat_session_key_t key0, key1;
+          u32 rx_fib_index0, rx_fib_index1;
+          u32 proto0, proto1;
+          snat_session_t * s0 = 0, * s1 = 0;
+          clib_bihash_kv_8_8_t kv0, value0, kv1, value1;
+          
+         /* 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, CLIB_CACHE_LINE_BYTES, STORE);
+           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+         }
+
+          /* speculatively enqueue b0 and b1 to the current next frame */
+         to_next[0] = bi0 = from[0];
+         to_next[1] = bi1 = from[1];
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+          
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+          ip0 = vlib_buffer_get_current (b0);
+          udp0 = ip4_next_header (ip0);
+          tcp0 = (tcp_header_t *) udp0;
+          icmp0 = (icmp46_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+         rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index0);
+
+          next0 = next1 = SNAT_IN2OUT_NEXT_LOOKUP;
+
+#if 0
+          /* Formally correct, but we send to slowpath, lookup or drop */
+         vnet_get_config_data (&cm->config_main,
+                                &b0->current_config_index,
+                                &next0,
+                                0 /* sizeof config data */);
+#endif
+
+          proto0 = ~0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto0;
+
+          /* Next configured feature, probably ip4-lookup */
+          if (is_slow_path)
+            {
+              if (PREDICT_FALSE (proto0 == ~0))
+                goto trace00;
+              
+              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+                {
+                  next0 = icmp_in2out_slow_path 
+                    (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, 
+                     node, next0, now);
+                  goto trace00;
+                }
+            }
+          else
+            {
+              if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP))
+                {
+                  next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace00;
+                }
+            }
+
+          key0.addr = ip0->src_address;
+          key0.port = udp0->src_port;
+          key0.protocol = proto0;
+          key0.fib_index = rx_fib_index0;
+          
+          kv0.key = key0.as_u64;
+
+          if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != 0))
+            {
+              if (is_slow_path)
+                {
+                  ip4_address_t * first_int_addr;
+                  
+                  if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+                    {
+                      first_int_addr = 
+                        ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+                                                     0 /* just want the address */);
+                      rt->cached_sw_if_index = sw_if_index0;
+                      rt->cached_ip4_address = first_int_addr->as_u32;
+                    }
+                  
+                  /* Don't NAT packet aimed at the intfc address */
+                  if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+                                    rt->cached_ip4_address))
+                    goto trace00;
+                  
+                  next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
+                                     &s0, node, next0);
+                  if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
+                    goto trace00;
+                }
+              else
+                {
+                  next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace00;
+                }
+            }
+          else
+            s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+          old_addr0 = ip0->src_address.as_u32;
+          ip0->src_address = s0->out2in.addr;
+          new_addr0 = ip0->src_address.as_u32;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                 ip4_header_t,
+                                 src_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              old_port0 = tcp0->ports.src;
+              tcp0->ports.src = s0->out2in.port;
+              new_port0 = tcp0->ports.src;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->src_port;
+              udp0->src_port = s0->out2in.port;
+              udp0->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s0->last_heard = now;
+          s0->total_pkts++;
+          s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+          clib_dlist_remove (sm->list_pool, s0->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                              s0->per_user_index);
+        trace00:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_in2out_trace_t *t = 
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->is_slow_path = is_slow_path;
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+                  t->session_index = ~0;
+              if (s0)
+                  t->session_index = s0 - sm->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
+
+          ip1 = vlib_buffer_get_current (b1);
+          udp1 = ip4_next_header (ip1);
+          tcp1 = (tcp_header_t *) udp1;
+          icmp1 = (icmp46_header_t *) udp1;
+
+          sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+         rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index1);
+
+#if 0
+         vnet_get_config_data (&cm->config_main,
+                                &b1->current_config_index,
+                                &next1,
+                                0 /* sizeof config data */);
+#endif
+
+          proto1 = ~0;
+          proto1 = (ip1->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto1;
+          proto1 = (ip1->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto1;
+          proto1 = (ip1->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto1;
+
+          /* Next configured feature, probably ip4-lookup */
+          if (is_slow_path)
+            {
+              if (PREDICT_FALSE (proto1 == ~0))
+                goto trace01;
+              
+              if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
+                {
+                  next1 = icmp_in2out_slow_path 
+                    (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1,
+                     now);
+                  goto trace01;
+                }
+            }
+          else
+            {
+              if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP))
+                {
+                  next1 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace01;
+                }
+            }
+
+          key1.addr = ip1->src_address;
+          key1.port = udp1->src_port;
+          key1.protocol = proto1;
+          key1.fib_index = rx_fib_index1;
+          
+          kv1.key = key1.as_u64;
+
+            if (PREDICT_FALSE(clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != 0))
+            {
+              if (is_slow_path)
+                {
+                  ip4_address_t * first_int_addr;
+                  
+                  if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index1))
+                    {
+                      first_int_addr = 
+                        ip4_interface_first_address (sm->ip4_main, sw_if_index1,
+                                                     0 /* just want the address */);
+                      rt->cached_sw_if_index = sw_if_index1;
+                      rt->cached_ip4_address = first_int_addr->as_u32;
+                    }
+                  
+                  /* Don't NAT packet aimed at the intfc address */
+                  if (PREDICT_FALSE(ip1->dst_address.as_u32 ==
+                                    rt->cached_ip4_address))
+                    goto trace01;
+                  
+                  next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1,
+                                     &s1, node, next1);
+                  if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP))
+                    goto trace01;
+                }
+              else
+                {
+                  next1 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace01;
+                }
+            }
+          else
+            s1 = pool_elt_at_index (sm->sessions, value1.value);
+
+          old_addr1 = ip1->src_address.as_u32;
+          ip1->src_address = s1->out2in.addr;
+          new_addr1 = ip1->src_address.as_u32;
+          vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index;
+
+          sum1 = ip1->checksum;
+          sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
+                                 ip4_header_t,
+                                 src_address /* changed member */);
+          ip1->checksum = ip_csum_fold (sum1);
+
+          if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
+            {
+              old_port1 = tcp1->ports.src;
+              tcp1->ports.src = s1->out2in.port;
+              new_port1 = tcp1->ports.src;
+
+              sum1 = tcp1->checksum;
+              sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+              sum1 = ip_csum_update (sum1, old_port1, new_port1,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp1->checksum = ip_csum_fold(sum1);
+            }
+          else
+            {
+              old_port1 = udp1->src_port;
+              udp1->src_port = s1->out2in.port;
+              udp1->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s1->last_heard = now;
+          s1->total_pkts++;
+          s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
+          clib_dlist_remove (sm->list_pool, s1->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
+                              s1->per_user_index);
+        trace01:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b1->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_in2out_trace_t *t = 
+                 vlib_add_trace (vm, node, b1, sizeof (*t));
+              t->sw_if_index = sw_if_index1;
+              t->next_index = next1;
+              t->session_index = ~0;
+              if (s1)
+                t->session_index = s1 - sm->sessions;
+            }
+
+          pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP;
+
+          /* verify speculative enqueues, maybe switch current next frame */
+          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                           to_next, n_left_to_next,
+                                           bi0, bi1, next0, next1);
+        }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+          u32 bi0;
+         vlib_buffer_t * b0;
+          u32 next0;
+          u32 sw_if_index0;
+          ip4_header_t * ip0;
+          ip_csum_t sum0;
+          u32 new_addr0, old_addr0;
+          u16 old_port0, new_port0;
+          udp_header_t * udp0;
+          tcp_header_t * tcp0;
+          icmp46_header_t * icmp0;
+          snat_session_key_t key0;
+          u32 rx_fib_index0;
+          u32 proto0;
+          snat_session_t * s0 = 0;
+          clib_bihash_kv_8_8_t kv0, value0;
+          
+          /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+          next0 = SNAT_IN2OUT_NEXT_LOOKUP;
+
+          ip0 = vlib_buffer_get_current (b0);
+          udp0 = ip4_next_header (ip0);
+          tcp0 = (tcp_header_t *) udp0;
+          icmp0 = (icmp46_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+         rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index0);
+
+
+#if 0
+         vnet_get_config_data (&cm->config_main,
+                                &b0->current_config_index,
+                                &next0,
+                                0 /* sizeof config data */);
+#endif
+
+          proto0 = ~0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto0;
+
+          /* Next configured feature, probably ip4-lookup */
+          if (is_slow_path)
+            {
+              if (PREDICT_FALSE (proto0 == ~0))
+                goto trace0;
+              
+              if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+                {
+                  next0 = icmp_in2out_slow_path 
+                    (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0,
+                     now);
+                  goto trace0;
+                }
+            }
+          else
+            {
+              if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP))
+                {
+                  next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace0;
+                }
+            }
+
+          key0.addr = ip0->src_address;
+          key0.port = udp0->src_port;
+          key0.protocol = proto0;
+          key0.fib_index = rx_fib_index0;
+          
+          kv0.key = key0.as_u64;
+
+          if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0))
+            {
+              if (is_slow_path)
+                {
+                  ip4_address_t * first_int_addr;
+                  
+                  if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+                    {
+                      first_int_addr = 
+                        ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+                                                     0 /* just want the address */);
+                      rt->cached_sw_if_index = sw_if_index0;
+                      rt->cached_ip4_address = first_int_addr->as_u32;
+                    }
+                  
+                  /* Don't NAT packet aimed at the intfc address */
+                  if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+                                    rt->cached_ip4_address))
+                    goto trace0;
+                  
+                  next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
+                                     &s0, node, next0);
+                  if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
+                    goto trace0;
+                }
+              else
+                {
+                  next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
+                  goto trace0;
+                }
+            }
+          else
+            s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+          old_addr0 = ip0->src_address.as_u32;
+          ip0->src_address = s0->out2in.addr;
+          new_addr0 = ip0->src_address.as_u32;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                 ip4_header_t,
+                                 src_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              old_port0 = tcp0->ports.src;
+              tcp0->ports.src = s0->out2in.port;
+              new_port0 = tcp0->ports.src;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->src_port;
+              udp0->src_port = s0->out2in.port;
+              udp0->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s0->last_heard = now;
+          s0->total_pkts++;
+          s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+          clib_dlist_remove (sm->list_pool, s0->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                              s0->per_user_index);
+
+        trace0:
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_in2out_trace_t *t = 
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->is_slow_path = is_slow_path;
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+                  t->session_index = ~0;
+              if (s0)
+                  t->session_index = s0 - sm->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
+
+          /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, stats_node_index, 
+                               SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, 
+                               pkts_processed);
+  return frame->n_vectors;
+}
+
+static uword
+snat_in2out_fast_path_fn (vlib_main_t * vm,
+                          vlib_node_runtime_t * node,
+                          vlib_frame_t * frame)
+{
+  return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */);
+}
+
+VLIB_REGISTER_NODE (snat_in2out_node) = {
+  .function = snat_in2out_fast_path_fn,
+  .name = "snat-in2out",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_in2out_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  
+  .n_errors = ARRAY_LEN(snat_in2out_error_strings),
+  .error_strings = snat_in2out_error_strings,
+
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+  
+  .n_next_nodes = SNAT_IN2OUT_N_NEXT,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
+    [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
+    [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_node, snat_in2out_fast_path_fn);
+
+static uword
+snat_in2out_slow_path_fn (vlib_main_t * vm,
+                          vlib_node_runtime_t * node,
+                          vlib_frame_t * frame)
+{
+  return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */);
+}
+
+VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
+  .function = snat_in2out_slow_path_fn,
+  .name = "snat-in2out-slowpath",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_in2out_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  
+  .n_errors = ARRAY_LEN(snat_in2out_error_strings),
+  .error_strings = snat_in2out_error_strings,
+
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+  
+  .n_next_nodes = SNAT_IN2OUT_N_NEXT,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
+    [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
+    [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, snat_in2out_slow_path_fn);
diff --git a/plugins/snat-plugin/snat/out2in.c b/plugins/snat-plugin/snat/out2in.c
new file mode 100644 (file)
index 0000000..0fa96c9
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <snat/snat.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+vlib_node_registration_t snat_out2in_node;
+
+typedef struct {
+  u32 sw_if_index;
+  u32 next_index;
+  u32 session_index;
+} snat_out2in_trace_t;
+
+/* packet trace format function */
+static u8 * format_snat_out2in_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 *);
+  snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *);
+  
+  s = format (s, "SNAT_OUT2IN: sw_if_index %d, next index %d, session index %d",
+              t->sw_if_index, t->next_index, t->session_index);
+  return s;
+}
+
+vlib_node_registration_t snat_out2in_node;
+
+#define foreach_snat_out2in_error                       \
+_(UNSUPPORTED_PROTOCOL, "Unsupported protocol")         \
+_(OUT2IN_PACKETS, "Good out2in packets processed")      \
+_(BAD_ICMP_TYPE, "icmp type not echo-reply")            \
+_(NO_TRANSLATION, "No translation")
+  
+typedef enum {
+#define _(sym,str) SNAT_OUT2IN_ERROR_##sym,
+  foreach_snat_out2in_error
+#undef _
+  SNAT_OUT2IN_N_ERROR,
+} snat_out2in_error_t;
+
+static char * snat_out2in_error_strings[] = {
+#define _(sym,string) string,
+  foreach_snat_out2in_error
+#undef _
+};
+
+typedef enum {
+  SNAT_OUT2IN_NEXT_DROP,
+  SNAT_OUT2IN_N_NEXT,
+} snat_out2in_next_t;
+
+static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
+                                         vlib_buffer_t * b0,
+                                         ip4_header_t * ip0,
+                                         icmp46_header_t * icmp0,
+                                         u32 sw_if_index0,
+                                         u32 rx_fib_index0,
+                                         vlib_node_runtime_t * node,
+                                         u32 next0, f64 now)
+{
+  snat_session_key_t key0;
+  icmp_echo_header_t *echo0;
+  clib_bihash_kv_8_8_t kv0, value0;
+  snat_session_t * s0;
+  u32 new_addr0, old_addr0;
+  u16 old_id0, new_id0;
+  ip_csum_t sum0;
+  snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
+
+  echo0 = (icmp_echo_header_t *)(icmp0+1);
+
+  key0.addr = ip0->dst_address;
+  key0.port = echo0->identifier;
+  key0.protocol = SNAT_PROTOCOL_ICMP;
+  key0.fib_index = rx_fib_index0;
+  
+  kv0.key = key0.as_u64;
+  
+  if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
+    {
+      ip4_address_t * first_int_addr;
+
+      if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
+        {
+          first_int_addr = 
+            ip4_interface_first_address (sm->ip4_main, sw_if_index0,
+                                         0 /* just want the address */);
+          rt->cached_sw_if_index = sw_if_index0;
+          rt->cached_ip4_address = first_int_addr->as_u32;
+        }
+      
+      /* Don't NAT packet aimed at the intfc address */
+      if (PREDICT_FALSE(ip0->dst_address.as_u32 ==
+                        rt->cached_ip4_address))
+        return next0;
+
+      b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+      return SNAT_OUT2IN_NEXT_DROP;
+    }
+  else
+    s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+  old_addr0 = ip0->dst_address.as_u32;
+  ip0->dst_address = s0->in2out.addr;
+  new_addr0 = ip0->dst_address.as_u32;
+  vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
+  
+  sum0 = ip0->checksum;
+  sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                         ip4_header_t,
+                         dst_address /* changed member */);
+  ip0->checksum = ip_csum_fold (sum0);
+  
+  old_id0 = echo0->identifier;
+  new_id0 = s0->in2out.port;
+  echo0->identifier = new_id0;
+
+  sum0 = icmp0->checksum;
+  sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
+                         identifier);
+  icmp0->checksum = ip_csum_fold (sum0);
+
+  /* Accounting, per-user LRU list maintenance */
+  s0->last_heard = now;
+  s0->total_pkts++;
+  s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
+  clib_dlist_remove (sm->list_pool, s0->per_user_index);
+  clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                      s0->per_user_index);
+
+  return next0;
+}
+
+static uword
+snat_out2in_node_fn (vlib_main_t * vm,
+                 vlib_node_runtime_t * node,
+                 vlib_frame_t * frame)
+{
+  u32 n_left_from, * from, * to_next;
+  snat_out2in_next_t next_index;
+  u32 pkts_processed = 0;
+  snat_main_t * sm = &snat_main;
+  ip_lookup_main_t * lm = sm->ip4_lookup_main;
+  ip_config_main_t * cm = &lm->rx_config_mains[VNET_UNICAST];
+  f64 now = vlib_time_now (vm);
+
+  from = vlib_frame_vector_args (frame);
+  n_left_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 = SNAT_OUT2IN_NEXT_DROP;
+          u32 next1 = SNAT_OUT2IN_NEXT_DROP;
+          u32 sw_if_index0, sw_if_index1;
+          ip4_header_t * ip0, *ip1;
+          ip_csum_t sum0, sum1;
+          u32 new_addr0, old_addr0;
+          u16 new_port0, old_port0;
+          u32 new_addr1, old_addr1;
+          u16 new_port1, old_port1;
+          udp_header_t * udp0, * udp1;
+          tcp_header_t * tcp0, * tcp1;
+          icmp46_header_t * icmp0, * icmp1;
+          snat_session_key_t key0, key1;
+          u32 rx_fib_index0, rx_fib_index1;
+          u32 proto0, proto1;
+          snat_session_t * s0 = 0, * s1 = 0;
+          clib_bihash_kv_8_8_t kv0, kv1, value0, value1;
+          
+         /* 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, CLIB_CACHE_LINE_BYTES, STORE);
+           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+         }
+
+          /* speculatively enqueue b0 and b1 to the current next frame */
+         to_next[0] = bi0 = from[0];
+         to_next[1] = bi1 = from[1];
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+            
+          ip0 = vlib_buffer_get_current (b0);
+          udp0 = ip4_next_header (ip0);
+          tcp0 = (tcp_header_t *) udp0;
+          icmp0 = (icmp46_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+         rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index0);
+
+         vnet_get_config_data (&cm->config_main,
+                                &b0->current_config_index,
+                                &next0,
+                                0 /* sizeof config data */);
+          proto0 = ~0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto0;
+
+          if (PREDICT_FALSE (proto0 == ~0))
+              goto trace0;
+
+          if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+            {
+              next0 = icmp_out2in_slow_path 
+                (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, 
+                 next0, now);
+              goto trace0;
+            }
+
+          key0.addr = ip0->dst_address;
+          key0.port = udp0->dst_port;
+          key0.protocol = proto0;
+          key0.fib_index = rx_fib_index0;
+          
+          kv0.key = key0.as_u64;
+
+          if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
+            goto trace0;
+          else
+            s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+          old_addr0 = ip0->dst_address.as_u32;
+          ip0->dst_address = s0->in2out.addr;
+          new_addr0 = ip0->dst_address.as_u32;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              old_port0 = tcp0->ports.dst;
+              tcp0->ports.dst = s0->in2out.port;
+              new_port0 = tcp0->ports.dst;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->dst_port;
+              udp0->dst_port = s0->in2out.port;
+              udp0->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s0->last_heard = now;
+          s0->total_pkts++;
+          s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+          clib_dlist_remove (sm->list_pool, s0->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                              s0->per_user_index);
+        trace0:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_out2in_trace_t *t = 
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+              t->session_index = ~0;
+              if (s0)
+                  t->session_index = s0 - sm->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
+
+
+          ip1 = vlib_buffer_get_current (b1);
+          udp1 = ip4_next_header (ip1);
+          tcp1 = (tcp_header_t *) udp1;
+          icmp1 = (icmp46_header_t *) udp1;
+
+          sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
+         rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index1);
+
+         vnet_get_config_data (&cm->config_main,
+                                &b1->current_config_index,
+                                &next1,
+                                0 /* sizeof config data */);
+          proto1 = ~0;
+          proto1 = (ip1->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto1;
+          proto1 = (ip1->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto1;
+          proto1 = (ip1->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto1;
+
+          if (PREDICT_FALSE (proto1 == ~0))
+              goto trace1;
+
+          if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
+            {
+              next1 = icmp_out2in_slow_path 
+                (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, 
+                 next1, now);
+              goto trace1;
+            }
+
+          key1.addr = ip1->dst_address;
+          key1.port = udp1->dst_port;
+          key1.protocol = proto1;
+          key1.fib_index = rx_fib_index1;
+          
+          kv1.key = key1.as_u64;
+
+          if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1))
+            goto trace1;
+          else
+            s1 = pool_elt_at_index (sm->sessions, value1.value);
+
+          old_addr1 = ip1->dst_address.as_u32;
+          ip1->dst_address = s1->in2out.addr;
+          new_addr1 = ip1->dst_address.as_u32;
+          vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index;
+
+          sum1 = ip1->checksum;
+          sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip1->checksum = ip_csum_fold (sum1);
+
+          if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
+            {
+              old_port1 = tcp1->ports.dst;
+              tcp1->ports.dst = s1->in2out.port;
+              new_port1 = tcp1->ports.dst;
+
+              sum1 = tcp1->checksum;
+              sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum1 = ip_csum_update (sum1, old_port1, new_port1,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp1->checksum = ip_csum_fold(sum1);
+            }
+          else
+            {
+              old_port1 = udp1->dst_port;
+              udp1->dst_port = s1->in2out.port;
+              udp1->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s1->last_heard = now;
+          s1->total_pkts++;
+          s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
+          clib_dlist_remove (sm->list_pool, s1->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s1->per_user_list_head_index,
+                              s1->per_user_index);
+        trace1:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b1->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_out2in_trace_t *t = 
+                 vlib_add_trace (vm, node, b1, sizeof (*t));
+              t->sw_if_index = sw_if_index1;
+              t->next_index = next1;
+              t->session_index = ~0;
+              if (s1)
+                  t->session_index = s1 - sm->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
+          pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
+
+          /* verify speculative enqueues, maybe switch current next frame */
+          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                           to_next, n_left_to_next,
+                                           bi0, bi1, next0, next1);
+        }
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+          u32 bi0;
+         vlib_buffer_t * b0;
+          u32 next0 = SNAT_OUT2IN_NEXT_DROP;
+          u32 sw_if_index0;
+          ip4_header_t * ip0;
+          ip_csum_t sum0;
+          u32 new_addr0, old_addr0;
+          u16 new_port0, old_port0;
+          udp_header_t * udp0;
+          tcp_header_t * tcp0;
+          icmp46_header_t * icmp0;
+          snat_session_key_t key0;
+          u32 rx_fib_index0;
+          u32 proto0;
+          snat_session_t * s0 = 0;
+          clib_bihash_kv_8_8_t kv0, value0;
+          
+          /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+          ip0 = vlib_buffer_get_current (b0);
+          udp0 = ip4_next_header (ip0);
+          tcp0 = (tcp_header_t *) udp0;
+          icmp0 = (icmp46_header_t *) udp0;
+
+          sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
+         rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, 
+                                   sw_if_index0);
+
+         vnet_get_config_data (&cm->config_main,
+                                &b0->current_config_index,
+                                &next0,
+                                0 /* sizeof config data */);
+          proto0 = ~0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_UDP) 
+            ? SNAT_PROTOCOL_UDP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_TCP) 
+            ? SNAT_PROTOCOL_TCP : proto0;
+          proto0 = (ip0->protocol == IP_PROTOCOL_ICMP) 
+            ? SNAT_PROTOCOL_ICMP : proto0;
+
+          if (PREDICT_FALSE (proto0 == ~0))
+              goto trace00;
+
+          if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
+            {
+              next0 = icmp_out2in_slow_path 
+                (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, 
+                 next0, now);
+              goto trace00;
+            }
+
+          key0.addr = ip0->dst_address;
+          key0.port = udp0->dst_port;
+          key0.protocol = proto0;
+          key0.fib_index = rx_fib_index0;
+          
+          kv0.key = key0.as_u64;
+
+          if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
+            goto trace00;
+          else
+            s0 = pool_elt_at_index (sm->sessions, value0.value);
+
+          old_addr0 = ip0->dst_address.as_u32;
+          ip0->dst_address = s0->in2out.addr;
+          new_addr0 = ip0->dst_address.as_u32;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
+
+          sum0 = ip0->checksum;
+          sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                 ip4_header_t,
+                                 dst_address /* changed member */);
+          ip0->checksum = ip_csum_fold (sum0);
+
+          if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+            {
+              old_port0 = tcp0->ports.dst;
+              tcp0->ports.dst = s0->in2out.port;
+              new_port0 = tcp0->ports.dst;
+
+              sum0 = tcp0->checksum;
+              sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                     ip4_header_t,
+                                     dst_address /* changed member */);
+
+              sum0 = ip_csum_update (sum0, old_port0, new_port0,
+                                     ip4_header_t /* cheat */,
+                                     length /* changed member */);
+              tcp0->checksum = ip_csum_fold(sum0);
+            }
+          else
+            {
+              old_port0 = udp0->dst_port;
+              udp0->dst_port = s0->in2out.port;
+              udp0->checksum = 0;
+            }
+
+          /* Accounting, per-user LRU list maintenance */
+          s0->last_heard = now;
+          s0->total_pkts++;
+          s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
+          clib_dlist_remove (sm->list_pool, s0->per_user_index);
+          clib_dlist_addtail (sm->list_pool, s0->per_user_list_head_index,
+                              s0->per_user_index);
+        trace00:
+
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
+            {
+              snat_out2in_trace_t *t = 
+                 vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+              t->session_index = ~0;
+              if (s0)
+                  t->session_index = s0 - sm->sessions;
+            }
+
+          pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
+
+          /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, snat_out2in_node.index, 
+                               SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, 
+                               pkts_processed);
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_out2in_node) = {
+  .function = snat_out2in_node_fn,
+  .name = "snat-out2in",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_out2in_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  
+  .n_errors = ARRAY_LEN(snat_out2in_error_strings),
+  .error_strings = snat_out2in_error_strings,
+
+  .runtime_data_bytes = sizeof (snat_runtime_t),
+  
+  .n_next_nodes = SNAT_OUT2IN_N_NEXT,
+
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
+  },
+};
+VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
diff --git a/plugins/snat-plugin/snat/snat.api b/plugins/snat-plugin/snat/snat.api
new file mode 100644 (file)
index 0000000..0c5f437
--- /dev/null
@@ -0,0 +1,25 @@
+define snat_add_address_range {
+  u32 client_index;
+  u32 context;
+  u8 is_ip4;
+  u8 first_ip_address[16];
+  u8 last_ip_address[16];
+};
+
+define snat_add_address_range_reply {
+  u32 context;
+  i32 retval;
+};
+
+define snat_interface_add_del_feature {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 is_inside;
+  u32 sw_if_index;
+};
+
+define snat_interface_add_del_feature_reply {
+  u32 context;
+  i32 retval;
+};
diff --git a/plugins/snat-plugin/snat/snat.c b/plugins/snat-plugin/snat/snat.c
new file mode 100644 (file)
index 0000000..3675602
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * snat.c - simple nat plugin
+ *
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <vlibapi/api.h>
+#include <snat/snat.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+
+snat_main_t snat_main;
+
+/* define message IDs */
+#include <snat/snat_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <snat/snat_all_api_h.h> 
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <snat/snat_all_api_h.h> 
+#undef vl_endianfun
+
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <snat/snat_all_api_h.h>
+#undef vl_api_version
+
+/* Macro to finish up custom dump fns */
+#define FINISH                                  \
+    vec_add1 (s, 0);                            \
+    vl_print (handle, (char *)s);               \
+    vec_free (s);                               \
+    return handle;
+
+/* 
+ * A handy macro to set up a message reply.
+ * Assumes that the following variables are available:
+ * mp - pointer to request message
+ * rmp - pointer to reply message type
+ * rv - return value
+ */
+
+#define REPLY_MACRO(t)                                          \
+do {                                                            \
+    unix_shared_memory_queue_t * q =                            \
+    vl_api_client_index_to_input_queue (mp->client_index);      \
+    if (!q)                                                     \
+        return;                                                 \
+                                                                \
+    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
+    rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base);               \
+    rmp->context = mp->context;                                 \
+    rmp->retval = ntohl(rv);                                    \
+                                                                \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+} while(0);
+
+
+/* Hook up input features */
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out, static) = {
+  .node_name = "snat-in2out",
+  .runs_before = {"snat-out2in", 0},
+  .feature_index = &snat_main.rx_feature_in2out,
+};
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in, static) = {
+  .node_name = "snat-out2in",
+  .runs_before = {"ip4-lookup", 0},
+  .feature_index = &snat_main.rx_feature_out2in,
+};
+
+/* 
+ * This routine exists to convince the vlib plugin framework that
+ * we haven't accidentally copied a random .dll into the plugin directory.
+ *
+ * Also collects global variable pointers passed from the vpp engine
+ */
+
+clib_error_t * 
+vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
+                      int from_early_init)
+{
+  snat_main_t * sm = &snat_main;
+  clib_error_t * error = 0;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = h->vnet_main;
+  sm->ethernet_main = h->ethernet_main;
+
+  return error;
+}
+
+/*$$$$$ move to an installed header file */
+#if (1 || CLIB_DEBUG > 0)       /* "trust, but verify" */
+
+#define VALIDATE_SW_IF_INDEX(mp)                               \
+ do { u32 __sw_if_index = ntohl(mp->sw_if_index);              \
+    vnet_main_t *__vnm = vnet_get_main();                       \
+    if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
+                           __sw_if_index)) {                    \
+        rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
+        goto bad_sw_if_index;                                   \
+    }                                                           \
+} while(0);
+
+#define BAD_SW_IF_INDEX_LABEL                   \
+do {                                            \
+bad_sw_if_index:                                \
+    ;                                           \
+} while (0);
+
+#define VALIDATE_RX_SW_IF_INDEX(mp)                            \
+ do { u32 __rx_sw_if_index = ntohl(mp->rx_sw_if_index);                \
+    vnet_main_t *__vnm = vnet_get_main();                       \
+    if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
+                           __rx_sw_if_index)) {                        \
+        rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
+        goto bad_rx_sw_if_index;                               \
+    }                                                           \
+} while(0);
+
+#define BAD_RX_SW_IF_INDEX_LABEL               \
+do {                                            \
+bad_rx_sw_if_index:                            \
+    ;                                           \
+} while (0);
+
+#define VALIDATE_TX_SW_IF_INDEX(mp)                            \
+ do { u32 __tx_sw_if_index = ntohl(mp->tx_sw_if_index);                \
+    vnet_main_t *__vnm = vnet_get_main();                       \
+    if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
+                           __tx_sw_if_index)) {                        \
+        rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
+        goto bad_tx_sw_if_index;                               \
+    }                                                           \
+} while(0);
+
+#define BAD_TX_SW_IF_INDEX_LABEL               \
+do {                                            \
+bad_tx_sw_if_index:                            \
+    ;                                           \
+} while (0);
+
+#else
+
+#define VALIDATE_SW_IF_INDEX(mp)
+#define BAD_SW_IF_INDEX_LABEL
+#define VALIDATE_RX_SW_IF_INDEX(mp)
+#define BAD_RX_SW_IF_INDEX_LABEL
+#define VALIDATE_TX_SW_IF_INDEX(mp)
+#define BAD_TX_SW_IF_INDEX_LABEL
+
+#endif  /* CLIB_DEBUG > 0 */
+
+void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
+{
+  snat_address_t * ap;
+
+  vec_add2 (sm->addresses, ap, 1);
+  ap->addr = *addr;
+
+}
+
+static void increment_v4_address (ip4_address_t * a)
+{
+  u32 v;
+  
+  v = clib_net_to_host_u32(a->as_u32) + 1;
+  a->as_u32 = clib_host_to_net_u32(v);
+}
+
+static void 
+vl_api_snat_add_address_range_t_handler
+(vl_api_snat_add_address_range_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_add_address_range_reply_t * rmp;
+  ip4_address_t this_addr;
+  u32 start_host_order, end_host_order;
+  int i, count;
+  int rv = 0;
+  u32 * tmp;
+
+  if (mp->is_ip4 != 1)
+    {
+      rv = VNET_API_ERROR_UNIMPLEMENTED;
+      goto send_reply;
+    }
+
+  tmp = (u32 *) mp->first_ip_address;
+  start_host_order = clib_host_to_net_u32 (tmp[0]);
+  tmp = (u32 *) mp->last_ip_address;
+  end_host_order = clib_host_to_net_u32 (tmp[0]);
+
+  count = (end_host_order - start_host_order) + 1;
+
+  if (count > 1024)
+    clib_warning ("%U - %U, %d addresses...",
+                  format_ip4_address, mp->first_ip_address,
+                  format_ip4_address, mp->last_ip_address,
+                  count);
+  
+  memcpy (&this_addr.as_u8, mp->first_ip_address, 4);
+
+  for (i = 0; i < count; i++)
+    {
+      snat_add_address (sm, &this_addr);
+      increment_v4_address (&this_addr);
+    }
+
+ send_reply:
+  REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
+}
+
+static void *vl_api_snat_add_address_range_t_print
+(vl_api_snat_add_address_range_t *mp, void * handle)
+{
+  u8 * s;
+
+  s = format (0, "SCRIPT: snat_add_address_range ");
+  s = format (s, "%U ", format_ip4_address, mp->first_ip_address);
+  if (memcmp (mp->first_ip_address, mp->last_ip_address, 4))
+    {
+      s = format (s, " - %U ", format_ip4_address, mp->last_ip_address);
+    }
+  FINISH;
+}
+
+static void
+vl_api_snat_interface_add_del_feature_t_handler
+(vl_api_snat_interface_add_del_feature_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_interface_add_del_feature_reply_t * rmp;
+  u8 is_del = mp->is_add == 0;
+  u32 sw_if_index = ntohl(mp->sw_if_index);
+  u32 ci;
+  ip4_main_t * im = &ip4_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
+  ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
+  u32 feature_index;
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX(mp);
+
+  feature_index = mp->is_inside ? sm->rx_feature_in2out
+    : sm->rx_feature_out2in;
+
+  ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
+  ci = (is_del
+        ? vnet_config_del_feature
+        : vnet_config_add_feature)
+    (sm->vlib_main, &rx_cm->config_main,
+     ci,
+     feature_index,
+     0 /* config struct */, 
+     0 /* sizeof config struct*/);
+  rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
+  
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO(VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY);
+}
+
+static void *vl_api_snat_interface_add_del_feature_t_print
+(vl_api_snat_interface_add_del_feature_t * mp, void *handle)
+{
+  u8 * s;
+
+  s = format (0, "SCRIPT: snat_interface_add_del_feature ");
+  s = format (s, "sw_if_index %d %s %s",
+              clib_host_to_net_u32(mp->sw_if_index),
+              mp->is_inside ? "in":"out",
+              mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+/* List of message types that this plugin understands */
+#define foreach_snat_plugin_api_msg                                     \
+_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
+_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)
+
+/* Set up the API message handling tables */
+static clib_error_t *
+snat_plugin_api_hookup (vlib_main_t *vm)
+{
+   snat_main_t * sm __attribute__ ((unused)) = &snat_main;
+#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_snat_plugin_api_msg;
+#undef _
+
+    return 0;
+}
+
+static void plugin_custom_dump_configure (snat_main_t * sm) 
+{
+#define _(n,f) sm->api_main->msg_print_handlers \
+  [VL_API_##n + sm->msg_id_base]                \
+    = (void *) vl_api_##f##_t_print;
+  foreach_snat_plugin_api_msg;
+#undef _
+}
+
+static clib_error_t * snat_init (vlib_main_t * vm)
+{
+  snat_main_t * sm = &snat_main;
+  clib_error_t * error = 0;
+  ip4_main_t * im = &ip4_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
+  u8 * name;
+
+  name = format (0, "snat_%08x%c", api_version, 0);
+
+  /* Ask for a correctly-sized block of API message decode slots */
+  sm->msg_id_base = vl_msg_api_get_msg_ids 
+      ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main();
+  sm->ip4_main = im;
+  sm->ip4_lookup_main = lm;
+  sm->api_main = &api_main;
+
+  error = snat_plugin_api_hookup (vm);
+  plugin_custom_dump_configure (sm);
+  vec_free(name);
+
+  return error;
+}
+
+VLIB_INIT_FUNCTION (snat_init);
+
+void snat_free_outside_address_and_port (snat_main_t * sm, 
+                                         snat_session_key_t * k, 
+                                         u32 address_index)
+{
+  snat_address_t *a;
+  u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
+  
+  ASSERT (address_index < vec_len (sm->addresses));
+
+  a = sm->addresses + address_index;
+
+  ASSERT (clib_bitmap_get (a->busy_port_bitmap, port_host_byte_order) == 1);
+
+  a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap, 
+                                         port_host_byte_order, 0);
+  a->busy_ports--;
+}  
+
+int snat_alloc_outside_address_and_port (snat_main_t * sm, 
+                                         snat_session_key_t * k,
+                                         u32 * address_indexp)
+{
+  int i;
+  snat_address_t *a;
+  u32 portnum;
+
+  for (i = 0; i < vec_len (sm->addresses); i++)
+    {
+      if (sm->addresses[i].busy_ports < (65535-1024))
+        {
+          a = sm->addresses + i;
+
+          while (1)
+            {
+              portnum = random_u32 (&sm->random_seed);
+              portnum &= 0xFFFF;
+              if (portnum < 1024)
+                continue;
+              if (clib_bitmap_get (a->busy_port_bitmap, portnum))
+                continue;
+              a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
+                                                     portnum, 1);
+              a->busy_ports++;
+              /* Caller sets protocol and fib index */
+              k->addr = a->addr;
+              k->port = clib_host_to_net_u16(portnum);
+              *address_indexp = i;
+              return 0;
+            }
+        }
+    }
+  /* Totally out of translations to use... */
+  return 1;
+}
+
+
+static clib_error_t *
+add_address_command_fn (vlib_main_t * vm,
+                        unformat_input_t * input,
+                        vlib_cli_command_t * cmd)
+{
+  snat_main_t * sm = &snat_main;
+  ip4_address_t start_addr, end_addr, this_addr;
+  u32 start_host_order, end_host_order;
+  int i, count;
+
+  if (unformat (input, "%U - %U", 
+                unformat_ip4_address, &start_addr,
+                unformat_ip4_address, &end_addr))
+    ;
+  else if (unformat (input, "%U", unformat_ip4_address, &start_addr))
+    end_addr = start_addr;
+
+  start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
+  end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
+  
+  if (end_host_order < start_host_order)
+    return clib_error_return (0, "end address less than start address");
+
+  count = (end_host_order - start_host_order) + 1;
+
+  if (count > 1024)
+    clib_warning ("%U - %U, %d addresses...",
+                  format_ip4_address, &start_addr,
+                  format_ip4_address, &end_addr,
+                  count);
+  
+  this_addr = start_addr;
+
+  for (i = 0; i < count; i++)
+    {
+      snat_add_address (sm, &this_addr);
+      increment_v4_address (&this_addr);
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (add_address_command, static) = {
+  .path = "snat add address",
+  .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>]",
+  .function = add_address_command_fn,
+};
+
+static clib_error_t *
+snat_feature_command_fn (vlib_main_t * vm,
+                          unformat_input_t * input,
+                          vlib_cli_command_t * cmd)
+{
+  vnet_main_t * vnm = vnet_get_main();
+  snat_main_t * sm = &snat_main;
+  ip4_main_t * im = &ip4_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
+  ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
+  clib_error_t * error = 0;
+  u32 sw_if_index, ci;
+  u32 feature_index;
+  u32 * inside_sw_if_indices = 0;
+  u32 * outside_sw_if_indices = 0;
+  int is_del = 0;
+  int i;
+
+  sw_if_index = ~0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "in %U", unformat_vnet_sw_interface, 
+                    vnm, &sw_if_index))
+        vec_add1 (inside_sw_if_indices, sw_if_index);
+      else if (unformat (input, "out %U", unformat_vnet_sw_interface, 
+                         vnm, &sw_if_index))
+        vec_add1 (outside_sw_if_indices, sw_if_index);
+      else if (unformat (input, "del"))
+        is_del = 1;
+      else
+        break;
+    }
+
+  if (vec_len (inside_sw_if_indices))
+    {
+      feature_index = sm->rx_feature_in2out;
+
+      for (i = 0; i < vec_len(inside_sw_if_indices); i++)
+        {
+          sw_if_index = inside_sw_if_indices[i];
+          ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
+          ci = (is_del
+                ? vnet_config_del_feature
+                : vnet_config_add_feature)
+            (vm, &rx_cm->config_main,
+             ci,
+             feature_index,
+             0 /* config struct */, 
+             0 /* sizeof config struct*/);
+          rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
+        }
+    }
+
+  if (vec_len (outside_sw_if_indices))
+    {
+      feature_index = sm->rx_feature_out2in;
+
+      for (i = 0; i < vec_len(outside_sw_if_indices); i++)
+        {
+          sw_if_index = outside_sw_if_indices[i];
+          ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
+          ci = (is_del
+                ? vnet_config_del_feature
+                : vnet_config_add_feature)
+            (vm, &rx_cm->config_main,
+             ci,
+             feature_index,
+             0 /* config struct */, 
+             0 /* sizeof config struct*/);
+          rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
+        }
+    }
+
+  vec_free (inside_sw_if_indices);
+  vec_free (outside_sw_if_indices);
+
+  return error;
+}
+
+VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
+  .path = "set interface snat",
+  .function = snat_feature_command_fn,
+  .short_help = "set interface snat in <intfc> out <intfc> [del]",
+};
+
+static clib_error_t *
+snat_config (vlib_main_t * vm, unformat_input_t * input)
+{
+  snat_main_t * sm = &snat_main;
+  u32 translation_buckets = 1024;
+  u32 translation_memory_size = 128<<20;
+  u32 user_buckets = 128;
+  u32 user_memory_size = 64<<20;
+  u32 max_translations_per_user = 100;
+  u32 outside_vrf_id = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "translation hash buckets %d", &translation_buckets))
+        ;
+      else if (unformat (input, "translation hash memory %d",
+                         &translation_memory_size));
+      else if (unformat (input, "user hash buckets %d", &user_buckets))
+        ;
+      else if (unformat (input, "user hash memory %d",
+                         &user_memory_size))
+        ;
+      else if (unformat (input, "max translations per user %d",
+                         &max_translations_per_user))
+        ;
+      else if (unformat (input, "outside VRF id %d",
+                         &outside_vrf_id))
+        ;
+      else 
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+
+  /* for show commands, etc. */
+  sm->translation_buckets = translation_buckets;
+  sm->translation_memory_size = translation_memory_size;
+  sm->user_buckets = user_buckets;
+  sm->user_memory_size = user_memory_size;
+  sm->max_translations_per_user = max_translations_per_user;
+  sm->outside_vrf_id = outside_vrf_id;
+
+  clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
+                        translation_memory_size);
+  
+  clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
+                        translation_memory_size);
+
+  clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
+                        user_memory_size);
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (snat_config, "snat");
+
+u8 * format_snat_key (u8 * s, va_list * args)
+{
+  snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
+  char * protocol_string = "unknown";
+  static char *protocol_strings[] = {
+      "UDP",
+      "TCP",
+      "ICMP",
+  };
+
+  if (key->protocol < ARRAY_LEN(protocol_strings))
+      protocol_string = protocol_strings[key->protocol];
+
+  s = format (s, "%U proto %s port %d fib %d",
+              format_ip4_address, &key->addr, protocol_string,
+              key->port, key->fib_index);
+  return s;
+}
+
+u8 * format_snat_session (u8 * s, va_list * args)
+{
+  snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
+  snat_session_t * sess = va_arg (*args, snat_session_t *);
+
+  s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
+  s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
+  s = format (s, "       last heard %.2f\n", sess->last_heard);
+  s = format (s, "       total pkts %d, total bytes %lld\n",
+              sess->total_pkts, sess->total_bytes);
+
+  return s;
+}
+
+u8 * format_snat_user (u8 * s, va_list * args)
+{
+  snat_main_t * sm = va_arg (*args, snat_main_t *);
+  snat_user_t * u = va_arg (*args, snat_user_t *);
+  int verbose = va_arg (*args, int);
+  dlist_elt_t * head, * elt;
+  u32 elt_index, head_index;
+  u32 session_index;
+  snat_session_t * sess;
+
+  s = format (s, "%U: %d translations\n",
+              format_ip4_address, &u->addr, u->nsessions);
+
+  if (verbose == 0)
+    return s;
+
+  head_index = u->sessions_per_user_list_head_index;
+  head = pool_elt_at_index (sm->list_pool, head_index);
+
+  elt_index = head->next;
+  elt = pool_elt_at_index (sm->list_pool, elt_index);
+  session_index = elt->value;
+
+  while (session_index != ~0)
+    {
+      sess = pool_elt_at_index (sm->sessions, session_index);
+
+      s = format (s, "  %U\n", format_snat_session, sm, sess);
+
+      elt_index = elt->next;
+      elt = pool_elt_at_index (sm->list_pool, elt_index);
+      session_index = elt->value;
+    }
+
+  return s;
+}
+
+static clib_error_t *
+show_snat_command_fn (vlib_main_t * vm,
+                unformat_input_t * input,
+                vlib_cli_command_t * cmd)
+{
+  int verbose = 0;
+  snat_main_t * sm = &snat_main;
+  snat_user_t * u;
+
+  if (unformat (input, "detail"))
+    verbose = 1;
+  else if (unformat (input, "verbose"))
+    verbose = 2;
+
+  vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions",
+                   pool_elts (sm->users),
+                   vec_len (sm->addresses),
+                   pool_elts (sm->sessions));
+  
+  if (verbose > 0)
+    {
+      vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
+                       verbose - 1);
+      vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
+                       verbose - 1);
+      vlib_cli_output (vm, "%d list pool elements",
+                       pool_elts (sm->list_pool));
+
+      pool_foreach (u, sm->users,
+      ({
+        vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
+      }));
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_snat_command, static) = {
+    .path = "show snat",
+    .short_help = "show snat",
+    .function = show_snat_command_fn,
+};
diff --git a/plugins/snat-plugin/snat/snat.h b/plugins/snat-plugin/snat/snat.h
new file mode 100644 (file)
index 0000000..e3aa562
--- /dev/null
@@ -0,0 +1,181 @@
+
+/*
+ * snat.h - simple nat definitions
+ *
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_snat_h__
+#define __included_snat_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/icmp46_packet.h>
+#include <vnet/api_errno.h>
+#include <vppinfra/bihash_8_8.h>
+#include <vppinfra/dlist.h>
+#include <vppinfra/error.h>
+#include <vlibapi/api.h>
+
+/* Key */
+typedef struct {
+  union 
+  {
+    struct 
+    {
+      ip4_address_t addr;
+      u16 port;
+      u16 protocol:3,
+        fib_index:13;
+    };
+    u64 as_u64;
+  };
+} snat_session_key_t;
+
+typedef struct {
+  union
+  {
+    struct
+    {
+      ip4_address_t addr;
+      u32 fib_index;
+    };
+    u64 as_u64;
+  };
+} snat_user_key_t;
+
+
+typedef enum {
+  SNAT_PROTOCOL_UDP = 0,
+  SNAT_PROTOCOL_TCP,
+  SNAT_PROTOCOL_ICMP,
+} snat_protocol_t;
+
+
+typedef CLIB_PACKED(struct {
+  snat_session_key_t out2in;    /* 0-15 */
+
+  snat_session_key_t in2out;    /* 16-31 */
+
+  u32 flags;                    /* 32-35 */
+
+  /* per-user translations */
+  u32 per_user_index;           /* 36-39 */
+
+  u32 per_user_list_head_index; /* 40-43 */
+
+  /* Last heard timer */
+  f64 last_heard;               /* 44-51 */
+
+  u64 total_bytes;              /* 52-59 */
+  
+  u32 total_pkts;               /* 60-63 */
+
+  /* Outside address */
+  u32 outside_address_index;    /* 64-67 */
+
+}) snat_session_t;
+
+#define SNAT_SESSION_STATIC (1<<0)
+
+typedef struct {
+  ip4_address_t addr;
+  u32 sessions_per_user_list_head_index;
+  u32 nsessions;
+} snat_user_t;
+
+typedef struct {
+  ip4_address_t addr;
+  u32 busy_ports;
+  uword * busy_port_bitmap;
+} snat_address_t;
+
+typedef struct {
+  /* Main lookup tables */
+  clib_bihash_8_8_t out2in;
+  clib_bihash_8_8_t in2out;
+
+  /* Find-a-user => src address lookup */
+  clib_bihash_8_8_t user_hash;
+
+  /* User pool */
+  snat_user_t * users;
+
+  /* Session pool */
+  snat_session_t * sessions;
+
+  /* Vector of outside addresses */
+  snat_address_t * addresses;
+
+  /* Pool of doubly-linked list elements */
+  dlist_elt_t * list_pool;
+
+  /* Randomize port allocation order */
+  u32 random_seed;
+
+  /* ip4 feature path indices */
+  u32 rx_feature_in2out;
+  u32 rx_feature_out2in;
+
+  /* Config parameters */
+  u32 translation_buckets;
+  u32 translation_memory_size;
+  u32 user_buckets;
+  u32 user_memory_size;
+  u32 max_translations_per_user;
+  u32 outside_vrf_id;
+  u32 outside_fib_index;
+
+  /* API message ID base */
+  u16 msg_id_base;
+
+  /* convenience */
+  vlib_main_t * vlib_main;
+  vnet_main_t * vnet_main;
+  ip4_main_t * ip4_main;
+  ip_lookup_main_t * ip4_lookup_main;
+  ethernet_main_t * ethernet_main;  
+  api_main_t * api_main;
+} snat_main_t;
+
+extern snat_main_t snat_main;
+extern vlib_node_registration_t snat_in2out_node;
+extern vlib_node_registration_t snat_out2in_node;
+
+void snat_free_outside_address_and_port (snat_main_t * sm, 
+                                         snat_session_key_t * k, 
+                                         u32 address_index);
+
+int snat_alloc_outside_address_and_port (snat_main_t * sm, 
+                                         snat_session_key_t * k,
+                                         u32 * address_indexp);
+format_function_t format_snat_user;
+
+typedef struct {
+  u32 cached_sw_if_index;
+  u32 cached_ip4_address;
+} snat_runtime_t;
+
+/* 
+ * Why is this here? Because we don't need to touch this layer to
+ * simply reply to an icmp. We need to change id to a unique
+ * value to NAT an echo request/reply.
+ */
+   
+typedef struct {
+  u16 identifier;
+  u16 sequence;
+} icmp_echo_header_t;
+
+#endif /* __included_snat_h__ */
diff --git a/plugins/snat-plugin/snat/snat_all_api_h.h b/plugins/snat-plugin/snat/snat_all_api_h.h
new file mode 100644 (file)
index 0000000..4901770
--- /dev/null
@@ -0,0 +1,19 @@
+
+/*
+ * snat_all_api_h.h - skeleton vpp engine plug-in api #include file
+ *
+ * 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 the generated file, see BUILT_SOURCES in Makefile.am */
+#include <snat/snat.api.h>
diff --git a/plugins/snat-plugin/snat/snat_msg_enum.h b/plugins/snat-plugin/snat/snat_msg_enum.h
new file mode 100644 (file)
index 0000000..2c76fd5
--- /dev/null
@@ -0,0 +1,31 @@
+
+/*
+ * snat_msg_enum.h - skeleton vpp engine plug-in message enumeration
+ *
+ * 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.
+ */
+#ifndef included_snat_msg_enum_h
+#define included_snat_msg_enum_h
+
+#include <vppinfra/byte_order.h>
+
+#define vl_msg_id(n,h) n,
+typedef enum {
+#include <snat/snat_all_api_h.h>
+    /* We'll want to know how many messages IDs we need... */
+    VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+#endif /* included_snat_msg_enum_h */
diff --git a/plugins/snat-plugin/snat/snat_test.c b/plugins/snat-plugin/snat/snat_test.c
new file mode 100644 (file)
index 0000000..cd2656c
--- /dev/null
@@ -0,0 +1,265 @@
+
+/*
+ * snat.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 <vlibsocket/api.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip.h>
+
+uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <snat/snat_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <snat/snat_all_api_h.h> 
+#undef vl_typedefs
+
+/* declare message handlers for each api */
+
+#define vl_endianfun             /* define message structures */
+#include <snat/snat_all_api_h.h> 
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <snat/snat_all_api_h.h> 
+#undef vl_printfun
+
+/* Get the API version number. */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <snat/snat_all_api_h.h>
+#undef vl_api_version
+
+typedef struct {
+    /* API message ID base */
+    u16 msg_id_base;
+    vat_main_t *vat_main;
+} snat_test_main_t;
+
+snat_test_main_t snat_test_main;
+
+#define foreach_standard_reply_retval_handler   \
+_(snat_add_address_range_reply)                 \
+_(snat_interface_add_del_feature_reply)
+
+#define _(n)                                            \
+    static void vl_api_##n##_t_handler                  \
+    (vl_api_##n##_t * mp)                               \
+    {                                                   \
+        vat_main_t * vam = snat_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                               \
+_(SNAT_ADD_ADDRESS_RANGE_REPLY, snat_add_address_range_reply)   \
+ _(SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY,                        \
+   snat_interface_add_del_feature_reply)
+
+/* 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_snat_add_address_range (vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  unformat_input_t * i = vam->input;
+  f64 timeout;
+  ip4_address_t start_addr, end_addr;
+  u32 start_host_order, end_host_order;
+  vl_api_snat_add_address_range_t * mp;
+  int count;
+
+  if (unformat (i, "%U - %U", 
+                unformat_ip4_address, &start_addr,
+                unformat_ip4_address, &end_addr))
+    ;
+  else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
+    end_addr = start_addr;
+
+  start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
+  end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
+  
+  if (end_host_order < start_host_order)
+    {
+      errmsg ("end address less than start address\n");
+      return -99;
+    }
+
+  count = (end_host_order - start_host_order) + 1;
+
+  if (count > 1024)
+    {
+    errmsg ("%U - %U, %d addresses...\n",
+           format_ip4_address, &start_addr,
+           format_ip4_address, &end_addr,
+           count);
+    }
+  
+  M(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range);
+
+  memcpy (mp->first_ip_address, &start_addr, 4);
+  memcpy (mp->last_ip_address, &end_addr, 4);
+  mp->is_ip4 = 1;
+
+  S; W;
+
+  /* NOTREACHED */
+  return 0;
+}
+
+static int api_snat_interface_add_del_feature (vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  unformat_input_t * i = vam->input;
+  f64 timeout;
+  vl_api_snat_interface_add_del_feature_t * mp;
+  u32 sw_if_index;
+  u8 sw_if_index_set = 0;
+  u8 is_inside = 1; 
+  u8 is_add = 1;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+        sw_if_index_set = 1;
+      else if (unformat (i, "sw_if_index %d", &sw_if_index))
+        sw_if_index_set = 1;
+      else if (unformat (i, "out"))
+        is_inside = 0;
+      else if (unformat (i, "in"))
+        is_inside = 1;
+      else if (unformat (i, "del"))
+        is_add = 0;
+    }
+
+  if (sw_if_index_set == 0)
+    {
+      errmsg ("interface / sw_if_index required\n");
+      return -99;
+    }
+
+  M(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature);
+  mp->sw_if_index = ntohl(sw_if_index);
+  mp->is_add = is_add;
+  mp->is_inside = is_inside;
+  
+  S; W;
+  /* NOTREACHED */
+  return 0;
+}
+
+/* 
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg                             \
+_(snat_add_address_range, "<start-addr> [- <end-addr]") \
+_(snat_interface_add_del_feature,                       \
+  "<intfc> | sw_if_index <id> [in] [out] [del]")
+
+void vat_api_hookup (vat_main_t *vam)
+{
+  snat_test_main_t * sm __attribute__((unused)) = &snat_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)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  u8 * name;
+
+  sm->vat_main = vam;
+
+  /* Ask the vpp engine for the first assigned message-id */
+  name = format (0, "snat_%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;
+}
index 5577c07..74d6d41 100644 (file)
@@ -71,6 +71,12 @@ BUILT_SOURCES += vpp_plugin_configure
 
 .PHONY: vpp_plugin_configure
 
+if WITH_DPDK
+PLUGIN_DPDK_ARG="--with-dpdk"
+else
+PLUGIN_DPDK_ARG=""
+endif
+
 vpp_plugin_configure:
        @echo "PLUGIN CONFIGURE " $@ 
        @echo "#!/bin/bash" > $@
@@ -78,10 +84,10 @@ vpp_plugin_configure:
        @echo "set +eu" >> $@
        @echo " " >> $@
        @echo "if [ -f ./configure ] ; then" >> $@
-       @echo "    CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ./configure --with-plugin-toolkit" >> $@
+       @echo "    CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ./configure --with-plugin-toolkit $(PLUGIN_DPDK_ARG)" >> $@
        @echo "else" >> $@
        @echo "    if [ -f ../configure ] ; then" >> $@
-       @echo "        CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ../configure --with-plugin-toolkit" >> $@
+       @echo "        CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ../configure --with-plugin-toolkit $(PLUGIN_DPDK_ARG)" >> $@
        @echo "    else" >> $@
        @echo "        echo Couldnt find ./configure or ../configure " >> $@
        @echo "        exit 1" >> $@