NAT44: don't add static mapping to resolution vector if failed (VPP-1225)
[vpp.git] / src / plugins / nat / nat.c
index 5c324f2..b4e5c79 100644 (file)
@@ -24,6 +24,7 @@
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
+#include <nat/nat66.h>
 #include <nat/dslite.h>
 #include <nat/nat_reass.h>
 #include <vnet/fib/fib_table.h>
@@ -38,79 +39,79 @@ snat_main_t snat_main;
 VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-in2out",
-  .runs_before = VNET_FEATURES ("nat44-out2in"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-out2in",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_nat_classify, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-classify",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-det-in2out",
-  .runs_before = VNET_FEATURES ("nat44-det-out2in"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-det-out2in",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_nat_det_classify, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-det-classify",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-in2out-worker-handoff",
-  .runs_before = VNET_FEATURES ("nat44-out2in-worker-handoff"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-out2in-worker-handoff",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-handoff-classify",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-in2out-fast",
-  .runs_before = VNET_FEATURES ("nat44-out2in-fast"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-out2in-fast",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-hairpin-dst",
-  .runs_before = VNET_FEATURES ("ip4-lookup"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
 };
 
 /* Hook up output features */
 VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
   .arc_name = "ip4-output",
   .node_name = "nat44-in2out-output",
-  .runs_before = VNET_FEATURES ("interface-output"),
+  .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
   .arc_name = "ip4-output",
   .node_name = "nat44-in2out-output-worker-handoff",
-  .runs_before = VNET_FEATURES ("interface-output"),
+  .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
 };
 VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = {
   .arc_name = "ip4-output",
   .node_name = "nat44-hairpin-src",
-  .runs_before = VNET_FEATURES ("interface-output"),
+  .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
 };
 
 /* Hook up ip4-local features */
@@ -521,7 +522,7 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
   if (is_add)
     fib_table_entry_update_one_path(fib_index,
                                     &prefix,
-                                    FIB_SOURCE_PLUGIN_HI,
+                                    FIB_SOURCE_PLUGIN_LOW,
                                     (FIB_ENTRY_FLAG_CONNECTED |
                                      FIB_ENTRY_FLAG_LOCAL |
                                      FIB_ENTRY_FLAG_EXCLUSIVE),
@@ -535,7 +536,7 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
   else
     fib_table_entry_delete(fib_index,
                            &prefix,
-                           FIB_SOURCE_PLUGIN_HI);
+                           FIB_SOURCE_PLUGIN_LOW);
 }
 
 void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
@@ -561,7 +562,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
   if (vrf_id != ~0)
     ap->fib_index =
       fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
-                                         FIB_SOURCE_PLUGIN_HI);
+                                         FIB_SOURCE_PLUGIN_LOW);
   else
     ap->fib_index = ~0;
 #define _(N, i, n, s) \
@@ -623,7 +624,8 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
                                        u32 vrf_id,
                                        snat_protocol_t proto,
                                        int addr_only,
-                                       int is_add)
+                                       int is_add,
+                                       u8 * tag)
 {
   snat_static_map_resolve_t *rp;
 
@@ -636,6 +638,7 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
   rp->proto = proto;
   rp->addr_only = addr_only;
   rp->is_add = is_add;
+  rp->tag = vec_dup (tag);
 }
 
 /**
@@ -653,13 +656,14 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @param is_add If 0 delete static mapping, otherwise add.
  * @param twice_nat If 1 translate external host address and port.
  * @param out2in_only If 1 rule match only out2in direction
+ * @param tag - opaque string tag
  *
  * @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,
                             u32 sw_if_index, snat_protocol_t proto, int is_add,
-                            u8 twice_nat, u8 out2in_only)
+                            u8 twice_nat, u8 out2in_only, u8 * tag)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -671,30 +675,80 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   snat_interface_t *interface;
   int i;
   snat_main_per_thread_data_t *tsm;
+  snat_user_key_t u_key;
+  snat_user_t *u;
+  dlist_elt_t * head, * elt;
+  u32 elt_index, head_index;
+  u32 ses_index;
+  u64 user_index;
+  snat_session_t * s;
+  snat_static_map_resolve_t *rp, *rp_match = 0;
 
   /* If the external address is a specific interface address */
   if (sw_if_index != ~0)
     {
       ip4_address_t * first_int_addr;
 
+      for (i = 0; i < vec_len (sm->to_resolve); i++)
+        {
+          rp = sm->to_resolve + i;
+          if (rp->sw_if_index != sw_if_index &&
+              rp->l_addr.as_u32 != l_addr.as_u32 &&
+              rp->vrf_id != vrf_id && rp->addr_only != addr_only)
+            continue;
+
+          if (!addr_only)
+            {
+              if (rp->l_port != l_port && rp->e_port != e_port && rp->proto != proto)
+                continue;
+            }
+
+          rp_match = rp;
+          break;
+        }
+
       /* Might be already set... */
       first_int_addr = ip4_interface_first_address
         (sm->ip4_main, sw_if_index, 0 /* just want the address*/);
 
-      /* DHCP resolution required? */
-      if (first_int_addr == 0)
+      if (is_add)
         {
+          if (rp_match)
+            return VNET_API_ERROR_VALUE_EXIST;
+
           snat_add_static_mapping_when_resolved
             (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto,
-             addr_only,  is_add);
-          return 0;
+             addr_only,  is_add, tag);
+
+          /* DHCP resolution required? */
+          if (first_int_addr == 0)
+            {
+              return 0;
+            }
+          else
+            {
+              e_addr.as_u32 = first_int_addr->as_u32;
+              /* Identity mapping? */
+              if (l_addr.as_u32 == 0)
+                l_addr.as_u32 = e_addr.as_u32;
+            }
         }
-        else
+      else
         {
-          e_addr.as_u32 = first_int_addr->as_u32;
-          /* Identity mapping? */
-          if (l_addr.as_u32 == 0)
-            l_addr.as_u32 = e_addr.as_u32;
+          if (!rp_match)
+            return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+          vec_del1 (sm->to_resolve, i);
+
+          if (first_int_addr)
+            {
+              e_addr.as_u32 = first_int_addr->as_u32;
+              /* Identity mapping? */
+              if (l_addr.as_u32 == 0)
+                l_addr.as_u32 = e_addr.as_u32;
+            }
+          else
+            return 0;
         }
     }
 
@@ -731,6 +785,17 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           vrf_id = sm->inside_vrf_id;
         }
 
+      if (!out2in_only)
+        {
+          m_key.addr = l_addr;
+          m_key.port = addr_only ? 0 : l_port;
+          m_key.protocol = addr_only ? 0 : proto;
+          m_key.fib_index = fib_index;
+          kv.key = m_key.as_u64;
+          if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value))
+            return VNET_API_ERROR_VALUE_EXIST;
+        }
+
       /* 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 || out2in_only))
@@ -764,12 +829,32 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                 }
             }
           /* External address must be allocated */
-          if (!a)
-            return VNET_API_ERROR_NO_SUCH_ENTRY;
+          if (!a && (l_addr.as_u32 != e_addr.as_u32))
+            {
+              if (sw_if_index != ~0)
+                {
+                  for (i = 0; i < vec_len (sm->to_resolve); i++)
+                    {
+                      rp = sm->to_resolve + i;
+                      if (rp->addr_only)
+                         continue;
+                      if (rp->sw_if_index != sw_if_index &&
+                          rp->l_addr.as_u32 != l_addr.as_u32 &&
+                          rp->vrf_id != vrf_id && rp->l_port != l_port &&
+                          rp->e_port != e_port && rp->proto != proto)
+                        continue;
+
+                      vec_del1 (sm->to_resolve, i);
+                      break;
+                    }
+                }
+              return VNET_API_ERROR_NO_SUCH_ENTRY;
+            }
         }
 
       pool_get (sm->static_mappings, m);
       memset (m, 0, sizeof (*m));
+      m->tag = vec_dup (tag);
       m->local_addr = l_addr;
       m->external_addr = e_addr;
       m->addr_only = addr_only;
@@ -827,11 +912,61 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
             clib_warning ("out2in key add failed");
         }
 
+      /* Delete dynamic sessions matching local address (+ local port) */
+      if (!(sm->static_mapping_only))
+        {
+          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 (&tsm->user_hash, &kv, &value))
+            {
+              user_index = value.value;
+              u = pool_elt_at_index (tsm->users, user_index);
+              if (u->nsessions)
+                {
+                  head_index = u->sessions_per_user_list_head_index;
+                  head = pool_elt_at_index (tsm->list_pool, head_index);
+                  elt_index = head->next;
+                  elt = pool_elt_at_index (tsm->list_pool, elt_index);
+                  ses_index = elt->value;
+                  while (ses_index != ~0)
+                    {
+                      s =  pool_elt_at_index (tsm->sessions, ses_index);
+                      elt = pool_elt_at_index (tsm->list_pool, elt->next);
+                      ses_index = elt->value;
+
+                      if (snat_is_session_static (s))
+                        continue;
+
+                      if (!addr_only)
+                        {
+                          if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
+                              (clib_net_to_host_u16 (s->out2in.port) != e_port))
+                            continue;
+                        }
+
+                      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
+                      pool_put_index (tsm->list_pool, s->per_user_index);
+                      pool_put (tsm->sessions, s);
+                      u->nsessions--;
+
+                      if (!addr_only)
+                        break;
+                    }
+                }
+            }
+        }
     }
   else
     {
       if (!m)
-        return VNET_API_ERROR_NO_SUCH_ENTRY;
+        {
+          if (sw_if_index != ~0)
+            return 0;
+          else
+            return VNET_API_ERROR_NO_SUCH_ENTRY;
+        }
 
       /* Free external address port */
       if (!(addr_only || sm->static_mapping_only || out2in_only))
@@ -902,14 +1037,6 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       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;
-          u64 user_index;
-          snat_session_t * s;
-
           u_key.addr = m->local_addr;
           u_key.fib_index = m->fib_index;
           kv.key = u_key.as_u64;
@@ -955,6 +1082,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
             }
         }
 
+      vec_free (m->tag);
       /* Delete static mapping from pool */
       pool_put (sm->static_mappings, m);
     }
@@ -983,10 +1111,37 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   return 0;
 }
 
+static int lb_local_exists (nat44_lb_addr_port_t * local,
+                            ip4_address_t * e_addr, u16 e_port)
+{
+  snat_main_t *sm = &snat_main;
+  snat_static_mapping_t *m;
+  nat44_lb_addr_port_t *ap;
+
+  /* *INDENT-OFF* */
+  pool_foreach (m, sm->static_mappings,
+  ({
+      if (vec_len(m->locals))
+        {
+          if (m->external_port == e_port && m->external_addr.as_u32 == e_addr->as_u32)
+            continue;
+
+          vec_foreach (ap, m->locals)
+          {
+            if (ap->port == local->port && ap->addr.as_u32 == local->addr.as_u32)
+              return 1;
+          }
+        }
+  }));
+  /* *INDENT-ON* */
+
+  return 0;
+}
+
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
                                      nat44_lb_addr_port_t *locals, u8 is_add,
-                                     u8 twice_nat, u8 out2in_only)
+                                     u8 twice_nat, u8 out2in_only, u8 *tag)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -1023,7 +1178,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
       fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
                                                      vrf_id,
-                                                     FIB_SOURCE_PLUGIN_HI);
+                                                     FIB_SOURCE_PLUGIN_LOW);
 
       /* Find external address in allocated addresses and reserve port for
          address and port pair mapping when dynamic translations enabled */
@@ -1064,6 +1219,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
       pool_get (sm->static_mappings, m);
       memset (m, 0, sizeof (*m));
+      m->tag = vec_dup (tag);
       m->external_addr = e_addr;
       m->addr_only = 0;
       m->vrf_id = vrf_id;
@@ -1135,7 +1291,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       if (!m)
         return VNET_API_ERROR_NO_SUCH_ENTRY;
 
-      fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI);
+      fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW);
 
       /* Free external address port */
       if (!(sm->static_mapping_only || out2in_only))
@@ -1202,12 +1358,15 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                 }
             }
 
-          m_key.port = clib_host_to_net_u16 (local->port);
-          kv.key = m_key.as_u64;
-          if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
+          if (!lb_local_exists(local, &e_addr, e_port))
             {
-              clib_warning ("in2out key del failed");
-              return VNET_API_ERROR_UNSPECIFIED;
+              m_key.port = clib_host_to_net_u16 (local->port);
+              kv.key = m_key.as_u64;
+              if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
+                {
+                  clib_warning ("in2out key del failed");
+                  return VNET_API_ERROR_UNSPECIFIED;
+                }
             }
           /* Delete sessions */
           u_key.addr = local->addr;
@@ -1243,6 +1402,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
             }
         }
       vec_free(m->locals);
+      vec_free(m->tag);
 
       pool_put (sm->static_mappings, m);
     }
@@ -1287,7 +1447,7 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
                                             m->local_port, m->external_port,
                                             m->vrf_id, m->addr_only, ~0,
                                             m->proto, 0, m->twice_nat,
-                                            m->out2in_only);
+                                            m->out2in_only, m->tag);
       }));
     }
   else
@@ -1302,7 +1462,7 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
 
   if (a->fib_index != ~0)
     fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4,
-                     FIB_SOURCE_PLUGIN_HI);
+                     FIB_SOURCE_PLUGIN_LOW);
 
   /* Delete sessions using address */
   if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
@@ -1376,6 +1536,12 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
   if (sm->out2in_dpo && !is_inside)
     return VNET_API_ERROR_UNSUPPORTED;
 
+  pool_foreach (i, sm->output_feature_interfaces,
+  ({
+    if (i->sw_if_index == sw_if_index)
+      return VNET_API_ERROR_VALUE_EXIST;
+  }));
+
   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
     feature_name = is_inside ?  "nat44-in2out-fast" : "nat44-out2in-fast";
   else
@@ -1527,6 +1693,12 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
       (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)))
     return VNET_API_ERROR_UNSUPPORTED;
 
+  pool_foreach (i, sm->interfaces,
+  ({
+    if (i->sw_if_index == sw_if_index)
+      return VNET_API_ERROR_VALUE_EXIST;
+  }));
+
   if (is_inside)
     {
       vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst",
@@ -1638,6 +1810,15 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
                                        u32 if_address_index,
                                        u32 is_delete);
 
+static void
+nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im,
+                                 uword opaque,
+                                 u32 sw_if_index,
+                                 ip4_address_t * address,
+                                 u32 address_length,
+                                 u32 if_address_index,
+                                 u32 is_delete);
+
 static int
 nat_alloc_addr_and_port_default (snat_address_t * addresses,
                                  u32 fib_index,
@@ -1716,6 +1897,11 @@ static clib_error_t * snat_init (vlib_main_t * vm)
 
   vec_add1 (im->add_del_interface_address_callbacks, cb4);
 
+  cb4.function = nat_ip4_add_del_addr_only_sm_cb;
+  cb4.function_opaque = 0;
+
+  vec_add1 (im->add_del_interface_address_callbacks, cb4);
+
   nat_dpo_module_init ();
 
   /* Init IPFIX logging */
@@ -1728,6 +1914,8 @@ static clib_error_t * snat_init (vlib_main_t * vm)
 
   dslite_init(vm);
 
+  nat66_init();
+
   /* Init virtual fragmenentation reassembly */
   return nat_reass_init(vm);
 }
@@ -2553,16 +2741,14 @@ u8 * format_snat_static_map_to_resolve (u8 * s, va_list * args)
   if (m->addr_only)
       s = format (s, "local %U external %U vrf %d",
                   format_ip4_address, &m->l_addr,
-                  format_vnet_sw_interface_name, vnm,
-                  vnet_get_sw_interface (vnm, m->sw_if_index),
+                  format_vnet_sw_if_index_name, vnm, m->sw_if_index,
                   m->vrf_id);
   else
       s = format (s, "%U local %U:%d external %U:%d vrf %d",
                   format_snat_protocol, m->proto,
                   format_ip4_address, &m->l_addr, m->l_port,
-                  format_vnet_sw_interface_name, vnm,
-                  vnet_get_sw_interface (vnm, m->sw_if_index), m->e_port,
-                  m->vrf_id);
+                  format_vnet_sw_if_index_name, vnm, m->sw_if_index,
+                  m->e_port, m->vrf_id);
 
   return s;
 }
@@ -2596,6 +2782,77 @@ u8 * format_det_map_ses (u8 * s, va_list * args)
   return s;
 }
 
+static void
+nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im,
+                                 uword opaque,
+                                 u32 sw_if_index,
+                                 ip4_address_t * address,
+                                 u32 address_length,
+                                 u32 if_address_index,
+                                 u32 is_delete)
+{
+  snat_main_t *sm = &snat_main;
+  snat_static_map_resolve_t *rp;
+  snat_static_mapping_t *m;
+  snat_session_key_t m_key;
+  clib_bihash_kv_8_8_t kv, value;
+  int i, rv;
+  ip4_address_t l_addr;
+
+  for (i = 0; i < vec_len (sm->to_resolve); i++)
+    {
+      rp = sm->to_resolve + i;
+      if (rp->addr_only == 0)
+        continue;
+      if (rp->sw_if_index == sw_if_index)
+        goto match;
+    }
+
+  return;
+
+match:
+  m_key.addr.as_u32 = address->as_u32;
+  m_key.port = rp->addr_only ? 0 : rp->e_port;
+  m_key.protocol = rp->addr_only ? 0 : rp->proto;
+  m_key.fib_index = sm->outside_fib_index;
+  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_delete)
+    {
+      /* Don't trip over lease renewal, static config */
+      if (m)
+        return;
+    }
+  else
+    {
+      if (!m)
+        return;
+    }
+
+  /* Indetity mapping? */
+  if (rp->l_addr.as_u32 == 0)
+    l_addr.as_u32 = address[0].as_u32;
+  else
+    l_addr.as_u32 = rp->l_addr.as_u32;
+  /* Add the static mapping */
+  rv = snat_add_static_mapping (l_addr,
+                                address[0],
+                                rp->l_port,
+                                rp->e_port,
+                                rp->vrf_id,
+                                rp->addr_only,
+                                ~0 /* sw_if_index */,
+                                rp->proto,
+                                !is_delete,
+                                0, 0, rp->tag);
+  if (rv)
+    clib_warning ("snat_add_static_mapping returned %d", rv);
+}
+
 static void
 snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
                                        uword opaque,
@@ -2607,7 +2864,6 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
 {
   snat_main_t *sm = &snat_main;
   snat_static_map_resolve_t *rp;
-  u32 *indices_to_delete = 0;
   ip4_address_t l_addr;
   int i, j;
   int rv;
@@ -2643,6 +2899,8 @@ match:
       for (j = 0; j < vec_len (sm->to_resolve); j++)
         {
           rp = sm->to_resolve + j;
+          if (rp->addr_only)
+            continue;
           /* On this interface? */
           if (rp->sw_if_index == sw_if_index)
             {
@@ -2661,21 +2919,12 @@ match:
                                             ~0 /* sw_if_index */,
                                             rp->proto,
                                             rp->is_add,
-                                            0, 0);
+                                            0, 0, rp->tag);
               if (rv)
                 clib_warning ("snat_add_static_mapping returned %d",
                               rv);
-              vec_add1 (indices_to_delete, j);
             }
         }
-      /* If we resolved any of the outstanding static mappings */
-      if (vec_len(indices_to_delete))
-        {
-          /* Delete them */
-          for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
-            vec_delete(sm->to_resolve, 1, j);
-          vec_free(indices_to_delete);
-        }
       return;
     }
   else
@@ -2767,7 +3016,7 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
   snat_user_t *u;
 
   ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
-  if (sm->num_workers)
+  if (sm->num_workers > 1)
     tsm =
       vec_elt_at_index (sm->per_thread_data,
                        sm->worker_in2out_cb (&ip, fib_index));