NAT44: asymmetrical load balancing static mapping rule (VPP-1132) 73/10073/1
authorMatus Fabian <matfabia@cisco.com>
Thu, 11 Jan 2018 12:09:17 +0000 (04:09 -0800)
committerMatus Fabian <matfabia@cisco.com>
Thu, 11 Jan 2018 12:10:26 +0000 (04:10 -0800)
Add option to NAT44 load balancing static mapping API/CLI to make rule asymmetrical (rule match only in out2in direction).

Change-Id: I325ecef5591e4bf44ce4469a24d44fe56c3bb2e9
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/nat/in2out.c
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 5f2f43f..0b87ba4 100755 (executable)
@@ -1467,7 +1467,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                     {
                       s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0,
                                           thread_index, now, vm, node);
-                      if (!s0)
+                      if (!s0 && !sm->forwarding_enabled)
                         next0 = SNAT_IN2OUT_NEXT_DROP;
                       goto trace00;
                     }
@@ -1646,7 +1646,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                     {
                       s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1,
                                           thread_index, now, vm, node);
-                      if (!s1)
+                      if (!s1 && !sm->forwarding_enabled)
                         next1 = SNAT_IN2OUT_NEXT_DROP;
                       goto trace01;
                     }
@@ -1858,7 +1858,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
                     {
                       s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0,
                                           thread_index, now, vm, node);
-                      if (!s0)
+                      if (!s0 && !sm->forwarding_enabled)
                         next0 = SNAT_IN2OUT_NEXT_DROP;
                       goto trace0;
                     }
index 6ebe4e3..78f3450 100644 (file)
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-vl_api_version 2.1.0
+vl_api_version 2.2.0
 
 /**
  * @file nat.api
@@ -567,6 +567,7 @@ autoreply manual_endian define nat44_add_del_lb_static_mapping {
   u8 protocol;
   u32 vrf_id;
   u8 twice_nat;
+  u8 out2in_only;
   u8 local_num;
   vl_api_nat44_lb_addr_port_t locals[local_num];
 };
@@ -583,6 +584,7 @@ manual_endian define nat44_lb_static_mapping_details {
   u8 protocol;
   u32 vrf_id;
   u8 twice_nat;
+  u8 out2in_only;
   u8 local_num;
   vl_api_nat44_lb_addr_port_t locals[local_num];
 };
index 90515ac..99c5f63 100644 (file)
@@ -973,7 +973,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
                                      nat44_lb_addr_port_t *locals, u8 is_add,
-                                     u8 twice_nat)
+                                     u8 twice_nat, u8 out2in_only)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -1014,7 +1014,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
       /* Find external address in allocated addresses and reserve port for
          address and port pair mapping when dynamic translations enabled */
-      if (!sm->static_mapping_only)
+      if (!(sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -1058,6 +1058,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       m->external_port = e_port;
       m->proto = proto;
       m->twice_nat = twice_nat;
+      m->out2in_only = out2in_only;
 
       m_key.addr = m->external_addr;
       m_key.port = m->external_port;
@@ -1095,10 +1096,13 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       for (i = 0; i < vec_len (locals); i++)
         {
           m_key.addr = locals[i].addr;
-          m_key.port = locals[i].port;
-          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 (!out2in_only)
+            {
+              m_key.port = locals[i].port;
+              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);
+            }
           locals[i].prefix = (i == 0) ? locals[i].probability :\
             (locals[i - 1].prefix + locals[i].probability);
           vec_add1 (m->locals, locals[i]);
@@ -1121,7 +1125,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI);
 
       /* Free external address port */
-      if (!sm->static_mapping_only)
+      if (!(sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -1173,13 +1177,16 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       vec_foreach (local, m->locals)
         {
           m_key.addr = local->addr;
-          m_key.port = local->port;
-          m_key.fib_index = m->fib_index;
-          kv.key = m_key.as_u64;
-          if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
+          if (!out2in_only)
             {
-              clib_warning ("static_mapping_by_local key del failed");
-              return VNET_API_ERROR_UNSPECIFIED;
+              m_key.port = local->port;
+              m_key.fib_index = m->fib_index;
+              kv.key = m_key.as_u64;
+              if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
+                {
+                  clib_warning ("static_mapping_by_local key del failed");
+                  return VNET_API_ERROR_UNSPECIFIED;
+                }
             }
 
           m_key.port = clib_host_to_net_u16 (local->port);
@@ -2509,6 +2516,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
   u8 proto_set = 0;
   nat44_lb_addr_port_t *locals = 0, local;
   u8 twice_nat = 0;
+  u8 out2in_only = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2535,6 +2543,8 @@ add_lb_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
@@ -2558,7 +2568,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
     }
 
   rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id,
-                                        locals, is_add, twice_nat);
+                                        locals, is_add, twice_nat, out2in_only);
 
   switch (rv)
     {
@@ -2591,7 +2601,7 @@ VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = {
   .short_help =
     "nat44 add load-balancing static mapping protocol tcp|udp "
     "external <addr>:<port> local <addr>:<port> probability <n> [twice-nat] "
-    "[vrf <table-id>] [del]",
+    "[vrf <table-id>] [out2in-only] [del]",
 };
 
 static clib_error_t *
index 614668d..ff7888a 100644 (file)
@@ -217,6 +217,7 @@ typedef struct {
   u16 external_port;
   u8 addr_only;
   u8 twice_nat;
+  u8 out2in_only;
   u32 vrf_id;
   u32 fib_index;
   snat_protocol_t proto;
@@ -557,7 +558,7 @@ u8 * format_snat_protocol(u8 * s, va_list * args);
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
                                      nat44_lb_addr_port_t *locals, u8 is_add,
-                                     u8 twice_nat);
+                                     u8 twice_nat, u8 out2in_only);
 int nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
                        snat_protocol_t proto, u32 vrf_id, int is_in);
 void nat_free_session_data (snat_main_t * sm, snat_session_t * s,
index d9f8bbd..dc9fc5d 100644 (file)
@@ -1238,7 +1238,8 @@ static void
     nat44_add_del_lb_static_mapping (e_addr,
                                     clib_net_to_host_u16 (mp->external_port),
                                     proto, clib_net_to_host_u32 (mp->vrf_id),
-                                    locals, mp->is_add, mp->twice_nat);
+                                    locals, mp->is_add, mp->twice_nat,
+                                    mp->out2in_only);
 
   vec_free (locals);
 
@@ -1251,7 +1252,8 @@ 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", mp->is_add, mp->twice_nat);
+  s = format (s, "is_add %d twice_nat %d out2in_only ",
+             mp->is_add, mp->twice_nat, mp->out2in_only);
 
   FINISH;
 }
@@ -1278,6 +1280,7 @@ send_nat44_lb_static_mapping_details (snat_static_mapping_t * m,
   rmp->vrf_id = ntohl (m->vrf_id);
   rmp->context = context;
   rmp->twice_nat = m->twice_nat;
+  rmp->out2in_only = m->out2in_only;
 
   locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals;
   vec_foreach (ap, m->locals)
index 5f6334e..384ec8f 100644 (file)
@@ -989,6 +989,8 @@ class TestNAT44(MethodHolder):
         if self.pg7.has_ip4_config:
             self.pg7.unconfig_ip4()
 
+        self.vapi.nat44_forwarding_enable_disable(0)
+
         interfaces = self.vapi.nat44_interface_addr_dump()
         for intf in interfaces:
             self.vapi.nat44_add_interface_addr(intf.sw_if_index,
@@ -1037,6 +1039,7 @@ class TestNAT44(MethodHolder):
                 lb_sm.protocol,
                 vrf_id=lb_sm.vrf_id,
                 twice_nat=lb_sm.twice_nat,
+                out2in_only=lb_sm.out2in_only,
                 is_add=0,
                 local_num=0,
                 locals=[])
@@ -1644,6 +1647,118 @@ class TestNAT44(MethodHolder):
                 server2_n += 1
         self.assertTrue(server1_n > server2_n)
 
+    def test_static_lb_2(self):
+        """ NAT44 local service load balancing (asymmetrical rule) """
+        external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr)
+        external_port = 80
+        local_port = 8080
+        server1 = self.pg0.remote_hosts[0]
+        server2 = self.pg0.remote_hosts[1]
+
+        locals = [{'addr': server1.ip4n,
+                   'port': local_port,
+                   'probability': 70},
+                  {'addr': server2.ip4n,
+                   'port': local_port,
+                   'probability': 30}]
+
+        self.vapi.nat44_forwarding_enable_disable(1)
+        self.vapi.nat44_add_del_lb_static_mapping(external_addr_n,
+                                                  external_port,
+                                                  IP_PROTOS.tcp,
+                                                  out2in_only=1,
+                                                  local_num=len(locals),
+                                                  locals=locals)
+        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.assertIn(ip.dst, [server1.ip4, server2.ip4])
+            if ip.dst == server1.ip4:
+                server = server1
+            else:
+                server = server2
+            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=server.mac, dst=self.pg0.local_mac) /
+             IP(src=server.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=server1.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, server1.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=server1.mac, dst=self.pg0.local_mac) /
+             IP(src=server1.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, server1.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_multiple_inside_interfaces(self):
         """ NAT44 multiple non-overlapping address space inside interfaces """
 
index 7a6d11f..6e6f7ca 100644 (file)
@@ -1420,6 +1420,7 @@ class VppPapiProvider(object):
             protocol,
             vrf_id=0,
             twice_nat=0,
+            out2in_only=0,
             local_num=0,
             locals=[],
             is_add=1):
@@ -1436,6 +1437,7 @@ class VppPapiProvider(object):
              'protocol': protocol,
              'vrf_id': vrf_id,
              'twice_nat': twice_nat,
+             'out2in_only': out2in_only,
              'local_num': local_num,
              'locals': locals})