From 7b929793feba7d966c34b1ddb31dc818174f3a57 Mon Sep 17 00:00:00 2001 From: Juraj Sloboda Date: Thu, 23 Nov 2017 13:20:48 +0100 Subject: [PATCH] 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 --- src/plugins/nat/in2out.c | 3 ++ src/plugins/nat/nat.api | 31 ++++++++++++ src/plugins/nat/nat.c | 68 ++++++++++++++++++++++++++ src/plugins/nat/nat.h | 3 ++ src/plugins/nat/nat_api.c | 59 ++++++++++++++++++++++ src/plugins/nat/out2in.c | 122 ++++++++++++++++++++++++++++------------------ test/test_nat.py | 80 ++++++++++++++++++++++++++++-- test/vpp_papi_provider.py | 11 +++++ 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) = { ": :", .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, -- 2.16.6