SNAT: add API and test for NAT pool address from interface 56/4656/2
authorMatus Fabian <matfabia@cisco.com>
Thu, 12 Jan 2017 12:24:35 +0000 (04:24 -0800)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 13 Jan 2017 08:54:07 +0000 (08:54 +0000)
Change-Id: I2a868f736fae8d37b438c604a9284653ea415541
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 a191eed..f046a96 100644 (file)
@@ -24,9 +24,9 @@
     @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
-    @is_add - 1 if add, 0 if delete
+    @param first_ip_address - first IP address
+    @param last_ip_address - last IP address
+    @param is_add - 1 if add, 0 if delete
 */
 define snat_add_address_range {
   u32 client_index;
@@ -38,7 +38,6 @@ define snat_add_address_range {
 };
 
 /** \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
 */
@@ -83,7 +82,6 @@ define snat_interface_add_del_feature {
 };
 
 /** \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
 */
@@ -138,7 +136,6 @@ define snat_add_static_mapping {
 };
 
 /** \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
 */
@@ -251,7 +248,6 @@ define snat_set_workers {
 };
 
 /** \brief Set S-NAT workers reply
-    @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param retval - return code
 */
@@ -281,3 +277,44 @@ define snat_worker_details {
   u32 lcore_id;
   u8 name[64];
 };
+
+/** \brief Add/delete S-NAT pool address from specific interfce
+    @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 sw_if_index - software index of the interface
+*/
+define snat_add_del_interface_addr {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 is_inside;
+  u32 sw_if_index;
+};
+
+/** \brief Add/delete S-NAT pool address from specific interfce reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+*/
+define snat_add_del_interface_addr_reply {
+  u32 context;
+  i32 retval;
+};
+
+/** \brief Dump S-NAT pool addresses interfaces
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_interface_addr_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief S-NAT pool addresses interfaces details response
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - software index of the interface
+*/
+define snat_interface_addr_details {
+  u32 context;
+  u32 sw_if_index;
+};
index 94e0507..a1236cf 100644 (file)
@@ -1094,6 +1094,84 @@ static void *vl_api_snat_worker_dump_t_print
   FINISH;
 }
 
+static int snat_add_interface_address(snat_main_t *sm,
+                                      u32 sw_if_index,
+                                      int is_del);
+
+static void
+vl_api_snat_add_del_interface_addr_t_handler
+(vl_api_snat_add_del_interface_addr_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_add_del_interface_addr_reply_t * rmp;
+  u8 is_del = mp->is_add == 0;
+  u32 sw_if_index = ntohl(mp->sw_if_index);
+  int rv = 0;
+
+  VALIDATE_SW_IF_INDEX(mp);
+
+  rv = snat_add_interface_address (sm, sw_if_index, is_del);
+  
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO(VL_API_SNAT_ADD_DEL_INTERFACE_ADDR_REPLY);
+}
+
+static void *vl_api_snat_add_del_interface_addr_t_print
+(vl_api_snat_add_del_interface_addr_t * mp, void *handle)
+{
+  u8 * s;
+
+  s = format (0, "SCRIPT: snat_add_del_interface_addr ");
+  s = format (s, "sw_if_index %d %s",
+              clib_host_to_net_u32(mp->sw_if_index),
+              mp->is_add ? "" : "del");
+
+  FINISH;
+}
+
+static void
+send_snat_interface_addr_details
+(u32 sw_if_index, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_snat_interface_addr_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_INTERFACE_ADDR_DETAILS+sm->msg_id_base);
+  rmp->sw_if_index = ntohl (sw_if_index);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_snat_interface_addr_dump_t_handler
+(vl_api_snat_interface_addr_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  snat_main_t * sm = &snat_main;
+  u32 * i;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  vec_foreach (i, sm->auto_add_sw_if_indices)
+    send_snat_interface_addr_details(*i, q, mp->context);
+}
+
+static void *vl_api_snat_interface_addr_dump_t_print
+(vl_api_snat_interface_addr_dump_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_interface_addr_dump ");
+
+  FINISH;
+}
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -1105,7 +1183,9 @@ _(SNAT_SHOW_CONFIG, snat_show_config)                                   \
 _(SNAT_ADDRESS_DUMP, snat_address_dump)                                 \
 _(SNAT_INTERFACE_DUMP, snat_interface_dump)                             \
 _(SNAT_SET_WORKERS, snat_set_workers)                                   \
-_(SNAT_WORKER_DUMP, snat_worker_dump)
+_(SNAT_WORKER_DUMP, snat_worker_dump)                                   \
+_(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr)             \
+_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump)
 
 /* Set up the API message handling tables */
 static clib_error_t *
@@ -1840,7 +1920,7 @@ show_snat_command_fn (vlib_main_t * vm,
   snat_address_t * ap;
   vnet_main_t *vnm = vnet_get_main();
   snat_main_per_thread_data_t *tsm;
-  u32 users_num = 0, sessions_num = 0, *worker;
+  u32 users_num = 0, sessions_num = 0, *worker, *sw_if_index;
   uword j = 0;
 
   if (unformat (input, "detail"))
@@ -1870,6 +1950,16 @@ show_snat_command_fn (vlib_main_t * vm,
                          i->is_inside ? "in" : "out");
       }));
 
+      if (vec_len (sm->auto_add_sw_if_indices))
+        {
+          vlib_cli_output (vm, "SNAT pool addresses interfaces:");
+          vec_foreach (sw_if_index, sm->auto_add_sw_if_indices)
+            {
+              vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm,
+                               vnet_get_sw_interface (vnm, *sw_if_index));
+            }
+        }
+
       vec_foreach (ap, sm->addresses)
         {
           u8 * s = format (0, "");
@@ -2011,7 +2101,9 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
 }
 
 
-static int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index)
+static int snat_add_interface_address (snat_main_t *sm,
+                                       u32 sw_if_index,
+                                       int is_del)
 {
   ip4_main_t * ip4_main = sm->ip4_main;
   ip4_address_t * first_int_addr;
@@ -2023,9 +2115,24 @@ static int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index)
   for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++)
     {
       if (sm->auto_add_sw_if_indices[i] == sw_if_index)
-        return 0;
+        {
+          if (is_del)
+            {
+              /* if have address remove it */
+              if (first_int_addr)
+                  (void) snat_del_address (sm, first_int_addr[0]);
+              vec_del1(sm->auto_add_sw_if_indices, i);
+            }
+          else
+            return VNET_API_ERROR_VALUE_EXIST;
+
+          return 0;
+        }
     }
   
+  if (is_del)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
   /* add to the auto-address list */
   vec_add1(sm->auto_add_sw_if_indices, sw_if_index);
 
@@ -2045,6 +2152,7 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
   unformat_input_t _line_input, *line_input = &_line_input;
   u32 sw_if_index;
   int rv;
+  int is_del = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2055,12 +2163,14 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
       if (unformat (line_input, "%U", unformat_vnet_sw_interface,
                     sm->vnet_main, &sw_if_index))
         ;
+      else if (unformat (line_input, "del"))
+        is_del = 1;
       else
         return clib_error_return (0, "unknown input '%U'",
                                  format_unformat_error, line_input);
     }
 
-  rv = snat_add_interface_address (sm, sw_if_index);
+  rv = snat_add_interface_address (sm, sw_if_index, is_del);
 
   switch (rv)
     {
@@ -2076,6 +2186,6 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
 
 VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = {
     .path = "snat add interface address",
-    .short_help = "snat add interface address <interface>",
+    .short_help = "snat add interface address <interface> [del]",
     .function = snat_add_interface_address_command_fn,
 };
index 2a003ba..6f87d80 100644 (file)
@@ -61,7 +61,8 @@ snat_test_main_t snat_test_main;
 _(snat_add_address_range_reply)                 \
 _(snat_interface_add_del_feature_reply)         \
 _(snat_add_static_mapping_reply)                \
-_(snat_set_workers_reply)
+_(snat_set_workers_reply)                       \
+_(snat_add_del_interface_addr_reply)
 
 #define _(n)                                            \
     static void vl_api_##n##_t_handler                  \
@@ -94,7 +95,10 @@ _(SNAT_SHOW_CONFIG_REPLY, snat_show_config_reply)               \
 _(SNAT_ADDRESS_DETAILS, snat_address_details)                   \
 _(SNAT_INTERFACE_DETAILS, snat_interface_details)               \
 _(SNAT_SET_WORKERS_REPLY, snat_set_workers_reply)               \
-_(SNAT_WORKER_DETAILS, snat_worker_details)
+_(SNAT_WORKER_DETAILS, snat_worker_details)                     \
+_(SNAT_ADD_DEL_INTERFACE_ADDR_REPLY,                            \
+  snat_add_del_interface_addr_reply)                            \
+_(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details)
 
 /* M: construct, but don't yet send a message */
 #define M(T,t)                                                  \
@@ -539,6 +543,80 @@ static int api_snat_worker_dump(vat_main_t * vam)
   return 0;
 }
 
+static int api_snat_add_del_interface_addr (vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  unformat_input_t * i = vam->input;
+  f64 timeout;
+  vl_api_snat_add_del_interface_addr_t * mp;
+  u32 sw_if_index;
+  u8 sw_if_index_set = 0;
+  u8 is_add = 1;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+        sw_if_index_set = 1;
+      else if (unformat (i, "sw_if_index %d", &sw_if_index))
+        sw_if_index_set = 1;
+      else if (unformat (i, "del"))
+        is_add = 0;
+      else
+        {
+          clib_warning("unknown input '%U'", format_unformat_error, i);
+          return -99;
+        }
+    }
+
+  if (sw_if_index_set == 0)
+    {
+      errmsg ("interface / sw_if_index required\n");
+      return -99;
+    }
+
+  M(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr);
+  mp->sw_if_index = ntohl(sw_if_index);
+  mp->is_add = is_add;
+  
+  S; W;
+  /* NOTREACHED */
+  return 0;
+}
+
+static void vl_api_snat_interface_addr_details_t_handler
+  (vl_api_snat_interface_addr_details_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+
+  fformat (vam->ofp, "sw_if_index %d\n", ntohl (mp->sw_if_index));
+}
+
+static int api_snat_interface_addr_dump(vat_main_t * vam)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  f64 timeout;
+  vl_api_snat_interface_addr_dump_t * mp;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_address_dump");
+      return -99;
+    }
+
+  M(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_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;
+}
+
 /* 
  * List of messages that the api test plugin sends,
  * and that the data plane plugin processes
@@ -554,7 +632,10 @@ _(snat_static_mapping_dump, "")                                  \
 _(snat_show_config, "")                                          \
 _(snat_address_dump, "")                                         \
 _(snat_interface_dump, "")                                       \
-_(snat_worker_dump, "")
+_(snat_worker_dump, "")                                          \
+_(snat_add_del_interface_addr,                                   \
+  "<intfc> | sw_if_index <id> [del]")                            \
+_(snat_interface_addr_dump, "")
 
 void vat_api_hookup (vat_main_t *vam)
 {
index d23becf..653496e 100644 (file)
@@ -26,7 +26,7 @@ class TestSNAT(VppTestCase):
             cls.icmp_id_out = 6305
             cls.snat_addr = '10.0.0.3'
 
-            cls.create_pg_interfaces(range(7))
+            cls.create_pg_interfaces(range(8))
             cls.interfaces = list(cls.pg_interfaces[0:4])
 
             for i in cls.interfaces:
@@ -48,6 +48,8 @@ class TestSNAT(VppTestCase):
                 i.admin_up()
                 i.resolve_arp()
 
+            cls.pg7.admin_up()
+
         except Exception:
             super(TestSNAT, cls).tearDownClass()
             raise
@@ -178,6 +180,10 @@ class TestSNAT(VppTestCase):
         """
         Clear SNAT configuration.
         """
+        interfaces = self.vapi.snat_interface_addr_dump()
+        for intf in interfaces:
+            self.vapi.snat_add_interface_addr(intf.sw_if_index, is_add=0)
+
         interfaces = self.vapi.snat_interface_dump()
         for intf in interfaces:
             self.vapi.snat_interface_add_del_feature(intf.sw_if_index,
@@ -623,6 +629,24 @@ class TestSNAT(VppTestCase):
         # verify number of translated packet
         self.pg1.get_capture(pkts_num)
 
+    def test_interface_addr(self):
+        """ Acquire SNAT addresses from interface """
+        self.vapi.snat_add_interface_addr(self.pg7.sw_if_index)
+
+        # no address in NAT pool
+        adresses = self.vapi.snat_address_dump()
+        self.assertEqual(0, len(adresses))
+
+        # configure interface address and check NAT address pool
+        self.pg7.config_ip4()
+        adresses = self.vapi.snat_address_dump()
+        self.assertEqual(1, len(adresses))
+
+        # remove interface address and check NAT address pool
+        self.pg7.unconfig_ip4()
+        adresses = self.vapi.snat_address_dump()
+        self.assertEqual(0, len(adresses))
+
     def tearDown(self):
         super(TestSNAT, self).tearDown()
         if not self.vpp_dead:
index b78e861..73d3b56 100644 (file)
@@ -916,6 +916,24 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.snat_show_config, {})
 
+    def snat_add_interface_addr(
+            self,
+            sw_if_index,
+            is_add=1):
+        """Add/del S-NAT address from interface
+
+        :param sw_if_index: Software index of the interface
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(self.papi.snat_add_del_interface_addr,
+                        {'is_add': is_add, 'sw_if_index': sw_if_index})
+
+    def snat_interface_addr_dump(self):
+        """Dump S-NAT addresses interfaces
+        :return: Dictionary of S-NAT addresses interfaces
+        """
+        return self.api(self.papi.snat_interface_addr_dump, {})
+
     def control_ping(self):
         self.api(self.papi.control_ping)