VPP-339: SNAT static mapping 16/2516/10
authorMatus Fabian <matfabia@cisco.com>
Fri, 26 Aug 2016 12:45:27 +0000 (05:45 -0700)
committerOle Trøan <otroan@employees.org>
Wed, 5 Oct 2016 11:41:51 +0000 (11:41 +0000)
1:1 NAT
1:1 NAT with ports
1:1 NAT static mapping only - add "static mapping only [connection tracking]"
                              to snat startup config

Change-Id: I37cd06a9d71d1943eb6618034d7c547c4a9348c4
Signed-off-by: Matus Fabian <matfabia@cisco.com>
plugins/snat-plugin/snat/in2out.c
plugins/snat-plugin/snat/out2in.c
plugins/snat-plugin/snat/snat.api
plugins/snat-plugin/snat/snat.c
plugins/snat-plugin/snat/snat.h
plugins/snat-plugin/snat/snat_test.c

index 3f7df91..25dad93 100644 (file)
@@ -25,8 +25,6 @@
 #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;
@@ -50,14 +48,29 @@ static u8 * format_snat_in2out_trace (u8 * s, va_list * args)
   return s;
 }
 
+static u8 * format_snat_in2out_fast_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 *);
+
+  s = format (s, "SANT_IN2OUT_FAST: sw_if_index %d, next index %d", 
+              t->sw_if_index, t->next_index);
+
+  return s;
+}
+
 vlib_node_registration_t snat_in2out_node;
+vlib_node_registration_t snat_in2out_slowpath_node;
+vlib_node_registration_t snat_in2out_fast_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")
+_(BAD_ICMP_TYPE, "icmp type not echo-request")          \
+_(NO_TRANSLATION, "No translation")
   
 typedef enum {
 #define _(sym,str) SNAT_IN2OUT_ERROR_##sym,
@@ -98,7 +111,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
   dlist_elt_t * per_user_list_head_elt;
   u32 session_index;
   snat_session_key_t key1;
-  u32 address_index;
+  u32 address_index = ~0;
   u32 outside_fib_index;
   uword * p;
 
@@ -128,9 +141,9 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
         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 */);
     }
@@ -139,30 +152,32 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
       u = pool_elt_at_index (sm->users, value0.value);
     }
 
-  /* Over quota? Recycle the least recently used translation */
+  /* Over quota? Recycle the least recently used dynamic 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 the oldest dynamic translation */
+      do {
+          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);
+      } while (!snat_is_session_static (s));
 
       /* Remove in2out, out2in keys */
       kv0.key = s->in2out.as_u64;
@@ -187,12 +202,18 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
     }
   else
     {
-      if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
-        {
-          ASSERT(0);
+      u8 static_mapping = 1;
 
-          b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
-          return SNAT_IN2OUT_NEXT_DROP;
+      /* First try to match static mapping by local address and port */
+      if (snat_static_mapping_match (sm, *key0, &key1, 0))
+        {
+          static_mapping = 0;
+          /* Try to create dynamic translation */
+          if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+            {
+              b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
+              return SNAT_IN2OUT_NEXT_DROP;
+            }
         }
 
       /* Create a new session */
@@ -201,19 +222,28 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
       
       s->outside_address_index = address_index;
 
+      if (static_mapping)
+        {
+          u->nstaticsessions++;
+          s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+        }
+      else
+        {
+          u->nsessions++;
+        }
+
       /* 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;
@@ -317,13 +347,17 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
                          identifier);
   icmp0->checksum = ip_csum_fold (sum0);
 
-  /* Accounting, per-user LRU list maintenance */
+  /* Accounting */
   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);
+  /* Per-user LRU list maintenance for dynamic translations */
+  if (!snat_is_session_static (s0))
+    {
+      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;
 }
@@ -523,13 +557,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s0))
+            {
+              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) 
@@ -668,13 +706,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp1->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s1))
+            {
+              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) 
@@ -849,13 +891,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s0))
+            {
+              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) 
@@ -951,3 +997,273 @@ VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
 };
 
 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, snat_in2out_slow_path_fn);
+
+static inline u32 icmp_in2out_static_map (snat_main_t *sm,
+                                          vlib_buffer_t * b0,
+                                          ip4_header_t * ip0,
+                                          icmp46_header_t * icmp0,
+                                          u32 sw_if_index0,
+                                          vlib_node_runtime_t * node,
+                                          u32 next0)
+{
+  snat_session_key_t key0, sm0;
+  icmp_echo_header_t *echo0;
+  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->src_address;
+  key0.port = echo0->identifier;
+  
+  if (snat_static_mapping_match(sm, key0, &sm0, 0))
+    {
+      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_IN2OUT_ERROR_NO_TRANSLATION];
+      return SNAT_IN2OUT_NEXT_DROP;
+    }
+
+  new_addr0 = sm0.addr.as_u32;
+  new_id0 = sm0.port;
+  vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+  old_addr0 = ip0->src_address.as_u32;
+  ip0->src_address.as_u32 = new_addr0;
+  
+  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_FALSE(new_id0 != echo0->identifier))
+    {
+      old_id0 = echo0->identifier;
+      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);
+    }
+
+  return next0;
+}
+
+static uword
+snat_in2out_fast_static_map_fn (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                vlib_frame_t * frame)
+{
+  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;
+  u32 stats_node_index;
+
+  stats_node_index = snat_in2out_fast_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 > 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, sm0;
+          u32 proto0;
+
+          /* 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];
+
+          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))
+            {
+              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 = icmp_in2out_static_map
+                (sm, b0, ip0, icmp0, sw_if_index0, node, next0);
+              goto trace0;
+            }
+
+          key0.addr = ip0->src_address;
+          key0.port = udp0->src_port;
+
+          if (snat_static_mapping_match(sm, key0, &sm0, 0))
+            {
+              b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
+              next0= SNAT_IN2OUT_NEXT_DROP;
+              goto trace0;
+            }
+
+          new_addr0 = sm0.addr.as_u32;
+          new_port0 = sm0.port;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+          old_addr0 = ip0->src_address.as_u32;
+          ip0->src_address.as_u32 = new_addr0;
+
+          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_FALSE(new_port0 != udp0->dst_port))
+            {
+              if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+                {
+                  old_port0 = tcp0->ports.src;
+                  tcp0->ports.src = new_port0;
+
+                  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 = new_port0;
+                  udp0->checksum = 0;
+                }
+            }
+          else
+            {
+              if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+                {
+                  sum0 = tcp0->checksum;
+                  sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                         ip4_header_t,
+                                         dst_address /* changed member */);
+                  tcp0->checksum = ip_csum_fold(sum0);
+                }
+            }
+
+        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->sw_if_index = sw_if_index0;
+              t->next_index = next0;
+            }
+
+          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;
+}
+
+
+VLIB_REGISTER_NODE (snat_in2out_fast_node) = {
+  .function = snat_in2out_fast_static_map_fn,
+  .name = "snat-in2out-fast",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_in2out_fast_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_fast_node, snat_in2out_fast_static_map_fn);
index 861dae6..b3497c4 100644 (file)
@@ -25,8 +25,6 @@
 #include <vppinfra/error.h>
 #include <vppinfra/elog.h>
 
-vlib_node_registration_t snat_out2in_node;
-
 typedef struct {
   u32 sw_if_index;
   u32 next_index;
@@ -45,7 +43,20 @@ static u8 * format_snat_out2in_trace (u8 * s, va_list * args)
   return s;
 }
 
+static u8 * format_snat_out2in_fast_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_FAST: sw_if_index %d, next index %d",
+              t->sw_if_index, t->next_index);
+  return s;
+}
+
+
 vlib_node_registration_t snat_out2in_node;
+vlib_node_registration_t snat_out2in_fast_node;
 
 #define foreach_snat_out2in_error                       \
 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol")         \
@@ -71,6 +82,101 @@ typedef enum {
   SNAT_OUT2IN_N_NEXT,
 } snat_out2in_next_t;
 
+/**
+ * @brief Create session for static mapping.
+ *
+ * Create NAT session initiated by host from external network with static
+ * mapping.
+ *
+ * @param sm     SNAT main.
+ * @param b0     Vlib buffer.
+ * @param in2out In2out SNAT session key.
+ * @param out2in Out2in SNAT session key.
+ * @param node   Vlib node.
+ *
+ * @returns SNAT session if successfully created otherwise 0.
+ */
+static inline snat_session_t *
+create_session_for_static_mapping (snat_main_t *sm,
+                                   vlib_buffer_t *b0,
+                                   snat_session_key_t in2out,
+                                   snat_session_key_t out2in,
+                                   vlib_node_runtime_t * node)
+{
+  snat_user_t *u;
+  snat_user_key_t user_key;
+  snat_session_t *s;
+  clib_bihash_kv_8_8_t kv0, value0;
+  dlist_elt_t * per_user_translation_list_elt;
+  dlist_elt_t * per_user_list_head_elt;
+
+  user_key.addr = in2out.addr;
+  user_key.fib_index = in2out.fib_index;
+  kv0.key = user_key.as_u64;
+
+  /* Ever heard of the "user" = inside 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 = in2out.addr;
+
+      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);
+    }
+
+  pool_get (sm->sessions, s);
+  memset (s, 0, sizeof (*s));
+
+  s->outside_address_index = ~0;
+  s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+  u->nstaticsessions++;
+
+  /* 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);
+
+  s->in2out = in2out;
+  s->out2in = out2in;
+  s->in2out.protocol = out2in.protocol;
+
+  /* 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 s;
+}
+
 static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
                                          vlib_buffer_t * b0,
                                          ip4_header_t * ip0,
@@ -80,7 +186,7 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
                                          vlib_node_runtime_t * node,
                                          u32 next0, f64 now)
 {
-  snat_session_key_t key0;
+  snat_session_key_t key0, sm0;
   icmp_echo_header_t *echo0;
   clib_bihash_kv_8_8_t kv0, value0;
   snat_session_t * s0;
@@ -100,24 +206,35 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
   
   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))
+      /* Try to match static mapping by external address and port,
+         destination address and port in packet */
+      if (snat_static_mapping_match(sm, key0, &sm0, 1))
         {
-          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;
+           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;
         }
-      
-      /* 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;
+      /* Create session initiated by host from external network */
+      s0 = create_session_for_static_mapping(sm, b0, sm0, key0,
+                                             node);
+      if (!s0)
+        return SNAT_OUT2IN_NEXT_DROP;
     }
   else
     s0 = pool_elt_at_index (sm->sessions, value0.value);
@@ -142,13 +259,17 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
                          identifier);
   icmp0->checksum = ip_csum_fold (sum0);
 
-  /* Accounting, per-user LRU list maintenance */
+  /* Accounting */
   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);
+  /* Per-user LRU list maintenance for dynamic translation */
+  if (!snat_is_session_static (s0))
+    {
+      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;
 }
@@ -193,7 +314,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
           udp_header_t * udp0, * udp1;
           tcp_header_t * tcp0, * tcp1;
           icmp46_header_t * icmp0, * icmp1;
-          snat_session_key_t key0, key1;
+          snat_session_key_t key0, key1, sm0, sm1;
           u32 rx_fib_index0, rx_fib_index1;
           u32 proto0, proto1;
           snat_session_t * s0 = 0, * s1 = 0;
@@ -264,14 +385,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
           kv0.key = key0.as_u64;
 
           if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
-            goto trace0;
+            {
+              /* Try to match static mapping by external address and port,
+                 destination address and port in packet */
+              if (snat_static_mapping_match(sm, key0, &sm0, 1))
+                {
+                  b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+                  goto trace0;
+                }
+
+              /* Create session initiated by host from external network */
+              s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node);
+              if (!s0)
+                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;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
 
           sum0 = ip0->checksum;
           sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
@@ -302,13 +436,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s0))
+            {
+              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) 
@@ -366,14 +504,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
           kv1.key = key1.as_u64;
 
           if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1))
-            goto trace1;
+            {
+              /* Try to match static mapping by external address and port,
+                 destination address and port in packet */
+              if (snat_static_mapping_match(sm, key1, &sm1, 1))
+                {
+                  b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+                  goto trace1;
+                }
+
+              /* Create session initiated by host from external network */
+              s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node);
+              if (!s1)
+                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;
+          vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index;
 
           sum1 = ip1->checksum;
           sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
@@ -404,13 +555,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
               udp1->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s1))
+            {
+              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) 
@@ -447,7 +602,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
           udp_header_t * udp0;
           tcp_header_t * tcp0;
           icmp46_header_t * icmp0;
-          snat_session_key_t key0;
+          snat_session_key_t key0, sm0;
           u32 rx_fib_index0;
           u32 proto0;
           snat_session_t * s0 = 0;
@@ -503,14 +658,27 @@ snat_out2in_node_fn (vlib_main_t * vm,
           kv0.key = key0.as_u64;
 
           if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
-            goto trace00;
+            {
+              /* Try to match static mapping by external address and port,
+                 destination address and port in packet */
+              if (snat_static_mapping_match(sm, key0, &sm0, 1))
+                {
+                  b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+                  goto trace00;
+                }
+
+              /* Create session initiated by host from external network */
+              s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node);
+              if (!s0)
+                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;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
 
           sum0 = ip0->checksum;
           sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
@@ -541,13 +709,17 @@ snat_out2in_node_fn (vlib_main_t * vm,
               udp0->checksum = 0;
             }
 
-          /* Accounting, per-user LRU list maintenance */
+          /* Accounting */
           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);
+          /* Per-user LRU list maintenance for dynamic translation */
+          if (!snat_is_session_static (s0))
+            {
+              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) 
@@ -599,3 +771,257 @@ VLIB_REGISTER_NODE (snat_out2in_node) = {
   },
 };
 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
+
+static inline u32 icmp_out2in_fast (snat_main_t *sm,
+                                    vlib_buffer_t * b0,
+                                    ip4_header_t * ip0,
+                                    icmp46_header_t * icmp0,
+                                    u32 sw_if_index0,
+                                    vlib_node_runtime_t * node,
+                                    u32 next0)
+{
+  snat_session_key_t key0, sm0;
+  icmp_echo_header_t *echo0;
+  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;
+
+  if (snat_static_mapping_match(sm, key0, &sm0, 1))
+    {
+      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;
+    }
+
+  new_addr0 = sm0.addr.as_u32;
+  new_id0 = sm0.port;
+  vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+
+  old_addr0 = ip0->dst_address.as_u32;
+  ip0->dst_address.as_u32 = new_addr0;
+
+  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_FALSE(new_id0 != echo0->identifier))
+    {
+      old_id0 = echo0->identifier;
+      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);
+    }
+
+  return next0;
+}
+
+static uword
+snat_out2in_fast_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->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
+
+  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 > 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, sm0;
+          u32 proto0;
+
+          /* 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];
+
+         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_fast
+                (sm, b0, ip0, icmp0, sw_if_index0, node, next0);
+              goto trace00;
+            }
+
+          key0.addr = ip0->dst_address;
+          key0.port = udp0->dst_port;
+
+          if (snat_static_mapping_match(sm, key0, &sm0, 1))
+            {
+              b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+              goto trace00;
+            }
+
+          new_addr0 = sm0.addr.as_u32;
+          new_port0 = sm0.port;
+          vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
+          old_addr0 = ip0->dst_address.as_u32;
+          ip0->dst_address.as_u32 = new_addr0;
+
+          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_FALSE(new_port0 != udp0->dst_port))
+            {
+               if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+                {
+                  old_port0 = tcp0->ports.dst;
+                  tcp0->ports.dst = new_port0;
+
+                  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 = new_port0;
+                  udp0->checksum = 0;
+                }
+            }
+          else
+            {
+              if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
+                {
+                  sum0 = tcp0->checksum;
+                  sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
+                                         ip4_header_t,
+                                         dst_address /* changed member */);
+
+                  tcp0->checksum = ip_csum_fold(sum0);
+                }
+            }
+
+        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;
+            }
+
+          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_fast_node.index,
+                               SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
+                               pkts_processed);
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (snat_out2in_fast_node) = {
+  .function = snat_out2in_fast_node_fn,
+  .name = "snat-out2in-fast",
+  .vector_size = sizeof (u32),
+  .format_trace = format_snat_out2in_fast_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_fast_node, snat_out2in_fast_node_fn);
index 0c5f437..daacf9f 100644 (file)
@@ -1,3 +1,32 @@
+/*
+ * 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.
+ */
+/**
+ * @file snat.api
+ * @brief VPP control-plane API messages.
+ *
+ * This file defines VPP control-plane API messages which are generally
+ * called through a shared memory interface.
+ */
+
+/** \brief Add S-NAT address range
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_ip4 - 1 if address type is IPv4
+    @first_ip_address - first IP address
+    @last_ip_address - last IP address
+*/
 define snat_add_address_range {
   u32 client_index;
   u32 context;
@@ -6,11 +35,23 @@ define snat_add_address_range {
   u8 last_ip_address[16];
 };
 
+/** \brief Add S-NAT address range reply
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+*/
 define snat_add_address_range_reply {
   u32 context;
   i32 retval;
 };
 
+/** \brief Enable/disable S-NAT feature on the interface
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - 1 if add, 0 if delete
+    @param is_inside - 1 if inside, 0 if outside
+    @param sw_if_index - software index of the interface
+*/
 define snat_interface_add_del_feature {
   u32 client_index;
   u32 context;
@@ -19,7 +60,139 @@ define snat_interface_add_del_feature {
   u32 sw_if_index;
 };
 
+/** \brief Enable/disable S-NAT feature on the interface reply
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+*/
 define snat_interface_add_del_feature_reply {
   u32 context;
   i32 retval;
 };
+
+/** \brief Add/delete S-NAT static mapping
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - 1 if add, 0 if delete
+    @param is_ip4 - 1 if address type is IPv4
+    @param addr_only - 1 if address only mapping
+    @param local_ip_address - local IP address
+    @param external_ip_address - external IP address
+    @param local_port - local port number
+    @param external_port - external port number
+    @param vfr_id - VRF ID
+*/
+define snat_add_static_mapping {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 is_ip4;
+  u8 addr_only;
+  u8 local_ip_address[16];
+  u8 external_ip_address[16];
+  u16 local_port;
+  u16 external_port;
+  u32 vrf_id;
+};
+
+/** \brief Add/delete S-NAT static mapping reply
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+*/
+define snat_add_static_mapping_reply {
+  u32 context;
+  i32 retval;
+};
+
+/** \brief Dump S-NAT static mappings
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_static_mapping_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief S-NAT static mapping details response
+    @param context - sender context, to match reply w/ request
+    @param is_ip4 - 1 if address type is IPv4
+    @param addr_only - 1 if address only mapping
+    @param local_ip_address - local IP address
+    @param external_ip_address - external IP address
+    @param local_port - local port number
+    @param external_port - external port number
+    @param vfr_id - VRF ID
+*/
+define snat_static_mapping_details {
+  u32 context;
+  u8 is_ip4;
+  u8 addr_only;
+  u8 local_ip_address[16];
+  u8 external_ip_address[16];
+  u16 local_port;
+  u16 external_port;
+  u32 vrf_id;
+};
+
+/** \brief Control ping from client to api server request
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_control_ping
+{
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Control ping from the client to the server response
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return code for the request
+    @param vpe_pid - the pid of the vpe, returned by the server
+*/
+define snat_control_ping_reply
+{
+  u32 context;
+  i32 retval;
+  u32 client_index;
+  u32 vpe_pid;
+};
+
+/** \brief Show S-NAT plugin startup config
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_show_config
+{
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Show S-NAT plugin startup config reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code for the request
+    @param static_mapping_only - if 1 dynamic translations disabled
+    @param static_mapping_connection_tracking - if 1 create session data
+    @param translation_buckets - number of translation hash buckets
+    @param translation_memory_size - translation hash memory size
+    @param user_buckets - number of user hash buckets
+    @param user_memory_size - user hash memory size
+    @param max_translations_per_user - maximum number of translations per user
+    @param outside_vrf_id - outside VRF id
+    @param inside_vrf_id - default inside VRF id
+*/
+define snat_show_config_reply
+{
+  u32 context;
+  i32 retval;
+  u8 static_mapping_only;
+  u8 static_mapping_connection_tracking;
+  u32 translation_buckets;
+  u32 translation_memory_size;
+  u32 user_buckets;
+  u32 user_memory_size;
+  u32 max_translations_per_user;
+  u32 outside_vrf_id;
+  u32 inside_vrf_id;
+};
index f3e278b..3fd9ecb 100644 (file)
@@ -76,6 +76,21 @@ do {                                                            \
     vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
 } while(0);
 
+#define REPLY_MACRO2(t, body)                                   \
+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);                                    \
+    do {body;} while (0);                                       \
+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
+} while(0);
+
 
 /* Hook up input features */
 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out, static) = {
@@ -88,6 +103,17 @@ VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in, static) = {
   .runs_before = (char *[]){"ip4-lookup", 0},
   .feature_index = &snat_main.rx_feature_out2in,
 };
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
+  .node_name = "snat-in2out-fast",
+  .runs_before = (char *[]){"snat-out2in-fast", 0},
+  .feature_index = &snat_main.rx_feature_in2out_fast,
+};
+VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
+  .node_name = "snat-out2in-fast",
+  .runs_before = (char *[]){"ip4-lookup", 0},
+  .feature_index = &snat_main.rx_feature_out2in_fast,
+};
+
 
 /* 
  * This routine exists to convince the vlib plugin framework that
@@ -189,6 +215,240 @@ static void increment_v4_address (ip4_address_t * a)
   a->as_u32 = clib_host_to_net_u32(v);
 }
 
+/**
+ * @brief Add static mapping.
+ *
+ * Create static mapping between local addr+port and external addr+port.
+ *
+ * @param l_addr Local IPv4 address.
+ * @param e_addr External IPv4 address.
+ * @param l_port Local port number.
+ * @param e_port External port number.
+ * @param vrf_id VRF ID.
+ * @param addr_only If 0 address port and pair mapping, otherwise address only.
+ * @param is_add If 0 delete static mapping, otherwise add.
+ *
+ * @returns
+ */
+int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
+                            u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
+                            int is_add)
+{
+  snat_main_t * sm = &snat_main;
+  snat_static_mapping_t *m;
+  snat_static_mapping_key_t m_key;
+  clib_bihash_kv_8_8_t kv, value;
+  snat_address_t *a = 0;
+  u32 fib_index = ~0;
+  uword * p;
+  int i;
+
+  m_key.addr = e_addr;
+  m_key.port = addr_only ? 0 : e_port;
+  m_key.pad = 0;
+  kv.key = m_key.as_u64;
+  if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+    m = 0;
+  else
+    m = pool_elt_at_index (sm->static_mappings, value.value);
+
+  if (is_add)
+    {
+      if (m)
+        return VNET_API_ERROR_VALUE_EXIST;
+
+      /* Convert VRF id to FIB index */
+      if (vrf_id != ~0)
+        {
+          p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
+          if (!p)
+            return VNET_API_ERROR_NO_SUCH_FIB;
+          fib_index = p[0];
+        }
+      /* If not specified use inside VRF id from SNAT plugin startup config */
+      else
+        {
+          if (sm->inside_fib_index == ~0)
+            {
+              p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->inside_vrf_id);
+              if (!p)
+                return VNET_API_ERROR_NO_SUCH_FIB;
+              fib_index = p[0];
+              sm->inside_fib_index = fib_index;
+            }
+          else
+            fib_index = sm->inside_fib_index;
+
+          vrf_id = sm->inside_vrf_id;
+        }
+
+      /* If outside FIB index is not resolved yet */
+      if (sm->outside_fib_index == ~0)
+        {
+          p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
+          if (!p)
+            return VNET_API_ERROR_NO_SUCH_FIB;
+          sm->outside_fib_index = p[0];
+        }
+
+      /* Find external address in allocated addresses and reserve port for
+         address and port pair mapping when dynamic translations enabled */
+      if (!addr_only && !(sm->static_mapping_only))
+        {
+          for (i = 0; i < vec_len (sm->addresses); i++)
+            {
+              if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
+                {
+                  a = sm->addresses + i;
+                  /* External port must be unused */
+                  if (clib_bitmap_get (a->busy_port_bitmap, e_port))
+                    return VNET_API_ERROR_INVALID_VALUE;
+                  a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
+                                                         e_port, 1);
+                  if (e_port > 1024)
+                    a->busy_ports++;
+
+                  break;
+                }
+            }
+          /* External address must be allocated */
+          if (!a)
+            return VNET_API_ERROR_NO_SUCH_ENTRY;
+        }
+
+      pool_get (sm->static_mappings, m);
+      memset (m, 0, sizeof (*m));
+      m->local_addr = l_addr;
+      m->external_addr = e_addr;
+      m->addr_only = addr_only;
+      m->vrf_id = vrf_id;
+      m->fib_index = fib_index;
+      if (!addr_only)
+        {
+          m->local_port = l_port;
+          m->external_port = e_port;
+        }
+
+      m_key.addr = m->local_addr;
+      m_key.port = m->local_port;
+      m_key.pad = 0;
+      kv.key = m_key.as_u64;
+      kv.value = m - sm->static_mappings;
+      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+
+      m_key.addr = m->external_addr;
+      m_key.port = m->external_port;
+      kv.key = m_key.as_u64;
+      kv.value = m - sm->static_mappings;
+      clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
+    }
+  else
+    {
+      if (!m)
+        return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      /* Free external address port */
+      if (!addr_only && !(sm->static_mapping_only))
+        {
+          for (i = 0; i < vec_len (sm->addresses); i++)
+            {
+              if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
+                {
+                  a = sm->addresses + i;
+                  a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
+                                                         e_port, 0);
+                  a->busy_ports--;
+
+                  break;
+                }
+            }
+        }
+
+      m_key.addr = m->local_addr;
+      m_key.port = m->local_port;
+      m_key.pad = 0;
+      kv.key = m_key.as_u64;
+      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
+
+      m_key.addr = m->external_addr;
+      m_key.port = m->external_port;
+      kv.key = m_key.as_u64;
+      clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
+
+      /* Delete session(s) for static mapping if exist */
+      if (!(sm->static_mapping_only) ||
+          (sm->static_mapping_only && sm->static_mapping_connection_tracking))
+        {
+          snat_user_key_t u_key;
+          snat_user_t *u;
+          dlist_elt_t * head, * elt;
+          u32 elt_index, head_index;
+          u32 ses_index;
+          snat_session_t * s;
+
+          u_key.addr = m->local_addr;
+          u_key.fib_index = m->fib_index;
+          kv.key = u_key.as_u64;
+          if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
+            {
+              u = pool_elt_at_index (sm->users, value.value);
+              if (u->nstaticsessions)
+                {
+                  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);
+                  ses_index = elt->value;
+                  while (ses_index != ~0)
+                    {
+                      s =  pool_elt_at_index (sm->sessions, ses_index);
+
+                      if (!addr_only)
+                        {
+                          if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
+                              (clib_net_to_host_u16 (s->out2in.port) != e_port))
+                            continue;
+                        }
+                      value.key = s->in2out.as_u64;
+                      clib_bihash_add_del_8_8 (&sm->in2out, &value, 0);
+                      value.key = s->out2in.as_u64;
+                      clib_bihash_add_del_8_8 (&sm->out2in, &value, 0);
+                      pool_put (sm->sessions, s);
+
+                      if (!addr_only)
+                        break;
+
+                      elt_index = elt->next;
+                      elt = pool_elt_at_index (sm->list_pool, elt_index);
+                      ses_index = elt->value;
+                    }
+                  if (addr_only)
+                    {
+                      while ((elt_index = clib_dlist_remove_head(sm->list_pool, head_index)) != ~0)
+                        pool_put_index (sm->list_pool, elt_index);
+                      pool_put (sm->users, u);
+                      clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0);
+                    }
+                  else
+                    {
+                      if (ses_index != ~0)
+                        {
+                          clib_dlist_remove (sm->list_pool, elt_index);
+                          pool_put (sm->list_pool, elt);
+                          u->nstaticsessions--;
+                        }
+                    }
+                }
+            }
+        }
+
+      /* Delete static mapping from pool */
+      pool_put (sm->static_mappings, m);
+    }
+
+  return 0;
+}
+
 static void 
 vl_api_snat_add_address_range_t_handler
 (vl_api_snat_add_address_range_t * mp)
@@ -207,6 +467,12 @@ vl_api_snat_add_address_range_t_handler
       goto send_reply;
     }
 
+  if (sm->static_mapping_only)
+    {
+      rv = VNET_API_ERROR_FEATURE_DISABLED;
+      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;
@@ -263,8 +529,12 @@ vl_api_snat_interface_add_del_feature_t_handler
 
   VALIDATE_SW_IF_INDEX(mp);
 
-  feature_index = mp->is_inside ? sm->rx_feature_in2out
-    : sm->rx_feature_out2in;
+  if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+    feature_index = mp->is_inside ?  sm->rx_feature_in2out_fast
+      : sm->rx_feature_out2in_fast;
+  else
+    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
@@ -296,10 +566,176 @@ static void *vl_api_snat_interface_add_del_feature_t_print
   FINISH;
 }
 
+static void
+vl_api_snat_add_static_mapping_t_handler
+(vl_api_snat_add_static_mapping_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_add_static_mapping_reply_t * rmp;
+  ip4_address_t local_addr, external_addr;
+  u16 local_port = 0, external_port = 0;
+  u32 vrf_id;
+  int rv = 0;
+
+  if (mp->is_ip4 != 1)
+    {
+      rv = VNET_API_ERROR_UNIMPLEMENTED;
+      goto send_reply;
+    }
+
+  memcpy (&local_addr.as_u8, mp->local_ip_address, 4);
+  memcpy (&external_addr.as_u8, mp->external_ip_address, 4);
+  if (mp->addr_only == 0)
+    {
+      local_port = clib_net_to_host_u16 (mp->local_port);
+      external_port = clib_net_to_host_u16 (mp->external_port);
+    }
+  vrf_id = clib_net_to_host_u32 (mp->vrf_id);
+
+  rv = snat_add_static_mapping(local_addr, external_addr, local_port,
+                               external_port, vrf_id, mp->addr_only,
+                               mp->is_add);
+
+ send_reply:
+  REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
+}
+
+static void *vl_api_snat_add_static_mapping_t_print
+(vl_api_snat_add_static_mapping_t *mp, void * handle)
+{
+  u8 * s;
+
+  s = format (0, "SCRIPT: snat_add_static_mapping ");
+  s = format (s, "local_addr %U external_addr %U ",
+              format_ip4_address, mp->local_ip_address,
+              format_ip4_address, mp->external_ip_address);
+
+  if (mp->addr_only == 0)
+    s = format (s, "local_port %d external_port %d ",
+                clib_net_to_host_u16 (mp->local_port),
+                clib_net_to_host_u16 (mp->external_port));
+
+  if (mp->vrf_id != ~0)
+    s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
+
+  FINISH;
+}
+
+static void
+send_snat_static_mapping_details
+(snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_snat_static_mapping_details_t *rmp;
+  snat_main_t * sm = &snat_main;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS+sm->msg_id_base);
+  rmp->is_ip4 = 1;
+  rmp->addr_only = m->addr_only;
+  clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4);
+  clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4);
+  rmp->local_port = htons (m->local_port);
+  rmp->external_port = htons (m->external_port);
+  rmp->vrf_id = htonl (m->vrf_id);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_snat_static_mapping_dump_t_handler
+(vl_api_snat_static_mapping_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  snat_main_t * sm = &snat_main;
+  snat_static_mapping_t * m;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  pool_foreach (m, sm->static_mappings,
+  ({
+      send_snat_static_mapping_details (m, q, mp->context);
+  }));
+}
+
+static void *vl_api_snat_static_mapping_dump_t_print
+(vl_api_snat_static_mapping_dump_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_static_mapping_dump ");
+
+  FINISH;
+}
+
+static void
+vl_api_snat_control_ping_t_handler
+(vl_api_snat_control_ping_t * mp)
+{
+  vl_api_snat_control_ping_reply_t *rmp;
+  snat_main_t * sm = &snat_main;
+  int rv = 0;
+
+  REPLY_MACRO2(VL_API_SNAT_CONTROL_PING_REPLY,
+  ({
+    rmp->vpe_pid = ntohl (getpid());
+  }));
+}
+
+static void *vl_api_snat_control_ping_t_print
+(vl_api_snat_control_ping_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_control_ping ");
+
+  FINISH;
+}
+
+static void
+vl_api_snat_show_config_t_handler
+(vl_api_snat_show_config_t * mp)
+{
+  vl_api_snat_show_config_reply_t *rmp;
+  snat_main_t * sm = &snat_main;
+  int rv = 0;
+
+  REPLY_MACRO2(VL_API_SNAT_SHOW_CONFIG_REPLY,
+  ({
+    rmp->translation_buckets = htons (sm->translation_buckets);
+    rmp->translation_memory_size = htons (sm->translation_memory_size);
+    rmp->user_buckets = htons (sm->user_buckets);
+    rmp->user_memory_size = htons (sm->user_memory_size);
+    rmp->max_translations_per_user = htons (sm->max_translations_per_user);
+    rmp->outside_vrf_id = htons (sm->outside_vrf_id);
+    rmp->inside_vrf_id = htons (sm->inside_vrf_id);
+    rmp->static_mapping_only = sm->static_mapping_only;
+    rmp->static_mapping_connection_tracking =
+      sm->static_mapping_connection_tracking;
+  }));
+}
+
+static void *vl_api_snat_show_config_t_print
+(vl_api_snat_show_config_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_show_config ");
+
+  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)
+_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)       \
+_(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping)                     \
+_(SNAT_CONTROL_PING, snat_control_ping)                                 \
+_(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump)                   \
+_(SNAT_SHOW_CONFIG, snat_show_config)
 
 /* Set up the API message handling tables */
 static clib_error_t *
@@ -376,6 +812,67 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
   a->busy_ports--;
 }  
 
+/**
+ * @brief Match SNAT static mapping.
+ *
+ * @param sm          SNAT main.
+ * @param match       Address and port to match.
+ * @param mapping     External or local address and port of the matched mapping.
+ * @param by_external If 0 match by local address otherwise match by external
+ *                    address.
+ *
+ * @returns 0 if match found otherwise 1.
+ */
+int snat_static_mapping_match (snat_main_t * sm,
+                               snat_session_key_t match,
+                               snat_session_key_t * mapping,
+                               u8 by_external)
+{
+  clib_bihash_kv_8_8_t kv, value;
+  snat_static_mapping_t *m;
+  snat_static_mapping_key_t m_key;
+  clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
+
+  if (by_external)
+    mapping_hash = &sm->static_mapping_by_external;
+
+  m_key.addr = match.addr;
+  m_key.port = clib_net_to_host_u16 (match.port);
+  m_key.pad = 0;
+
+  kv.key = m_key.as_u64;
+
+  if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+    {
+      /* Try address only mapping */
+      m_key.port = 0;
+      kv.key = m_key.as_u64;
+      if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+        return 1;
+    }
+
+  m = pool_elt_at_index (sm->static_mappings, value.value);
+
+  if (by_external)
+    {
+      mapping->addr = m->local_addr;
+      /* Address only mapping doesn't change port */
+      mapping->port = m->addr_only ? match.port
+        : clib_host_to_net_u16 (m->local_port);
+      mapping->fib_index = m->fib_index;
+    }
+  else
+    {
+      mapping->addr = m->external_addr;
+      /* Address only mapping doesn't change port */
+      mapping->port = m->addr_only ? match.port
+        : clib_host_to_net_u16 (m->external_port);
+      mapping->fib_index = sm->outside_fib_index;
+    }
+
+  return 0;
+}
+
 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
                                          snat_session_key_t * k,
                                          u32 * address_indexp)
@@ -419,17 +916,29 @@ add_address_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
 {
+  unformat_input_t _line_input, *line_input = &_line_input;
   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", 
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  if (unformat (line_input, "%U - %U",
                 unformat_ip4_address, &start_addr,
                 unformat_ip4_address, &end_addr))
     ;
-  else if (unformat (input, "%U", unformat_ip4_address, &start_addr))
+  else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
     end_addr = start_addr;
+  else
+    return clib_error_return (0, "unknown input '%U'", format_unformat_error,
+      input);
+  unformat_free (line_input);
+
+  if (sm->static_mapping_only)
+    return clib_error_return (0, "static mapping only mode");
 
   start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
   end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
@@ -467,6 +976,7 @@ snat_feature_command_fn (vlib_main_t * vm,
                           unformat_input_t * input,
                           vlib_cli_command_t * cmd)
 {
+  unformat_input_t _line_input, *line_input = &_line_input;
   vnet_main_t * vnm = vnet_get_main();
   snat_main_t * sm = &snat_main;
   ip4_main_t * im = &ip4_main;
@@ -482,23 +992,32 @@ snat_feature_command_fn (vlib_main_t * vm,
 
   sw_if_index = ~0;
 
-  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (input, "in %U", unformat_vnet_sw_interface, 
+      if (unformat (line_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, 
+      else if (unformat (line_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"))
+      else if (unformat (line_input, "del"))
         is_del = 1;
       else
-        break;
+        return clib_error_return (0, "unknown input '%U'",
+          format_unformat_error, input);
     }
+  unformat_free (line_input);
 
   if (vec_len (inside_sw_if_indices))
     {
-      feature_index = sm->rx_feature_in2out;
+      if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+        feature_index = sm->rx_feature_in2out_fast;
+      else
+        feature_index = sm->rx_feature_in2out;
 
       for (i = 0; i < vec_len(inside_sw_if_indices); i++)
         {
@@ -518,7 +1037,10 @@ snat_feature_command_fn (vlib_main_t * vm,
 
   if (vec_len (outside_sw_if_indices))
     {
-      feature_index = sm->rx_feature_out2in;
+      if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+        feature_index = sm->rx_feature_out2in_fast;
+      else
+        feature_index = sm->rx_feature_out2in;
 
       for (i = 0; i < vec_len(outside_sw_if_indices); i++)
         {
@@ -548,6 +1070,92 @@ VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
   .short_help = "set interface snat in <intfc> out <intfc> [del]",
 };
 
+static clib_error_t *
+add_static_mapping_command_fn (vlib_main_t * vm,
+                               unformat_input_t * input,
+                               vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t * error = 0;
+  ip4_address_t l_addr, e_addr;
+  u32 l_port = 0, e_port = 0, vrf_id = ~0;
+  int is_add = 1;
+  int addr_only = 1;
+  int rv;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
+                    &l_port))
+        addr_only = 0;
+      else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr))
+        ;
+      else if (unformat (line_input, "external %U %u", unformat_ip4_address,
+                         &e_addr, &e_port))
+        addr_only = 0;
+      else if (unformat (line_input, "external %U", unformat_ip4_address,
+                         &e_addr))
+        ;
+      else if (unformat (line_input, "vrf %u", &vrf_id))
+        ;
+      else if (unformat (line_input, "del"))
+        is_add = 0;
+      else
+        return clib_error_return (0, "unknown input: '%U'",
+          format_unformat_error, line_input);
+    }
+  unformat_free (line_input);
+
+  rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
+                               vrf_id, addr_only, is_add);
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_INVALID_VALUE:
+      return clib_error_return (0, "External port already in use.");
+      break;
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      if (is_add)
+        return clib_error_return (0, "External addres must be allocated.");
+      else
+        return clib_error_return (0, "Mapping not exist.");
+      break;
+    case VNET_API_ERROR_NO_SUCH_FIB:
+      return clib_error_return (0, "No such VRF id.");
+    case VNET_API_ERROR_VALUE_EXIST:
+      return clib_error_return (0, "Mapping already exist.");
+    default:
+      break;
+    }
+
+  return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{snat add static mapping}
+ * Static mapping allows hosts on the external network to initiate connection
+ * to to the local network host.
+ * To create static mapping between local host address 10.0.0.3 port 6303 and
+ * external address 4.4.4.4 port 3606 use:
+ *  vpp# snat add static mapping local 10.0.0.3 6303 external 4.4.4.4 3606
+ * If not runnig "static mapping only" S-NAT plugin mode use before:
+ *  vpp# snat add address 4.4.4.4
+ * To create static mapping between local and external address use:
+ *  vpp# snat add static mapping local 10.0.0.3 external 4.4.4.4
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
+  .path = "snat add static mapping",
+  .function = add_static_mapping_command_fn,
+  .short_help =
+    "snat add static mapping local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
+};
+
 static clib_error_t *
 snat_config (vlib_main_t * vm, unformat_input_t * input)
 {
@@ -558,6 +1166,11 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   u32 user_memory_size = 64<<20;
   u32 max_translations_per_user = 100;
   u32 outside_vrf_id = 0;
+  u32 inside_vrf_id = 0;
+  u32 static_mapping_buckets = 1024;
+  u32 static_mapping_memory_size = 64<<20;
+  u8 static_mapping_only = 0;
+  u8 static_mapping_connection_tracking = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
@@ -576,8 +1189,17 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
       else if (unformat (input, "outside VRF id %d",
                          &outside_vrf_id))
         ;
+      else if (unformat (input, "inside VRF id %d",
+                         &inside_vrf_id))
+        ;
+      else if (unformat (input, "static mapping only"))
+        {
+          static_mapping_only = 1;
+          if (unformat (input, "connection tracking"))
+            static_mapping_connection_tracking = 1;
+        }
       else 
-       return clib_error_return (0, "unknown input `%U'",
+       return clib_error_return (0, "unknown input '%U'",
                                  format_unformat_error, input);
     }
 
@@ -588,15 +1210,31 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   sm->user_memory_size = user_memory_size;
   sm->max_translations_per_user = max_translations_per_user;
   sm->outside_vrf_id = outside_vrf_id;
+  sm->outside_fib_index = ~0;
+  sm->inside_vrf_id = inside_vrf_id;
+  sm->inside_fib_index = ~0;
+  sm->static_mapping_only = static_mapping_only;
+  sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
+
+  if (!static_mapping_only ||
+      (static_mapping_only && static_mapping_connection_tracking))
+    {
+      clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
+                            translation_memory_size);
 
-  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->out2in, "out2in", translation_buckets,
+                            translation_memory_size);
 
-  clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
-                        user_memory_size);
+      clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
+                            user_memory_size);
+    }
+  clib_bihash_init_8_8 (&sm->static_mapping_by_local,
+                        "static_mapping_by_local", static_mapping_buckets,
+                        static_mapping_memory_size);
+
+  clib_bihash_init_8_8 (&sm->static_mapping_by_external,
+                        "static_mapping_by_external", static_mapping_buckets,
+                        static_mapping_memory_size);
   return 0;
 }
 
@@ -617,7 +1255,7 @@ u8 * format_snat_key (u8 * s, va_list * args)
 
   s = format (s, "%U proto %s port %d fib %d",
               format_ip4_address, &key->addr, protocol_string,
-              key->port, key->fib_index);
+              clib_net_to_host_u16 (key->port), key->fib_index);
   return s;
 }
 
@@ -631,6 +1269,10 @@ u8 * format_snat_session (u8 * s, va_list * args)
   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);
+  if (snat_is_session_static (sess))
+    s = format (s, "       static translation\n");
+  else
+    s = format (s, "       dynamic translation\n");
 
   return s;
 }
@@ -645,33 +1287,54 @@ u8 * format_snat_user (u8 * s, va_list * args)
   u32 session_index;
   snat_session_t * sess;
 
-  s = format (s, "%U: %d translations\n",
-              format_ip4_address, &u->addr, u->nsessions);
+  s = format (s, "%U: %d dynamic translations, %d static translations\n",
+              format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
 
   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)
+  if (u->nsessions || u->nstaticsessions)
     {
-      sess = pool_elt_at_index (sm->sessions, session_index);
-
-      s = format (s, "  %U\n", format_snat_session, sm, sess);
+      head_index = u->sessions_per_user_list_head_index;
+      head = pool_elt_at_index (sm->list_pool, head_index);
 
-      elt_index = elt->next;
+      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;
 }
 
+u8 * format_snat_static_mapping (u8 * s, va_list * args)
+{
+  snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *);
+
+  if (m->addr_only)
+      s = format (s, "local %U external %U vrf %d",
+                  format_ip4_address, &m->local_addr,
+                  format_ip4_address, &m->external_addr,
+                  m->vrf_id);
+  else
+      s = format (s, "local %U:%d external %U:%d vrf %d",
+                  format_ip4_address, &m->local_addr, m->local_port,
+                  format_ip4_address, &m->external_addr, m->external_port,
+                  m->vrf_id);
+
+  return s;
+}
+
 static clib_error_t *
 show_snat_command_fn (vlib_main_t * vm,
                 unformat_input_t * input,
@@ -680,30 +1343,71 @@ show_snat_command_fn (vlib_main_t * vm,
   int verbose = 0;
   snat_main_t * sm = &snat_main;
   snat_user_t * u;
+  snat_static_mapping_t *m;
 
   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)
+  if (sm->static_mapping_only)
+    {
+      if (sm->static_mapping_connection_tracking)
+        vlib_cli_output (vm, "SNAT mode: static mapping only connection "
+                         "tracking");
+      else
+        vlib_cli_output (vm, "SNAT mode: static mapping only");
+    }
+  else
+    {
+      vlib_cli_output (vm, "SNAT mode: dynamic translations enabled");
+    }
+
+  if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
+    {
+      vlib_cli_output (vm, "%d static mappings",
+                       pool_elts (sm->static_mappings));
+
+      if (verbose > 0)
+        {
+          pool_foreach (m, sm->static_mappings,
+          ({
+            vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
+          }));
+        }
+    }
+  else
     {
-      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);
-      }));
+      vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
+                       " %d static mappings",
+                       pool_elts (sm->users),
+                       vec_len (sm->addresses),
+                       pool_elts (sm->sessions),
+                       pool_elts (sm->static_mappings));
+
+      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);
+          }));
+
+          if (pool_elts (sm->static_mappings))
+            {
+              vlib_cli_output (vm, "static mappings:");
+              pool_foreach (m, sm->static_mappings,
+              ({
+                vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
+              }));
+            }
+        }
     }
 
   return 0;
index e3aa562..823b860 100644 (file)
@@ -55,6 +55,19 @@ typedef struct {
   };
 } snat_user_key_t;
 
+typedef struct {
+  union
+  {
+    struct
+    {
+      ip4_address_t addr;
+      u16 port;
+      u16 pad;
+    };
+    u64 as_u64;
+  };
+} snat_static_mapping_key_t;
+
 
 typedef enum {
   SNAT_PROTOCOL_UDP = 0,
@@ -63,6 +76,8 @@ typedef enum {
 } snat_protocol_t;
 
 
+#define SNAT_SESSION_FLAG_STATIC_MAPPING 1
+
 typedef CLIB_PACKED(struct {
   snat_session_key_t out2in;    /* 0-15 */
 
@@ -87,12 +102,12 @@ typedef CLIB_PACKED(struct {
 
 }) snat_session_t;
 
-#define SNAT_SESSION_STATIC (1<<0)
 
 typedef struct {
   ip4_address_t addr;
   u32 sessions_per_user_list_head_index;
   u32 nsessions;
+  u32 nstaticsessions;
 } snat_user_t;
 
 typedef struct {
@@ -101,6 +116,16 @@ typedef struct {
   uword * busy_port_bitmap;
 } snat_address_t;
 
+typedef struct {
+  ip4_address_t local_addr;
+  ip4_address_t external_addr;
+  u16 local_port;
+  u16 external_port;
+  u8 addr_only;
+  u32 vrf_id;
+  u32 fib_index;
+} snat_static_mapping_t;
+
 typedef struct {
   /* Main lookup tables */
   clib_bihash_8_8_t out2in;
@@ -109,12 +134,21 @@ typedef struct {
   /* Find-a-user => src address lookup */
   clib_bihash_8_8_t user_hash;
 
+  /* Find a static mapping by local */
+  clib_bihash_8_8_t static_mapping_by_local;
+
+  /* Find a static mapping by external */
+  clib_bihash_8_8_t static_mapping_by_external;
+
   /* User pool */
   snat_user_t * users;
 
   /* Session pool */
   snat_session_t * sessions;
 
+  /* Static mapping pool */
+  snat_static_mapping_t * static_mappings;
+
   /* Vector of outside addresses */
   snat_address_t * addresses;
 
@@ -127,8 +161,12 @@ typedef struct {
   /* ip4 feature path indices */
   u32 rx_feature_in2out;
   u32 rx_feature_out2in;
+  u32 rx_feature_in2out_fast;
+  u32 rx_feature_out2in_fast;
 
   /* Config parameters */
+  u8 static_mapping_only;
+  u8 static_mapping_connection_tracking;
   u32 translation_buckets;
   u32 translation_memory_size;
   u32 user_buckets;
@@ -136,6 +174,8 @@ typedef struct {
   u32 max_translations_per_user;
   u32 outside_vrf_id;
   u32 outside_fib_index;
+  u32 inside_vrf_id;
+  u32 inside_fib_index;
 
   /* API message ID base */
   u16 msg_id_base;
@@ -152,6 +192,8 @@ typedef struct {
 extern snat_main_t snat_main;
 extern vlib_node_registration_t snat_in2out_node;
 extern vlib_node_registration_t snat_out2in_node;
+extern vlib_node_registration_t snat_in2out_fast_node;
+extern vlib_node_registration_t snat_out2in_fast_node;
 
 void snat_free_outside_address_and_port (snat_main_t * sm, 
                                          snat_session_key_t * k, 
@@ -160,6 +202,12 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
                                          snat_session_key_t * k,
                                          u32 * address_indexp);
+
+int snat_static_mapping_match (snat_main_t * sm,
+                               snat_session_key_t match,
+                               snat_session_key_t * mapping,
+                               u8 by_external);
+
 format_function_t format_snat_user;
 
 typedef struct {
@@ -167,6 +215,12 @@ typedef struct {
   u32 cached_ip4_address;
 } snat_runtime_t;
 
+/** \brief Check if SNAT session is created from static mapping.
+    @param s SNAT session
+    @return 1 if SNAT session is created from static mapping otherwise 0
+*/
+#define snat_is_session_static(s) s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING
+
 /* 
  * 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
index cd2656c..453c63f 100644 (file)
@@ -59,7 +59,8 @@ snat_test_main_t snat_test_main;
 
 #define foreach_standard_reply_retval_handler   \
 _(snat_add_address_range_reply)                 \
-_(snat_interface_add_del_feature_reply)
+_(snat_interface_add_del_feature_reply)         \
+_(snat_add_static_mapping_reply)
 
 #define _(n)                                            \
     static void vl_api_##n##_t_handler                  \
@@ -83,8 +84,12 @@ foreach_standard_reply_retval_handler;
  */
 #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)
+_(SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY,                         \
+  snat_interface_add_del_feature_reply)                         \
+_(SNAT_ADD_STATIC_MAPPING_REPLY, snat_add_static_mapping_reply) \
+_(SNAT_CONTROL_PING_REPLY, snat_control_ping_reply)             \
+_(SNAT_STATIC_MAPPING_DETAILS, snat_static_mapping_details)     \
+_(SNAT_SHOW_CONFIG_REPLY, snat_show_config_reply)
 
 /* M: construct, but don't yet send a message */
 #define M(T,t)                                                  \
@@ -137,6 +142,11 @@ static int api_snat_add_address_range (vat_main_t * vam)
     ;
   else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
     end_addr = start_addr;
+  else
+    {
+      clib_warning("unknown input '%U'", format_unformat_error, i);
+      return -99;
+    }
 
   start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
   end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
@@ -192,6 +202,11 @@ static int api_snat_interface_add_del_feature (vat_main_t * vam)
         is_inside = 1;
       else if (unformat (i, "del"))
         is_add = 0;
+      else
+        {
+          clib_warning("unknown input '%U'", format_unformat_error, i);
+          return -99;
+        }
     }
 
   if (sw_if_index_set == 0)
@@ -210,14 +225,188 @@ static int api_snat_interface_add_del_feature (vat_main_t * vam)
   return 0;
 }
 
+static int api_snat_add_static_mapping(vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  unformat_input_t * i = vam->input;
+  f64 timeout;
+  vl_api_snat_add_static_mapping_t * mp;
+  u8 addr_set_n = 0;
+  u8 is_add = 1;
+  u8 addr_only = 1;
+  ip4_address_t local_addr, external_addr;
+  u32 local_port = 0, external_port = 0, vrf_id = ~0;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr))
+        addr_set_n++;
+      else if (unformat (i, "external_addr %U", unformat_ip4_address,
+                         &external_addr))
+        addr_set_n++;
+      else if (unformat (i, "local_port %u", &local_port))
+        addr_only = 0;
+      else if (unformat (i, "external_port %u", &external_port))
+        addr_only = 0;
+      else if (unformat (i, "vrf %u", &vrf_id))
+        ;
+      else if (unformat (i, "del"))
+        is_add = 0;
+      else
+        {
+          clib_warning("unknown input '%U'", format_unformat_error, i);
+          return -99;
+        }
+    }
+
+  if (addr_set_n != 2)
+    {
+      errmsg ("local_addr and remote_addr required\n");
+      return -99;
+    }
+
+  M(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping);
+  mp->is_add = is_add;
+  mp->is_ip4 = 1;
+  mp->addr_only = addr_only;
+  mp->local_port = ntohs ((u16) local_port);
+  mp->external_port = ntohs ((u16) external_port);
+  mp->vrf_id = ntohl (vrf_id);
+  memcpy (mp->local_ip_address, &local_addr, 4);
+  memcpy (mp->external_ip_address, &external_addr, 4);
+
+  S; W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static void vl_api_snat_control_ping_reply_t_handler
+  (vl_api_snat_control_ping_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  i32 retval = ntohl (mp->retval);
+  if (vam->async_mode)
+    {
+      vam->async_errors += (retval < 0);
+    }
+  else
+    {
+      vam->retval = retval;
+      vam->result_ready = 1;
+    }
+}
+
+static void vl_api_snat_static_mapping_details_t_handler
+  (vl_api_snat_static_mapping_details_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+
+  if (mp->addr_only)
+      fformat (vam->ofp, "%15U%6s%15U%6s%11d\n",
+               format_ip4_address, &mp->local_ip_address, "",
+               format_ip4_address, &mp->external_ip_address, "",
+               ntohl (mp->vrf_id));
+  else
+      fformat (vam->ofp, "%15U%6d%15U%6d%11d\n",
+               format_ip4_address, &mp->local_ip_address,
+               ntohs (mp->local_port),
+               format_ip4_address, &mp->external_ip_address,
+               ntohs (mp->external_port),
+               ntohl (mp->vrf_id));
+
+}
+
+static int api_snat_static_mapping_dump(vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  f64 timeout;
+  vl_api_snat_static_mapping_dump_t * mp;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_static_mapping_dump");
+      return -99;
+    }
+
+  fformat (vam->ofp, "%21s%21s\n", "local", "external");
+  fformat (vam->ofp, "%15s%6s%15s%6s%11s\n", "address", "port", "address",
+           "port", "vrf");
+
+  M(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump);
+  S;
+  /* Use a control ping for synchronization */
+  {
+    vl_api_snat_control_ping_t *mp;
+    M (SNAT_CONTROL_PING, snat_control_ping);
+    S;
+  }
+  W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static void vl_api_snat_show_config_reply_t_handler
+  (vl_api_snat_show_config_reply_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+  i32 retval = ntohl (mp->retval);
+
+  if (retval >= 0)
+    {
+      fformat (vam->ofp, "translation hash buckets %d\n",
+               ntohl (mp->translation_buckets));
+      fformat (vam->ofp, "translation hash memory %d\n",
+               ntohl (mp->translation_memory_size));
+      fformat (vam->ofp, "user hash buckets %d\n", ntohl (mp->user_buckets));
+      fformat (vam->ofp, "user hash memory %d\n", ntohl (mp->user_memory_size));
+      fformat (vam->ofp, "max translations per user %d\n",
+               ntohl (mp->max_translations_per_user));
+      fformat (vam->ofp, "outside VRF id %d\n", ntohl (mp->outside_vrf_id));
+      fformat (vam->ofp, "inside VRF id %d\n", ntohl (mp->inside_vrf_id));
+      if (mp->static_mapping_only)
+        {
+          fformat (vam->ofp, "static mapping only");
+          if (mp->static_mapping_connection_tracking)
+            fformat (vam->ofp, " connection tracking");
+          fformat (vam->ofp, "\n");
+        }
+    }
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static int api_snat_show_config(vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  f64 timeout;
+  vl_api_snat_show_config_t * mp;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_show_config");
+      return -99;
+    }
+
+  M(SNAT_SHOW_CONFIG, snat_show_config);
+  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]")
+#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]")                 \
+_(snat_add_static_mapping, "local_addr <ip> external_addr <ip> " \
+  "[local_port <n>] [external_port <n>] [vrf <table-id>] [del]") \
+_(snat_static_mapping_dump, "")                                  \
+_(snat_show_config, "")
 
 void vat_api_hookup (vat_main_t *vam)
 {