SNAT: static mappings for dhcp addressed interfaces (VPP-590) 28/4828/2
authorMatus Fabian <matfabia@cisco.com>
Tue, 24 Jan 2017 07:42:28 +0000 (23:42 -0800)
committerOle Trøan <otroan@employees.org>
Wed, 25 Jan 2017 13:27:25 +0000 (13:27 +0000)
updated API
added test

Change-Id: I3f6017ecf09b924cb320c1b5f323cd33f7a37447
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/snat/snat.api
src/plugins/snat/snat.c
src/plugins/snat/snat_test.c
test/test_snat.py
test/vpp_papi_provider.py

index ff1d9bc..b562e7a 100644 (file)
@@ -120,6 +120,8 @@ define snat_interface_details {
     @param external_ip_address - external IP address
     @param local_port - local port number
     @param external_port - external port number
+    @param external_sw_if_index - external interface (if set
+                                  external_ip_address is ignored)
     @param vfr_id - VRF ID
 */
 define snat_add_static_mapping {
@@ -132,6 +134,7 @@ define snat_add_static_mapping {
   u8 external_ip_address[16];
   u16 local_port;
   u16 external_port;
+  u32 external_sw_if_index;
   u32 vrf_id;
 };
 
index 337a0e0..fb56659 100644 (file)
@@ -240,81 +240,6 @@ static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
   return 0;
 }
 
-int snat_del_address (snat_main_t *sm, ip4_address_t addr)
-{
-  snat_address_t *a = 0;
-  snat_session_t *ses;
-  u32 *ses_to_be_removed = 0, *ses_index;
-  clib_bihash_kv_8_8_t kv, value;
-  snat_user_key_t user_key;
-  snat_user_t *u;
-  snat_main_per_thread_data_t *tsm;
-
-  int i;
-
-  /* Find SNAT address */
-  for (i=0; i < vec_len (sm->addresses); i++)
-    {
-      if (sm->addresses[i].addr.as_u32 == addr.as_u32)
-        {
-          a = sm->addresses + i;
-          break;
-        }
-    }
-  if (!a)
-    return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-  /* Check if address is used in some static mapping */
-  if (is_snat_address_used_in_static_mapping(sm, addr))
-    {
-      clib_warning ("address used in static mapping");
-      return VNET_API_ERROR_UNSPECIFIED;
-    }
-
-  /* Delete sessions using address */
-  if (a->busy_ports)
-    {
-      vec_foreach (tsm, sm->per_thread_data)
-        {
-          pool_foreach (ses, tsm->sessions, ({
-            if (ses->out2in.addr.as_u32 == addr.as_u32)
-              {
-                /* log NAT event */
-                snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
-                                                    ses->out2in.addr.as_u32,
-                                                    ses->in2out.protocol,
-                                                    ses->in2out.port,
-                                                    ses->out2in.port,
-                                                    ses->in2out.fib_index);
-                vec_add1 (ses_to_be_removed, ses - tsm->sessions);
-                kv.key = ses->in2out.as_u64;
-                clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
-                kv.key = ses->out2in.as_u64;
-                clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0);
-                clib_dlist_remove (tsm->list_pool, ses->per_user_index);
-                user_key.addr = ses->in2out.addr;
-                user_key.fib_index = ses->in2out.fib_index;
-                kv.key = user_key.as_u64;
-                if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
-                  {
-                    u = pool_elt_at_index (tsm->users, value.value);
-                    u->nsessions--;
-                  }
-              }
-          }));
-
-          vec_foreach (ses_index, ses_to_be_removed)
-            pool_put_index (tsm->sessions, ses_index[0]);
-
-          vec_free (ses_to_be_removed);
-       }
-    }
-
-  vec_del1 (sm->addresses, i);
-
-  return 0;
-}
-
 static void increment_v4_address (ip4_address_t * a)
 {
   u32 v;
@@ -356,6 +281,7 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @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 sw_if_index External port instead of specific IP address.
  * @param is_add If 0 delete static mapping, otherwise add.
  *
  * @returns
@@ -638,6 +564,94 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   return 0;
 }
 
+int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
+{
+  snat_address_t *a = 0;
+  snat_session_t *ses;
+  u32 *ses_to_be_removed = 0, *ses_index;
+  clib_bihash_kv_8_8_t kv, value;
+  snat_user_key_t user_key;
+  snat_user_t *u;
+  snat_main_per_thread_data_t *tsm;
+  snat_static_mapping_t *m;
+  int i;
+
+  /* Find SNAT address */
+  for (i=0; i < vec_len (sm->addresses); i++)
+    {
+      if (sm->addresses[i].addr.as_u32 == addr.as_u32)
+        {
+          a = sm->addresses + i;
+          break;
+        }
+    }
+  if (!a)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  if (delete_sm)
+    {
+      pool_foreach (m, sm->static_mappings,
+      ({
+          if (m->external_addr.as_u32 == addr.as_u32)
+            (void) snat_add_static_mapping (m->local_addr, m->external_addr,
+                                            m->local_port, m->external_port,
+                                            m->vrf_id, m->addr_only, ~0, 0);
+      }));
+    }
+  else
+    {
+      /* Check if address is used in some static mapping */
+      if (is_snat_address_used_in_static_mapping(sm, addr))
+        {
+          clib_warning ("address used in static mapping");
+          return VNET_API_ERROR_UNSPECIFIED;
+        }
+    }
+
+  /* Delete sessions using address */
+  if (a->busy_ports)
+    {
+      vec_foreach (tsm, sm->per_thread_data)
+        {
+          pool_foreach (ses, tsm->sessions, ({
+            if (ses->out2in.addr.as_u32 == addr.as_u32)
+              {
+                /* log NAT event */
+                snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
+                                                    ses->out2in.addr.as_u32,
+                                                    ses->in2out.protocol,
+                                                    ses->in2out.port,
+                                                    ses->out2in.port,
+                                                    ses->in2out.fib_index);
+                vec_add1 (ses_to_be_removed, ses - tsm->sessions);
+                kv.key = ses->in2out.as_u64;
+                clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
+                kv.key = ses->out2in.as_u64;
+                clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0);
+                clib_dlist_remove (tsm->list_pool, ses->per_user_index);
+                user_key.addr = ses->in2out.addr;
+                user_key.fib_index = ses->in2out.fib_index;
+                kv.key = user_key.as_u64;
+                if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
+                  {
+                    u = pool_elt_at_index (tsm->users, value.value);
+                    u->nsessions--;
+                  }
+              }
+          }));
+
+          vec_foreach (ses_index, ses_to_be_removed)
+            pool_put_index (tsm->sessions, ses_index[0]);
+
+          vec_free (ses_to_be_removed);
+       }
+    }
+
+  vec_del1 (sm->addresses, i);
+
+  return 0;
+}
+
 static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
 {
   snat_main_t *sm = &snat_main;
@@ -750,7 +764,7 @@ vl_api_snat_add_address_range_t_handler
       if (mp->is_add)
         snat_add_address (sm, &this_addr);
       else
-        rv = snat_del_address (sm, this_addr);
+        rv = snat_del_address (sm, this_addr, 0);
 
       if (rv)
         goto send_reply;
@@ -904,7 +918,7 @@ vl_api_snat_add_static_mapping_t_handler
   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;
+  u32 vrf_id, external_sw_if_index;
   int rv = 0;
 
   if (mp->is_ip4 != 1)
@@ -921,10 +935,11 @@ vl_api_snat_add_static_mapping_t_handler
       external_port = clib_net_to_host_u16 (mp->external_port);
     }
   vrf_id = clib_net_to_host_u32 (mp->vrf_id);
+  external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index);
 
   rv = snat_add_static_mapping(local_addr, external_addr, local_port,
                                external_port, vrf_id, mp->addr_only,
-                               ~0 /* sw_if_index */,
+                               external_sw_if_index,
                                mp->is_add);
 
  send_reply:
@@ -949,6 +964,9 @@ static void *vl_api_snat_add_static_mapping_t_print
   if (mp->vrf_id != ~0)
     s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
 
+  if (mp->external_sw_if_index != ~0)
+    s = format (s, "external_sw_if_index %d",
+                clib_net_to_host_u32 (mp->external_sw_if_index));
   FINISH;
 }
 
@@ -1578,7 +1596,7 @@ add_address_command_fn (vlib_main_t * vm,
       if (is_add)
         snat_add_address (sm, &this_addr);
       else
-        rv = snat_del_address (sm, this_addr);
+        rv = snat_del_address (sm, this_addr, 0);
 
       switch (rv)
         {
@@ -2285,7 +2303,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
             }
           else
             {
-              (void) snat_del_address(sm, address[0]);
+              (void) snat_del_address(sm, address[0], 1);
               return;
             }
         }
@@ -2299,7 +2317,9 @@ static int snat_add_interface_address (snat_main_t *sm,
 {
   ip4_main_t * ip4_main = sm->ip4_main;
   ip4_address_t * first_int_addr;
-  int i;
+  snat_static_map_resolve_t *rp;
+  u32 *indices_to_delete = 0;
+  int i, j;
 
   first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index,
                                                 0 /* just want the address*/);
@@ -2312,7 +2332,22 @@ static int snat_add_interface_address (snat_main_t *sm,
             {
               /* if have address remove it */
               if (first_int_addr)
-                  (void) snat_del_address (sm, first_int_addr[0]);
+                  (void) snat_del_address (sm, first_int_addr[0], 1);
+              else
+                {
+                  for (j = 0; j < vec_len (sm->to_resolve); j++)
+                    {
+                      rp = sm->to_resolve + j;
+                      if (rp->sw_if_index == sw_if_index)
+                        vec_add1 (indices_to_delete, j);
+                    }
+                  if (vec_len(indices_to_delete))
+                    {
+                      for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
+                        vec_del1(sm->to_resolve, j);
+                      vec_free(indices_to_delete);
+                    }
+                }
               vec_del1(sm->auto_add_sw_if_indices, i);
             }
           else
index b601d7d..67dc438 100644 (file)
@@ -213,24 +213,32 @@ static int api_snat_add_static_mapping(vat_main_t * vam)
   unformat_input_t * i = vam->input;
   f64 timeout;
   vl_api_snat_add_static_mapping_t * mp;
-  u8 addr_set_n = 0;
+  u8 external_addr_set = 0;
+  u8 local_addr_set;
   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;
+  u32 sw_if_index = ~0;
+  u8 sw_if_index_set = 0;
 
   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr))
-        addr_set_n++;
+        local_addr_set = 1;
       else if (unformat (i, "external_addr %U", unformat_ip4_address,
                          &external_addr))
-        addr_set_n++;
+        external_addr_set = 1;
       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, "external_if %U", unformat_sw_if_index, vam,
+                         &sw_if_index))
+        sw_if_index_set = 1;
+      else if (unformat (i, "external_sw_if_index %d", &sw_if_index))
+        sw_if_index_set = 1;
+       else if (unformat (i, "vrf %u", &vrf_id))
         ;
       else if (unformat (i, "del"))
         is_add = 0;
@@ -241,9 +249,14 @@ static int api_snat_add_static_mapping(vat_main_t * vam)
         }
     }
 
-  if (addr_set_n != 2)
+  if (!local_addr_set)
+    {
+      errmsg ("local addr required\n");
+      return -99;
+    }
+  if (!external_addr_set && !sw_if_index_set)
     {
-      errmsg ("local_addr and remote_addr required\n");
+      errmsg ("external addr or interface required\n");
       return -99;
     }
 
@@ -253,6 +266,7 @@ static int api_snat_add_static_mapping(vat_main_t * vam)
   mp->addr_only = addr_only;
   mp->local_port = ntohs ((u16) local_port);
   mp->external_port = ntohs ((u16) external_port);
+  mp->external_sw_if_index = ntohl (sw_if_index);
   mp->vrf_id = ntohl (vrf_id);
   memcpy (mp->local_ip_address, &local_addr, 4);
   memcpy (mp->external_ip_address, &external_addr, 4);
@@ -618,6 +632,7 @@ _(snat_add_address_range, "<start-addr> [- <end-addr] [del]")    \
 _(snat_interface_add_del_feature,                                \
   "<intfc> | sw_if_index <id> [in] [out] [del]")                 \
 _(snat_add_static_mapping, "local_addr <ip> external_addr <ip> " \
+  "| external_if <intfc> | external_sw_if_ndex <id>) "           \
   "[local_port <n>] [external_port <n>] [vrf <table-id>] [del]") \
 _(snat_set_workers, "<wokrers_bitmap>")                          \
 _(snat_static_mapping_dump, "")                                  \
index 09fdb10..b6cc1c9 100644 (file)
@@ -269,6 +269,9 @@ class TestSNAT(VppTestCase):
         """
         Clear SNAT configuration.
         """
+        if self.pg7.has_ip4_config:
+            self.pg7.unconfig_ip4()
+
         interfaces = self.vapi.snat_interface_addr_dump()
         for intf in interfaces:
             self.vapi.snat_add_interface_addr(intf.sw_if_index, is_add=0)
@@ -297,8 +300,9 @@ class TestSNAT(VppTestCase):
                                              addr.ip_address,
                                              is_add=0)
 
-    def snat_add_static_mapping(self, local_ip, external_ip, local_port=0,
-                                external_port=0, vrf_id=0, is_add=1):
+    def snat_add_static_mapping(self, local_ip, external_ip='0.0.0.0',
+                                local_port=0, external_port=0, vrf_id=0,
+                                is_add=1, external_sw_if_index=0xFFFFFFFF):
         """
         Add/delete S-NAT static mapping
 
@@ -308,6 +312,7 @@ class TestSNAT(VppTestCase):
         :param external_port: External port number (Optional)
         :param vrf_id: VRF ID (Default 0)
         :param is_add: 1 if add, 0 if delete (Default add)
+        :param external_sw_if_index: External interface instead of IP address
         """
         addr_only = 1
         if local_port and external_port:
@@ -317,6 +322,7 @@ class TestSNAT(VppTestCase):
         self.vapi.snat_add_static_mapping(
             l_ip,
             e_ip,
+            external_sw_if_index,
             local_port,
             external_port,
             addr_only,
@@ -762,12 +768,35 @@ class TestSNAT(VppTestCase):
         self.pg7.config_ip4()
         adresses = self.vapi.snat_address_dump()
         self.assertEqual(1, len(adresses))
+        self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n)
 
         # remove interface address and check NAT address pool
         self.pg7.unconfig_ip4()
         adresses = self.vapi.snat_address_dump()
         self.assertEqual(0, len(adresses))
 
+    def test_interface_addr_static_mapping(self):
+        """ Static mapping with addresses from interface """
+        self.vapi.snat_add_interface_addr(self.pg7.sw_if_index)
+        self.snat_add_static_mapping('1.2.3.4',
+                                     external_sw_if_index=self.pg7.sw_if_index)
+
+        # no static mappings
+        static_mappings = self.vapi.snat_static_mapping_dump()
+        self.assertEqual(0, len(static_mappings))
+
+        # configure interface address and check static mappings
+        self.pg7.config_ip4()
+        static_mappings = self.vapi.snat_static_mapping_dump()
+        self.assertEqual(1, len(static_mappings))
+        self.assertEqual(static_mappings[0].external_ip_address[0:4],
+                         self.pg7.local_ip4n)
+
+        # remove interface address and check static mappings
+        self.pg7.unconfig_ip4()
+        static_mappings = self.vapi.snat_static_mapping_dump()
+        self.assertEqual(0, len(static_mappings))
+
     def test_ipfix_nat44_sess(self):
         """ S-NAT IPFIX logging NAT44 session created/delted """
         self.snat_add_address(self.snat_addr)
index 96f3ddc..901ea27 100644 (file)
@@ -843,7 +843,8 @@ class VppPapiProvider(object):
     def snat_add_static_mapping(
             self,
             local_ip,
-            external_ip,
+            external_ip=0,
+            external_sw_if_index=0xFFFFFFFF,
             local_port=0,
             external_port=0,
             addr_only=1,
@@ -854,6 +855,7 @@ class VppPapiProvider(object):
 
         :param local_ip: Local IP address
         :param external_ip: External IP address
+        :param external_sw_if_index: External interface instead of IP address
         :param local_port: Local port number (Default value = 0)
         :param external_port: External port number (Default value = 0)
         :param addr_only: 1 if address only mapping, 0 if address and port
@@ -870,6 +872,7 @@ class VppPapiProvider(object):
              'external_ip_address': external_ip,
              'local_port': local_port,
              'external_port': external_port,
+             'external_sw_if_index': external_sw_if_index,
              'vrf_id': vrf_id})
 
     def snat_add_address_range(