nat: fix update of outside fibs (output-feature) 23/21423/11
authorDmitry Vakhrushev <dmitry@netgate.com>
Tue, 20 Aug 2019 18:44:51 +0000 (14:44 -0400)
committerDamjan Marion <dmarion@me.com>
Mon, 23 Sep 2019 12:43:13 +0000 (12:43 +0000)
NAT hasn't worked when NAT interfaces wasn't in
default VRF (fib_index = 0). This issue has been occurred with
interfaces with output-feature in endpoint-dependent mode.

Update VAT commands:
  - update nat44_add_del_address_range
  - add nat44_interface_add_del_output_feature

Ticket: VPP-1732
Type: fix

Change-Id: Iddea15dde4b948f159a0056d48c55bd917037fd1
Signed-off-by: Dmitry Vakhrushev <dmitry@netgate.com>
src/plugins/nat/nat.c
src/plugins/nat/nat_test.c
src/plugins/nat/test/test_nat.py

index 248cd75..85072bc 100755 (executable)
@@ -2199,6 +2199,16 @@ snat_update_outside_fib (u32 sw_if_index, u32 new_fib_index,
           match = 1;
         }
     }));
+
+  pool_foreach (i, sm->output_feature_interfaces,
+    ({
+      if (i->sw_if_index == sw_if_index)
+        {
+          if (!(nat_interface_is_outside (i)))
+           return;
+          match = 1;
+        }
+    }));
   /* *INDENT-ON* */
 
   if (!match)
index 1dd25b3..685f85b 100644 (file)
@@ -64,6 +64,7 @@ snat_test_main_t snat_test_main;
 #define foreach_standard_reply_retval_handler    \
 _(nat44_add_del_address_range_reply)             \
 _(nat44_interface_add_del_feature_reply)         \
+_(nat44_interface_add_del_output_feature_reply)  \
 _(nat44_add_del_static_mapping_reply)            \
 _(nat_set_workers_reply)                         \
 _(nat44_add_del_interface_addr_reply)            \
@@ -98,6 +99,8 @@ _(NAT44_ADD_DEL_ADDRESS_RANGE_REPLY,                            \
   nat44_add_del_address_range_reply)                            \
 _(NAT44_INTERFACE_ADD_DEL_FEATURE_REPLY,                        \
   nat44_interface_add_del_feature_reply)                        \
+_(NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY,                 \
+  nat44_interface_add_del_output_feature_reply)                 \
 _(NAT44_ADD_DEL_STATIC_MAPPING_REPLY,                           \
   nat44_add_del_static_mapping_reply)                           \
 _(NAT_CONTROL_PING_REPLY, nat_control_ping_reply)               \
@@ -133,6 +136,8 @@ static int api_nat44_add_del_address_range (vat_main_t * vam)
   u32 start_host_order, end_host_order;
   vl_api_nat44_add_del_address_range_t * mp;
   u8 is_add = 1;
+  u8 twice_nat = 0;
+  int vrf_id = ~0;
   int count;
   int ret;
 
@@ -144,6 +149,10 @@ static int api_nat44_add_del_address_range (vat_main_t * vam)
         ;
       else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
         end_addr = start_addr;
+      else if (unformat (i, "twice-nat"))
+        twice_nat = 1;
+      else if (unformat (i, "vrf %u", &vrf_id))
+        ;
       else if (unformat (i, "del"))
         is_add = 0;
       else
@@ -176,6 +185,9 @@ static int api_nat44_add_del_address_range (vat_main_t * vam)
 
   memcpy (mp->first_ip_address, &start_addr, 4);
   memcpy (mp->last_ip_address, &end_addr, 4);
+  mp->vrf_id = vrf_id;
+  if (twice_nat)
+    mp->flags = (vl_api_nat_config_flags_t)NAT_API_IS_TWICE_NAT;
   mp->is_add = is_add;
 
   S(mp);
@@ -229,6 +241,52 @@ static int api_nat44_interface_add_del_feature (vat_main_t * vam)
   return ret;
 }
 
+static int api_nat44_interface_add_del_output_feature (vat_main_t * vam)
+{
+  unformat_input_t * i = vam->input;
+  vl_api_nat44_interface_add_del_output_feature_t * mp;
+  u32 sw_if_index;
+  u8 sw_if_index_set = 0;
+  u8 is_inside = 1;
+  u8 is_add = 1;
+  int ret;
+
+  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, "out"))
+        is_inside = 0;
+      else if (unformat (i, "in"))
+        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)
+    {
+      errmsg ("interface / sw_if_index required\n");
+      return -99;
+    }
+
+  M(NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE, mp);
+  mp->sw_if_index = ntohl(sw_if_index);
+  mp->is_add = is_add;
+  if (is_inside)
+    mp->flags |= NAT_API_IS_INSIDE;
+
+  S(mp);
+  W (ret);
+  return ret;
+}
+
 static int api_nat44_add_del_static_mapping(vat_main_t * vam)
 {
   unformat_input_t * i = vam->input;
@@ -578,6 +636,7 @@ static int api_nat44_add_del_interface_addr (vat_main_t * vam)
   u32 sw_if_index;
   u8 sw_if_index_set = 0;
   u8 is_add = 1;
+  u8 twice_nat = 0;
   int ret;
 
   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
@@ -586,6 +645,8 @@ static int api_nat44_add_del_interface_addr (vat_main_t * vam)
         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, "twice-nat"))
+        twice_nat = 1;
       else if (unformat (i, "del"))
         is_add = 0;
       else
@@ -603,7 +664,8 @@ static int api_nat44_add_del_interface_addr (vat_main_t * vam)
 
   M(NAT44_ADD_DEL_INTERFACE_ADDR, mp);
   mp->sw_if_index = ntohl(sw_if_index);
-
+  if (twice_nat)
+    mp->flags = (vl_api_nat_config_flags_t)NAT_API_IS_TWICE_NAT;
   mp->is_add = is_add;
 
   S(mp);
@@ -1099,9 +1161,12 @@ static int api_nat_det_session_dump(vat_main_t * vam)
  * and that the data plane plugin processes
  */
 #define foreach_vpe_api_msg                                       \
-_(nat44_add_del_address_range, "<start-addr> [- <end-addr] [del]")\
+_(nat44_add_del_address_range,                                    \
+  "<start-addr> [- <end-addr>] [vrf <table-id>] [twice-nat] [del]") \
 _(nat44_interface_add_del_feature,                                \
   "<intfc> | sw_if_index <id> [in] [out] [del]")                  \
+_(nat44_interface_add_del_output_feature,                         \
+  "<intfc> | sw_if_index <id> [in] [out] [del]")                  \
 _(nat44_add_del_static_mapping, "local_addr <ip>"                 \
   " (external_addr <ip> | external_if <intfc> |"                  \
   " external_sw_if_ndex <id>) [local_port <n>]"                   \
@@ -1113,7 +1178,7 @@ _(nat44_address_dump, "")                                         \
 _(nat44_interface_dump, "")                                       \
 _(nat_worker_dump, "")                                            \
 _(nat44_add_del_interface_addr,                                   \
-  "<intfc> | sw_if_index <id> [del]")                             \
+  "<intfc> | sw_if_index <id> [twice-nat] [del]")                 \
 _(nat44_interface_addr_dump, "")                                  \
 _(nat_ipfix_enable_disable, "[domain <id>] [src_port <n>] "       \
   "[disable]")                                                    \
index 4d48ee4..ad44dbd 100644 (file)
@@ -4414,7 +4414,7 @@ class TestNAT44EndpointDependent(MethodHolder):
             cls.ipfix_domain_id = 1
             cls.tcp_external_port = 80
 
-            cls.create_pg_interfaces(range(7))
+            cls.create_pg_interfaces(range(9))
             cls.interfaces = list(cls.pg_interfaces[0:3])
 
             for i in cls.interfaces:
@@ -4493,6 +4493,16 @@ class TestNAT44EndpointDependent(MethodHolder):
             cls.pg5.resolve_arp()
             cls.pg6.resolve_arp()
 
+            cls.pg7.admin_up()
+            cls.pg7.config_ip4()
+            cls.pg7.resolve_arp()
+            cls.pg7.generate_remote_hosts(3)
+            cls.pg7.configure_ipv4_neighbors()
+
+            cls.pg8.admin_up()
+            cls.pg8.config_ip4()
+            cls.pg8.resolve_arp()
+
         except Exception:
             super(TestNAT44EndpointDependent, cls).tearDownClass()
             raise
@@ -4767,6 +4777,115 @@ class TestNAT44EndpointDependent(MethodHolder):
         sessions = self.statistics.get_counter('/nat44/total-sessions')
         self.assertEqual(sessions[0][0], 3)
 
+    def test_dynamic_output_feature_vrf(self):
+        """ NAT44 dynamic translation test: output-feature, VRF"""
+
+        # other then default (0)
+        new_vrf_id = 22
+
+        self.nat44_add_address(self.nat_addr)
+        flags = self.config_flags.NAT_IS_INSIDE
+        self.vapi.nat44_interface_add_del_output_feature(
+            sw_if_index=self.pg7.sw_if_index,
+            flags=flags, is_add=1)
+        self.vapi.nat44_interface_add_del_output_feature(
+            sw_if_index=self.pg8.sw_if_index,
+            is_add=1)
+
+        try:
+            self.vapi.ip_table_add_del(is_add=1, table_id=new_vrf_id)
+
+            self.pg7.unconfig_ip4()
+            self.pg7.set_table_ip4(new_vrf_id)
+            self.pg7.config_ip4()
+            self.pg7.resolve_arp()
+
+            self.pg8.unconfig_ip4()
+            self.pg8.set_table_ip4(new_vrf_id)
+            self.pg8.config_ip4()
+            self.pg8.resolve_arp()
+
+            nat_config = self.vapi.nat_show_config()
+            self.assertEqual(1, nat_config.endpoint_dependent)
+
+            # in2out
+            tcpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/TCP packets')
+            udpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/UDP packets')
+            icmpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/ICMP packets')
+            totaln = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
+
+            pkts = self.create_stream_in(self.pg7, self.pg8)
+            self.pg7.add_stream(pkts)
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+            capture = self.pg8.get_capture(len(pkts))
+            self.verify_capture_out(capture)
+
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/TCP packets')
+            self.assertEqual(err - tcpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/UDP packets')
+            self.assertEqual(err - udpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/ICMP packets')
+            self.assertEqual(err - icmpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
+            self.assertEqual(err - totaln, 3)
+
+            # out2in
+            tcpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/TCP packets')
+            udpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/UDP packets')
+            icmpn = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in-slowpath/ICMP packets')
+            totaln = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/good out2in packets processed')
+
+            pkts = self.create_stream_out(self.pg8)
+            self.pg8.add_stream(pkts)
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+            capture = self.pg7.get_capture(len(pkts))
+            self.verify_capture_in(capture, self.pg7)
+
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/TCP packets')
+            self.assertEqual(err - tcpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/UDP packets')
+            self.assertEqual(err - udpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in-slowpath/ICMP packets')
+            self.assertEqual(err - icmpn, 1)
+            err = self.statistics.get_err_counter(
+                '/err/nat44-ed-out2in/good out2in packets processed')
+            self.assertEqual(err - totaln, 2)
+
+            users = self.statistics.get_counter('/nat44/total-users')
+            self.assertEqual(users[0][0], 1)
+            sessions = self.statistics.get_counter('/nat44/total-sessions')
+            self.assertEqual(sessions[0][0], 3)
+
+        finally:
+            self.pg7.unconfig_ip4()
+            self.pg7.set_table_ip4(1)
+            self.pg7.config_ip4()
+            self.pg7.resolve_arp()
+
+            self.pg8.unconfig_ip4()
+            self.pg8.set_table_ip4(1)
+            self.pg8.config_ip4()
+            self.pg8.resolve_arp()
+
+            self.vapi.ip_table_add_del(is_add=0, table_id=new_vrf_id)
+
     def test_forwarding(self):
         """ NAT44 forwarding test """