SNAT: in2out translation as an output feature (VPP-903)

in2out translation as an output feature on the outside interface (postrouting)

Change-Id: I32c0311be09bdf102b9a0885b8b89c7588cb558f
Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:
Matus Fabian
2017-07-19 08:06:01 -07:00
committed by Florin Coras
parent b12ac56c44
commit 93d84c9fc2
7 changed files with 664 additions and 38 deletions

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,42 @@ define snat_interface_details {
u32 sw_if_index;
};
/** \brief Enable/disbale S-NAT as an interface output feature (postrouting
in2out translation)
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param is_add - 1 if add, 0 if delete
@param is_inside - 1 if inside, 0 if outside
@param sw_if_index - software index of the interface
*/
autoreply define snat_interface_add_del_output_feature {
u32 client_index;
u32 context;
u8 is_add;
u8 is_inside;
u32 sw_if_index;
};
/** \brief Dump interfaces with S-NAT output feature
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
*/
define snat_interface_output_feature_dump {
u32 client_index;
u32 context;
};
/** \brief S-NAT interface with output feature details response
@param context - sender context, to match reply w/ request
@param is_inside - 1 if inside, 0 if outside
@param sw_if_index - software index of the interface
*/
define snat_interface_output_feature_details {
u32 context;
u8 is_inside;
u32 sw_if_index;
};
/** \brief Add/delete S-NAT static mapping
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request

View File

@ -73,6 +73,20 @@ VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
.runs_before = VNET_FEATURES ("ip4-lookup"),
};
/* Hook up output features */
VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
.arc_name = "ip4-output",
.node_name = "snat-in2out-output",
.runs_before = VNET_FEATURES ("interface-output"),
};
VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
.arc_name = "ip4-output",
.node_name = "snat-in2out-output-worker-handoff",
.runs_before = VNET_FEATURES ("interface-output"),
};
/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
@ -157,6 +171,14 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
break;
}));
pool_foreach (i, sm->output_feature_interfaces,
({
if (i->is_inside)
continue;
snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
break;
}));
}
static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
@ -542,6 +564,14 @@ delete:
snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
break;
}));
pool_foreach (interface, sm->output_feature_interfaces,
({
if (interface->is_inside)
continue;
snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
break;
}));
return 0;
}
@ -668,6 +698,14 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
break;
}));
pool_foreach (interface, sm->output_feature_interfaces,
({
if (interface->is_inside)
continue;
snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
break;
}));
return 0;
}
@ -746,6 +784,85 @@ fib:
return 0;
}
int snat_interface_add_del_output_feature (u32 sw_if_index,
u8 is_inside,
int is_del)
{
snat_main_t *sm = &snat_main;
snat_interface_t *i;
snat_address_t * ap;
snat_static_mapping_t * m;
if (sm->deterministic ||
(sm->static_mapping_only && !(sm->static_mapping_connection_tracking)))
return VNET_API_ERROR_UNSUPPORTED;
if (is_inside)
goto find;
if (sm->num_workers > 1)
{
vnet_feature_enable_disable ("ip4-unicast", "snat-out2in-worker-handoff",
sw_if_index, !is_del, 0, 0);
vnet_feature_enable_disable ("ip4-output",
"snat-in2out-output-worker-handoff",
sw_if_index, !is_del, 0, 0);
}
else
{
vnet_feature_enable_disable ("ip4-unicast", "snat-out2in", sw_if_index,
!is_del, 0, 0);
vnet_feature_enable_disable ("ip4-output", "snat-in2out-output",
sw_if_index, !is_del, 0, 0);
}
if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
sm->fq_in2out_output_index =
vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
find:
pool_foreach (i, sm->output_feature_interfaces,
({
if (i->sw_if_index == sw_if_index)
{
if (is_del)
pool_put (sm->output_feature_interfaces, i);
else
return VNET_API_ERROR_VALUE_EXIST;
goto fib;
}
}));
if (is_del)
return VNET_API_ERROR_NO_SUCH_ENTRY;
pool_get (sm->output_feature_interfaces, i);
i->sw_if_index = sw_if_index;
i->is_inside = is_inside;
/* Add/delete external addresses to FIB */
fib:
if (is_inside)
return 0;
vec_foreach (ap, sm->addresses)
snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
pool_foreach (m, sm->static_mappings,
({
if (!(m->addr_only))
continue;
snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
}));
return 0;
}
int snat_set_workers (uword * bitmap)
{
snat_main_t *sm = &snat_main;
@ -1103,6 +1220,7 @@ snat_feature_command_fn (vlib_main_t * vm,
u32 sw_if_index;
u32 * inside_sw_if_indices = 0;
u32 * outside_sw_if_indices = 0;
u8 is_output_feature = 0;
int is_del = 0;
int i;
@ -1120,6 +1238,8 @@ snat_feature_command_fn (vlib_main_t * vm,
else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
vnm, &sw_if_index))
vec_add1 (outside_sw_if_indices, sw_if_index);
else if (unformat (line_input, "output-feature"))
is_output_feature = 1;
else if (unformat (line_input, "del"))
is_del = 1;
else
@ -1135,7 +1255,30 @@ snat_feature_command_fn (vlib_main_t * vm,
for (i = 0; i < vec_len(inside_sw_if_indices); i++)
{
sw_if_index = inside_sw_if_indices[i];
snat_interface_add_del (sw_if_index, 1, is_del);
if (is_output_feature)
{
if (snat_interface_add_del_output_feature (sw_if_index, 1, is_del))
{
error = clib_error_return (0, "%s %U failed",
is_del ? "del" : "add",
format_vnet_sw_interface_name, vnm,
vnet_get_sw_interface (vnm,
sw_if_index));
goto done;
}
}
else
{
if (snat_interface_add_del (sw_if_index, 1, is_del))
{
error = clib_error_return (0, "%s %U failed",
is_del ? "del" : "add",
format_vnet_sw_interface_name, vnm,
vnet_get_sw_interface (vnm,
sw_if_index));
goto done;
}
}
}
}
@ -1144,7 +1287,30 @@ snat_feature_command_fn (vlib_main_t * vm,
for (i = 0; i < vec_len(outside_sw_if_indices); i++)
{
sw_if_index = outside_sw_if_indices[i];
snat_interface_add_del (sw_if_index, 0, is_del);
if (is_output_feature)
{
if (snat_interface_add_del_output_feature (sw_if_index, 0, is_del))
{
error = clib_error_return (0, "%s %U failed",
is_del ? "del" : "add",
format_vnet_sw_interface_name, vnm,
vnet_get_sw_interface (vnm,
sw_if_index));
goto done;
}
}
else
{
if (snat_interface_add_del (sw_if_index, 0, is_del))
{
error = clib_error_return (0, "%s %U failed",
is_del ? "del" : "add",
format_vnet_sw_interface_name, vnm,
vnet_get_sw_interface (vnm,
sw_if_index));
goto done;
}
}
}
}
@ -1159,7 +1325,8 @@ done:
VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
.path = "set interface snat",
.function = snat_feature_command_fn,
.short_help = "set interface snat in <intfc> out <intfc> [del]",
.short_help = "set interface snat in <intfc> out <intfc> [output-feature] "
"[del]",
};
uword
@ -1597,6 +1764,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
if (sm->deterministic)
{
sm->in2out_node_index = snat_det_in2out_node.index;
sm->in2out_output_node_index = ~0;
sm->out2in_node_index = snat_det_out2in_node.index;
sm->icmp_match_in2out_cb = icmp_match_in2out_det;
sm->icmp_match_out2in_cb = icmp_match_out2in_det;
@ -1606,6 +1774,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
sm->worker_in2out_cb = snat_get_worker_in2out_cb;
sm->worker_out2in_cb = snat_get_worker_out2in_cb;
sm->in2out_node_index = snat_in2out_node.index;
sm->in2out_output_node_index = snat_in2out_output_node.index;
sm->out2in_node_index = snat_out2in_node.index;
if (!static_mapping_only ||
(static_mapping_only && static_mapping_connection_tracking))
@ -1881,6 +2050,14 @@ show_snat_command_fn (vlib_main_t * vm,
i->is_inside ? "in" : "out");
}));
pool_foreach (i, sm->output_feature_interfaces,
({
vlib_cli_output (vm, "%U output-feature %s",
format_vnet_sw_interface_name, vnm,
vnet_get_sw_interface (vnm, i->sw_if_index),
i->is_inside ? "in" : "out");
}));
if (vec_len (sm->auto_add_sw_if_indices))
{
vlib_cli_output (vm, "SNAT pool addresses interfaces:");

View File

@ -297,6 +297,7 @@ typedef struct snat_main_s {
/* Interface pool */
snat_interface_t * interfaces;
snat_interface_t * output_feature_interfaces;
/* Vector of outside addresses */
snat_address_t * addresses;
@ -312,10 +313,12 @@ typedef struct snat_main_s {
/* Worker handoff index */
u32 fq_in2out_index;
u32 fq_in2out_output_index;
u32 fq_out2in_index;
/* in2out and out2in node index */
u32 in2out_node_index;
u32 in2out_output_node_index;
u32 out2in_node_index;
/* Deterministic NAT */
@ -357,10 +360,12 @@ typedef struct snat_main_s {
extern snat_main_t snat_main;
extern vlib_node_registration_t snat_in2out_node;
extern vlib_node_registration_t snat_in2out_output_node;
extern vlib_node_registration_t snat_out2in_node;
extern vlib_node_registration_t snat_in2out_fast_node;
extern vlib_node_registration_t snat_out2in_fast_node;
extern vlib_node_registration_t snat_in2out_worker_handoff_node;
extern vlib_node_registration_t snat_in2out_output_worker_handoff_node;
extern vlib_node_registration_t snat_out2in_worker_handoff_node;
extern vlib_node_registration_t snat_det_in2out_node;
extern vlib_node_registration_t snat_det_out2in_node;
@ -477,6 +482,8 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
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);
int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside,
int is_del);
int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del);
uword unformat_snat_protocol(unformat_input_t * input, va_list * args);
u8 * format_snat_protocol(u8 * s, va_list * args);

View File

@ -253,7 +253,91 @@ static void *vl_api_snat_interface_dump_t_print
s = format (0, "SCRIPT: snat_interface_dump ");
FINISH;
} static void
}
static void
vl_api_snat_interface_add_del_output_feature_t_handler
(vl_api_snat_interface_add_del_output_feature_t * mp)
{
snat_main_t *sm = &snat_main;
vl_api_snat_interface_add_del_output_feature_reply_t *rmp;
u8 is_del = mp->is_add == 0;
u32 sw_if_index = ntohl (mp->sw_if_index);
int rv = 0;
VALIDATE_SW_IF_INDEX (mp);
rv = snat_interface_add_del_output_feature (sw_if_index, mp->is_inside,
is_del);
BAD_SW_IF_INDEX_LABEL;
REPLY_MACRO (VL_API_SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY);
}
static void *vl_api_snat_interface_add_del_output_feature_t_print
(vl_api_snat_interface_add_del_output_feature_t * mp, void *handle)
{
u8 *s;
s = format (0, "SCRIPT: snat_interface_add_del_output_feature ");
s = format (s, "sw_if_index %d %s %s",
clib_host_to_net_u32 (mp->sw_if_index),
mp->is_inside ? "in" : "out", mp->is_add ? "" : "del");
FINISH;
}
static void
send_snat_interface_output_feature_details (snat_interface_t * i,
unix_shared_memory_queue_t * q,
u32 context)
{
vl_api_snat_interface_output_feature_details_t *rmp;
snat_main_t *sm = &snat_main;
rmp = vl_msg_api_alloc (sizeof (*rmp));
memset (rmp, 0, sizeof (*rmp));
rmp->_vl_msg_id =
ntohs (VL_API_SNAT_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base);
rmp->sw_if_index = ntohl (i->sw_if_index);
rmp->context = context;
rmp->is_inside = i->is_inside;
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
static void
vl_api_snat_interface_output_feature_dump_t_handler
(vl_api_snat_interface_output_feature_dump_t * mp)
{
unix_shared_memory_queue_t *q;
snat_main_t *sm = &snat_main;
snat_interface_t *i;
q = vl_api_client_index_to_input_queue (mp->client_index);
if (q == 0)
return;
/* *INDENT-OFF* */
pool_foreach (i, sm->output_feature_interfaces,
({
send_snat_interface_output_feature_details(i, q, mp->context);
}));
/* *INDENT-ON* */
}
static void *vl_api_snat_interface_output_feature_dump_t_print
(vl_api_snat_interface_output_feature_dump_t * mp, void *handle)
{
u8 *s;
s = format (0, "SCRIPT: snat_interface_output_feature_dump ");
FINISH;
}
static void
vl_api_snat_add_static_mapping_t_handler
(vl_api_snat_add_static_mapping_t * mp)
{
@ -1792,6 +1876,10 @@ _(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) \
_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable) \
_(SNAT_USER_DUMP, snat_user_dump) \
_(SNAT_USER_SESSION_DUMP, snat_user_session_dump) \
_(SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \
snat_interface_add_del_output_feature) \
_(SNAT_INTERFACE_OUTPUT_FEATURE_DUMP, \
snat_interface_output_feature_dump) \
_(SNAT_ADD_DET_MAP, snat_add_det_map) \
_(SNAT_DET_FORWARD, snat_det_forward) \
_(SNAT_DET_REVERSE, snat_det_reverse) \

View File

@ -615,6 +615,12 @@ class TestSNAT(MethodHolder):
intf.is_inside,
is_add=0)
interfaces = self.vapi.snat_interface_output_feature_dump()
for intf in interfaces:
self.vapi.snat_interface_add_del_output_feature(intf.sw_if_index,
intf.is_inside,
is_add=0)
static_mappings = self.vapi.snat_static_mapping_dump()
for sm in static_mappings:
self.vapi.snat_add_static_mapping(sm.local_ip_address,
@ -2108,6 +2114,146 @@ class TestSNAT(MethodHolder):
self.logger.error(ppp("Unexpected or invalid packet:", packet))
raise
def test_output_feature(self):
""" S-NAT interface output feature (in2out postrouting) """
self.snat_add_address(self.snat_addr)
self.vapi.snat_interface_add_del_output_feature(self.pg0.sw_if_index)
self.vapi.snat_interface_add_del_output_feature(self.pg1.sw_if_index,
is_inside=0)
# in2out
pkts = self.create_stream_in(self.pg0, self.pg1)
self.pg0.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg1.get_capture(len(pkts))
self.verify_capture_out(capture)
# out2in
pkts = self.create_stream_out(self.pg1)
self.pg1.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg0.get_capture(len(pkts))
self.verify_capture_in(capture, self.pg0)
def test_output_feature_vrf_aware(self):
""" S-NAT interface output feature VRF aware (in2out postrouting) """
nat_ip_vrf10 = "10.0.0.10"
nat_ip_vrf20 = "10.0.0.20"
self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n,
dst_address_length=32,
next_hop_address=self.pg3.remote_ip4n,
next_hop_sw_if_index=self.pg3.sw_if_index,
table_id=10)
self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n,
dst_address_length=32,
next_hop_address=self.pg3.remote_ip4n,
next_hop_sw_if_index=self.pg3.sw_if_index,
table_id=20)
self.snat_add_address(nat_ip_vrf10, vrf_id=10)
self.snat_add_address(nat_ip_vrf20, vrf_id=20)
self.vapi.snat_interface_add_del_output_feature(self.pg4.sw_if_index)
self.vapi.snat_interface_add_del_output_feature(self.pg6.sw_if_index)
self.vapi.snat_interface_add_del_output_feature(self.pg3.sw_if_index,
is_inside=0)
# in2out VRF 10
pkts = self.create_stream_in(self.pg4, self.pg3)
self.pg4.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg3.get_capture(len(pkts))
self.verify_capture_out(capture, nat_ip=nat_ip_vrf10)
# out2in VRF 10
pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf10)
self.pg3.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg4.get_capture(len(pkts))
self.verify_capture_in(capture, self.pg4)
# in2out VRF 20
pkts = self.create_stream_in(self.pg6, self.pg3)
self.pg6.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg3.get_capture(len(pkts))
self.verify_capture_out(capture, nat_ip=nat_ip_vrf20)
# out2in VRF 20
pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf20)
self.pg3.add_stream(pkts)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg6.get_capture(len(pkts))
self.verify_capture_in(capture, self.pg6)
def _test_output_feature_hairpinning(self):
""" S-NAT interface output feature hairpinning (in2out postrouting) """
host = self.pg0.remote_hosts[0]
server = self.pg0.remote_hosts[1]
host_in_port = 1234
host_out_port = 0
server_in_port = 5678
server_out_port = 8765
self.snat_add_address(self.snat_addr)
self.vapi.snat_interface_add_del_output_feature(self.pg0.sw_if_index)
self.vapi.snat_interface_add_del_output_feature(self.pg1.sw_if_index,
is_inside=0)
# add static mapping for server
self.snat_add_static_mapping(server.ip4, self.snat_addr,
server_in_port, server_out_port,
proto=IP_PROTOS.tcp)
# send packet from host to server
p = (Ether(src=host.mac, dst=self.pg0.local_mac) /
IP(src=host.ip4, dst=self.snat_addr) /
TCP(sport=host_in_port, dport=server_out_port))
self.pg0.add_stream(p)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg0.get_capture(1)
p = capture[0]
try:
ip = p[IP]
tcp = p[TCP]
self.assertEqual(ip.src, self.snat_addr)
self.assertEqual(ip.dst, server.ip4)
self.assertNotEqual(tcp.sport, host_in_port)
self.assertEqual(tcp.dport, server_in_port)
self.check_tcp_checksum(p)
host_out_port = tcp.sport
except:
self.logger.error(ppp("Unexpected or invalid packet:", p))
raise
# send reply from server to host
p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
IP(src=server.ip4, dst=self.snat_addr) /
TCP(sport=server_in_port, dport=host_out_port))
self.pg0.add_stream(p)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
capture = self.pg0.get_capture(1)
p = capture[0]
try:
ip = p[IP]
tcp = p[TCP]
self.assertEqual(ip.src, self.snat_addr)
self.assertEqual(ip.dst, host.ip4)
self.assertEqual(tcp.sport, server_out_port)
self.assertEqual(tcp.dport, host_in_port)
self.check_tcp_checksum(p)
except:
self.logger.error(ppp("Unexpected or invalid packet:"), p)
raise
def tearDown(self):
super(TestSNAT, self).tearDown()
if not self.vpp_dead:

View File

@ -1055,6 +1055,23 @@ class VppPapiProvider(object):
'is_inside': is_inside,
'sw_if_index': sw_if_index})
def snat_interface_add_del_output_feature(
self,
sw_if_index,
is_inside=1,
is_add=1):
"""Enable/disable S-NAT output feature on the interface
:param sw_if_index: Software index of the interface
:param is_inside: 1 if inside, 0 if outside (Default value = 1)
:param is_add: 1 if add, 0 if delete (Default value = 1)
"""
return self.api(
self.papi.snat_interface_add_del_output_feature,
{'is_add': is_add,
'is_inside': is_inside,
'sw_if_index': sw_if_index})
def snat_add_static_mapping(
self,
local_ip,
@ -1128,6 +1145,12 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.snat_interface_dump, {})
def snat_interface_output_feature_dump(self):
"""Dump interfaces with S-NAT output feature
:return: Dictionary of interfaces with S-NAT output feature
"""
return self.api(self.papi.snat_interface_output_feature_dump, {})
def snat_static_mapping_dump(self):
"""Dump S-NAT static mappings
:return: Dictionary of S-NAT static mappings