NAT44: asymmetrical static mapping and one-armed NAT (VPP-1138) 06/10206/2
authorMatus Fabian <matfabia@cisco.com>
Tue, 23 Jan 2018 14:07:01 +0000 (06:07 -0800)
committerOle Trøan <otroan@employees.org>
Wed, 24 Jan 2018 07:46:54 +0000 (07:46 +0000)
One-armed NAT should work for asymmetrical static mappings without adding external address to the NAT44 pool.

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

index bedf4e5..56904ef 100644 (file)
@@ -387,7 +387,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
               if (ip0->dst_address.as_u32 == ap->addr.as_u32)
                 {
                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
-                  break;
+                  goto enqueue0;
                 }
             }
 
@@ -401,8 +401,17 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
               if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0))
                 {
                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                  goto enqueue0;
                 }
+              udp_header_t * udp0 = ip4_next_header (ip0);
+              m_key0.port = clib_net_to_host_u16 (udp0->dst_port);
+              m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol);
+              kv0.key = m_key0.as_u64;
+              if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0))
+                next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
             }
+
+        enqueue0:
           /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
index d2bc456..2988638 100644 (file)
@@ -3240,6 +3240,70 @@ class TestNAT44(MethodHolder):
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
+    def test_one_armed_nat44_static(self):
+        """ One armed NAT44 and 1:1 NAPT symmetrical rule """
+        remote_host = self.pg9.remote_hosts[0]
+        local_host = self.pg9.remote_hosts[1]
+        external_port = 80
+        local_port = 8080
+        eh_port_in = 0
+
+        self.vapi.nat44_forwarding_enable_disable(1)
+        self.nat44_add_address(self.nat_addr, twice_nat=1)
+        self.nat44_add_static_mapping(local_host.ip4, self.nat_addr,
+                                      local_port, external_port,
+                                      proto=IP_PROTOS.tcp, out2in_only=1,
+                                      twice_nat=1)
+        self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index,
+                                                  is_inside=0)
+
+        # from client to service
+        p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) /
+             IP(src=remote_host.ip4, dst=self.nat_addr) /
+             TCP(sport=12345, dport=external_port))
+        self.pg9.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg9.get_capture(1)
+        p = capture[0]
+        server = None
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, local_host.ip4)
+            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(tcp.dport, local_port)
+            self.assertNotEqual(tcp.sport, 12345)
+            eh_port_in = tcp.sport
+            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.pg9.remote_mac, dst=self.pg9.local_mac) /
+             IP(src=local_host.ip4, dst=self.nat_addr) /
+             TCP(sport=local_port, dport=eh_port_in))
+        self.pg9.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg9.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(ip.dst, remote_host.ip4)
+            self.assertEqual(tcp.sport, external_port)
+            self.assertEqual(tcp.dport, 12345)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
     def test_del_session(self):
         """ Delete NAT44 session """
         self.nat44_add_address(self.nat_addr)