summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuraj Sloboda <jsloboda@cisco.com>2017-11-23 13:20:48 +0100
committerOle Trøan <otroan@employees.org>2017-12-20 11:12:24 +0000
commit7b929793feba7d966c34b1ddb31dc818174f3a57 (patch)
tree34eba85b00e775127732dddb56e679d14fc9cc75
parent5ff506a194f4951372500608a233c5c4cc6990a9 (diff)
Translate matching packets using NAT (VPP-1069)
Add API function which enables forwarding of packets not matching existing translation or static mapping instead of dropping them. When forwarding is enabled matching packets will be translated while non-matching packets will be forwarded without translation. Change-Id: Ic13040cbad16d3a1ecdc3e02a497171bef6aa413 Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
-rwxr-xr-xsrc/plugins/nat/in2out.c3
-rw-r--r--src/plugins/nat/nat.api31
-rw-r--r--src/plugins/nat/nat.c68
-rw-r--r--src/plugins/nat/nat.h3
-rw-r--r--src/plugins/nat/nat_api.c59
-rwxr-xr-xsrc/plugins/nat/out2in.c122
-rw-r--r--test/test_nat.py80
-rw-r--r--test/vpp_papi_provider.py11
8 files changed, 325 insertions, 52 deletions
diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c
index 603abb8ff38..517011eec06 100755
--- a/src/plugins/nat/in2out.c
+++ b/src/plugins/nat/in2out.c
@@ -242,6 +242,9 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node,
else
return 0;
+ if (sm->forwarding_enabled)
+ return 1;
+
return snat_not_translate_fast(sm, node, sw_if_index0, ip0, proto0,
rx_fib_index0);
}
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api
index 2a8eeb9489f..d6a912b72d0 100644
--- a/src/plugins/nat/nat.api
+++ b/src/plugins/nat/nat.api
@@ -606,6 +606,37 @@ autoreply define nat44_del_session {
u32 vrf_id;
};
+/** \brief Enable/disable forwarding for NAT44
+ Forward packets which don't match existing translation
+ or static mapping instead of dropping them.
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param enable - 0 for enable, 1 for disable
+*/
+autoreply define nat44_forwarding_enable_disable {
+ u32 client_index;
+ u32 context;
+ u8 enable;
+};
+
+/** \brief Check if forwarding is enabled or disabled
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define nat44_forwarding_is_enabled {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Response to check if forwarding is enabled or disabled
+ @param context - sender context, to match reply w/ request
+ @param enabled - 1 if enabled, 0 if disabled
+*/
+define nat44_forwarding_is_enabled_reply {
+ u32 context;
+ u8 enabled;
+};
+
/*
* Deterministic NAT (CGN) APIs
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 52944014ab0..df00f5e2ece 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -1642,6 +1642,7 @@ static clib_error_t * snat_init (vlib_main_t * vm)
sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
+ sm->forwarding_enabled = 0;
p = hash_get_mem (tm->thread_registrations_by_name, "workers");
if (p)
@@ -4208,3 +4209,70 @@ VLIB_CLI_COMMAND (snat_det_close_session_in_command, static) = {
"<in_addr>:<in_port> <ext_addr>:<ext_port>",
.function = snat_det_close_session_in_fn,
};
+
+static clib_error_t *
+snat_forwarding_set_command_fn (vlib_main_t *vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ snat_main_t *sm = &snat_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u8 forwarding_enable;
+ u8 forwarding_enable_set = 0;
+ clib_error_t *error = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "'enable' or 'disable' expected");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (!forwarding_enable_set && unformat (line_input, "enable"))
+ {
+ forwarding_enable = 1;
+ forwarding_enable_set = 1;
+ }
+ else if (!forwarding_enable_set && unformat (line_input, "disable"))
+ {
+ forwarding_enable = 0;
+ forwarding_enable_set = 1;
+ }
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (!forwarding_enable_set)
+ {
+ error = clib_error_return (0, "'enable' or 'disable' expected");
+ goto done;
+ }
+
+ sm->forwarding_enabled = forwarding_enable;
+
+done:
+ unformat_free(line_input);
+
+ return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{nat44 forwarding}
+ * Enable or disable forwarding
+ * Forward packets which don't match existing translation
+ * or static mapping instead of dropping them.
+ * To enable forwarding, use:
+ * vpp# nat44 forwarding enable
+ * To disable forwarding, use:
+ * vpp# nat44 forwarding disable
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (snat_forwarding_set_command, static) = {
+ .path = "nat44 forwarding",
+ .short_help = "nat44 forwarding enable|disable",
+ .function = snat_forwarding_set_command_fn,
+};
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index 5a2d0855e9b..1e8e3ca0403 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -350,6 +350,9 @@ typedef struct snat_main_s {
/* Deterministic NAT */
snat_det_map_t * det_maps;
+ /* If forwarding is enabled */
+ u8 forwarding_enabled;
+
/* Config parameters */
u8 static_mapping_only;
u8 static_mapping_connection_tracking;
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index 5071609c0b9..2397663270a 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -1366,6 +1366,63 @@ vl_api_nat44_del_session_t_print (vl_api_nat44_del_session_t * mp,
FINISH;
}
+static void
+ vl_api_nat44_forwarding_enable_disable_t_handler
+ (vl_api_nat44_forwarding_enable_disable_t * mp)
+{
+ snat_main_t *sm = &snat_main;
+ vl_api_nat44_forwarding_enable_disable_reply_t *rmp;
+ int rv = 0;
+
+ sm->forwarding_enabled = mp->enable != 0;
+
+ REPLY_MACRO (VL_API_NAT44_FORWARDING_ENABLE_DISABLE_REPLY);
+}
+
+static void *vl_api_nat44_forwarding_enable_disable_t_print
+ (vl_api_nat44_forwarding_enable_disable_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: nat44_forwarding_enable_disable ");
+ s = format (s, "enable %d", mp->enable != 0);
+
+ FINISH;
+}
+
+static void
+ vl_api_nat44_forwarding_is_enabled_t_handler
+ (vl_api_nat44_forwarding_is_enabled_t * mp)
+{
+ unix_shared_memory_queue_t *q;
+ snat_main_t *sm = &snat_main;
+ vl_api_nat44_forwarding_is_enabled_reply_t *rmp;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (q == 0)
+ return;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id =
+ ntohs (VL_API_NAT44_FORWARDING_IS_ENABLED_REPLY + sm->msg_id_base);
+ rmp->context = mp->context;
+
+ rmp->enabled = sm->forwarding_enabled;
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void *vl_api_nat44_forwarding_is_enabled_t_print
+ (vl_api_nat44_forwarding_is_enabled_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: nat44_forwarding_is_enabled ");
+
+ FINISH;
+}
+
/*******************************/
/*** Deterministic NAT (CGN) ***/
/*******************************/
@@ -2434,6 +2491,8 @@ _(NAT44_INTERFACE_OUTPUT_FEATURE_DUMP, \
_(NAT44_ADD_DEL_LB_STATIC_MAPPING, nat44_add_del_lb_static_mapping) \
_(NAT44_LB_STATIC_MAPPING_DUMP, nat44_lb_static_mapping_dump) \
_(NAT44_DEL_SESSION, nat44_del_session) \
+_(NAT44_FORWARDING_ENABLE_DISABLE, nat44_forwarding_enable_disable) \
+_(NAT44_FORWARDING_IS_ENABLED, nat44_forwarding_is_enabled) \
_(NAT_DET_ADD_DEL_MAP, nat_det_add_del_map) \
_(NAT_DET_FORWARD, nat_det_forward) \
_(NAT_DET_REVERSE, nat_det_reverse) \
diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c
index b5464e0ad41..d548ab31fc5 100755
--- a/src/plugins/nat/out2in.c
+++ b/src/plugins/nat/out2in.c
@@ -320,16 +320,24 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node,
destination address and port in packet */
if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only, 0))
{
- /* Don't NAT packet aimed at the intfc address */
- if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
- ip0->dst_address.as_u32)))
+ if (!sm->forwarding_enabled)
+ {
+ /* Don't NAT packet aimed at the intfc address */
+ if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
+ ip0->dst_address.as_u32)))
+ {
+ dont_translate = 1;
+ goto out;
+ }
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ next0 = SNAT_OUT2IN_NEXT_DROP;
+ goto out;
+ }
+ else
{
dont_translate = 1;
goto out;
}
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- next0 = SNAT_OUT2IN_NEXT_DROP;
- goto out;
}
if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
@@ -1017,16 +1025,21 @@ snat_out2in_node_fn (vlib_main_t * vm,
destination address and port in packet */
if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
{
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- /*
- * Send DHCP packets to the ipv4 stack, or we won't
- * be able to use dhcp client on the outside interface
- */
- if (proto0 != SNAT_PROTOCOL_UDP
- || (udp0->dst_port
- != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
- next0 = SNAT_OUT2IN_NEXT_DROP;
- goto trace0;
+ if (!sm->forwarding_enabled)
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ /*
+ * Send DHCP packets to the ipv4 stack, or we won't
+ * be able to use dhcp client on the outside interface
+ */
+ if (proto0 != SNAT_PROTOCOL_UDP
+ || (udp0->dst_port
+ != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
+ next0 = SNAT_OUT2IN_NEXT_DROP;
+ goto trace0;
+ }
+ else
+ goto trace0;
}
/* Create session initiated by host from external network */
@@ -1175,16 +1188,21 @@ snat_out2in_node_fn (vlib_main_t * vm,
destination address and port in packet */
if (snat_static_mapping_match(sm, key1, &sm1, 1, 0, 0))
{
- b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- /*
- * Send DHCP packets to the ipv4 stack, or we won't
- * be able to use dhcp client on the outside interface
- */
- if (proto1 != SNAT_PROTOCOL_UDP
- || (udp1->dst_port
- != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
- next1 = SNAT_OUT2IN_NEXT_DROP;
- goto trace1;
+ if (!sm->forwarding_enabled)
+ {
+ b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ /*
+ * Send DHCP packets to the ipv4 stack, or we won't
+ * be able to use dhcp client on the outside interface
+ */
+ if (proto1 != SNAT_PROTOCOL_UDP
+ || (udp1->dst_port
+ != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
+ next1 = SNAT_OUT2IN_NEXT_DROP;
+ goto trace1;
+ }
+ else
+ goto trace1;
}
/* Create session initiated by host from external network */
@@ -1369,17 +1387,21 @@ snat_out2in_node_fn (vlib_main_t * vm,
destination address and port in packet */
if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
{
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- /*
- * Send DHCP packets to the ipv4 stack, or we won't
- * be able to use dhcp client on the outside interface
- */
- if (proto0 != SNAT_PROTOCOL_UDP
- || (udp0->dst_port
- != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
-
- next0 = SNAT_OUT2IN_NEXT_DROP;
- goto trace00;
+ if (!sm->forwarding_enabled)
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ /*
+ * Send DHCP packets to the ipv4 stack, or we won't
+ * be able to use dhcp client on the outside interface
+ */
+ if (proto0 != SNAT_PROTOCOL_UDP
+ || (udp0->dst_port
+ != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
+ next0 = SNAT_OUT2IN_NEXT_DROP;
+ goto trace00;
+ }
+ else
+ goto trace00;
}
/* Create session initiated by host from external network */
@@ -1605,17 +1627,21 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm,
destination address and port in packet */
if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
{
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- /*
- * Send DHCP packets to the ipv4 stack, or we won't
- * be able to use dhcp client on the outside interface
- */
- if (proto0 != SNAT_PROTOCOL_UDP
- || (udp0->dst_port
- != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
-
- next0 = SNAT_OUT2IN_NEXT_DROP;
- goto trace0;
+ if (!sm->forwarding_enabled)
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ /*
+ * Send DHCP packets to the ipv4 stack, or we won't
+ * be able to use dhcp client on the outside interface
+ */
+ if (proto0 != SNAT_PROTOCOL_UDP
+ || (udp0->dst_port
+ != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
+ next0 = SNAT_OUT2IN_NEXT_DROP;
+ goto trace0;
+ }
+ else
+ goto trace0;
}
/* Create session initiated by host from external network */
diff --git a/test/test_nat.py b/test/test_nat.py
index 1f87bffab57..aeeb5aa2bd5 100644
--- a/test/test_nat.py
+++ b/test/test_nat.py
@@ -242,33 +242,44 @@ class MethodHolder(VppTestCase):
return pkts
- def create_stream_out(self, out_if, dst_ip=None, ttl=64):
+ def create_stream_out(self, out_if, dst_ip=None, ttl=64,
+ use_inside_ports=False):
"""
Create packet stream for outside network
:param out_if: Outside interface
:param dst_ip: Destination IP address (Default use global NAT address)
:param ttl: TTL of generated packets
+ :param use_inside_ports: Use inside NAT ports as destination ports
+ instead of outside ports
"""
if dst_ip is None:
dst_ip = self.nat_addr
+ if not use_inside_ports:
+ tcp_port = self.tcp_port_out
+ udp_port = self.udp_port_out
+ icmp_id = self.icmp_id_out
+ else:
+ tcp_port = self.tcp_port_in
+ udp_port = self.udp_port_in
+ icmp_id = self.icmp_id_in
pkts = []
# TCP
p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
- TCP(dport=self.tcp_port_out, sport=20))
+ TCP(dport=tcp_port, sport=20))
pkts.append(p)
# UDP
p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
- UDP(dport=self.udp_port_out, sport=20))
+ UDP(dport=udp_port, sport=20))
pkts.append(p)
# ICMP
p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
- ICMP(id=self.icmp_id_out, type='echo-reply'))
+ ICMP(id=icmp_id, type='echo-reply'))
pkts.append(p)
return pkts
@@ -654,6 +665,7 @@ class TestNAT44(MethodHolder):
cls.icmp_id_in = 6305
cls.icmp_id_out = 6305
cls.nat_addr = '10.0.0.3'
+ cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr)
cls.ipfix_src_port = 4739
cls.ipfix_domain_id = 1
@@ -1044,6 +1056,66 @@ class TestNAT44(MethodHolder):
self.verify_capture_out(capture, same_port=True, packet_num=1)
self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp)
+ def test_forwarding(self):
+ """ NAT44 forwarding test """
+
+ 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)
+ self.vapi.nat44_forwarding_enable_disable(1)
+
+ real_ip = self.pg0.remote_ip4n
+ alias_ip = self.nat_addr_n
+ self.vapi.nat44_add_del_static_mapping(local_ip=real_ip,
+ external_ip=alias_ip)
+
+ try:
+ # in2out - static mapping match
+
+ 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)
+
+ 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, same_port=True)
+
+ # in2out - no static mapping match
+
+ host0 = self.pg0.remote_hosts[0]
+ self.pg0.remote_hosts[0] = self.pg0.remote_hosts[1]
+ try:
+ pkts = self.create_stream_out(self.pg1,
+ dst_ip=self.pg0.remote_ip4,
+ use_inside_ports=True)
+ 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)
+
+ 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, nat_ip=self.pg0.remote_ip4,
+ same_port=True)
+ finally:
+ self.pg0.remote_hosts[0] = host0
+
+ finally:
+ self.vapi.nat44_forwarding_enable_disable(0)
+ self.vapi.nat44_add_del_static_mapping(local_ip=real_ip,
+ external_ip=alias_ip,
+ is_add=0)
+
def test_static_in(self):
""" 1:1 NAT initialized from inside network """
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 3644d3c6779..aa06cfef4e9 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -1457,6 +1457,17 @@ class VppPapiProvider(object):
'vrf_id': vrf_id,
'is_in': is_in})
+ def nat44_forwarding_enable_disable(
+ self,
+ enable):
+ """Enable/disable forwarding for NAT44
+
+ :param enable: 1 for enable, 0 for disable
+ """
+ return self.api(
+ self.papi.nat44_forwarding_enable_disable,
+ {'enable': enable})
+
def nat_set_reass(
self,
timeout=2,