From 240b5ef57cc79cf32934ba8f218de29b6685406b Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 11 Jan 2018 04:09:17 -0800 Subject: [PATCH] NAT44: asymmetrical load balancing static mapping rule (VPP-1132) 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 --- src/plugins/nat/in2out.c | 6 +-- src/plugins/nat/nat.api | 4 +- src/plugins/nat/nat.c | 40 ++++++++++------ src/plugins/nat/nat.h | 3 +- src/plugins/nat/nat_api.c | 7 ++- test/test_nat.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 2 + 7 files changed, 155 insertions(+), 22 deletions(-) diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 5f2f43f7f81..0b87ba47812 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -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; } diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 6ebe4e37933..78f34509b69 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -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]; }; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 90515acc5b6..99c5f63cf12 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -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 : local : probability [twice-nat] " - "[vrf ] [del]", + "[vrf ] [out2in-only] [del]", }; static clib_error_t * diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 614668d8421..ff7888a9128 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -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, diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index d9f8bbd41be..dc9fc5d9a6d 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -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) diff --git a/test/test_nat.py b/test/test_nat.py index 5f6334ebb39..384ec8f273e 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -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 """ diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 7a6d11f75ae..6e6f7caf287 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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}) -- 2.16.6