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 <matfabia@cisco.com>
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
};
|
||||
|
@ -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 *
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
115
test/test_nat.py
115
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 """
|
||||
|
||||
|
@ -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})
|
||||
|
||||
|
Reference in New Issue
Block a user