NAT44: asymmetrical static mapping rule (VPP-1135) 54/10154/2
authorMatus Fabian <matfabia@cisco.com>
Thu, 18 Jan 2018 11:38:45 +0000 (03:38 -0800)
committerOle Trøan <otroan@employees.org>
Mon, 22 Jan 2018 09:44:45 +0000 (09:44 +0000)
add option to NAT44 static mapping API/CLI to make rule asymmetrical (rule match only out2in direction)

Change-Id: If262a3ff375a24d3059f0de1f1ac387a4fe09475
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/nat/nat.api
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat_api.c
test/test_nat.py
test/vpp_papi_provider.py

index 78f3450..1de70ed 100644 (file)
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-vl_api_version 2.2.0
+vl_api_version 2.3.0
 
 /**
  * @file nat.api
@@ -352,6 +352,7 @@ define nat44_interface_output_feature_details {
     @param vfr_id - VRF ID
     @param twice_nat - if 1 translate external host address and port, only for
                        1:1 NAPT (addr_only must be 0)
+    @param out2in_only - if 1 rule match only out2in direction
 */
 autoreply define nat44_add_del_static_mapping {
   u32 client_index;
@@ -366,6 +367,7 @@ autoreply define nat44_add_del_static_mapping {
   u32 external_sw_if_index;
   u32 vrf_id;
   u8 twice_nat;
+  u8 out2in_only;
 };
 
 /** \brief Dump NAT44 static mappings
@@ -388,6 +390,7 @@ define nat44_static_mapping_dump {
     @param external_sw_if_index - external interface
     @param vfr_id - VRF ID
     @param twice_nat - if 1 translate external host address and port
+    @param out2in_only - if 1 rule match only out2in direction
 */
 define nat44_static_mapping_details {
   u32 context;
@@ -400,6 +403,7 @@ define nat44_static_mapping_details {
   u32 external_sw_if_index;
   u32 vrf_id;
   u8 twice_nat;
+  u8 out2in_only;
 };
 
 /** \brief Add/delete NAT44 identity mapping
index 99c5f63..6ed4877 100644 (file)
@@ -643,13 +643,14 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @param sw_if_index External port instead of specific IP address.
  * @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
  *
  * @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 twice_nat, u8 out2in_only)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -723,7 +724,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
 
       /* 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))
+      if (!(addr_only || sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -766,6 +767,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m->vrf_id = vrf_id;
       m->fib_index = fib_index;
       m->twice_nat = twice_nat;
+      m->out2in_only = out2in_only;
       if (!addr_only)
         {
           m->local_port = l_port;
@@ -790,8 +792,9 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.fib_index = m->fib_index;
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
-      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
-      if (twice_nat)
+      if (!out2in_only)
+        clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (l_port);
           kv.key = m_key.as_u64;
@@ -806,7 +809,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
-      if (twice_nat)
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (e_port);
           kv.key = m_key.as_u64;
@@ -822,7 +825,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
         return VNET_API_ERROR_NO_SUCH_ENTRY;
 
       /* Free external address port */
-      if (!addr_only && !(sm->static_mapping_only))
+      if (!(addr_only || sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -861,8 +864,9 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.protocol = m->proto;
       m_key.fib_index = m->fib_index;
       kv.key = m_key.as_u64;
-      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
-      if (twice_nat)
+      if (!out2in_only)
+        clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (m->local_port);
           kv.key = m_key.as_u64;
@@ -876,7 +880,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.fib_index = sm->outside_fib_index;
       kv.key = m_key.as_u64;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
-      if (twice_nat)
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (m->external_port);
           kv.key = m_key.as_u64;
@@ -1273,7 +1277,8 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
             (void) snat_add_static_mapping (m->local_addr, m->external_addr,
                                             m->local_port, m->external_port,
                                             m->vrf_id, m->addr_only, ~0,
-                                            m->proto, 0, m->twice_nat);
+                                            m->proto, 0, m->twice_nat,
+                                            m->out2in_only);
       }));
     }
   else
@@ -2299,6 +2304,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
   snat_protocol_t proto = ~0;
   u8 proto_set = 0;
   u8 twice_nat = 0;
+  u8 out2in_only = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2331,6 +2337,8 @@ add_static_mapping_command_fn (vlib_main_t * vm,
         proto_set = 1;
       else if (unformat (line_input, "twice-nat"))
         twice_nat = 1;
+      else if (unformat (line_input, "out2in-only"))
+        out2in_only = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -2355,7 +2363,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
 
   rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
                                vrf_id, addr_only, sw_if_index, proto, is_add,
-                               twice_nat);
+                               twice_nat, out2in_only);
 
   switch (rv)
     {
@@ -2403,7 +2411,7 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
   .function = add_static_mapping_command_fn,
   .short_help =
     "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] "
-    "external <addr> [<port>] [vrf <table-id>] [twice-nat] [del]",
+    "external <addr> [<port>] [vrf <table-id>] [twice-nat] [out2in-only] [del]",
 };
 
 static clib_error_t *
@@ -2452,7 +2460,7 @@ add_identity_mapping_command_fn (vlib_main_t * vm,
 
   rv = snat_add_static_mapping(addr, addr, (u16) port, (u16) port,
                                vrf_id, addr_only, sw_if_index, proto, is_add,
-                               0);
+                               0, 0);
 
   switch (rv)
     {
@@ -3175,22 +3183,24 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
    {
       if (vec_len (m->locals))
         {
-          s = format (s, "%U vrf %d external %U:%d %s",
+          s = format (s, "%U vrf %d external %U:%d %s %s",
                       format_snat_protocol, m->proto,
                       m->vrf_id,
                       format_ip4_address, &m->external_addr, m->external_port,
-                      m->twice_nat ? "twice-nat" : "");
+                      m->twice_nat ? "twice-nat" : "",
+                      m->out2in_only ? "out2in-only" : "");
           vec_foreach (local, m->locals)
             s = format (s, "\n  local %U:%d probability %d\%",
                         format_ip4_address, &local->addr, local->port,
                         local->probability);
         }
       else
-        s = format (s, "%U local %U:%d external %U:%d vrf %d %s",
+        s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s",
                     format_snat_protocol, m->proto,
                     format_ip4_address, &m->local_addr, m->local_port,
                     format_ip4_address, &m->external_addr, m->external_port,
-                    m->vrf_id, m->twice_nat ? "twice-nat" : "");
+                    m->vrf_id, m->twice_nat ? "twice-nat" : "",
+                    m->out2in_only ? "out2in-only" : "");
    }
   return s;
 }
@@ -3562,7 +3572,7 @@ match:
                                             ~0 /* sw_if_index */,
                                             rp->proto,
                                             rp->is_add,
-                                            0);
+                                            0, 0);
               if (rv)
                 clib_warning ("snat_add_static_mapping returned %d",
                               rv);
index ff7888a..eba0051 100644 (file)
@@ -545,7 +545,7 @@ void nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add);
 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 twice_nat, u8 out2in_only);
 clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm);
 int snat_set_workers (uword * bitmap);
 int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del);
index f3d353f..f23efa8 100644 (file)
@@ -711,7 +711,7 @@ static void
   rv = snat_add_static_mapping (local_addr, external_addr, local_port,
                                external_port, vrf_id, mp->addr_only,
                                external_sw_if_index, proto, mp->is_add,
-                               mp->twice_nat);
+                               mp->twice_nat, mp->out2in_only);
 
   REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_REPLY);
 }
@@ -732,7 +732,8 @@ static void *vl_api_nat44_add_del_static_mapping_t_print
                clib_net_to_host_u16 (mp->local_port),
                clib_net_to_host_u16 (mp->external_port));
 
-  s = format (s, "twice_nat %d ", mp->twice_nat);
+  s = format (s, "twice_nat %d out2in_only %d ",
+             mp->twice_nat, mp->out2in_only);
 
   if (mp->vrf_id != ~0)
     s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
@@ -764,6 +765,7 @@ send_nat44_static_mapping_details (snat_static_mapping_t * m,
   rmp->protocol = snat_proto_to_ip_proto (m->proto);
   rmp->context = context;
   rmp->twice_nat = m->twice_nat;
+  rmp->out2in_only = m->out2in_only;
 
   vl_api_send_msg (reg, (u8 *) rmp);
 }
@@ -861,7 +863,7 @@ static void
 
   rv =
     snat_add_static_mapping (addr, addr, port, port, vrf_id, mp->addr_only,
-                            sw_if_index, proto, mp->is_add, 0);
+                            sw_if_index, proto, mp->is_add, 0, 0);
 
   REPLY_MACRO (VL_API_NAT44_ADD_DEL_IDENTITY_MAPPING_REPLY);
 }
@@ -1259,7 +1261,7 @@ static void *vl_api_nat44_add_del_lb_static_mapping_t_print
   u8 *s;
 
   s = format (0, "SCRIPT: nat44_add_del_lb_static_mapping ");
-  s = format (s, "is_add %d twice_nat %d out2in_only ",
+  s = format (s, "is_add %d twice_nat %d out2in_only %d ",
              mp->is_add, mp->twice_nat, mp->out2in_only);
 
   FINISH;
index 384ec8f..d2bc456 100644 (file)
@@ -1029,6 +1029,7 @@ class TestNAT44(MethodHolder):
                 vrf_id=sm.vrf_id,
                 protocol=sm.protocol,
                 twice_nat=sm.twice_nat,
+                out2in_only=sm.out2in_only,
                 is_add=0)
 
         lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump()
@@ -1068,7 +1069,7 @@ class TestNAT44(MethodHolder):
     def nat44_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,
-                                 proto=0, twice_nat=0):
+                                 proto=0, twice_nat=0, out2in_only=0):
         """
         Add/delete NAT44 static mapping
 
@@ -1081,6 +1082,7 @@ class TestNAT44(MethodHolder):
         :param external_sw_if_index: External interface instead of IP address
         :param proto: IP protocol (Mandatory if port specified)
         :param twice_nat: 1 if translate external host address and port
+        :param out2in_only: if 1 rule is matching only out2in direction
         """
         addr_only = 1
         if local_port and external_port:
@@ -1097,6 +1099,7 @@ class TestNAT44(MethodHolder):
             vrf_id,
             proto,
             twice_nat,
+            out2in_only,
             is_add)
 
     def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0):
@@ -1493,6 +1496,102 @@ class TestNAT44(MethodHolder):
         capture = self.pg1.get_capture(len(pkts))
         self.verify_capture_out(capture)
 
+    def test_static_with_port_out2(self):
+        """ 1:1 NAPT symmetrical rule """
+
+        external_port = 80
+        local_port = 8080
+
+        self.vapi.nat44_forwarding_enable_disable(1)
+        self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
+                                      local_port, external_port,
+                                      proto=IP_PROTOS.tcp, out2in_only=1)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+
+        # from client to service
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=12345, dport=external_port))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        p = capture[0]
+        server = None
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, self.pg0.remote_ip4)
+            self.assertEqual(tcp.dport, local_port)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        # from service back to client
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=local_port, dport=12345))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(tcp.sport, external_port)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        # from client to server (no translation)
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) /
+             TCP(sport=12346, dport=local_port))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        p = capture[0]
+        server = None
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, self.pg0.remote_ip4)
+            self.assertEqual(tcp.dport, local_port)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        # from service back to client (no translation)
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=local_port, dport=12346))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, self.pg0.remote_ip4)
+            self.assertEqual(tcp.sport, local_port)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
     def test_static_vrf_aware(self):
         """ 1:1 NAT VRF awareness """
 
@@ -2023,7 +2122,7 @@ class TestNAT44(MethodHolder):
             self.assertEqual(tcp.dport, host_in_port)
             self.check_tcp_checksum(p)
         except:
-            self.logger.error(ppp("Unexpected or invalid packet:"), p)
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
     def test_hairpinning2(self):
@@ -3082,7 +3181,7 @@ class TestNAT44(MethodHolder):
             self.assertEqual(tcp.dport, host_in_port)
             self.check_tcp_checksum(p)
         except:
-            self.logger.error(ppp("Unexpected or invalid packet:"), p)
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
     def test_one_armed_nat44(self):
index 6e6f7ca..13dccc9 100644 (file)
@@ -1235,6 +1235,7 @@ class VppPapiProvider(object):
             vrf_id=0,
             protocol=0,
             twice_nat=0,
+            out2in_only=0,
             is_add=1):
         """Add/delete NAT44 static mapping
 
@@ -1247,6 +1248,7 @@ class VppPapiProvider(object):
         :param vrf_id: VRF ID
         :param protocol: IP protocol (Default value = 0)
         :param twice_nat: 1 if translate external host address and port
+        :param out2in_only: if 1 rule is matching only out2in direction
         :param is_add: 1 if add, 0 if delete (Default value = 1)
         """
         return self.api(
@@ -1260,7 +1262,8 @@ class VppPapiProvider(object):
              'external_sw_if_index': external_sw_if_index,
              'vrf_id': vrf_id,
              'protocol': protocol,
-             'twice_nat': twice_nat})
+             'twice_nat': twice_nat,
+             'out2in_only': out2in_only})
 
     def nat44_add_del_identity_mapping(
             self,