Add setting of tenant VRF id for SNAT addresses (VPP-641) 42/5642/4
authorJuraj Sloboda <jsloboda@cisco.com>
Tue, 7 Mar 2017 03:55:21 +0000 (19:55 -0800)
committerDamjan Marion <dmarion.lists@gmail.com>
Tue, 7 Mar 2017 12:25:31 +0000 (12:25 +0000)
Change-Id: I9c0bb35ba16e04206ac481495f6638d3763754a1
Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
src/plugins/snat/in2out.c
src/plugins/snat/snat.api
src/plugins/snat/snat.c
src/plugins/snat/snat.h
test/test_snat.py
test/vpp_papi_provider.py

index fd8d30a..e9bc538 100644 (file)
@@ -316,7 +316,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
         (sm, &s->out2in, s->outside_address_index);
       s->outside_address_index = ~0;
 
-      if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+      if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, &key1,
+                                               &address_index))
         {
           ASSERT(0);
 
@@ -334,7 +335,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
         {
           static_mapping = 0;
           /* Try to create dynamic translation */
-          if (snat_alloc_outside_address_and_port (sm, &key1, &address_index))
+          if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, &key1,
+                                                   &address_index))
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
               return SNAT_IN2OUT_NEXT_DROP;
index 052d30f..8b1537b 100644 (file)
@@ -26,6 +26,7 @@
     @param is_ip4 - 1 if address type is IPv4
     @param first_ip_address - first IP address
     @param last_ip_address - last IP address
+    @param vrf_id - VRF id of tenant, ~0 means independent of VRF
     @param is_add - 1 if add, 0 if delete
 */
 define snat_add_address_range {
@@ -34,6 +35,7 @@ define snat_add_address_range {
   u8 is_ip4;
   u8 first_ip_address[16];
   u8 last_ip_address[16];
+  u32 vrf_id;
   u8 is_add;
 };
 
@@ -59,11 +61,13 @@ define snat_address_dump {
     @param context - sender context, to match reply w/ request
     @param is_ip4 - 1 if address type is IPv4
     @param ip_address - IP address
+    @param vrf_id - VRF id of tenant, ~0 means independent of VRF
 */
 define snat_address_details {
   u32 context;
   u8 is_ip4;
   u8 ip_address[16];
+  u32 vrf_id;
 };
 
 /** \brief Enable/disable S-NAT feature on the interface
index 9712a6c..12d1df4 100644 (file)
@@ -241,11 +241,14 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u32 sw_if_index, int is_add)
                            FIB_SOURCE_PLUGIN_HI);
 }
 
-void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
+void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
 {
   snat_address_t * ap;
   snat_interface_t *i;
 
+  if (vrf_id != ~0)
+    sm->vrf_mode = 1;
+
   /* Check if address already exists */
   vec_foreach (ap, sm->addresses)
     {
@@ -255,6 +258,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
 
   vec_add2 (sm->addresses, ap, 1);
   ap->addr = *addr;
+  ap->fib_index = ip4_fib_index_from_table_id(vrf_id);
 #define _(N, i, n, s) \
   clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535);
   foreach_snat_protocol
@@ -824,6 +828,7 @@ vl_api_snat_add_address_range_t_handler
   vl_api_snat_add_address_range_reply_t * rmp;
   ip4_address_t this_addr;
   u32 start_host_order, end_host_order;
+  u32 vrf_id;
   int i, count;
   int rv = 0;
   u32 * tmp;
@@ -847,6 +852,8 @@ vl_api_snat_add_address_range_t_handler
 
   count = (end_host_order - start_host_order) + 1;
 
+  vrf_id = clib_host_to_net_u32 (mp->vrf_id);
+
   if (count > 1024)
     clib_warning ("%U - %U, %d addresses...",
                   format_ip4_address, mp->first_ip_address,
@@ -858,7 +865,7 @@ vl_api_snat_add_address_range_t_handler
   for (i = 0; i < count; i++)
     {
       if (mp->is_add)
-        snat_add_address (sm, &this_addr);
+        snat_add_address (sm, &this_addr, vrf_id);
       else
         rv = snat_del_address (sm, this_addr, 0);
 
@@ -898,6 +905,10 @@ send_snat_address_details
   rmp->_vl_msg_id = ntohs (VL_API_SNAT_ADDRESS_DETAILS+sm->msg_id_base);
   rmp->is_ip4 = 1;
   clib_memcpy (rmp->ip_address, &(a->addr), 4);
+  if (a->fib_index != ~0)
+    rmp->vrf_id = ntohl(ip4_fib_get(a->fib_index)->table_id);
+  else
+    rmp->vrf_id = ~0;
   rmp->context = context;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
@@ -1786,6 +1797,7 @@ int snat_static_mapping_match (snat_main_t * sm,
 }
 
 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
+                                         u32 fib_index,
                                          snat_session_key_t * k,
                                          u32 * address_indexp)
 {
@@ -1796,6 +1808,8 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm,
   for (i = 0; i < vec_len (sm->addresses); i++)
     {
       a = sm->addresses + i;
+      if (sm->vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index)
+        continue;
       switch (k->protocol)
         {
 #define _(N, j, n, s) \
@@ -1842,6 +1856,7 @@ add_address_command_fn (vlib_main_t * vm,
   snat_main_t * sm = &snat_main;
   ip4_address_t start_addr, end_addr, this_addr;
   u32 start_host_order, end_host_order;
+  u32 vrf_id = ~0;
   int i, count;
   int is_add = 1;
   int rv = 0;
@@ -1857,6 +1872,8 @@ add_address_command_fn (vlib_main_t * vm,
                     unformat_ip4_address, &start_addr,
                     unformat_ip4_address, &end_addr))
         ;
+      else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
+        ;
       else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
         end_addr = start_addr;
       else if (unformat (line_input, "del"))
@@ -1897,7 +1914,7 @@ add_address_command_fn (vlib_main_t * vm,
   for (i = 0; i < count; i++)
     {
       if (is_add)
-        snat_add_address (sm, &this_addr);
+        snat_add_address (sm, &this_addr, vrf_id);
       else
         rv = snat_del_address (sm, this_addr, 0);
 
@@ -1924,7 +1941,8 @@ done:
 
 VLIB_CLI_COMMAND (add_address_command, static) = {
   .path = "snat add address",
-  .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>] [del]",
+  .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>] "
+                "[tenant-vrf <vrf-id>] [del]",
   .function = add_address_command_fn,
 };
 
@@ -2543,6 +2561,11 @@ show_snat_command_fn (vlib_main_t * vm,
       vec_foreach (ap, sm->addresses)
         {
           vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
+          if (ap->fib_index != ~0)
+              vlib_cli_output (vm, "  tenant VRF: %u",
+                               ip4_fib_get(ap->fib_index)->table_id);
+          else
+            vlib_cli_output (vm, "  tenant VRF independent");
 #define _(N, i, n, s) \
           vlib_cli_output (vm, "  %d busy %s ports", ap->busy_##n##_ports, s);
           foreach_snat_protocol
@@ -2675,7 +2698,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
                 if (sm->addresses[j].addr.as_u32 == address->as_u32)
                   return;
 
-              snat_add_address (sm, address);
+              snat_add_address (sm, address, ~0);
               /* Scan static map resolution vector */
               for (j = 0; j < vec_len (sm->to_resolve); j++)
                 {
@@ -2773,7 +2796,7 @@ static int snat_add_interface_address (snat_main_t *sm,
 
   /* If the address is already bound - or static - add it now */
   if (first_int_addr)
-      snat_add_address (sm, first_int_addr);
+      snat_add_address (sm, first_int_addr, ~0);
 
   return 0;
 }
index 47f2e6e..1d203aa 100644 (file)
@@ -118,6 +118,7 @@ typedef struct {
 
 typedef struct {
   ip4_address_t addr;
+  u32 fib_index;
 #define _(N, i, n, s) \
   u32 busy_##n##_ports; \
   uword * busy_##n##_port_bitmap;
@@ -226,6 +227,9 @@ typedef struct {
   u32 inside_vrf_id;
   u32 inside_fib_index;
 
+  /* tenant VRF aware address pool activation flag */
+  u8 vrf_mode;
+
   /* API message ID base */
   u16 msg_id_base;
 
@@ -250,6 +254,7 @@ void snat_free_outside_address_and_port (snat_main_t * sm,
                                          u32 address_index);
 
 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
+                                         u32 fib_index,
                                          snat_session_key_t * k,
                                          u32 * address_indexp);
 
index 4cb5116..f5e6e13 100644 (file)
@@ -405,7 +405,7 @@ class TestSNAT(VppTestCase):
             proto,
             is_add)
 
-    def snat_add_address(self, ip, is_add=1):
+    def snat_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF):
         """
         Add/delete S-NAT address
 
@@ -413,7 +413,8 @@ class TestSNAT(VppTestCase):
         :param is_add: 1 if add, 0 if delete (Default add)
         """
         snat_addr = socket.inet_pton(socket.AF_INET, ip)
-        self.vapi.snat_add_address_range(snat_addr, snat_addr, is_add)
+        self.vapi.snat_add_address_range(snat_addr, snat_addr, is_add,
+                                         vrf_id=vrf_id)
 
     def test_dynamic(self):
         """ SNAT dynamic translation test """
@@ -1201,6 +1202,73 @@ class TestSNAT(VppTestCase):
         self.pg_start()
         capture = self.pg1.get_capture(0)
 
+    def test_vrf_mode(self):
+        """ S-NAT tenant VRF aware address pool mode """
+
+        vrf_id1 = 1
+        vrf_id2 = 2
+        nat_ip1 = "10.0.0.10"
+        nat_ip2 = "10.0.0.11"
+
+        self.pg0.unconfig_ip4()
+        self.pg1.unconfig_ip4()
+        self.pg0.set_table_ip4(vrf_id1)
+        self.pg1.set_table_ip4(vrf_id2)
+        self.pg0.config_ip4()
+        self.pg1.config_ip4()
+
+        self.snat_add_address(nat_ip1, vrf_id=vrf_id1)
+        self.snat_add_address(nat_ip2, vrf_id=vrf_id2)
+        self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index)
+        self.vapi.snat_interface_add_del_feature(self.pg2.sw_if_index,
+                                                 is_inside=0)
+
+        # first VRF
+        pkts = self.create_stream_in(self.pg0, self.pg2)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg2.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip1)
+
+        # second VRF
+        pkts = self.create_stream_in(self.pg1, self.pg2)
+        self.pg1.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg2.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip2)
+
+    def test_vrf_feature_independent(self):
+        """ S-NAT tenant VRF independent address pool mode """
+
+        nat_ip1 = "10.0.0.10"
+        nat_ip2 = "10.0.0.11"
+
+        self.snat_add_address(nat_ip1)
+        self.snat_add_address(nat_ip2)
+        self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index)
+        self.vapi.snat_interface_add_del_feature(self.pg2.sw_if_index,
+                                                 is_inside=0)
+
+        # first VRF
+        pkts = self.create_stream_in(self.pg0, self.pg2)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg2.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip1)
+
+        # second VRF
+        pkts = self.create_stream_in(self.pg1, self.pg2)
+        self.pg1.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg2.get_capture(len(pkts))
+        self.verify_capture_out(capture, nat_ip1)
+
     def tearDown(self):
         super(TestSNAT, self).tearDown()
         if not self.vpp_dead:
index fe152de..c7e875f 100644 (file)
@@ -1023,11 +1023,13 @@ class VppPapiProvider(object):
             first_ip_address,
             last_ip_address,
             is_add=1,
-            is_ip4=1):
+            is_ip4=1,
+            vrf_id=0xFFFFFFFF):
         """Add/del S-NAT address range
 
         :param first_ip_address: First IP address
         :param last_ip_address: Last IP address
+        :param vrf_id: VRF id for the address range
         :param is_add: 1 if add, 0 if delete (Default value = 1)
         :param is_ip4: 1 if address type is IPv4 (Default value = 1)
         """
@@ -1036,6 +1038,7 @@ class VppPapiProvider(object):
             {'is_ip4': is_ip4,
              'first_ip_address': first_ip_address,
              'last_ip_address': last_ip_address,
+             'vrf_id': vrf_id,
              'is_add': is_add})
 
     def snat_address_dump(self):