From 25fd8ad03e6ef321604437fe8fc56ab2b3858cd7 Mon Sep 17 00:00:00 2001 From: Filip Varga Date: Mon, 9 Nov 2020 12:24:03 +0100 Subject: nat: cleanup & reorganization Fixed compatibility issue between nat ei and nat ed modes. Moved nat syslogging to nat librarry. Deprecating apis that will be integrated in upcoming candidate configuration patch. Type: refactor Change-Id: I334b1b05b81b74667c5c76a05f768442e0dcf7e8 Signed-off-by: Filip Varga --- src/plugins/nat/CMakeLists.txt | 6 +- src/plugins/nat/det44/det44.c | 5 +- src/plugins/nat/det44/det44.h | 14 - src/plugins/nat/dslite/dslite_in2out.c | 2 +- src/plugins/nat/in2out.c | 2 +- src/plugins/nat/in2out_ed.c | 2 +- src/plugins/nat/lib/ipfix_logging.c | 7 + src/plugins/nat/lib/ipfix_logging.h | 2 + src/plugins/nat/lib/lib.h | 24 +- src/plugins/nat/lib/nat_syslog.c | 288 ++ src/plugins/nat/lib/nat_syslog.h | 73 + src/plugins/nat/nat.c | 9 +- src/plugins/nat/nat.h | 12 +- src/plugins/nat/nat44.api | 36 +- src/plugins/nat/nat44_api.c | 31 +- src/plugins/nat/nat44_cli.c | 27 +- src/plugins/nat/nat64/nat64_db.c | 2 +- src/plugins/nat/nat_inlines.h | 38 +- src/plugins/nat/nat_syslog.c | 288 -- src/plugins/nat/nat_syslog.h | 73 - src/plugins/nat/nat_types.api | 18 + src/plugins/nat/out2in.c | 2 +- src/plugins/nat/out2in_ed.c | 2 +- src/plugins/nat/test/test_nat.py | 7276 -------------------------------- src/plugins/nat/test/test_nat44.py | 7276 ++++++++++++++++++++++++++++++++ 25 files changed, 7756 insertions(+), 7759 deletions(-) create mode 100644 src/plugins/nat/lib/nat_syslog.c create mode 100644 src/plugins/nat/lib/nat_syslog.h delete mode 100644 src/plugins/nat/nat_syslog.c delete mode 100644 src/plugins/nat/nat_syslog.h delete mode 100644 src/plugins/nat/test/test_nat.py create mode 100644 src/plugins/nat/test/test_nat44.py (limited to 'src') diff --git a/src/plugins/nat/CMakeLists.txt b/src/plugins/nat/CMakeLists.txt index 054ad687f75..0c28fa31da5 100644 --- a/src/plugins/nat/CMakeLists.txt +++ b/src/plugins/nat/CMakeLists.txt @@ -16,10 +16,12 @@ set(CMAKE_VERBOSE_MAKEFILE ON) add_vpp_library(nat SOURCES lib/ipfix_logging.c + lib/nat_syslog.c lib/alloc.c INSTALL_HEADERS lib/ipfix_logging.h + lib/nat_syslog.h lib/alloc.h ) @@ -38,7 +40,6 @@ add_vpp_plugin(nat nat44_classify.c nat_affinity.c nat_format.c - nat_syslog.c nat_ha.c MULTIARCH_SOURCES @@ -59,7 +60,6 @@ add_vpp_plugin(nat add_vpp_plugin(dslite SOURCES - nat_syslog.c dslite/dslite_api.c dslite/dslite_dpo.c dslite/dslite.c @@ -83,7 +83,6 @@ add_vpp_plugin(dslite add_vpp_plugin(nat66 SOURCES - nat_syslog.c nat66/nat66.c nat66/nat66_cli.c nat66/nat66_api.c @@ -122,7 +121,6 @@ add_vpp_plugin(det44 add_vpp_plugin(nat64 SOURCES - nat_syslog.c nat64/nat64.c nat64/nat64_db.c nat64/nat64_cli.c diff --git a/src/plugins/nat/det44/det44.c b/src/plugins/nat/det44/det44.c index 886092cea50..582fd20cd5d 100644 --- a/src/plugins/nat/det44/det44.c +++ b/src/plugins/nat/det44/det44.c @@ -188,10 +188,7 @@ void det44_reset_timeouts () { det44_main_t *dm = &det44_main; - dm->timeouts.udp = 300; - dm->timeouts.tcp.established = 7440; - dm->timeouts.tcp.transitory = 240; - dm->timeouts.icmp = 60; + nat_reset_timeouts (&dm->timeouts); } int diff --git a/src/plugins/nat/det44/det44.h b/src/plugins/nat/det44/det44.h index 83eefc9fe0d..84d570bfd56 100644 --- a/src/plugins/nat/det44/det44.h +++ b/src/plugins/nat/det44/det44.h @@ -80,20 +80,6 @@ typedef struct u32 cached_ip4_address; } det44_runtime_t; -typedef struct nat_timeouts_s -{ - u32 udp; - - struct - { - u32 transitory; - u32 established; - } tcp; - - u32 icmp; - -} nat_timeouts_t; - /* deterministic session outside key */ typedef struct { diff --git a/src/plugins/nat/dslite/dslite_in2out.c b/src/plugins/nat/dslite/dslite_in2out.c index a8204179a58..409c59c218c 100644 --- a/src/plugins/nat/dslite/dslite_in2out.c +++ b/src/plugins/nat/dslite/dslite_in2out.c @@ -13,7 +13,7 @@ * limitations under the License. */ #include -#include +#include typedef enum { diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 0656b305df7..85df078dfd2 100644 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index 2af0ba706fe..5973d36ba19 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/nat/lib/ipfix_logging.c b/src/plugins/nat/lib/ipfix_logging.c index 6a944692f47..f66458572d3 100644 --- a/src/plugins/nat/lib/ipfix_logging.c +++ b/src/plugins/nat/lib/ipfix_logging.c @@ -1269,6 +1269,13 @@ nat_ipfix_flush (u32 thread_index) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, do_flush); } +int +nat_ipfix_logging_enabled () +{ + nat_ipfix_logging_main_t *silm = &nat_ipfix_logging_main; + return !clib_atomic_fetch_or(&silm->enabled, 0); +} + void nat_ipfix_flush_from_main (void) { diff --git a/src/plugins/nat/lib/ipfix_logging.h b/src/plugins/nat/lib/ipfix_logging.h index 53aa86b2a38..0b2357a2604 100644 --- a/src/plugins/nat/lib/ipfix_logging.h +++ b/src/plugins/nat/lib/ipfix_logging.h @@ -112,6 +112,8 @@ typedef struct { extern nat_ipfix_logging_main_t nat_ipfix_logging_main; +int nat_ipfix_logging_enabled (); + void nat_ipfix_logging_init (vlib_main_t * vm); int nat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port); void nat_ipfix_logging_nat44_ses_create (u32 thread_index, u32 src_ip, diff --git a/src/plugins/nat/lib/lib.h b/src/plugins/nat/lib/lib.h index febe829ce56..04bad5c74f7 100644 --- a/src/plugins/nat/lib/lib.h +++ b/src/plugins/nat/lib/lib.h @@ -67,12 +67,34 @@ typedef enum #undef _ } nat_protocol_t; -/* default session timeouts */ +/* default protocol timeouts */ #define NAT_UDP_TIMEOUT 300 #define NAT_TCP_TRANSITORY_TIMEOUT 240 #define NAT_TCP_ESTABLISHED_TIMEOUT 7440 #define NAT_ICMP_TIMEOUT 60 +typedef struct +{ + struct + { + u32 established; + u32 transitory; + } tcp; + + u32 udp; + u32 icmp; + +} nat_timeouts_t; + +static_always_inline void +nat_reset_timeouts (nat_timeouts_t * timeouts) +{ + timeouts->udp = NAT_UDP_TIMEOUT; + timeouts->tcp.established = NAT_TCP_ESTABLISHED_TIMEOUT; + timeouts->tcp.transitory = NAT_TCP_TRANSITORY_TIMEOUT; + timeouts->icmp = NAT_ICMP_TIMEOUT; +} + // TODO: move common formating definitions here #endif /* included_nat_lib_h__ */ diff --git a/src/plugins/nat/lib/nat_syslog.c b/src/plugins/nat/lib/nat_syslog.c new file mode 100644 index 00000000000..2c395bf7fd8 --- /dev/null +++ b/src/plugins/nat/lib/nat_syslog.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT syslog logging + */ +#include +#include +#include + +#include +#include + +#define NAT_FACILITY SYSLOG_FACILITY_LOCAL0 + +#define NAT_APPNAME "NAT" + +#define SADD_SDEL_SEVERITY SYSLOG_SEVERITY_INFORMATIONAL +#define APMADD_APMDEL_SEVERITY SYSLOG_SEVERITY_INFORMATIONAL + +#define SADD_MSGID "SADD" +#define SDEL_MSGID "SDEL" +#define APMADD_MSGID "APMADD" +#define APMDEL_MSGID "APMDEL" + +#define NSESS_SDID "nsess" +#define NAPMAP_SDID "napmap" + +#define SSUBIX_SDPARAM_NAME "SSUBIX" +#define SVLAN_SDPARAM_NAME "SVLAN" +#define IATYP_SDPARAM_NAME "IATYP" +#define ISADDR_SDPARAM_NAME "ISADDR" +#define ISPORT_SDPARAM_NAME "ISPORT" +#define IDADDR_SDPARAM_NAME "IDADDR" +#define IDPORT_SDPARAM_NAME "IDPORT" +#define XATYP_SDPARAM_NAME "XATYP" +#define XSADDR_SDPARAM_NAME "XSADDR" +#define XSPORT_SDPARAM_NAME "XSPORT" +#define XDADDR_SDPARAM_NAME "XDADDR" +#define XDPORT_SDPARAM_NAME "XDPORT" +#define PROTO_SDPARAM_NAME "PROTO" +#define SV6ENC_SDPARAM_NAME "SV6ENC" + +#define IATYP_IPV4 "IPv4" +#define IATYP_IPV6 "IPv6" + +static inline void +nat_syslog_nat44_apmap (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto, u8 is_add, + ip6_address_t * sv6enc) +{ + syslog_msg_t syslog_msg; + fib_table_t *fib; + + if (!syslog_is_enabled ()) + return; + + if (syslog_severity_filter_block (APMADD_APMDEL_SEVERITY)) + return; + + syslog_msg_init (&syslog_msg, NAT_FACILITY, APMADD_APMDEL_SEVERITY, + NAT_APPNAME, is_add ? APMADD_MSGID : APMDEL_MSGID); + + syslog_msg_sd_init (&syslog_msg, NAPMAP_SDID); + syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix); + if (sv6enc) + { + syslog_msg_add_sd_param (&syslog_msg, SV6ENC_SDPARAM_NAME, "%U", + format_ip6_address, sv6enc); + } + else + { + fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4); + syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", + fib->ft_table_id); + } + syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", + format_ip4_address, isaddr); + syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (isport)); + syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", + format_ip4_address, xsaddr); + syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xsport)); + syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", + nat_proto_to_ip_proto (proto)); + + syslog_msg_send (&syslog_msg); +} + +void +nat_syslog_nat44_apmadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto) +{ + nat_syslog_nat44_apmap (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + proto, 1, 0); +} + +void +nat_syslog_nat44_apmdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto) +{ + nat_syslog_nat44_apmap (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + proto, 0, 0); +} + +void +nat_syslog_dslite_apmadd (u32 ssubix, ip6_address_t * sv6enc, + ip4_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto) +{ + nat_syslog_nat44_apmap (ssubix, 0, isaddr, isport, xsaddr, xsport, + proto, 1, sv6enc); +} + +void +nat_syslog_dslite_apmdel (u32 ssubix, ip6_address_t * sv6enc, + ip4_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto) +{ + nat_syslog_nat44_apmap (ssubix, 0, isaddr, isport, xsaddr, xsport, + proto, 0, sv6enc); +} + +static inline void +nat_syslog_nat44_sess (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * idaddr, u16 idport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_add, u8 is_twicenat) +{ + syslog_msg_t syslog_msg; + fib_table_t *fib; + + if (!syslog_is_enabled ()) + return; + + if (syslog_severity_filter_block (SADD_SDEL_SEVERITY)) + return; + + fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4); + + syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME, + is_add ? SADD_MSGID : SDEL_MSGID); + + syslog_msg_sd_init (&syslog_msg, NSESS_SDID); + syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix); + syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", + fib->ft_table_id); + syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", + format_ip4_address, isaddr); + syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (isport)); + syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", + format_ip4_address, xsaddr); + syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xsport)); + syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", + nat_proto_to_ip_proto (proto)); + syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U", + format_ip4_address, xdaddr); + syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xdport)); + if (is_twicenat) + { + syslog_msg_add_sd_param (&syslog_msg, IDADDR_SDPARAM_NAME, "%U", + format_ip4_address, idaddr); + syslog_msg_add_sd_param (&syslog_msg, IDPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (idport)); + } + + syslog_msg_send (&syslog_msg); +} + +void +nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * idaddr, u16 idport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_twicenat) +{ + nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + idaddr, idport, xdaddr, xdport, proto, 1, + is_twicenat); +} + +void +nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * idaddr, u16 idport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_twicenat) +{ + nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + idaddr, idport, xdaddr, xdport, proto, 0, + is_twicenat); +} + +static inline void +nat_syslog_nat64_sess (u32 sfibix, ip6_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_add) +{ + syslog_msg_t syslog_msg; + fib_table_t *fib; + + if (!syslog_is_enabled ()) + return; + + if (syslog_severity_filter_block (SADD_SDEL_SEVERITY)) + return; + + fib = fib_table_get (sfibix, FIB_PROTOCOL_IP6); + + syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME, + is_add ? SADD_MSGID : SDEL_MSGID); + + syslog_msg_sd_init (&syslog_msg, NSESS_SDID); + syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", + fib->ft_table_id); + syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV6); + syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", + format_ip6_address, isaddr); + syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (isport)); + syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", + format_ip4_address, xsaddr); + syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xsport)); + syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", proto); + syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U", + format_ip4_address, xdaddr); + syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xdport)); + + syslog_msg_send (&syslog_msg); +} + +void +nat_syslog_nat64_sadd (u32 sfibix, ip6_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto) +{ + nat_syslog_nat64_sess (sfibix, isaddr, isport, xsaddr, xsport, xdaddr, + xdport, proto, 1); +} + +void +nat_syslog_nat64_sdel (u32 sfibix, ip6_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto) +{ + nat_syslog_nat64_sess (sfibix, isaddr, isport, xsaddr, xsport, xdaddr, + xdport, proto, 0); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/lib/nat_syslog.h b/src/plugins/nat/lib/nat_syslog.h new file mode 100644 index 00000000000..9721664cf54 --- /dev/null +++ b/src/plugins/nat/lib/nat_syslog.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT syslog logging + */ +#ifndef __included_nat_syslog_h__ +#define __included_nat_syslog_h__ + +#include + +void nat_syslog_nat44_apmadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto); + +void nat_syslog_nat44_apmdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto); + +void +nat_syslog_dslite_apmadd (u32 ssubix, ip6_address_t * sv6enc, + ip4_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto); + +void +nat_syslog_dslite_apmdel (u32 ssubix, ip6_address_t * sv6enc, + ip4_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + nat_protocol_t proto); + +void nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * idaddr, u16 idport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_twicenat); + +void nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, + u16 isport, ip4_address_t * idaddr, u16 idport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto, u8 is_twicenat); + +void nat_syslog_nat64_sadd (u32 sfibix, ip6_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto); + +void nat_syslog_nat64_sdel (u32 sfibix, ip6_address_t * isaddr, u16 isport, + ip4_address_t * xsaddr, u16 xsport, + ip4_address_t * xdaddr, u16 xdport, + nat_protocol_t proto); + +#endif /* __included_nat_syslog_h__ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 39dd1db650a..74adae9ce34 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -22,10 +22,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -1379,7 +1379,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, uword *bitmap = 0; if (!sm->endpoint_dependent) - return VNET_API_ERROR_FEATURE_DISABLED; + return VNET_API_ERROR_UNSUPPORTED; init_nat_k (&kv, e_addr, e_port, 0, proto); if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) @@ -2875,10 +2875,7 @@ nat44_plugin_enable (nat44_config_t c) sm->alloc_addr_and_port = nat_alloc_addr_and_port_default; sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; // - sm->udp_timeout = SNAT_UDP_TIMEOUT; - sm->icmp_timeout = SNAT_ICMP_TIMEOUT; - sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; + nat_reset_timeouts (&sm->timeouts); // nat44 feature configuration sm->endpoint_dependent = c.endpoint_dependent; diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 9b11736f5ac..710c86fc8d9 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -36,12 +36,6 @@ #include #include -/* default session timeouts */ -#define SNAT_UDP_TIMEOUT 300 -#define SNAT_TCP_TRANSITORY_TIMEOUT 240 -#define SNAT_TCP_ESTABLISHED_TIMEOUT 7440 -#define SNAT_ICMP_TIMEOUT 60 - /* number of worker handoff frame queue elements */ #define NAT_FQ_NELTS 64 @@ -643,11 +637,7 @@ typedef struct snat_main_s u32 inside_vrf_id; u32 inside_fib_index; - /* values of various timeouts */ - u32 udp_timeout; - u32 tcp_transitory_timeout; - u32 tcp_established_timeout; - u32 icmp_timeout; + nat_timeouts_t timeouts; /* TCP MSS clamping */ u16 mss_clamping; diff --git a/src/plugins/nat/nat44.api b/src/plugins/nat/nat44.api index ec8c41640b0..4bbd254f67a 100644 --- a/src/plugins/nat/nat44.api +++ b/src/plugins/nat/nat44.api @@ -19,17 +19,13 @@ import "vnet/interface_types.api"; import "plugins/nat/nat_types.api"; /** - * @file nat.api + * @file nat44.api * @brief VPP control-plane API messages. * * This file defines VPP control-plane API messages which are generally * called through a shared memory interface. */ -/* - * Common NAT plugin APIs - */ - enum nat44_config_flags : u8 { NAT44_IS_ENDPOINT_INDEPENDENT = 0x00, @@ -250,30 +246,20 @@ define nat44_show_running_config_reply option in_progress; u32 context; i32 retval; - u32 inside_vrf; u32 outside_vrf; - u32 users; u32 sessions; u32 user_sessions; - u32 user_buckets; u32 translation_buckets; - + bool forwarding_enabled; + bool ipfix_logging_enabled; + vl_api_nat_timeouts_t timeouts; + vl_api_nat_log_level_t log_level; vl_api_nat44_config_flags_t flags; }; -enum nat_log_level : u8 -{ - NAT_LOG_NONE = 0x00, - NAT_LOG_ERROR = 0x01, - NAT_LOG_WARNING = 0x02, - NAT_LOG_NOTICE = 0x03, - NAT_LOG_INFO = 0x04, - NAT_LOG_DEBUG = 0x05, -}; - /** \brief Run nat44 garbage collection @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -303,6 +289,7 @@ autoreply define nat44_set_session_limit { @param log_level - logging level */ autoreply define nat_set_log_level { + option deprecated; u32 client_index; u32 context; vl_api_nat_log_level_t log_level; @@ -349,6 +336,7 @@ define nat_worker_details { @param enable - true if enable, false if disable */ autoreply define nat_ipfix_enable_disable { + option deprecated; u32 client_index; u32 context; u32 domain_id; @@ -365,6 +353,7 @@ autoreply define nat_ipfix_enable_disable { @param icmp - ICMP timeout (default 60sec) */ autoreply define nat_set_timeouts { + option deprecated; u32 client_index; u32 context; u32 udp; @@ -378,6 +367,7 @@ autoreply define nat_set_timeouts { @param context - sender context, to match reply w/ request */ define nat_get_timeouts { + option deprecated; u32 client_index; u32 context; }; @@ -391,6 +381,7 @@ define nat_get_timeouts { @param icmp - ICMP timeout */ define nat_get_timeouts_reply { + option deprecated; u32 context; i32 retval; u32 udp; @@ -610,10 +601,6 @@ service { rpc nat_ha_resync returns nat_ha_resync_reply events nat_ha_resync_completed_event; }; -/* - * NAT44 APIs - */ - /** \brief Del NAT44 user @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -1164,6 +1151,7 @@ autoreply define nat44_del_session { @param enable - true for enable, false for disable */ autoreply define nat44_forwarding_enable_disable { + option deprecated; u32 client_index; u32 context; bool enable; @@ -1174,6 +1162,7 @@ autoreply define nat44_forwarding_enable_disable { @param context - sender context, to match reply w/ request */ define nat44_forwarding_is_enabled { + option deprecated; u32 client_index; u32 context; }; @@ -1183,6 +1172,7 @@ define nat44_forwarding_is_enabled { @param enabled - true if enabled, false if disabled */ define nat44_forwarding_is_enabled_reply { + option deprecated; u32 context; bool enabled; }; diff --git a/src/plugins/nat/nat44_api.c b/src/plugins/nat/nat44_api.c index 2028b3510df..6e282856943 100644 --- a/src/plugins/nat/nat44_api.c +++ b/src/plugins/nat/nat44_api.c @@ -129,6 +129,15 @@ vl_api_nat44_show_running_config_t_handler (vl_api_nat44_show_running_config_t rmp->user_buckets = htonl (sm->user_buckets); rmp->translation_buckets = htonl (sm->translation_buckets); + rmp->timeouts.udp = htonl (sm->timeouts.udp); + rmp->timeouts.tcp_established = htonl (sm->timeouts.tcp.established); + rmp->timeouts.tcp_transitory = htonl (sm->timeouts.tcp.transitory); + rmp->timeouts.icmp = htonl (sm->timeouts.icmp); + + rmp->forwarding_enabled = sm->forwarding_enabled == 1; + // consider how to split functionality between subplugins + rmp->ipfix_logging_enabled = nat_ipfix_logging_enabled (); + if (rc->endpoint_dependent) rmp->flags |= NAT44_IS_ENDPOINT_DEPENDENT; else @@ -300,10 +309,10 @@ vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp) vl_api_nat_set_timeouts_reply_t *rmp; int rv = 0; - sm->udp_timeout = ntohl (mp->udp); - sm->tcp_established_timeout = ntohl (mp->tcp_established); - sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); - sm->icmp_timeout = ntohl (mp->icmp); + sm->timeouts.udp = ntohl (mp->udp); + sm->timeouts.tcp.established = ntohl (mp->tcp_established); + sm->timeouts.tcp.transitory = ntohl (mp->tcp_transitory); + sm->timeouts.icmp = ntohl (mp->icmp); REPLY_MACRO (VL_API_NAT_SET_TIMEOUTS_REPLY); } @@ -318,10 +327,10 @@ vl_api_nat_get_timeouts_t_handler (vl_api_nat_get_timeouts_t * mp) /* *INDENT-OFF* */ REPLY_MACRO2 (VL_API_NAT_GET_TIMEOUTS_REPLY, ({ - rmp->udp = htonl (sm->udp_timeout); - rmp->tcp_established = htonl (sm->tcp_established_timeout); - rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); - rmp->icmp = htonl (sm->icmp_timeout); + rmp->udp = htonl (sm->timeouts.udp); + rmp->tcp_established = htonl (sm->timeouts.tcp.established); + rmp->tcp_transitory = htonl (sm->timeouts.tcp.transitory); + rmp->icmp = htonl (sm->timeouts.icmp); })) /* *INDENT-ON* */ } @@ -335,6 +344,12 @@ static void int rv = 0; u16 port_start, port_end; + if (sm->endpoint_dependent) + { + rv = VNET_API_ERROR_UNSUPPORTED; + goto send_reply; + } + switch (mp->alg) { case NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT: diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 695e8cc142e..d9cfe32287d 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -342,6 +342,10 @@ nat44_set_alloc_addr_and_port_alg_command_fn (vlib_main_t * vm, unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; u32 psid, psid_offset, psid_length, port_start, port_end; + snat_main_t *sm = &snat_main; + + if (sm->endpoint_dependent) + return clib_error_return (0, UNSUPPORTED_IN_ED_MODE_STR); /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -1857,19 +1861,14 @@ set_timeout_command_fn (vlib_main_t * vm, while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { - if (unformat (line_input, "udp %u", &sm->udp_timeout)); + if (unformat (line_input, "udp %u", &sm->timeouts.udp)); else if (unformat (line_input, "tcp-established %u", - &sm->tcp_established_timeout)); + &sm->timeouts.tcp.established)); else if (unformat (line_input, "tcp-transitory %u", - &sm->tcp_transitory_timeout)); - else if (unformat (line_input, "icmp %u", &sm->icmp_timeout)); + &sm->timeouts.tcp.transitory)); + else if (unformat (line_input, "icmp %u", &sm->timeouts.icmp)); else if (unformat (line_input, "reset")) - { - sm->udp_timeout = SNAT_UDP_TIMEOUT; - sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - sm->icmp_timeout = SNAT_ICMP_TIMEOUT; - } + nat_reset_timeouts (&sm->timeouts); else { error = clib_error_return (0, "unknown input '%U'", @@ -1889,12 +1888,12 @@ nat_show_timeouts_command_fn (vlib_main_t * vm, { snat_main_t *sm = &snat_main; - vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout); + vlib_cli_output (vm, "udp timeout: %dsec", sm->timeouts.udp); vlib_cli_output (vm, "tcp-established timeout: %dsec", - sm->tcp_established_timeout); + sm->timeouts.tcp.established); vlib_cli_output (vm, "tcp-transitory timeout: %dsec", - sm->tcp_transitory_timeout); - vlib_cli_output (vm, "icmp timeout: %dsec", sm->icmp_timeout); + sm->timeouts.tcp.transitory); + vlib_cli_output (vm, "icmp timeout: %dsec", sm->timeouts.icmp); return 0; } diff --git a/src/plugins/nat/nat64/nat64_db.c b/src/plugins/nat/nat64/nat64_db.c index 2c52d3b6ca2..49909fdfeb6 100644 --- a/src/plugins/nat/nat64/nat64_db.c +++ b/src/plugins/nat/nat64/nat64_db.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h index 8f9be6f9d5c..3d0f5ba05c4 100644 --- a/src/plugins/nat/nat_inlines.h +++ b/src/plugins/nat/nat_inlines.h @@ -227,16 +227,6 @@ is_interface_addr (snat_main_t * sm, vlib_node_runtime_t * node, return 0; } -always_inline u8 -maximum_sessions_exceeded (snat_main_t * sm, u32 thread_index) -{ - if (pool_elts (sm->per_thread_data[thread_index].sessions) >= - sm->max_translations_per_thread) - return 1; - - return 0; -} - always_inline void user_session_increment (snat_main_t * sm, snat_user_t * u, u8 is_static) { @@ -339,7 +329,7 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now, ses->state |= NAT44_SES_O2I_FIN_ACK; if (nat44_is_ses_closed (ses)) { // if session is now closed, save the timestamp - ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout; + ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; ses->last_lru_update = now; } } @@ -385,7 +375,7 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now, ses->state |= NAT44_SES_I2O_FIN_ACK; if (nat44_is_ses_closed (ses)) { // if session is now closed, save the timestamp - ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout; + ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; ses->last_lru_update = now; } } @@ -408,18 +398,18 @@ nat44_session_get_timeout (snat_main_t * sm, snat_session_t * s) switch (s->nat_proto) { case NAT_PROTOCOL_ICMP: - return sm->icmp_timeout; + return sm->timeouts.icmp; case NAT_PROTOCOL_UDP: - return sm->udp_timeout; + return sm->timeouts.udp; case NAT_PROTOCOL_TCP: { if (s->state) - return sm->tcp_transitory_timeout; + return sm->timeouts.tcp.transitory; else - return sm->tcp_established_timeout; + return sm->timeouts.tcp.established; } default: - return sm->udp_timeout; + return sm->timeouts.udp; } return 0; @@ -494,20 +484,6 @@ ed_value_get_session_index (clib_bihash_kv_16_8_t * value) return value->value & ~(u32) 0; } -always_inline void -split_ed_value (clib_bihash_kv_16_8_t * value, u32 * thread_index, - u32 * session_index) -{ - if (thread_index) - { - *thread_index = ed_value_get_thread_index (value); - } - if (session_index) - { - *session_index = ed_value_get_session_index (value); - } -} - always_inline void split_ed_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t * l_addr, ip4_address_t * r_addr, u8 * proto, diff --git a/src/plugins/nat/nat_syslog.c b/src/plugins/nat/nat_syslog.c deleted file mode 100644 index 0d7053d9ce3..00000000000 --- a/src/plugins/nat/nat_syslog.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT syslog logging - */ -#include -#include -#include - -#include -#include - -#define NAT_FACILITY SYSLOG_FACILITY_LOCAL0 - -#define NAT_APPNAME "NAT" - -#define SADD_SDEL_SEVERITY SYSLOG_SEVERITY_INFORMATIONAL -#define APMADD_APMDEL_SEVERITY SYSLOG_SEVERITY_INFORMATIONAL - -#define SADD_MSGID "SADD" -#define SDEL_MSGID "SDEL" -#define APMADD_MSGID "APMADD" -#define APMDEL_MSGID "APMDEL" - -#define NSESS_SDID "nsess" -#define NAPMAP_SDID "napmap" - -#define SSUBIX_SDPARAM_NAME "SSUBIX" -#define SVLAN_SDPARAM_NAME "SVLAN" -#define IATYP_SDPARAM_NAME "IATYP" -#define ISADDR_SDPARAM_NAME "ISADDR" -#define ISPORT_SDPARAM_NAME "ISPORT" -#define IDADDR_SDPARAM_NAME "IDADDR" -#define IDPORT_SDPARAM_NAME "IDPORT" -#define XATYP_SDPARAM_NAME "XATYP" -#define XSADDR_SDPARAM_NAME "XSADDR" -#define XSPORT_SDPARAM_NAME "XSPORT" -#define XDADDR_SDPARAM_NAME "XDADDR" -#define XDPORT_SDPARAM_NAME "XDPORT" -#define PROTO_SDPARAM_NAME "PROTO" -#define SV6ENC_SDPARAM_NAME "SV6ENC" - -#define IATYP_IPV4 "IPv4" -#define IATYP_IPV6 "IPv6" - -static inline void -nat_syslog_nat44_apmap (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto, u8 is_add, - ip6_address_t * sv6enc) -{ - syslog_msg_t syslog_msg; - fib_table_t *fib; - - if (!syslog_is_enabled ()) - return; - - if (syslog_severity_filter_block (APMADD_APMDEL_SEVERITY)) - return; - - syslog_msg_init (&syslog_msg, NAT_FACILITY, APMADD_APMDEL_SEVERITY, - NAT_APPNAME, is_add ? APMADD_MSGID : APMDEL_MSGID); - - syslog_msg_sd_init (&syslog_msg, NAPMAP_SDID); - syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix); - if (sv6enc) - { - syslog_msg_add_sd_param (&syslog_msg, SV6ENC_SDPARAM_NAME, "%U", - format_ip6_address, sv6enc); - } - else - { - fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4); - syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", - fib->ft_table_id); - } - syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4); - syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", - format_ip4_address, isaddr); - syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (isport)); - syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); - syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", - format_ip4_address, xsaddr); - syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (xsport)); - syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", - nat_proto_to_ip_proto (proto)); - - syslog_msg_send (&syslog_msg); -} - -void -nat_syslog_nat44_apmadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto) -{ - nat_syslog_nat44_apmap (ssubix, sfibix, isaddr, isport, xsaddr, xsport, - proto, 1, 0); -} - -void -nat_syslog_nat44_apmdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto) -{ - nat_syslog_nat44_apmap (ssubix, sfibix, isaddr, isport, xsaddr, xsport, - proto, 0, 0); -} - -void -nat_syslog_dslite_apmadd (u32 ssubix, ip6_address_t * sv6enc, - ip4_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto) -{ - nat_syslog_nat44_apmap (ssubix, 0, isaddr, isport, xsaddr, xsport, - proto, 1, sv6enc); -} - -void -nat_syslog_dslite_apmdel (u32 ssubix, ip6_address_t * sv6enc, - ip4_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto) -{ - nat_syslog_nat44_apmap (ssubix, 0, isaddr, isport, xsaddr, xsport, - proto, 0, sv6enc); -} - -static inline void -nat_syslog_nat44_sess (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * idaddr, u16 idport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_add, u8 is_twicenat) -{ - syslog_msg_t syslog_msg; - fib_table_t *fib; - - if (!syslog_is_enabled ()) - return; - - if (syslog_severity_filter_block (SADD_SDEL_SEVERITY)) - return; - - fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4); - - syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME, - is_add ? SADD_MSGID : SDEL_MSGID); - - syslog_msg_sd_init (&syslog_msg, NSESS_SDID); - syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix); - syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", - fib->ft_table_id); - syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4); - syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", - format_ip4_address, isaddr); - syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (isport)); - syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); - syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", - format_ip4_address, xsaddr); - syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (xsport)); - syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", - nat_proto_to_ip_proto (proto)); - syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U", - format_ip4_address, xdaddr); - syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (xdport)); - if (is_twicenat) - { - syslog_msg_add_sd_param (&syslog_msg, IDADDR_SDPARAM_NAME, "%U", - format_ip4_address, idaddr); - syslog_msg_add_sd_param (&syslog_msg, IDPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (idport)); - } - - syslog_msg_send (&syslog_msg); -} - -void -nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * idaddr, u16 idport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_twicenat) -{ - nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, - idaddr, idport, xdaddr, xdport, proto, 1, - is_twicenat); -} - -void -nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * idaddr, u16 idport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_twicenat) -{ - nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, - idaddr, idport, xdaddr, xdport, proto, 0, - is_twicenat); -} - -static inline void -nat_syslog_nat64_sess (u32 sfibix, ip6_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_add) -{ - syslog_msg_t syslog_msg; - fib_table_t *fib; - - if (!syslog_is_enabled ()) - return; - - if (syslog_severity_filter_block (SADD_SDEL_SEVERITY)) - return; - - fib = fib_table_get (sfibix, FIB_PROTOCOL_IP6); - - syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME, - is_add ? SADD_MSGID : SDEL_MSGID); - - syslog_msg_sd_init (&syslog_msg, NSESS_SDID); - syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", - fib->ft_table_id); - syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV6); - syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", - format_ip6_address, isaddr); - syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (isport)); - syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); - syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", - format_ip4_address, xsaddr); - syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (xsport)); - syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", proto); - syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U", - format_ip4_address, xdaddr); - syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d", - clib_net_to_host_u16 (xdport)); - - syslog_msg_send (&syslog_msg); -} - -void -nat_syslog_nat64_sadd (u32 sfibix, ip6_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto) -{ - nat_syslog_nat64_sess (sfibix, isaddr, isport, xsaddr, xsport, xdaddr, - xdport, proto, 1); -} - -void -nat_syslog_nat64_sdel (u32 sfibix, ip6_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto) -{ - nat_syslog_nat64_sess (sfibix, isaddr, isport, xsaddr, xsport, xdaddr, - xdport, proto, 0); -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_syslog.h b/src/plugins/nat/nat_syslog.h deleted file mode 100644 index 755e344ee67..00000000000 --- a/src/plugins/nat/nat_syslog.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT syslog logging - */ -#ifndef __included_nat_syslog_h__ -#define __included_nat_syslog_h__ - -#include - -void nat_syslog_nat44_apmadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto); - -void nat_syslog_nat44_apmdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto); - -void -nat_syslog_dslite_apmadd (u32 ssubix, ip6_address_t * sv6enc, - ip4_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto); - -void -nat_syslog_dslite_apmdel (u32 ssubix, ip6_address_t * sv6enc, - ip4_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - nat_protocol_t proto); - -void nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * idaddr, u16 idport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_twicenat); - -void nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t * isaddr, - u16 isport, ip4_address_t * idaddr, u16 idport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto, u8 is_twicenat); - -void nat_syslog_nat64_sadd (u32 sfibix, ip6_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto); - -void nat_syslog_nat64_sdel (u32 sfibix, ip6_address_t * isaddr, u16 isport, - ip4_address_t * xsaddr, u16 xsport, - ip4_address_t * xdaddr, u16 xdport, - nat_protocol_t proto); - -#endif /* __included_nat_syslog_h__ */ -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_types.api b/src/plugins/nat/nat_types.api index f348e6154a3..ad4ee9faba0 100644 --- a/src/plugins/nat/nat_types.api +++ b/src/plugins/nat/nat_types.api @@ -15,6 +15,24 @@ option version = "0.0.1"; +typedef nat_timeouts +{ + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +enum nat_log_level : u8 +{ + NAT_LOG_NONE = 0x00, + NAT_LOG_ERROR = 0x01, + NAT_LOG_WARNING = 0x02, + NAT_LOG_NOTICE = 0x03, + NAT_LOG_INFO = 0x04, + NAT_LOG_DEBUG = 0x05, +}; + enum nat_config_flags : u8 { NAT_IS_NONE = 0x00, diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index dc1d0876d55..c8c4542ab83 100644 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c index 2f515d0f2f4..b0e8892f77a 100644 --- a/src/plugins/nat/out2in_ed.c +++ b/src/plugins/nat/out2in_ed.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/nat/test/test_nat.py b/src/plugins/nat/test/test_nat.py deleted file mode 100644 index d635abf9825..00000000000 --- a/src/plugins/nat/test/test_nat.py +++ /dev/null @@ -1,7276 +0,0 @@ -#!/usr/bin/env python3 - -import ipaddress -import random -import socket -import struct -import unittest -from io import BytesIO -from time import sleep - -import scapy.compat -from framework import VppTestCase, VppTestRunner, running_extended_tests -from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder -from scapy.all import bind_layers, Packet, ByteEnumField, ShortField, \ - IPField, IntField, LongField, XByteField, FlagsField, FieldLenField, \ - PacketListField -from scapy.data import IP_PROTOS -from scapy.layers.inet import IP, TCP, UDP, ICMP -from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror -from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment -from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply, \ - ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, fragment6 -from scapy.layers.l2 import Ether, ARP, GRE -from scapy.packet import Raw -from syslog_rfc5424_parser import SyslogMessage, ParseError -from syslog_rfc5424_parser.constants import SyslogSeverity -from util import ip4_range -from util import ppc, ppp -from vpp_acl import AclRule, VppAcl, VppAclInterface -from vpp_ip_route import VppIpRoute, VppRoutePath -from vpp_neighbor import VppNeighbor -from vpp_papi import VppEnum - - -# NAT HA protocol event data -class Event(Packet): - name = "Event" - fields_desc = [ByteEnumField("event_type", None, - {1: "add", 2: "del", 3: "refresh"}), - ByteEnumField("protocol", None, - {0: "other", 1: "udp", 2: "tcp", 3: "icmp"}), - ShortField("flags", 0), - IPField("in_addr", None), - IPField("out_addr", None), - ShortField("in_port", None), - ShortField("out_port", None), - IPField("eh_addr", None), - IPField("ehn_addr", None), - ShortField("eh_port", None), - ShortField("ehn_port", None), - IntField("fib_index", None), - IntField("total_pkts", 0), - LongField("total_bytes", 0)] - - def extract_padding(self, s): - return "", s - - -# NAT HA protocol header -class HANATStateSync(Packet): - name = "HA NAT state sync" - fields_desc = [XByteField("version", 1), - FlagsField("flags", 0, 8, ['ACK']), - FieldLenField("count", None, count_of="events"), - IntField("sequence_number", 1), - IntField("thread_index", 0), - PacketListField("events", [], Event, - count_from=lambda pkt: pkt.count)] - - -class MethodHolder(VppTestCase): - """ NAT create capture and verify method holder """ - - @property - def config_flags(self): - return VppEnum.vl_api_nat_config_flags_t - - @property - def nat44_config_flags(self): - return VppEnum.vl_api_nat44_config_flags_t - - @property - def SYSLOG_SEVERITY(self): - return VppEnum.vl_api_syslog_severity_t - - def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', - local_port=0, external_port=0, vrf_id=0, - is_add=1, external_sw_if_index=0xFFFFFFFF, - proto=0, tag="", flags=0): - """ - Add/delete NAT44 static mapping - - :param local_ip: Local IP address - :param external_ip: External IP address - :param local_port: Local port number (Optional) - :param external_port: External port number (Optional) - :param vrf_id: VRF ID (Default 0) - :param is_add: 1 if add, 0 if delete (Default add) - :param external_sw_if_index: External interface instead of IP address - :param proto: IP protocol (Mandatory if port specified) - :param tag: Opaque string tag - :param flags: NAT configuration flags - """ - - if not (local_port and external_port): - flags |= self.config_flags.NAT_IS_ADDR_ONLY - - self.vapi.nat44_add_del_static_mapping( - is_add=is_add, - local_ip_address=local_ip, - external_ip_address=external_ip, - external_sw_if_index=external_sw_if_index, - local_port=local_port, - external_port=external_port, - vrf_id=vrf_id, protocol=proto, - flags=flags, - tag=tag) - - def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): - """ - Add/delete NAT44 address - - :param ip: IP address - :param is_add: 1 if add, 0 if delete (Default add) - :param twice_nat: twice NAT address for external hosts - """ - flags = self.config_flags.NAT_IS_TWICE_NAT if twice_nat else 0 - self.vapi.nat44_add_del_address_range(first_ip_address=ip, - last_ip_address=ip, - vrf_id=vrf_id, - is_add=is_add, - flags=flags) - - def create_stream_in(self, in_if, out_if, dst_ip=None, ttl=64): - """ - Create packet stream for inside network - - :param in_if: Inside interface - :param out_if: Outside interface - :param dst_ip: Destination address - :param ttl: TTL of generated packets - """ - if dst_ip is None: - dst_ip = out_if.remote_ip4 - - pkts = [] - # TCP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / - TCP(sport=self.tcp_port_in, dport=20)) - pkts.extend([p, p]) - - # UDP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / - UDP(sport=self.udp_port_in, dport=20)) - pkts.append(p) - - # ICMP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - - return pkts - - def compose_ip6(self, ip4, pref, plen): - """ - Compose IPv4-embedded IPv6 addresses - - :param ip4: IPv4 address - :param pref: IPv6 prefix - :param plen: IPv6 prefix length - :returns: IPv4-embedded IPv6 addresses - """ - pref_n = list(socket.inet_pton(socket.AF_INET6, pref)) - ip4_n = list(socket.inet_pton(socket.AF_INET, ip4)) - if plen == 32: - pref_n[4] = ip4_n[0] - pref_n[5] = ip4_n[1] - pref_n[6] = ip4_n[2] - pref_n[7] = ip4_n[3] - elif plen == 40: - pref_n[5] = ip4_n[0] - pref_n[6] = ip4_n[1] - pref_n[7] = ip4_n[2] - pref_n[9] = ip4_n[3] - elif plen == 48: - pref_n[6] = ip4_n[0] - pref_n[7] = ip4_n[1] - pref_n[9] = ip4_n[2] - pref_n[10] = ip4_n[3] - elif plen == 56: - pref_n[7] = ip4_n[0] - pref_n[9] = ip4_n[1] - pref_n[10] = ip4_n[2] - pref_n[11] = ip4_n[3] - elif plen == 64: - pref_n[9] = ip4_n[0] - pref_n[10] = ip4_n[1] - pref_n[11] = ip4_n[2] - pref_n[12] = ip4_n[3] - elif plen == 96: - pref_n[12] = ip4_n[0] - pref_n[13] = ip4_n[1] - pref_n[14] = ip4_n[2] - pref_n[15] = ip4_n[3] - packed_pref_n = b''.join([scapy.compat.chb(x) for x in pref_n]) - return socket.inet_ntop(socket.AF_INET6, packed_pref_n) - - def extract_ip4(self, ip6, plen): - """ - Extract IPv4 address embedded in IPv6 addresses - - :param ip6: IPv6 address - :param plen: IPv6 prefix length - :returns: extracted IPv4 address - """ - ip6_n = list(socket.inet_pton(socket.AF_INET6, ip6)) - ip4_n = [None] * 4 - if plen == 32: - ip4_n[0] = ip6_n[4] - ip4_n[1] = ip6_n[5] - ip4_n[2] = ip6_n[6] - ip4_n[3] = ip6_n[7] - elif plen == 40: - ip4_n[0] = ip6_n[5] - ip4_n[1] = ip6_n[6] - ip4_n[2] = ip6_n[7] - ip4_n[3] = ip6_n[9] - elif plen == 48: - ip4_n[0] = ip6_n[6] - ip4_n[1] = ip6_n[7] - ip4_n[2] = ip6_n[9] - ip4_n[3] = ip6_n[10] - elif plen == 56: - ip4_n[0] = ip6_n[7] - ip4_n[1] = ip6_n[9] - ip4_n[2] = ip6_n[10] - ip4_n[3] = ip6_n[11] - elif plen == 64: - ip4_n[0] = ip6_n[9] - ip4_n[1] = ip6_n[10] - ip4_n[2] = ip6_n[11] - ip4_n[3] = ip6_n[12] - elif plen == 96: - ip4_n[0] = ip6_n[12] - ip4_n[1] = ip6_n[13] - ip4_n[2] = ip6_n[14] - ip4_n[3] = ip6_n[15] - return socket.inet_ntop(socket.AF_INET, ''.join(ip4_n)) - - 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=tcp_port, sport=20)) - pkts.extend([p, 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=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=icmp_id, type='echo-reply')) - pkts.append(p) - - return pkts - - def create_stream_out_ip6(self, out_if, src_ip, dst_ip, hl=64): - """ - Create packet stream for outside network - - :param out_if: Outside interface - :param dst_ip: Destination IP address (Default use global NAT address) - :param hl: HL of generated packets - """ - pkts = [] - # TCP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IPv6(src=src_ip, dst=dst_ip, hlim=hl) / - TCP(dport=self.tcp_port_out, sport=20)) - pkts.append(p) - - # UDP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IPv6(src=src_ip, dst=dst_ip, hlim=hl) / - UDP(dport=self.udp_port_out, sport=20)) - pkts.append(p) - - # ICMP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IPv6(src=src_ip, dst=dst_ip, hlim=hl) / - ICMPv6EchoReply(id=self.icmp_id_out)) - pkts.append(p) - - return pkts - - def verify_capture_out(self, capture, nat_ip=None, same_port=False, - dst_ip=None, is_ip6=False, ignore_port=False): - """ - Verify captured packets on outside network - - :param capture: Captured packets - :param nat_ip: Translated IP address (Default use global NAT address) - :param same_port: Source port number is not translated (Default False) - :param dst_ip: Destination IP address (Default do not verify) - :param is_ip6: If L3 protocol is IPv6 (Default False) - """ - if is_ip6: - IP46 = IPv6 - ICMP46 = ICMPv6EchoRequest - else: - IP46 = IP - ICMP46 = ICMP - if nat_ip is None: - nat_ip = self.nat_addr - for packet in capture: - try: - if not is_ip6: - self.assert_packet_checksums_valid(packet) - self.assertEqual(packet[IP46].src, nat_ip) - if dst_ip is not None: - self.assertEqual(packet[IP46].dst, dst_ip) - if packet.haslayer(TCP): - if not ignore_port: - if same_port: - self.assertEqual( - packet[TCP].sport, self.tcp_port_in) - else: - self.assertNotEqual( - packet[TCP].sport, self.tcp_port_in) - self.tcp_port_out = packet[TCP].sport - self.assert_packet_checksums_valid(packet) - elif packet.haslayer(UDP): - if not ignore_port: - if same_port: - self.assertEqual( - packet[UDP].sport, self.udp_port_in) - else: - self.assertNotEqual( - packet[UDP].sport, self.udp_port_in) - self.udp_port_out = packet[UDP].sport - else: - if not ignore_port: - if same_port: - self.assertEqual( - packet[ICMP46].id, self.icmp_id_in) - else: - self.assertNotEqual( - packet[ICMP46].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP46].id - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def verify_capture_out_ip6(self, capture, nat_ip, same_port=False, - dst_ip=None): - """ - Verify captured packets on outside network - - :param capture: Captured packets - :param nat_ip: Translated IP address - :param same_port: Source port number is not translated (Default False) - :param dst_ip: Destination IP address (Default do not verify) - """ - return self.verify_capture_out(capture, nat_ip, same_port, dst_ip, - True) - - def verify_capture_in(self, capture, in_if): - """ - Verify captured packets on inside network - - :param capture: Captured packets - :param in_if: Inside interface - """ - for packet in capture: - try: - self.assert_packet_checksums_valid(packet) - self.assertEqual(packet[IP].dst, in_if.remote_ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_in_ip6(self, capture, src_ip, dst_ip): - """ - Verify captured IPv6 packets on inside network - - :param capture: Captured packets - :param src_ip: Source IP - :param dst_ip: Destination IP address - """ - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, src_ip) - self.assertEqual(packet[IPv6].dst, dst_ip) - self.assert_packet_checksums_valid(packet) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - else: - self.assertEqual(packet[ICMPv6EchoReply].id, - self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_no_translation(self, capture, ingress_if, egress_if): - """ - Verify captured packet that don't have to be translated - - :param capture: Captured packets - :param ingress_if: Ingress interface - :param egress_if: Egress interface - """ - for packet in capture: - try: - self.assertEqual(packet[IP].src, ingress_if.remote_ip4) - self.assertEqual(packet[IP].dst, egress_if.remote_ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].sport, self.tcp_port_in) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].sport, self.udp_port_in) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_out_with_icmp_errors(self, capture, src_ip=None, - icmp_type=11): - """ - Verify captured packets with ICMP errors on outside network - - :param capture: Captured packets - :param src_ip: Translated IP address or IP address of VPP - (Default use global NAT address) - :param icmp_type: Type of error ICMP packet - we are expecting (Default 11) - """ - if src_ip is None: - src_ip = self.nat_addr - for packet in capture: - try: - self.assertEqual(packet[IP].src, src_ip) - self.assertEqual(packet.haslayer(ICMP), 1) - icmp = packet[ICMP] - self.assertEqual(icmp.type, icmp_type) - self.assertTrue(icmp.haslayer(IPerror)) - inner_ip = icmp[IPerror] - if inner_ip.haslayer(TCPerror): - self.assertEqual(inner_ip[TCPerror].dport, - self.tcp_port_out) - elif inner_ip.haslayer(UDPerror): - self.assertEqual(inner_ip[UDPerror].dport, - self.udp_port_out) - else: - self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_out) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def verify_capture_in_with_icmp_errors(self, capture, in_if, icmp_type=11): - """ - Verify captured packets with ICMP errors on inside network - - :param capture: Captured packets - :param in_if: Inside interface - :param icmp_type: Type of error ICMP packet - we are expecting (Default 11) - """ - for packet in capture: - try: - self.assertEqual(packet[IP].dst, in_if.remote_ip4) - self.assertEqual(packet.haslayer(ICMP), 1) - icmp = packet[ICMP] - self.assertEqual(icmp.type, icmp_type) - self.assertTrue(icmp.haslayer(IPerror)) - inner_ip = icmp[IPerror] - if inner_ip.haslayer(TCPerror): - self.assertEqual(inner_ip[TCPerror].sport, - self.tcp_port_in) - elif inner_ip.haslayer(UDPerror): - self.assertEqual(inner_ip[UDPerror].sport, - self.udp_port_in) - else: - self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def create_stream_frag(self, src_if, dst, sport, dport, data, - proto=IP_PROTOS.tcp, echo_reply=False): - """ - Create fragmented packet stream - - :param src_if: Source interface - :param dst: Destination IPv4 address - :param sport: Source port - :param dport: Destination port - :param data: Payload data - :param proto: protocol (TCP, UDP, ICMP) - :param echo_reply: use echo_reply if protocol is ICMP - :returns: Fragments - """ - if proto == IP_PROTOS.tcp: - p = (IP(src=src_if.remote_ip4, dst=dst) / - TCP(sport=sport, dport=dport) / - Raw(data)) - p = p.__class__(scapy.compat.raw(p)) - chksum = p[TCP].chksum - proto_header = TCP(sport=sport, dport=dport, chksum=chksum) - elif proto == IP_PROTOS.udp: - proto_header = UDP(sport=sport, dport=dport) - elif proto == IP_PROTOS.icmp: - if not echo_reply: - proto_header = ICMP(id=sport, type='echo-request') - else: - proto_header = ICMP(id=sport, type='echo-reply') - else: - raise Exception("Unsupported protocol") - id = random.randint(0, 65535) - pkts = [] - if proto == IP_PROTOS.tcp: - raw = Raw(data[0:4]) - else: - raw = Raw(data[0:16]) - p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / - IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=0, id=id) / - proto_header / - raw) - pkts.append(p) - if proto == IP_PROTOS.tcp: - raw = Raw(data[4:20]) - else: - raw = Raw(data[16:32]) - p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / - IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=3, id=id, - proto=proto) / - raw) - pkts.append(p) - if proto == IP_PROTOS.tcp: - raw = Raw(data[20:]) - else: - raw = Raw(data[32:]) - p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / - IP(src=src_if.remote_ip4, dst=dst, frag=5, proto=proto, - id=id) / - raw) - pkts.append(p) - return pkts - - def reass_frags_and_verify(self, frags, src, dst): - """ - Reassemble and verify fragmented packet - - :param frags: Captured fragments - :param src: Source IPv4 address to verify - :param dst: Destination IPv4 address to verify - - :returns: Reassembled IPv4 packet - """ - buffer = BytesIO() - for p in frags: - self.assertEqual(p[IP].src, src) - self.assertEqual(p[IP].dst, dst) - self.assert_ip_checksum_valid(p) - buffer.seek(p[IP].frag * 8) - buffer.write(bytes(p[IP].payload)) - ip = IP(src=frags[0][IP].src, dst=frags[0][IP].dst, - proto=frags[0][IP].proto) - if ip.proto == IP_PROTOS.tcp: - p = (ip / TCP(buffer.getvalue())) - self.logger.debug(ppp("Reassembled:", p)) - self.assert_tcp_checksum_valid(p) - elif ip.proto == IP_PROTOS.udp: - p = (ip / UDP(buffer.getvalue()[:8]) / - Raw(buffer.getvalue()[8:])) - elif ip.proto == IP_PROTOS.icmp: - p = (ip / ICMP(buffer.getvalue())) - return p - - def reass_frags_and_verify_ip6(self, frags, src, dst): - """ - Reassemble and verify fragmented packet - - :param frags: Captured fragments - :param src: Source IPv6 address to verify - :param dst: Destination IPv6 address to verify - - :returns: Reassembled IPv6 packet - """ - buffer = BytesIO() - for p in frags: - self.assertEqual(p[IPv6].src, src) - self.assertEqual(p[IPv6].dst, dst) - buffer.seek(p[IPv6ExtHdrFragment].offset * 8) - buffer.write(bytes(p[IPv6ExtHdrFragment].payload)) - ip = IPv6(src=frags[0][IPv6].src, dst=frags[0][IPv6].dst, - nh=frags[0][IPv6ExtHdrFragment].nh) - if ip.nh == IP_PROTOS.tcp: - p = (ip / TCP(buffer.getvalue())) - elif ip.nh == IP_PROTOS.udp: - p = (ip / UDP(buffer.getvalue())) - self.logger.debug(ppp("Reassembled:", p)) - self.assert_packet_checksums_valid(p) - return p - - def initiate_tcp_session(self, in_if, out_if): - """ - Initiates TCP session - - :param in_if: Inside interface - :param out_if: Outside interface - """ - try: - # SYN packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="S")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = out_if.get_capture(1) - p = capture[0] - self.tcp_port_out = p[TCP].sport - - # SYN + ACK packet out->in - p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / - IP(src=out_if.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="SA")) - out_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - in_if.get_capture(1) - - # ACK packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - out_if.get_capture(1) - - except: - self.logger.error("TCP 3 way handshake failed") - raise - - def verify_ipfix_nat44_ses(self, data): - """ - Verify IPFIX NAT44 session create/delete event - - :param data: Decoded IPFIX data records - """ - nat44_ses_create_num = 0 - nat44_ses_delete_num = 0 - self.assertEqual(6, len(data)) - for record in data: - # natEvent - self.assertIn(scapy.compat.orb(record[230]), [4, 5]) - if scapy.compat.orb(record[230]) == 4: - nat44_ses_create_num += 1 - else: - nat44_ses_delete_num += 1 - # sourceIPv4Address - self.assertEqual(self.pg0.remote_ip4, - str(ipaddress.IPv4Address(record[8]))) - # postNATSourceIPv4Address - self.assertEqual(socket.inet_pton(socket.AF_INET, self.nat_addr), - record[225]) - # ingressVRFID - self.assertEqual(struct.pack("!I", 0), record[234]) - # protocolIdentifier/sourceTransportPort - # /postNAPTSourceTransportPort - if IP_PROTOS.icmp == scapy.compat.orb(record[4]): - self.assertEqual(struct.pack("!H", self.icmp_id_in), record[7]) - self.assertEqual(struct.pack("!H", self.icmp_id_out), - record[227]) - elif IP_PROTOS.tcp == scapy.compat.orb(record[4]): - self.assertEqual(struct.pack("!H", self.tcp_port_in), - record[7]) - self.assertEqual(struct.pack("!H", self.tcp_port_out), - record[227]) - elif IP_PROTOS.udp == scapy.compat.orb(record[4]): - self.assertEqual(struct.pack("!H", self.udp_port_in), - record[7]) - self.assertEqual(struct.pack("!H", self.udp_port_out), - record[227]) - else: - self.fail("Invalid protocol") - self.assertEqual(3, nat44_ses_create_num) - self.assertEqual(3, nat44_ses_delete_num) - - def verify_ipfix_addr_exhausted(self, data): - """ - Verify IPFIX NAT addresses event - - :param data: Decoded IPFIX data records - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(scapy.compat.orb(record[230]), 3) - # natPoolID - self.assertEqual(struct.pack("!I", 0), record[283]) - - def verify_ipfix_max_sessions(self, data, limit): - """ - Verify IPFIX maximum session entries exceeded event - - :param data: Decoded IPFIX data records - :param limit: Number of maximum session entries that can be created. - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(scapy.compat.orb(record[230]), 13) - # natQuotaExceededEvent - self.assertEqual(struct.pack("I", 1), record[466]) - # maxSessionEntries - self.assertEqual(struct.pack("I", limit), record[471]) - - def verify_ipfix_max_bibs(self, data, limit): - """ - Verify IPFIX maximum BIB entries exceeded event - - :param data: Decoded IPFIX data records - :param limit: Number of maximum BIB entries that can be created. - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(scapy.compat.orb(record[230]), 13) - # natQuotaExceededEvent - self.assertEqual(struct.pack("I", 2), record[466]) - # maxBIBEntries - self.assertEqual(struct.pack("I", limit), record[472]) - - def verify_no_nat44_user(self): - """ Verify that there is no NAT44 user """ - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 0) - users = self.statistics.get_counter('/nat44/total-users') - self.assertEqual(users[0][0], 0) - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 0) - - def verify_ipfix_max_entries_per_user(self, data, limit, src_addr): - """ - Verify IPFIX maximum entries per user exceeded event - - :param data: Decoded IPFIX data records - :param limit: Number of maximum entries per user - :param src_addr: IPv4 source address - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(scapy.compat.orb(record[230]), 13) - # natQuotaExceededEvent - self.assertEqual(struct.pack("I", 3), record[466]) - # maxEntriesPerUser - self.assertEqual(struct.pack("I", limit), record[473]) - # sourceIPv4Address - self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8]) - - def verify_syslog_apmap(self, data, is_add=True): - message = data.decode('utf-8') - try: - message = SyslogMessage.parse(message) - except ParseError as e: - self.logger.error(e) - raise - else: - self.assertEqual(message.severity, SyslogSeverity.info) - self.assertEqual(message.appname, 'NAT') - self.assertEqual(message.msgid, 'APMADD' if is_add else 'APMDEL') - sd_params = message.sd.get('napmap') - self.assertTrue(sd_params is not None) - self.assertEqual(sd_params.get('IATYP'), 'IPv4') - self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4) - self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in) - self.assertEqual(sd_params.get('XATYP'), 'IPv4') - self.assertEqual(sd_params.get('XSADDR'), self.nat_addr) - self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out) - self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp) - self.assertTrue(sd_params.get('SSUBIX') is not None) - self.assertEqual(sd_params.get('SVLAN'), '0') - - def verify_syslog_sess(self, data, is_add=True, is_ip6=False): - message = data.decode('utf-8') - try: - message = SyslogMessage.parse(message) - except ParseError as e: - self.logger.error(e) - raise - else: - self.assertEqual(message.severity, SyslogSeverity.info) - self.assertEqual(message.appname, 'NAT') - self.assertEqual(message.msgid, 'SADD' if is_add else 'SDEL') - sd_params = message.sd.get('nsess') - self.assertTrue(sd_params is not None) - if is_ip6: - self.assertEqual(sd_params.get('IATYP'), 'IPv6') - self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip6) - else: - self.assertEqual(sd_params.get('IATYP'), 'IPv4') - self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4) - self.assertTrue(sd_params.get('SSUBIX') is not None) - self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in) - self.assertEqual(sd_params.get('XATYP'), 'IPv4') - self.assertEqual(sd_params.get('XSADDR'), self.nat_addr) - self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out) - self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp) - self.assertEqual(sd_params.get('SVLAN'), '0') - self.assertEqual(sd_params.get('XDADDR'), self.pg1.remote_ip4) - self.assertEqual(sd_params.get('XDPORT'), - "%d" % self.tcp_external_port) - - def verify_mss_value(self, pkt, mss): - """ - Verify TCP MSS value - - :param pkt: - :param mss: - """ - if not pkt.haslayer(IP) or not pkt.haslayer(TCP): - raise TypeError("Not a TCP/IP packet") - - for option in pkt[TCP].options: - if option[0] == 'MSS': - self.assertEqual(option[1], mss) - self.assert_tcp_checksum_valid(pkt) - - @staticmethod - def proto2layer(proto): - if proto == IP_PROTOS.tcp: - return TCP - elif proto == IP_PROTOS.udp: - return UDP - elif proto == IP_PROTOS.icmp: - return ICMP - else: - raise Exception("Unsupported protocol") - - def frag_in_order(self, proto=IP_PROTOS.tcp, dont_translate=False, - ignore_port=False): - layer = self.proto2layer(proto) - - if proto == IP_PROTOS.tcp: - data = b"A" * 4 + b"B" * 16 + b"C" * 3 - else: - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - self.port_in = random.randint(1025, 65535) - - # in2out - pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4, - self.port_in, 20, data, proto) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg1.get_capture(len(pkts)) - if not dont_translate: - p = self.reass_frags_and_verify(frags, - self.nat_addr, - self.pg1.remote_ip4) - else: - p = self.reass_frags_and_verify(frags, - self.pg0.remote_ip4, - self.pg1.remote_ip4) - if proto != IP_PROTOS.icmp: - if not dont_translate: - self.assertEqual(p[layer].dport, 20) - if not ignore_port: - self.assertNotEqual(p[layer].sport, self.port_in) - else: - self.assertEqual(p[layer].sport, self.port_in) - else: - if not ignore_port: - if not dont_translate: - self.assertNotEqual(p[layer].id, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - # out2in - if not dont_translate: - dst_addr = self.nat_addr - else: - dst_addr = self.pg0.remote_ip4 - if proto != IP_PROTOS.icmp: - sport = 20 - dport = p[layer].sport - else: - sport = p[layer].id - dport = 0 - pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, data, - proto, echo_reply=True) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.pg1.remote_ip4, - self.pg0.remote_ip4) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].sport, 20) - self.assertEqual(p[layer].dport, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - def frag_in_order_in_plus_out(self, proto=IP_PROTOS.tcp): - layer = self.proto2layer(proto) - - if proto == IP_PROTOS.tcp: - data = b"A" * 4 + b"B" * 16 + b"C" * 3 - else: - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - self.port_in = random.randint(1025, 65535) - - for i in range(2): - # out2in - pkts = self.create_stream_frag(self.pg0, self.server_out_addr, - self.port_in, self.server_out_port, - data, proto) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg1.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.pg0.remote_ip4, - self.server_in_addr) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].sport, self.port_in) - self.assertEqual(p[layer].dport, self.server_in_port) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - # in2out - if proto != IP_PROTOS.icmp: - pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, - self.server_in_port, - p[layer].sport, data, proto) - else: - pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, - p[layer].id, 0, data, proto, - echo_reply=True) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.server_out_addr, - self.pg0.remote_ip4) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].sport, self.server_out_port) - self.assertEqual(p[layer].dport, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - def reass_hairpinning(self, proto=IP_PROTOS.tcp, ignore_port=False): - layer = self.proto2layer(proto) - - if proto == IP_PROTOS.tcp: - data = b"A" * 4 + b"B" * 16 + b"C" * 3 - else: - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - - # send packet from host to server - pkts = self.create_stream_frag(self.pg0, - self.nat_addr, - self.host_in_port, - self.server_out_port, - data, - proto) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.nat_addr, - self.server.ip4) - if proto != IP_PROTOS.icmp: - if not ignore_port: - self.assertNotEqual(p[layer].sport, self.host_in_port) - self.assertEqual(p[layer].dport, self.server_in_port) - else: - if not ignore_port: - self.assertNotEqual(p[layer].id, self.host_in_port) - self.assertEqual(data, p[Raw].load) - - def frag_out_of_order(self, proto=IP_PROTOS.tcp, dont_translate=False, - ignore_port=False): - layer = self.proto2layer(proto) - - if proto == IP_PROTOS.tcp: - data = b"A" * 4 + b"B" * 16 + b"C" * 3 - else: - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - self.port_in = random.randint(1025, 65535) - - for i in range(2): - # in2out - pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4, - self.port_in, 20, data, proto) - pkts.reverse() - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg1.get_capture(len(pkts)) - if not dont_translate: - p = self.reass_frags_and_verify(frags, - self.nat_addr, - self.pg1.remote_ip4) - else: - p = self.reass_frags_and_verify(frags, - self.pg0.remote_ip4, - self.pg1.remote_ip4) - if proto != IP_PROTOS.icmp: - if not dont_translate: - self.assertEqual(p[layer].dport, 20) - if not ignore_port: - self.assertNotEqual(p[layer].sport, self.port_in) - else: - self.assertEqual(p[layer].sport, self.port_in) - else: - if not ignore_port: - if not dont_translate: - self.assertNotEqual(p[layer].id, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - # out2in - if not dont_translate: - dst_addr = self.nat_addr - else: - dst_addr = self.pg0.remote_ip4 - if proto != IP_PROTOS.icmp: - sport = 20 - dport = p[layer].sport - else: - sport = p[layer].id - dport = 0 - pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, - data, proto, echo_reply=True) - pkts.reverse() - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.pg1.remote_ip4, - self.pg0.remote_ip4) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].sport, 20) - self.assertEqual(p[layer].dport, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - def frag_out_of_order_in_plus_out(self, proto=IP_PROTOS.tcp): - layer = self.proto2layer(proto) - - if proto == IP_PROTOS.tcp: - data = b"A" * 4 + b"B" * 16 + b"C" * 3 - else: - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - self.port_in = random.randint(1025, 65535) - - for i in range(2): - # out2in - pkts = self.create_stream_frag(self.pg0, self.server_out_addr, - self.port_in, self.server_out_port, - data, proto) - pkts.reverse() - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg1.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.pg0.remote_ip4, - self.server_in_addr) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].dport, self.server_in_port) - self.assertEqual(p[layer].sport, self.port_in) - self.assertEqual(p[layer].dport, self.server_in_port) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - # in2out - if proto != IP_PROTOS.icmp: - pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, - self.server_in_port, - p[layer].sport, data, proto) - else: - pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, - p[layer].id, 0, data, proto, - echo_reply=True) - pkts.reverse() - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.server_out_addr, - self.pg0.remote_ip4) - if proto != IP_PROTOS.icmp: - self.assertEqual(p[layer].sport, self.server_out_port) - self.assertEqual(p[layer].dport, self.port_in) - else: - self.assertEqual(p[layer].id, self.port_in) - self.assertEqual(data, p[Raw].load) - - -class TestNATMisc(MethodHolder): - """ NAT misc Test Cases """ - - max_translations = 10240 - max_users = 10240 - - def setUp(self): - super(TestNATMisc, self).setUp() - self.vapi.nat44_plugin_enable_disable( - sessions=self.max_translations, - users=self.max_users, enable=1) - - def tearDown(self): - super(TestNATMisc, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def test_show_max_translations(self): - """ API test - max translations per thread """ - nat_config = self.vapi.nat_show_config_2() - self.assertEqual(self.max_translations, - nat_config.max_translations_per_thread) - - -class TestNAT44(MethodHolder): - """ NAT44 Test Cases """ - - max_translations = 10240 - max_users = 10240 - - @classmethod - def setUpClass(cls): - super(TestNAT44, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") - - cls.tcp_port_in = 6303 - cls.tcp_port_out = 6303 - cls.udp_port_in = 6304 - cls.udp_port_out = 6304 - cls.icmp_id_in = 6305 - cls.icmp_id_out = 6305 - cls.nat_addr = '10.0.0.3' - cls.ipfix_src_port = 4739 - cls.ipfix_domain_id = 1 - cls.tcp_external_port = 80 - cls.udp_external_port = 69 - - cls.create_pg_interfaces(range(10)) - cls.interfaces = list(cls.pg_interfaces[0:4]) - - for i in cls.interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - cls.pg0.generate_remote_hosts(3) - cls.pg0.configure_ipv4_neighbors() - - cls.pg1.generate_remote_hosts(1) - cls.pg1.configure_ipv4_neighbors() - - cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7])) - cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 10}) - cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 20}) - - cls.pg4._local_ip4 = "172.16.255.1" - cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2" - cls.pg4.set_table_ip4(10) - cls.pg5._local_ip4 = "172.17.255.3" - cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4" - cls.pg5.set_table_ip4(10) - cls.pg6._local_ip4 = "172.16.255.1" - cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2" - cls.pg6.set_table_ip4(20) - for i in cls.overlapping_interfaces: - i.config_ip4() - i.admin_up() - i.resolve_arp() - - cls.pg7.admin_up() - cls.pg8.admin_up() - - cls.pg9.generate_remote_hosts(2) - cls.pg9.config_ip4() - cls.vapi.sw_interface_add_del_address( - sw_if_index=cls.pg9.sw_if_index, - prefix="10.0.0.1/24") - - cls.pg9.admin_up() - cls.pg9.resolve_arp() - cls.pg9._remote_hosts[1]._ip4 = cls.pg9._remote_hosts[0]._ip4 - cls.pg4._remote_ip4 = cls.pg9._remote_hosts[0]._ip4 = "10.0.0.2" - cls.pg9.resolve_arp() - - def setUp(self): - super(TestNAT44, self).setUp() - self.vapi.nat44_plugin_enable_disable( - sessions=self.max_translations, - users=self.max_users, enable=1) - - @classmethod - def tearDownClass(cls): - super(TestNAT44, cls).tearDownClass() - - def tearDown(self): - super(TestNAT44, self).tearDown() - if not self.vpp_dead: - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=0) - self.ipfix_src_port = 4739 - self.ipfix_domain_id = 1 - - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def test_clear_sessions(self): - """ NAT44 session clearing test """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - nat_config = self.vapi.nat_show_config() - self.assertEqual(0, nat_config.endpoint_dependent) - - 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) - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertTrue(sessions[0][0] > 0) - self.logger.info("sessions before clearing: %s" % sessions[0][0]) - - self.vapi.cli("clear nat44 sessions") - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 0) - self.logger.info("sessions after clearing: %s" % sessions[0][0]) - - def test_dynamic(self): - """ NAT44 dynamic translation test """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # in2out - tcpn = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] - icmpn = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] - drops = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] - - 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) - - if_idx = self.pg0.sw_if_index - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - # out2in - tcpn = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] - icmpn = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] - drops = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] - - 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) - - if_idx = self.pg1.sw_if_index - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - users = self.statistics.get_counter('/nat44/total-users') - self.assertEqual(users[0][0], 1) - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 3) - - def test_dynamic_icmp_errors_in2out_ttl_1(self): - """ NAT44 handling of client packets with TTL=1 """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # Client side - generate traffic - pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - verify ICMP type 11 packets - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_with_icmp_errors(capture, self.pg0) - - def test_dynamic_icmp_errors_out2in_ttl_1(self): - """ NAT44 handling of server packets with TTL=1 """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # Client side - create sessions - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - generate traffic - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - pkts = self.create_stream_out(self.pg1, ttl=1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - verify ICMP type 11 packets - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out_with_icmp_errors(capture, - src_ip=self.pg1.local_ip4) - - def test_dynamic_icmp_errors_in2out_ttl_2(self): - """ NAT44 handling of error responses to client packets with TTL=2 """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # Client side - generate traffic - pkts = self.create_stream_in(self.pg0, self.pg1, ttl=2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - simulate ICMP type 11 response - capture = self.pg1.get_capture(len(pkts)) - pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - ICMP(type=11) / packet[IP] for packet in capture] - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - verify ICMP type 11 packets - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_with_icmp_errors(capture, self.pg0) - - def test_dynamic_icmp_errors_out2in_ttl_2(self): - """ NAT44 handling of error responses to server packets with TTL=2 """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # Client side - create sessions - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - generate traffic - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - pkts = self.create_stream_out(self.pg1, ttl=2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - simulate ICMP type 11 response - capture = self.pg0.get_capture(len(pkts)) - pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - ICMP(type=11) / packet[IP] for packet in capture] - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - verify ICMP type 11 packets - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out_with_icmp_errors(capture) - - def test_ping_out_interface_from_outside(self): - """ Ping NAT44 out interface from outside network """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / - ICMP(id=self.icmp_id_out, type='echo-request')) - pkts = [p] - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - packet = capture[0] - try: - self.assertEqual(packet[IP].src, self.pg1.local_ip4) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.assertEqual(packet[ICMP].type, 0) # echo reply - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def test_ping_internal_host_from_outside(self): - """ Ping internal host from outside network """ - - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # out2in - pkt = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr, ttl=64) / - ICMP(id=self.icmp_id_out, type='echo-request')) - self.pg1.add_stream(pkt) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - self.verify_capture_in(capture, self.pg0) - self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) - - # in2out - pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / - ICMP(id=self.icmp_id_in, type='echo-reply')) - self.pg0.add_stream(pkt) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.verify_capture_out(capture, same_port=True) - self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) - - def test_forwarding(self): - """ NAT44 forwarding test """ - - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) - - real_ip = self.pg0.remote_ip4 - alias_ip = self.nat_addr - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping(is_add=1, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - try: - # 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) - - # 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(enable=0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping( - is_add=0, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - def test_static_in(self): - """ 1:1 NAT initialized from inside network """ - - nat_ip = "10.0.0.10" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - sm = self.vapi.nat44_static_mapping_dump() - self.assertEqual(len(sm), 1) - self.assertEqual(sm[0].tag, '') - self.assertEqual(sm[0].protocol, 0) - self.assertEqual(sm[0].local_port, 0) - self.assertEqual(sm[0].external_port, 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, nat_ip, True) - - # out2in - pkts = self.create_stream_out(self.pg1, nat_ip) - 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_static_out(self): - """ 1:1 NAT initialized from outside network """ - - nat_ip = "10.0.0.20" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - tag = "testTAG" - - self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip, tag=tag) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - sm = self.vapi.nat44_static_mapping_dump() - self.assertEqual(len(sm), 1) - self.assertEqual(sm[0].tag, tag) - - # out2in - pkts = self.create_stream_out(self.pg1, nat_ip) - 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) - - # 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, nat_ip, True) - - def test_static_with_port_in(self): - """ 1:1 NAPT initialized from inside network """ - - self.tcp_port_out = 3606 - self.udp_port_out = 3607 - self.icmp_id_out = 3608 - - self.nat44_add_address(self.nat_addr) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # 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_static_with_port_out(self): - """ 1:1 NAPT initialized from outside network """ - - self.tcp_port_out = 30606 - self.udp_port_out = 30607 - self.icmp_id_out = 30608 - - self.nat44_add_address(self.nat_addr) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # 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) - - # 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) - - def test_static_vrf_aware(self): - """ 1:1 NAT VRF awareness """ - - nat_ip1 = "10.0.0.30" - nat_ip2 = "10.0.0.40" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.nat44_add_static_mapping(self.pg4.remote_ip4, nat_ip1, - vrf_id=10) - self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip2, - vrf_id=10) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg3.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg4.sw_if_index, - flags=flags, is_add=1) - - # inside interface VRF match NAT44 static mapping VRF - 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_ip1, True) - - # inside interface VRF don't match NAT44 static mapping VRF (packets - # are dropped) - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg3.assert_nothing_captured() - - def test_dynamic_to_static(self): - """ Switch from dynamic translation to 1:1NAT """ - nat_ip = "10.0.0.10" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # dynamic - 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) - - # 1:1NAT - self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions), 0) - 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, True) - - def test_identity_nat(self): - """ Identity NAT """ - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_identity_mapping( - ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, - flags=flags, is_add=1) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=12345, dport=56789)) - self.pg1.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.dst, self.pg0.remote_ip4) - self.assertEqual(ip.src, self.pg1.remote_ip4) - self.assertEqual(tcp.dport, 56789) - self.assertEqual(tcp.sport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions), 0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_identity_mapping( - ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, - flags=flags, vrf_id=1, is_add=1) - identity_mappings = self.vapi.nat44_identity_mapping_dump() - self.assertEqual(len(identity_mappings), 2) - - def test_multiple_inside_interfaces(self): - """ NAT44 multiple non-overlapping address space inside interfaces """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg3.sw_if_index, - is_add=1) - - # between two NAT44 inside interfaces (no translation) - 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_no_translation(capture, self.pg0, self.pg1) - - # from NAT44 inside to interface without NAT44 feature (no translation) - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg0, self.pg2) - - # in2out 1st interface - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.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) - - # out2in 1st interface - pkts = self.create_stream_out(self.pg3) - self.pg3.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) - - # in2out 2nd interface - pkts = self.create_stream_in(self.pg1, self.pg3) - self.pg1.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) - - # out2in 2nd interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg1) - - def test_inside_overlapping_interfaces(self): - """ NAT44 multiple inside interfaces with overlapping address space """ - - static_nat_ip = "10.0.0.10" - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg3.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg4.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg5.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg6.sw_if_index, - flags=flags, is_add=1) - self.nat44_add_static_mapping(self.pg6.remote_ip4, static_nat_ip, - vrf_id=20) - - # between NAT44 inside interfaces with same VRF (no translation) - pkts = self.create_stream_in(self.pg4, self.pg5) - self.pg4.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg4, self.pg5) - - # between NAT44 inside interfaces with different VRF (hairpinning) - p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / - IP(src=self.pg4.remote_ip4, dst=static_nat_ip) / - TCP(sport=1234, dport=5678)) - self.pg4.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, self.pg6.remote_ip4) - self.assertNotEqual(tcp.sport, 1234) - self.assertEqual(tcp.dport, 5678) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # in2out 1st interface - 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) - - # out2in 1st interface - pkts = self.create_stream_out(self.pg3) - 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 2nd interface - pkts = self.create_stream_in(self.pg5, self.pg3) - self.pg5.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) - - # out2in 2nd interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg5) - - # pg5 session dump - addresses = self.vapi.nat44_address_dump() - self.assertEqual(len(addresses), 1) - sessions = self.vapi.nat44_user_session_dump(self.pg5.remote_ip4, 10) - self.assertEqual(len(sessions), 3) - for session in sessions: - self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) - self.assertEqual(str(session.inside_ip_address), - self.pg5.remote_ip4) - self.assertEqual(session.outside_ip_address, - addresses[0].ip_address) - self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp) - self.assertEqual(sessions[1].protocol, IP_PROTOS.udp) - self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp) - self.assertEqual(sessions[0].inside_port, self.tcp_port_in) - self.assertEqual(sessions[1].inside_port, self.udp_port_in) - self.assertEqual(sessions[2].inside_port, self.icmp_id_in) - self.assertEqual(sessions[0].outside_port, self.tcp_port_out) - self.assertEqual(sessions[1].outside_port, self.udp_port_out) - self.assertEqual(sessions[2].outside_port, self.icmp_id_out) - - # in2out 3rd interface - 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, static_nat_ip, True) - - # out2in 3rd interface - pkts = self.create_stream_out(self.pg3, static_nat_ip) - 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) - - # general user and session dump verifications - users = self.vapi.nat44_user_dump() - self.assertGreaterEqual(len(users), 3) - addresses = self.vapi.nat44_address_dump() - self.assertEqual(len(addresses), 1) - for user in users: - sessions = self.vapi.nat44_user_session_dump(user.ip_address, - user.vrf_id) - for session in sessions: - self.assertEqual(user.ip_address, session.inside_ip_address) - self.assertTrue(session.total_bytes > session.total_pkts > 0) - self.assertTrue(session.protocol in - [IP_PROTOS.tcp, IP_PROTOS.udp, - IP_PROTOS.icmp]) - self.assertFalse(session.flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) - - # pg4 session dump - sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4, 10) - self.assertGreaterEqual(len(sessions), 4) - for session in sessions: - self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) - self.assertEqual(str(session.inside_ip_address), - self.pg4.remote_ip4) - self.assertEqual(session.outside_ip_address, - addresses[0].ip_address) - - # pg6 session dump - sessions = self.vapi.nat44_user_session_dump(self.pg6.remote_ip4, 20) - self.assertGreaterEqual(len(sessions), 3) - for session in sessions: - self.assertTrue(session.flags & self.config_flags.NAT_IS_STATIC) - self.assertEqual(str(session.inside_ip_address), - self.pg6.remote_ip4) - self.assertEqual(str(session.outside_ip_address), - static_nat_ip) - self.assertTrue(session.inside_port in - [self.tcp_port_in, self.udp_port_in, - self.icmp_id_in]) - - def test_hairpinning(self): - """ NAT44 hairpinning - 1:1 NAPT """ - - 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.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # add static mapping for server - self.nat44_add_static_mapping(server.ip4, self.nat_addr, - server_in_port, server_out_port, - proto=IP_PROTOS.tcp) - - cnt = self.statistics.get_counter('/nat44/hairpinning')[0] - # send packet from host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=self.nat_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.nat_addr) - self.assertEqual(ip.dst, server.ip4) - self.assertNotEqual(tcp.sport, host_in_port) - self.assertEqual(tcp.dport, server_in_port) - self.assert_packet_checksums_valid(p) - host_out_port = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - after = self.statistics.get_counter('/nat44/hairpinning')[0] - if_idx = self.pg0.sw_if_index - self.assertEqual(after[if_idx] - cnt[if_idx], 1) - - # send reply from server to host - p = (Ether(src=server.mac, dst=self.pg0.local_mac) / - IP(src=server.ip4, dst=self.nat_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.nat_addr) - self.assertEqual(ip.dst, host.ip4) - self.assertEqual(tcp.sport, server_out_port) - self.assertEqual(tcp.dport, host_in_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - after = self.statistics.get_counter('/nat44/hairpinning')[0] - if_idx = self.pg0.sw_if_index - self.assertEqual(after[if_idx] - cnt[if_idx], 2) - - def test_hairpinning2(self): - """ NAT44 hairpinning - 1:1 NAT""" - - server1_nat_ip = "10.0.0.10" - server2_nat_ip = "10.0.0.11" - host = self.pg0.remote_hosts[0] - server1 = self.pg0.remote_hosts[1] - server2 = self.pg0.remote_hosts[2] - server_tcp_port = 22 - server_udp_port = 20 - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # add static mapping for servers - self.nat44_add_static_mapping(server1.ip4, server1_nat_ip) - self.nat44_add_static_mapping(server2.ip4, server2_nat_ip) - - # host to server1 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - TCP(sport=self.tcp_port_in, dport=server_tcp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - UDP(sport=self.udp_port_in, dport=server_udp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, server1.ip4) - if packet.haslayer(TCP): - self.assertNotEqual(packet[TCP].sport, self.tcp_port_in) - self.assertEqual(packet[TCP].dport, server_tcp_port) - self.tcp_port_out = packet[TCP].sport - self.assert_packet_checksums_valid(packet) - elif packet.haslayer(UDP): - self.assertNotEqual(packet[UDP].sport, self.udp_port_in) - self.assertEqual(packet[UDP].dport, server_udp_port) - self.udp_port_out = packet[UDP].sport - else: - self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP].id - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server1 to host - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.nat_addr) / - TCP(sport=server_tcp_port, dport=self.tcp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.nat_addr) / - UDP(sport=server_udp_port, dport=self.udp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.nat_addr) / - ICMP(id=self.icmp_id_out, type='echo-reply')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server1_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.assertEqual(packet[TCP].sport, server_tcp_port) - self.assert_packet_checksums_valid(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.assertEqual(packet[UDP].sport, server_udp_port) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server2 to server1 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - TCP(sport=self.tcp_port_in, dport=server_tcp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - UDP(sport=self.udp_port_in, dport=server_udp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server2_nat_ip) - self.assertEqual(packet[IP].dst, server1.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].sport, self.tcp_port_in) - self.assertEqual(packet[TCP].dport, server_tcp_port) - self.tcp_port_out = packet[TCP].sport - self.assert_packet_checksums_valid(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].sport, self.udp_port_in) - self.assertEqual(packet[UDP].dport, server_udp_port) - self.udp_port_out = packet[UDP].sport - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP].id - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server1 to server2 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - TCP(sport=server_tcp_port, dport=self.tcp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - UDP(sport=server_udp_port, dport=self.udp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - ICMP(id=self.icmp_id_out, type='echo-reply')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server1_nat_ip) - self.assertEqual(packet[IP].dst, server2.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.assertEqual(packet[TCP].sport, server_tcp_port) - self.assert_packet_checksums_valid(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.assertEqual(packet[UDP].sport, server_udp_port) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_interface_addr(self): - """ Acquire NAT44 addresses from interface """ - self.vapi.nat44_add_del_interface_addr( - is_add=1, - sw_if_index=self.pg7.sw_if_index) - - # no address in NAT pool - addresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(addresses)) - - # configure interface address and check NAT address pool - self.pg7.config_ip4() - addresses = self.vapi.nat44_address_dump() - self.assertEqual(1, len(addresses)) - self.assertEqual(str(addresses[0].ip_address), self.pg7.local_ip4) - - # remove interface address and check NAT address pool - self.pg7.unconfig_ip4() - addresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(addresses)) - - def test_interface_addr_static_mapping(self): - """ Static mapping with addresses from interface """ - tag = "testTAG" - - self.vapi.nat44_add_del_interface_addr( - is_add=1, - sw_if_index=self.pg7.sw_if_index) - self.nat44_add_static_mapping( - '1.2.3.4', - external_sw_if_index=self.pg7.sw_if_index, - tag=tag) - - # static mappings with external interface - static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(1, len(static_mappings)) - self.assertEqual(self.pg7.sw_if_index, - static_mappings[0].external_sw_if_index) - self.assertEqual(static_mappings[0].tag, tag) - - # configure interface address and check static mappings - self.pg7.config_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(2, len(static_mappings)) - resolved = False - for sm in static_mappings: - if sm.external_sw_if_index == 0xFFFFFFFF: - self.assertEqual(str(sm.external_ip_address), - self.pg7.local_ip4) - self.assertEqual(sm.tag, tag) - resolved = True - self.assertTrue(resolved) - - # remove interface address and check static mappings - self.pg7.unconfig_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(1, len(static_mappings)) - self.assertEqual(self.pg7.sw_if_index, - static_mappings[0].external_sw_if_index) - self.assertEqual(static_mappings[0].tag, tag) - - # configure interface address again and check static mappings - self.pg7.config_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(2, len(static_mappings)) - resolved = False - for sm in static_mappings: - if sm.external_sw_if_index == 0xFFFFFFFF: - self.assertEqual(str(sm.external_ip_address), - self.pg7.local_ip4) - self.assertEqual(sm.tag, tag) - resolved = True - self.assertTrue(resolved) - - # remove static mapping - self.nat44_add_static_mapping( - '1.2.3.4', - external_sw_if_index=self.pg7.sw_if_index, - tag=tag, - is_add=0) - static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(0, len(static_mappings)) - - def test_interface_addr_identity_nat(self): - """ Identity NAT with addresses from interface """ - - port = 53053 - self.vapi.nat44_add_del_interface_addr( - is_add=1, - sw_if_index=self.pg7.sw_if_index) - self.vapi.nat44_add_del_identity_mapping( - ip_address=b'0', - sw_if_index=self.pg7.sw_if_index, - port=port, - protocol=IP_PROTOS.tcp, - is_add=1) - - # identity mappings with external interface - identity_mappings = self.vapi.nat44_identity_mapping_dump() - self.assertEqual(1, len(identity_mappings)) - self.assertEqual(self.pg7.sw_if_index, - identity_mappings[0].sw_if_index) - - # configure interface address and check identity mappings - self.pg7.config_ip4() - identity_mappings = self.vapi.nat44_identity_mapping_dump() - resolved = False - self.assertEqual(2, len(identity_mappings)) - for sm in identity_mappings: - if sm.sw_if_index == 0xFFFFFFFF: - self.assertEqual(str(identity_mappings[0].ip_address), - self.pg7.local_ip4) - self.assertEqual(port, identity_mappings[0].port) - self.assertEqual(IP_PROTOS.tcp, identity_mappings[0].protocol) - resolved = True - self.assertTrue(resolved) - - # remove interface address and check identity mappings - self.pg7.unconfig_ip4() - identity_mappings = self.vapi.nat44_identity_mapping_dump() - self.assertEqual(1, len(identity_mappings)) - self.assertEqual(self.pg7.sw_if_index, - identity_mappings[0].sw_if_index) - - def test_ipfix_nat44_sess(self): - """ IPFIX logging NAT44 session created/deleted """ - self.ipfix_domain_id = 10 - self.ipfix_src_port = 20202 - collector_port = 30303 - bind_layers(UDP, IPFIX, dport=30303) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, - src_address=self.pg3.local_ip4, - path_mtu=512, - template_interval=10, - collector_port=collector_port) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) - - 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) - self.nat44_add_address(self.nat_addr, is_add=0) - self.vapi.ipfix_flush() - capture = self.pg3.get_capture(7) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, collector_port) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_nat44_ses(data) - - def test_ipfix_addr_exhausted(self): - """ IPFIX logging NAT addresses exhausted """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, - src_address=self.pg3.local_ip4, - path_mtu=512, - template_interval=10) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=3025)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sleep(1) - self.vapi.ipfix_flush() - capture = self.pg3.get_capture(7) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, 4739) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_addr_exhausted(data) - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_ipfix_max_sessions(self): - """ IPFIX logging maximum session entries exceeded """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - max_sessions = self.max_translations - - pkts = [] - for i in range(0, max_sessions): - src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=src, dst=self.pg1.remote_ip4) / - TCP(sport=1025)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - self.pg1.get_capture(max_sessions) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, - src_address=self.pg3.local_ip4, - path_mtu=512, - template_interval=10) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=1025)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sleep(1) - self.vapi.ipfix_flush() - capture = self.pg3.get_capture(7) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, 4739) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_max_sessions(data, max_sessions) - - def test_syslog_apmap(self): - """ Test syslog address and port mapping creation and deletion """ - self.vapi.syslog_set_filter( - self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO) - self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=20)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.tcp_port_out = capture[0][TCP].sport - capture = self.pg3.get_capture(1) - self.verify_syslog_apmap(capture[0][Raw].load) - - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.nat44_add_address(self.nat_addr, is_add=0) - capture = self.pg3.get_capture(1) - self.verify_syslog_apmap(capture[0][Raw].load, False) - - def test_pool_addr_fib(self): - """ NAT44 add pool addresses to FIB """ - static_addr = '10.0.0.10' - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr) - - # NAT44 address - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.nat_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.assertTrue(capture[0].haslayer(ARP)) - self.assertTrue(capture[0][ARP].op, ARP.is_at) - - # 1:1 NAT address - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=static_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.assertTrue(capture[0].haslayer(ARP)) - self.assertTrue(capture[0][ARP].op, ARP.is_at) - - # send ARP to non-NAT44 interface - p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.nat_addr, - psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac)) - self.pg2.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - # remove addresses and verify - self.nat44_add_address(self.nat_addr, is_add=0) - self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr, - is_add=0) - - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.nat_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=static_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - def test_vrf_mode(self): - """ NAT44 tenant VRF aware address pool mode """ - - vrf_id1 = 1 - vrf_id2 = 2 - nat_ip1 = "10.0.0.10" - nat_ip2 = "10.0.0.11" - - self.pg0.unconfig_ip4() - self.pg1.unconfig_ip4() - self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1}) - self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2}) - self.pg0.set_table_ip4(vrf_id1) - self.pg1.set_table_ip4(vrf_id2) - self.pg0.config_ip4() - self.pg1.config_ip4() - self.pg0.resolve_arp() - self.pg1.resolve_arp() - - self.nat44_add_address(nat_ip1, vrf_id=vrf_id1) - self.nat44_add_address(nat_ip2, vrf_id=vrf_id2) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg2.sw_if_index, - is_add=1) - - try: - # first VRF - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - # second VRF - pkts = self.create_stream_in(self.pg1, self.pg2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip2) - - finally: - self.pg0.unconfig_ip4() - self.pg1.unconfig_ip4() - self.pg0.set_table_ip4(0) - self.pg1.set_table_ip4(0) - self.pg0.config_ip4() - self.pg1.config_ip4() - self.pg0.resolve_arp() - self.pg1.resolve_arp() - self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id1}) - self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id2}) - - def test_vrf_feature_independent(self): - """ NAT44 tenant VRF independent address pool mode """ - - nat_ip1 = "10.0.0.10" - nat_ip2 = "10.0.0.11" - - self.nat44_add_address(nat_ip1) - self.nat44_add_address(nat_ip2, vrf_id=99) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg2.sw_if_index, - is_add=1) - - # first VRF - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - # second VRF - pkts = self.create_stream_in(self.pg1, self.pg2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - def create_routes_and_neigbors(self): - r1 = VppIpRoute(self, self.pg7.remote_ip4, 32, - [VppRoutePath(self.pg7.remote_ip4, - self.pg7.sw_if_index)]) - r2 = VppIpRoute(self, self.pg8.remote_ip4, 32, - [VppRoutePath(self.pg8.remote_ip4, - self.pg8.sw_if_index)]) - r1.add_vpp_config() - r2.add_vpp_config() - - n1 = VppNeighbor(self, - self.pg7.sw_if_index, - self.pg7.remote_mac, - self.pg7.remote_ip4, - is_static=1) - n2 = VppNeighbor(self, - self.pg8.sw_if_index, - self.pg8.remote_mac, - self.pg8.remote_ip4, - is_static=1) - n1.add_vpp_config() - n2.add_vpp_config() - - def test_dynamic_ipless_interfaces(self): - """ NAT44 interfaces without configured IP address """ - self.create_routes_and_neigbors() - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg7.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg8.sw_if_index, - is_add=1) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg8, self.nat_addr) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - def test_static_ipless_interfaces(self): - """ NAT44 interfaces without configured IP address - 1:1 NAT """ - - self.create_routes_and_neigbors() - self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg7.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg8.sw_if_index, - is_add=1) - - # out2in - pkts = self.create_stream_out(self.pg8) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture, self.nat_addr, True) - - def test_static_with_port_ipless_interfaces(self): - """ NAT44 interfaces without configured IP address - 1:1 NAPT """ - - self.tcp_port_out = 30606 - self.udp_port_out = 30607 - self.icmp_id_out = 30608 - - self.create_routes_and_neigbors() - self.nat44_add_address(self.nat_addr) - self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg7.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg8.sw_if_index, - is_add=1) - - # out2in - pkts = self.create_stream_out(self.pg8) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture) - - def test_static_unknown_proto(self): - """ 1:1 NAT translate packet with unknown protocol """ - nat_ip = "10.0.0.10" - self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, nat_ip) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=nat_ip) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.pg1.remote_ip4) - self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_static_unknown_proto(self): - """ 1:1 NAT translate packet with unknown protocol - hairpinning """ - - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - - host_nat_ip = "10.0.0.10" - server_nat_ip = "10.0.0.11" - - self.nat44_add_static_mapping(host.ip4, host_nat_ip) - self.nat44_add_static_mapping(server.ip4, server_nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # host to server - p = (Ether(dst=self.pg0.local_mac, src=host.mac) / - IP(src=host.ip4, dst=server_nat_ip) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, host_nat_ip) - self.assertEqual(packet[IP].dst, server.ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to host - p = (Ether(dst=self.pg0.local_mac, src=server.mac) / - IP(src=server.ip4, dst=host_nat_ip) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, server_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_output_feature(self): - """ NAT44 interface output feature (in2out postrouting) """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, flags=flags, - sw_if_index=self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, flags=flags, - sw_if_index=self.pg1.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg3.sw_if_index) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.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) - - # out2in - pkts = self.create_stream_out(self.pg3) - self.pg3.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) - - # from non-NAT interface to NAT inside interface - pkts = self.create_stream_in(self.pg2, self.pg0) - self.pg2.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg2, self.pg0) - - def test_output_feature_vrf_aware(self): - """ NAT44 interface output feature VRF aware (in2out postrouting) """ - nat_ip_vrf10 = "10.0.0.10" - nat_ip_vrf20 = "10.0.0.20" - - r1 = VppIpRoute(self, self.pg3.remote_ip4, 32, - [VppRoutePath(self.pg3.remote_ip4, - self.pg3.sw_if_index)], - table_id=10) - r2 = VppIpRoute(self, self.pg3.remote_ip4, 32, - [VppRoutePath(self.pg3.remote_ip4, - self.pg3.sw_if_index)], - table_id=20) - r1.add_vpp_config() - r2.add_vpp_config() - - self.nat44_add_address(nat_ip_vrf10, vrf_id=10) - self.nat44_add_address(nat_ip_vrf20, vrf_id=20) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, flags=flags, - sw_if_index=self.pg4.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, flags=flags, - sw_if_index=self.pg6.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg3.sw_if_index) - - # 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): - """ NAT44 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.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, flags=flags, - sw_if_index=self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - - # add static mapping for server - self.nat44_add_static_mapping(server.ip4, self.nat_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.nat_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.nat_addr) - self.assertEqual(ip.dst, server.ip4) - self.assertNotEqual(tcp.sport, host_in_port) - self.assertEqual(tcp.dport, server_in_port) - self.assert_packet_checksums_valid(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.nat_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.nat_addr) - self.assertEqual(ip.dst, host.ip4) - self.assertEqual(tcp.sport, server_out_port) - self.assertEqual(tcp.dport, host_in_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_one_armed_nat44(self): - """ One armed NAT44 """ - remote_host = self.pg9.remote_hosts[0] - local_host = self.pg9.remote_hosts[1] - external_port = 0 - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg9.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg9.sw_if_index, - flags=flags, is_add=1) - - # in2out - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=local_host.ip4, dst=remote_host.ip4) / - TCP(sport=12345, dport=80)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, remote_host.ip4) - self.assertNotEqual(tcp.sport, 12345) - external_port = tcp.sport - self.assertEqual(tcp.dport, 80) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # out2in - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=remote_host.ip4, dst=self.nat_addr) / - TCP(sport=80, dport=external_port)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, remote_host.ip4) - self.assertEqual(ip.dst, local_host.ip4) - self.assertEqual(tcp.sport, 80) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - err = self.statistics.get_err_counter( - '/err/nat44-classify/next in2out') - self.assertEqual(err, 1) - err = self.statistics.get_err_counter( - '/err/nat44-classify/next out2in') - self.assertEqual(err, 1) - - def test_del_session(self): - """ Delete NAT44 session """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(len(pkts)) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - nsessions = len(sessions) - - self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=self.config_flags.NAT_IS_INSIDE) - self.vapi.nat44_del_session(address=sessions[1].outside_ip_address, - port=sessions[1].outside_port, - protocol=sessions[1].protocol) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(nsessions - len(sessions), 2) - - self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=self.config_flags.NAT_IS_INSIDE) - - self.verify_no_nat44_user() - - def test_frag_in_order(self): - """ NAT44 translate fragments arriving in order """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - self.frag_in_order(proto=IP_PROTOS.tcp) - self.frag_in_order(proto=IP_PROTOS.udp) - self.frag_in_order(proto=IP_PROTOS.icmp) - - def test_frag_forwarding(self): - """ NAT44 forwarding fragment test """ - self.vapi.nat44_add_del_interface_addr( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) - - data = b"A" * 16 + b"B" * 16 + b"C" * 3 - pkts = self.create_stream_frag(self.pg1, - self.pg0.remote_ip4, - 4789, - 4789, - data, - proto=IP_PROTOS.udp) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg0.get_capture(len(pkts)) - p = self.reass_frags_and_verify(frags, - self.pg1.remote_ip4, - self.pg0.remote_ip4) - self.assertEqual(p[UDP].sport, 4789) - self.assertEqual(p[UDP].dport, 4789) - self.assertEqual(data, p[Raw].load) - - def test_reass_hairpinning(self): - """ NAT44 fragments hairpinning """ - - self.server = self.pg0.remote_hosts[1] - self.host_in_port = random.randint(1025, 65535) - self.server_in_port = random.randint(1025, 65535) - self.server_out_port = random.randint(1025, 65535) - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - # add static mapping for server - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr) - - self.reass_hairpinning(proto=IP_PROTOS.tcp) - self.reass_hairpinning(proto=IP_PROTOS.udp) - self.reass_hairpinning(proto=IP_PROTOS.icmp) - - def test_frag_out_of_order(self): - """ NAT44 translate fragments arriving out of order """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - self.frag_out_of_order(proto=IP_PROTOS.tcp) - self.frag_out_of_order(proto=IP_PROTOS.udp) - self.frag_out_of_order(proto=IP_PROTOS.icmp) - - def test_port_restricted(self): - """ Port restricted NAT44 (MAP-E CE) """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_set_addr_and_port_alloc_alg(alg=1, - psid_offset=6, - psid_length=6, - psid=10) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=4567, dport=22)) - 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.dst, self.pg1.remote_ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, 22) - self.assertNotEqual(tcp.sport, 4567) - self.assertEqual((tcp.sport >> 6) & 63, 10) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_port_range(self): - """ External address port range """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_set_addr_and_port_alloc_alg(alg=2, - start_port=1025, - end_port=1027) - - pkts = [] - for port in range(0, 5): - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=1125 + port)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(3) - for p in capture: - tcp = p[TCP] - self.assertGreaterEqual(tcp.sport, 1025) - self.assertLessEqual(tcp.sport, 1027) - - def test_multiple_outside_vrf(self): - """ Multiple outside VRF """ - vrf_id1 = 1 - vrf_id2 = 2 - - self.pg1.unconfig_ip4() - self.pg2.unconfig_ip4() - self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1}) - self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2}) - self.pg1.set_table_ip4(vrf_id1) - self.pg2.set_table_ip4(vrf_id2) - self.pg1.config_ip4() - self.pg2.config_ip4() - self.pg1.resolve_arp() - self.pg2.resolve_arp() - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg2.sw_if_index, - is_add=1) - - try: - # first VRF - 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, self.nat_addr) - - pkts = self.create_stream_out(self.pg1, self.nat_addr) - 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) - - self.tcp_port_in = 60303 - self.udp_port_in = 60304 - self.icmp_id_in = 60305 - - # second VRF - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, self.nat_addr) - - pkts = self.create_stream_out(self.pg2, self.nat_addr) - self.pg2.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) - - finally: - self.nat44_add_address(self.nat_addr, is_add=0) - self.pg1.unconfig_ip4() - self.pg2.unconfig_ip4() - self.pg1.set_table_ip4(0) - self.pg2.set_table_ip4(0) - self.pg1.config_ip4() - self.pg2.config_ip4() - self.pg1.resolve_arp() - self.pg2.resolve_arp() - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_session_timeout(self): - """ NAT44 session timeouts """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_set_timeouts(udp=5, tcp_established=7440, - tcp_transitory=240, icmp=60) - - max_sessions = 1000 - pkts = [] - for i in range(0, max_sessions): - src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=src, dst=self.pg1.remote_ip4) / - UDP(sport=1025, dport=53)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(max_sessions) - - sleep(6) - - pkts = [] - for i in range(0, max_sessions): - src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=src, dst=self.pg1.remote_ip4) / - UDP(sport=1026, dport=53)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(max_sessions) - - nsessions = 0 - users = self.vapi.nat44_user_dump() - for user in users: - nsessions = nsessions + user.nsessions - self.assertLess(nsessions, 2 * max_sessions) - - def test_mss_clamping(self): - """ TCP MSS clamping """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="S", options=[('MSS', 1400)])) - - self.vapi.nat_set_mss_clamping(enable=1, mss_value=1000) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - # Negotiated MSS value greater than configured - changed - self.verify_mss_value(capture[0], 1000) - - self.vapi.nat_set_mss_clamping(enable=0, mss_value=1500) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - # MSS clamping disabled - negotiated MSS unchanged - self.verify_mss_value(capture[0], 1400) - - self.vapi.nat_set_mss_clamping(enable=1, mss_value=1500) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - # Negotiated MSS value smaller than configured - unchanged - self.verify_mss_value(capture[0], 1400) - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_ha_send(self): - """ Send HA session synchronization events (active) """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, - port=12345, - path_mtu=512) - self.vapi.nat_ha_set_failover(ip_address=self.pg3.remote_ip4, - port=12346, session_refresh_interval=10) - bind_layers(UDP, HANATStateSync, sport=12345) - - # create sessions - 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) - # active send HA events - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/add-event-send') - self.assertEqual(stats[0][0], 3) - capture = self.pg3.get_capture(1) - p = capture[0] - self.assert_packet_checksums_valid(p) - try: - ip = p[IP] - udp = p[UDP] - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(ip.src, self.pg3.local_ip4) - self.assertEqual(ip.dst, self.pg3.remote_ip4) - self.assertEqual(udp.sport, 12345) - self.assertEqual(udp.dport, 12346) - self.assertEqual(hanat.version, 1) - self.assertEqual(hanat.thread_index, 0) - self.assertEqual(hanat.count, 3) - seq = hanat.sequence_number - for event in hanat.events: - self.assertEqual(event.event_type, 1) - self.assertEqual(event.in_addr, self.pg0.remote_ip4) - self.assertEqual(event.out_addr, self.nat_addr) - self.assertEqual(event.fib_index, 0) - - # ACK received events - ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / - IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / - UDP(sport=12346, dport=12345) / - HANATStateSync(sequence_number=seq, flags='ACK')) - self.pg3.add_stream(ack) - self.pg_start() - stats = self.statistics.get_counter('/nat44/ha/ack-recv') - self.assertEqual(stats[0][0], 1) - - # delete one session - self.pg_enable_capture(self.pg_interfaces) - self.vapi.nat44_del_session(address=self.pg0.remote_ip4, - port=self.tcp_port_in, - protocol=IP_PROTOS.tcp, - flags=self.config_flags.NAT_IS_INSIDE) - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/del-event-send') - self.assertEqual(stats[0][0], 1) - capture = self.pg3.get_capture(1) - p = capture[0] - try: - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertGreater(hanat.sequence_number, seq) - - # do not send ACK, active retry send HA event again - self.pg_enable_capture(self.pg_interfaces) - sleep(12) - stats = self.statistics.get_counter('/nat44/ha/retry-count') - self.assertEqual(stats[0][0], 3) - stats = self.statistics.get_counter('/nat44/ha/missed-count') - self.assertEqual(stats[0][0], 1) - capture = self.pg3.get_capture(3) - for packet in capture: - self.assertEqual(packet, p) - - # session counters refresh - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(2) - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/refresh-event-send') - self.assertEqual(stats[0][0], 2) - capture = self.pg3.get_capture(1) - p = capture[0] - self.assert_packet_checksums_valid(p) - try: - ip = p[IP] - udp = p[UDP] - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(ip.src, self.pg3.local_ip4) - self.assertEqual(ip.dst, self.pg3.remote_ip4) - self.assertEqual(udp.sport, 12345) - self.assertEqual(udp.dport, 12346) - self.assertEqual(hanat.version, 1) - self.assertEqual(hanat.count, 2) - seq = hanat.sequence_number - for event in hanat.events: - self.assertEqual(event.event_type, 3) - self.assertEqual(event.out_addr, self.nat_addr) - self.assertEqual(event.fib_index, 0) - self.assertEqual(event.total_pkts, 2) - self.assertGreater(event.total_bytes, 0) - - ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / - IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / - UDP(sport=12346, dport=12345) / - HANATStateSync(sequence_number=seq, flags='ACK')) - self.pg3.add_stream(ack) - self.pg_start() - stats = self.statistics.get_counter('/nat44/ha/ack-recv') - self.assertEqual(stats[0][0], 2) - - def test_ha_recv(self): - """ Receive HA session synchronization events (passive) """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, - port=12345, - path_mtu=512) - bind_layers(UDP, HANATStateSync, sport=12345) - - self.tcp_port_out = random.randint(1025, 65535) - self.udp_port_out = random.randint(1025, 65535) - - # send HA session add events to failover/passive - p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / - IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / - UDP(sport=12346, dport=12345) / - HANATStateSync(sequence_number=1, events=[ - Event(event_type='add', protocol='tcp', - in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, - in_port=self.tcp_port_in, out_port=self.tcp_port_out, - eh_addr=self.pg1.remote_ip4, - ehn_addr=self.pg1.remote_ip4, - eh_port=self.tcp_external_port, - ehn_port=self.tcp_external_port, fib_index=0), - Event(event_type='add', protocol='udp', - in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, - in_port=self.udp_port_in, out_port=self.udp_port_out, - eh_addr=self.pg1.remote_ip4, - ehn_addr=self.pg1.remote_ip4, - eh_port=self.udp_external_port, - ehn_port=self.udp_external_port, fib_index=0)])) - - self.pg3.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - # receive ACK - capture = self.pg3.get_capture(1) - p = capture[0] - try: - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(hanat.sequence_number, 1) - self.assertEqual(hanat.flags, 'ACK') - self.assertEqual(hanat.version, 1) - self.assertEqual(hanat.thread_index, 0) - stats = self.statistics.get_counter('/nat44/ha/ack-send') - self.assertEqual(stats[0][0], 1) - stats = self.statistics.get_counter('/nat44/ha/add-event-recv') - self.assertEqual(stats[0][0], 2) - users = self.statistics.get_counter('/nat44/total-users') - self.assertEqual(users[0][0], 1) - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 2) - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 1) - self.assertEqual(str(users[0].ip_address), - self.pg0.remote_ip4) - # there should be 2 sessions created by HA - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) - self.assertEqual(len(sessions), 2) - for session in sessions: - self.assertEqual(str(session.inside_ip_address), - self.pg0.remote_ip4) - self.assertEqual(str(session.outside_ip_address), - self.nat_addr) - self.assertIn(session.inside_port, - [self.tcp_port_in, self.udp_port_in]) - self.assertIn(session.outside_port, - [self.tcp_port_out, self.udp_port_out]) - self.assertIn(session.protocol, [IP_PROTOS.tcp, IP_PROTOS.udp]) - - # send HA session delete event to failover/passive - p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / - IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / - UDP(sport=12346, dport=12345) / - HANATStateSync(sequence_number=2, events=[ - Event(event_type='del', protocol='udp', - in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, - in_port=self.udp_port_in, out_port=self.udp_port_out, - eh_addr=self.pg1.remote_ip4, - ehn_addr=self.pg1.remote_ip4, - eh_port=self.udp_external_port, - ehn_port=self.udp_external_port, fib_index=0)])) - - self.pg3.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - # receive ACK - capture = self.pg3.get_capture(1) - p = capture[0] - try: - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(hanat.sequence_number, 2) - self.assertEqual(hanat.flags, 'ACK') - self.assertEqual(hanat.version, 1) - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 1) - self.assertEqual(str(users[0].ip_address), - self.pg0.remote_ip4) - # now we should have only 1 session, 1 deleted by HA - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) - self.assertEqual(len(sessions), 1) - stats = self.statistics.get_counter('/nat44/ha/del-event-recv') - self.assertEqual(stats[0][0], 1) - - stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') - self.assertEqual(stats, 2) - - # send HA session refresh event to failover/passive - p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / - IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / - UDP(sport=12346, dport=12345) / - HANATStateSync(sequence_number=3, events=[ - Event(event_type='refresh', protocol='tcp', - in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, - in_port=self.tcp_port_in, out_port=self.tcp_port_out, - eh_addr=self.pg1.remote_ip4, - ehn_addr=self.pg1.remote_ip4, - eh_port=self.tcp_external_port, - ehn_port=self.tcp_external_port, fib_index=0, - total_bytes=1024, total_pkts=2)])) - self.pg3.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - # receive ACK - capture = self.pg3.get_capture(1) - p = capture[0] - try: - hanat = p[HANATStateSync] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(hanat.sequence_number, 3) - self.assertEqual(hanat.flags, 'ACK') - self.assertEqual(hanat.version, 1) - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 1) - self.assertEqual(str(users[0].ip_address), - self.pg0.remote_ip4) - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) - self.assertEqual(len(sessions), 1) - session = sessions[0] - self.assertEqual(session.total_bytes, 1024) - self.assertEqual(session.total_pkts, 2) - stats = self.statistics.get_counter('/nat44/ha/refresh-event-recv') - self.assertEqual(stats[0][0], 1) - - stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') - self.assertEqual(stats, 3) - - # send packet to test session created by HA - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out)) - self.pg1.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] - except IndexError: - self.logger.error(ppp("Invalid packet:", p)) - raise - else: - self.assertEqual(ip.src, self.pg1.remote_ip4) - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.sport, self.tcp_external_port) - self.assertEqual(tcp.dport, self.tcp_port_in) - - def show_commands_at_teardown(self): - self.logger.info(self.vapi.cli("show nat44 addresses")) - self.logger.info(self.vapi.cli("show nat44 interfaces")) - self.logger.info(self.vapi.cli("show nat44 static mappings")) - self.logger.info(self.vapi.cli("show nat44 interface address")) - self.logger.info(self.vapi.cli("show nat44 sessions detail")) - self.logger.info(self.vapi.cli("show nat44 hash tables detail")) - self.logger.info(self.vapi.cli("show nat timeouts")) - self.logger.info( - self.vapi.cli("show nat addr-port-assignment-alg")) - self.logger.info(self.vapi.cli("show nat ha")) - - -class TestNAT44EndpointDependent2(MethodHolder): - """ Endpoint-Dependent mapping and filtering test cases """ - - @classmethod - def tearDownClass(cls): - super(TestNAT44EndpointDependent2, cls).tearDownClass() - - def tearDown(self): - super(TestNAT44EndpointDependent2, self).tearDown() - - @classmethod - def create_and_add_ip4_table(cls, i, table_id): - cls.vapi.ip_table_add_del(is_add=1, table={'table_id': table_id}) - i.set_table_ip4(table_id) - - @classmethod - def setUpClass(cls): - super(TestNAT44EndpointDependent2, cls).setUpClass() - - cls.create_pg_interfaces(range(3)) - cls.interfaces = list(cls.pg_interfaces) - - cls.create_and_add_ip4_table(cls.pg1, 10) - - for i in cls.interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - i.generate_remote_hosts(1) - i.configure_ipv4_neighbors() - - def setUp(self): - super(TestNAT44EndpointDependent2, self).setUp() - flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT - self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) - - def tearDown(self): - super(TestNAT44EndpointDependent2, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def nat_add_inside_interface(self, i): - self.vapi.nat44_interface_add_del_feature( - flags=self.config_flags.NAT_IS_INSIDE, - sw_if_index=i.sw_if_index, is_add=1) - - def nat_add_outside_interface(self, i): - self.vapi.nat44_interface_add_del_feature( - flags=self.config_flags.NAT_IS_OUTSIDE, - sw_if_index=i.sw_if_index, is_add=1) - - def nat_add_interface_address(self, i): - self.nat_addr = i.local_ip4 - self.vapi.nat44_add_del_interface_addr( - sw_if_index=i.sw_if_index, is_add=1) - - def nat_add_address(self, address, vrf_id=0xFFFFFFFF): - self.nat_addr = address - self.nat44_add_address(address, vrf_id=vrf_id) - - def cli(self, command): - result = self.vapi.cli(command) - self.logger.info(result) - # print(result) - - def show_configuration(self): - self.cli("show interface") - self.cli("show interface address") - self.cli("show nat44 addresses") - self.cli("show nat44 interfaces") - - def create_tcp_stream(self, in_if, out_if, count): - """ - Create tcp packet stream - - :param in_if: Inside interface - :param out_if: Outside interface - :param count: count of packets to generate - """ - pkts = [] - port = 6303 - - for i in range(count): - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=64) / - TCP(sport=port + i, dport=20)) - pkts.append(p) - - return pkts - - def test_session_limit_per_vrf(self): - - inside = self.pg0 - inside_vrf10 = self.pg1 - outside = self.pg2 - - limit = 5 - - # 2 interfaces pg0, pg1 (vrf10, limit 1 tcp session) - # non existing vrf_id makes process core dump - self.vapi.nat44_set_session_limit(session_limit=limit, vrf_id=10) - - self.nat_add_inside_interface(inside) - self.nat_add_inside_interface(inside_vrf10) - self.nat_add_outside_interface(outside) - - # vrf independent - self.nat_add_interface_address(outside) - - # BUG: causing core dump - when bad vrf_id is specified - # self.nat44_add_address(outside.local_ip4, vrf_id=20) - - self.show_configuration() - - stream = self.create_tcp_stream(inside_vrf10, outside, limit * 2) - inside_vrf10.add_stream(stream) - - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - capture = outside.get_capture(limit) - - stream = self.create_tcp_stream(inside, outside, limit * 2) - inside.add_stream(stream) - - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - capture = outside.get_capture(len(stream)) - - -class TestNAT44EndpointDependent(MethodHolder): - """ Endpoint-Dependent mapping and filtering test cases """ - - @classmethod - def setUpClass(cls): - super(TestNAT44EndpointDependent, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") - - cls.tcp_port_in = 6303 - cls.tcp_port_out = 6303 - cls.udp_port_in = 6304 - cls.udp_port_out = 6304 - cls.icmp_id_in = 6305 - cls.icmp_id_out = 6305 - cls.nat_addr = '10.0.0.3' - cls.ipfix_src_port = 4739 - cls.ipfix_domain_id = 1 - cls.tcp_external_port = 80 - - cls.create_pg_interfaces(range(9)) - cls.interfaces = list(cls.pg_interfaces[0:3]) - - for i in cls.interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - cls.pg0.generate_remote_hosts(3) - cls.pg0.configure_ipv4_neighbors() - - cls.pg3.admin_up() - - cls.pg4.generate_remote_hosts(2) - cls.pg4.config_ip4() - cls.vapi.sw_interface_add_del_address( - sw_if_index=cls.pg4.sw_if_index, - prefix="10.0.0.1/24") - - cls.pg4.admin_up() - cls.pg4.resolve_arp() - cls.pg4._remote_hosts[1]._ip4 = cls.pg4._remote_hosts[0]._ip4 - cls.pg4.resolve_arp() - - zero_ip4 = socket.inet_pton(socket.AF_INET, "0.0.0.0") - cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 1}) - - cls.pg5._local_ip4 = "10.1.1.1" - cls.pg5._remote_hosts[0]._ip4 = "10.1.1.2" - cls.pg5.set_table_ip4(1) - cls.pg5.config_ip4() - cls.pg5.admin_up() - r1 = VppIpRoute(cls, cls.pg5.remote_ip4, 32, - [VppRoutePath("0.0.0.0", - cls.pg5.sw_if_index)], - table_id=1, - register=False) - r1.add_vpp_config() - - cls.pg6._local_ip4 = "10.1.2.1" - cls.pg6._remote_hosts[0]._ip4 = "10.1.2.2" - cls.pg6.set_table_ip4(1) - cls.pg6.config_ip4() - cls.pg6.admin_up() - - r2 = VppIpRoute(cls, cls.pg6.remote_ip4, 32, - [VppRoutePath("0.0.0.0", - cls.pg6.sw_if_index)], - table_id=1, - register=False) - r3 = VppIpRoute(cls, cls.pg6.remote_ip4, 16, - [VppRoutePath("0.0.0.0", - 0xffffffff, - nh_table_id=1)], - table_id=0, - register=False) - r4 = VppIpRoute(cls, "0.0.0.0", 0, - [VppRoutePath("0.0.0.0", 0xffffffff, - nh_table_id=0)], - table_id=1, - register=False) - r5 = VppIpRoute(cls, "0.0.0.0", 0, - [VppRoutePath(cls.pg1.local_ip4, - cls.pg1.sw_if_index)], - register=False) - r2.add_vpp_config() - r3.add_vpp_config() - r4.add_vpp_config() - r5.add_vpp_config() - - cls.pg5.resolve_arp() - cls.pg6.resolve_arp() - - cls.pg7.admin_up() - cls.pg7.config_ip4() - cls.pg7.resolve_arp() - cls.pg7.generate_remote_hosts(3) - cls.pg7.configure_ipv4_neighbors() - - cls.pg8.admin_up() - cls.pg8.config_ip4() - cls.pg8.resolve_arp() - - @classmethod - def tearDownClass(cls): - super(TestNAT44EndpointDependent, cls).tearDownClass() - - def setUp(self): - super(TestNAT44EndpointDependent, self).setUp() - flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT - self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) - self.vapi.nat_set_timeouts( - udp=300, tcp_established=7440, - tcp_transitory=240, icmp=60) - - def tearDown(self): - super(TestNAT44EndpointDependent, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def test_frag_in_order(self): - """ NAT44 translate fragments arriving in order """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.frag_in_order(proto=IP_PROTOS.tcp, ignore_port=True) - self.frag_in_order(proto=IP_PROTOS.udp, ignore_port=True) - self.frag_in_order(proto=IP_PROTOS.icmp, ignore_port=True) - - def test_frag_in_order_dont_translate(self): - """ NAT44 don't translate fragments arriving in order """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=True) - self.frag_in_order(proto=IP_PROTOS.tcp, dont_translate=True) - - def test_frag_out_of_order(self): - """ NAT44 translate fragments arriving out of order """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.frag_out_of_order(proto=IP_PROTOS.tcp, ignore_port=True) - self.frag_out_of_order(proto=IP_PROTOS.udp, ignore_port=True) - self.frag_out_of_order(proto=IP_PROTOS.icmp, ignore_port=True) - - def test_frag_out_of_order_dont_translate(self): - """ NAT44 don't translate fragments arriving out of order """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=True) - self.frag_out_of_order(proto=IP_PROTOS.tcp, dont_translate=True) - - def test_frag_in_order_in_plus_out(self): - """ in+out interface fragments in order """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - flags=flags, is_add=1) - - self.server = self.pg1.remote_hosts[0] - - self.server_in_addr = self.server.ip4 - self.server_out_addr = '11.11.11.11' - self.server_in_port = random.randint(1025, 65535) - self.server_out_port = random.randint(1025, 65535) - - self.nat44_add_address(self.server_out_addr) - - # add static mappings for server - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - proto=IP_PROTOS.icmp) - - self.frag_in_order_in_plus_out(proto=IP_PROTOS.tcp) - self.frag_in_order_in_plus_out(proto=IP_PROTOS.udp) - self.frag_in_order_in_plus_out(proto=IP_PROTOS.icmp) - - def test_frag_out_of_order_in_plus_out(self): - """ in+out interface fragments out of order """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - flags=flags, is_add=1) - - self.server = self.pg1.remote_hosts[0] - - self.server_in_addr = self.server.ip4 - self.server_out_addr = '11.11.11.11' - self.server_in_port = random.randint(1025, 65535) - self.server_out_port = random.randint(1025, 65535) - - self.nat44_add_address(self.server_out_addr) - - # add static mappings for server - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.server_in_addr, - self.server_out_addr, - proto=IP_PROTOS.icmp) - - self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.tcp) - self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.udp) - self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.icmp) - - def test_reass_hairpinning(self): - """ NAT44 fragments hairpinning """ - self.server = self.pg0.remote_hosts[1] - self.host_in_port = random.randint(1025, 65535) - self.server_in_port = random.randint(1025, 65535) - self.server_out_port = random.randint(1025, 65535) - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - # add static mapping for server - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.tcp) - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, - self.server_in_port, - self.server_out_port, - proto=IP_PROTOS.udp) - self.nat44_add_static_mapping(self.server.ip4, self.nat_addr) - - self.reass_hairpinning(proto=IP_PROTOS.tcp, ignore_port=True) - self.reass_hairpinning(proto=IP_PROTOS.udp, ignore_port=True) - self.reass_hairpinning(proto=IP_PROTOS.icmp, ignore_port=True) - - def test_clear_sessions(self): - """ NAT44 ED session clearing test """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.endpoint_dependent) - - 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, ignore_port=True) - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertTrue(sessions[0][0] > 0) - self.logger.info("sessions before clearing: %s" % sessions[0][0]) - - # just for testing purposes - self.logger.info(self.vapi.cli("show nat44 summary")) - - self.vapi.cli("clear nat44 sessions") - - self.logger.info(self.vapi.cli("show nat44 summary")) - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 0) - self.logger.info("sessions after clearing: %s" % sessions[0][0]) - - def test_dynamic(self): - """ NAT44 dynamic translation test """ - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.endpoint_dependent) - - # in2out - tcpn = self.statistics.get_counter('/nat44/ed/in2out/slowpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/ed/in2out/slowpath/udp')[0] - icmpn = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/icmp')[0] - drops = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/drops')[0] - - 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, ignore_port=True) - - if_idx = self.pg0.sw_if_index - cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - # out2in - tcpn = self.statistics.get_counter('/nat44/ed/out2in/fastpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/ed/out2in/fastpath/udp')[0] - icmpn = self.statistics.get_counter( - '/nat44/ed/out2in/slowpath/icmp')[0] - drops = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/drops')[0] - - 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) - - if_idx = self.pg1.sw_if_index - cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/ed/out2in/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 3) - - def test_dynamic_out_of_ports(self): - """ NAT44 dynamic translation test: out of ports """ - - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.endpoint_dependent) - - # in2out and no NAT addresses added - err_old = self.statistics.get_err_counter( - '/err/nat44-ed-in2out-slowpath/out of ports') - - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(0, timeout=1) - - err_new = self.statistics.get_err_counter( - '/err/nat44-ed-in2out-slowpath/out of ports') - - self.assertEqual(err_new - err_old, len(pkts)) - - # in2out after NAT addresses added - self.nat44_add_address(self.nat_addr) - - err_old = self.statistics.get_err_counter( - '/err/nat44-ed-in2out-slowpath/out of ports') - - 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, ignore_port=True) - - err_new = self.statistics.get_err_counter( - '/err/nat44-ed-in2out-slowpath/out of ports') - - self.assertEqual(err_new, err_old) - - def test_dynamic_output_feature_vrf(self): - """ NAT44 dynamic translation test: output-feature, VRF""" - - # other then default (0) - new_vrf_id = 22 - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( - sw_if_index=self.pg7.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - sw_if_index=self.pg8.sw_if_index, - is_add=1) - - try: - self.vapi.ip_table_add_del(is_add=1, - table={'table_id': new_vrf_id}) - - self.pg7.unconfig_ip4() - self.pg7.set_table_ip4(new_vrf_id) - self.pg7.config_ip4() - self.pg7.resolve_arp() - - self.pg8.unconfig_ip4() - self.pg8.set_table_ip4(new_vrf_id) - self.pg8.config_ip4() - self.pg8.resolve_arp() - - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.endpoint_dependent) - - # in2out - tcpn = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/tcp')[0] - udpn = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/udp')[0] - icmpn = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/icmp')[0] - drops = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/drops')[0] - - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture, ignore_port=True) - - if_idx = self.pg7.sw_if_index - cnt = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter( - '/nat44/ed/in2out/slowpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - # out2in - tcpn = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/tcp')[0] - udpn = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/udp')[0] - icmpn = self.statistics.get_counter( - '/nat44/ed/out2in/slowpath/icmp')[0] - drops = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/drops')[0] - - pkts = self.create_stream_out(self.pg8) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - if_idx = self.pg8.sw_if_index - cnt = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/tcp')[0] - self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/udp')[0] - self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter( - '/nat44/ed/out2in/slowpath/icmp')[0] - self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter( - '/nat44/ed/out2in/fastpath/drops')[0] - self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - - sessions = self.statistics.get_counter('/nat44/total-sessions') - self.assertEqual(sessions[0][0], 3) - - finally: - self.pg7.unconfig_ip4() - self.pg7.set_table_ip4(1) - self.pg7.config_ip4() - self.pg7.resolve_arp() - - self.pg8.unconfig_ip4() - self.pg8.set_table_ip4(1) - self.pg8.config_ip4() - self.pg8.resolve_arp() - - self.vapi.ip_table_add_del(is_add=0, - table={'table_id': new_vrf_id}) - - def test_forwarding(self): - """ NAT44 forwarding test """ - - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) - - real_ip = self.pg0.remote_ip4 - alias_ip = self.nat_addr - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping(is_add=1, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - 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 - - user = self.pg0.remote_hosts[1] - sessions = self.vapi.nat44_user_session_dump(user.ip4, 0) - self.assertEqual(len(sessions), 3) - self.assertTrue(sessions[0].flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) - self.vapi.nat44_del_session( - address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=(self.config_flags.NAT_IS_INSIDE | - self.config_flags.NAT_IS_EXT_HOST_VALID), - ext_host_address=sessions[0].ext_host_address, - ext_host_port=sessions[0].ext_host_port) - sessions = self.vapi.nat44_user_session_dump(user.ip4, 0) - self.assertEqual(len(sessions), 2) - - finally: - self.vapi.nat44_forwarding_enable_disable(enable=0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping( - is_add=0, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - def test_static_lb(self): - """ NAT44 local service load balancing """ - external_addr_n = self.nat_addr - external_port = 80 - local_port = 8080 - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] - - locals = [{'addr': server1.ip4, - 'port': local_port, - 'probability': 70, - 'vrf_id': 0}, - {'addr': server2.ip4, - 'port': local_port, - 'probability': 30, - 'vrf_id': 0}] - - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_add_del_lb_static_mapping( - is_add=1, - external_addr=external_addr_n, - external_port=external_port, - protocol=IP_PROTOS.tcp, - local_num=len(locals), - locals=locals) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) - self.assertEqual(len(sessions), 1) - self.assertTrue(sessions[0].flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) - self.vapi.nat44_del_session( - address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=(self.config_flags.NAT_IS_INSIDE | - self.config_flags.NAT_IS_EXT_HOST_VALID), - ext_host_address=sessions[0].ext_host_address, - ext_host_port=sessions[0].ext_host_port) - sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) - self.assertEqual(len(sessions), 0) - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_static_lb_multi_clients(self): - """ NAT44 local service load balancing - multiple clients""" - - external_addr = self.nat_addr - external_port = 80 - local_port = 8080 - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] - server3 = self.pg0.remote_hosts[2] - - locals = [{'addr': server1.ip4, - 'port': local_port, - 'probability': 90, - 'vrf_id': 0}, - {'addr': server2.ip4, - 'port': local_port, - 'probability': 10, - 'vrf_id': 0}] - - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_add_del_lb_static_mapping(is_add=1, - external_addr=external_addr, - external_port=external_port, - protocol=IP_PROTOS.tcp, - local_num=len(locals), - locals=locals) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - server1_n = 0 - server2_n = 0 - clients = ip4_range(self.pg1.remote_ip4, 10, 50) - pkts = [] - for client in clients: - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=client, dst=self.nat_addr) / - TCP(sport=12345, dport=external_port)) - pkts.append(p) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for p in capture: - if p[IP].dst == server1.ip4: - server1_n += 1 - else: - server2_n += 1 - self.assertGreater(server1_n, server2_n) - - local = { - 'addr': server3.ip4, - 'port': local_port, - 'probability': 20, - 'vrf_id': 0 - } - - # add new back-end - self.vapi.nat44_lb_static_mapping_add_del_local( - is_add=1, - external_addr=external_addr, - external_port=external_port, - local=local, - protocol=IP_PROTOS.tcp) - server1_n = 0 - server2_n = 0 - server3_n = 0 - clients = ip4_range(self.pg1.remote_ip4, 60, 110) - pkts = [] - for client in clients: - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=client, dst=self.nat_addr) / - TCP(sport=12346, dport=external_port)) - pkts.append(p) - self.assertGreater(len(pkts), 0) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for p in capture: - if p[IP].dst == server1.ip4: - server1_n += 1 - elif p[IP].dst == server2.ip4: - server2_n += 1 - else: - server3_n += 1 - self.assertGreater(server1_n, 0) - self.assertGreater(server2_n, 0) - self.assertGreater(server3_n, 0) - - local = { - 'addr': server2.ip4, - 'port': local_port, - 'probability': 10, - 'vrf_id': 0 - } - - # remove one back-end - self.vapi.nat44_lb_static_mapping_add_del_local( - is_add=0, - external_addr=external_addr, - external_port=external_port, - local=local, - protocol=IP_PROTOS.tcp) - server1_n = 0 - server2_n = 0 - server3_n = 0 - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for p in capture: - if p[IP].dst == server1.ip4: - server1_n += 1 - elif p[IP].dst == server2.ip4: - server2_n += 1 - else: - server3_n += 1 - self.assertGreater(server1_n, 0) - self.assertEqual(server2_n, 0) - self.assertGreater(server3_n, 0) - - def test_static_lb_2(self): - """ NAT44 local service load balancing (asymmetrical rule) """ - external_addr = self.nat_addr - external_port = 80 - local_port = 8080 - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] - - locals = [{'addr': server1.ip4, - 'port': local_port, - 'probability': 70, - 'vrf_id': 0}, - {'addr': server2.ip4, - 'port': local_port, - 'probability': 30, - 'vrf_id': 0}] - - self.vapi.nat44_forwarding_enable_disable(enable=1) - flags = self.config_flags.NAT_IS_OUT2IN_ONLY - self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags, - external_addr=external_addr, - external_port=external_port, - protocol=IP_PROTOS.tcp, - local_num=len(locals), - locals=locals) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_lb_affinity(self): - """ NAT44 local service load balancing affinity """ - external_addr = self.nat_addr - external_port = 80 - local_port = 8080 - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] - - locals = [{'addr': server1.ip4, - 'port': local_port, - 'probability': 50, - 'vrf_id': 0}, - {'addr': server2.ip4, - 'port': local_port, - 'probability': 50, - 'vrf_id': 0}] - - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_add_del_lb_static_mapping(is_add=1, - external_addr=external_addr, - external_port=external_port, - protocol=IP_PROTOS.tcp, - affinity=10800, - local_num=len(locals), - locals=locals) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=1025, dport=external_port)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - backend = capture[0][IP].dst - - sessions = self.vapi.nat44_user_session_dump(backend, 0) - self.assertEqual(len(sessions), 1) - self.assertTrue(sessions[0].flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) - self.vapi.nat44_del_session( - address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=(self.config_flags.NAT_IS_INSIDE | - self.config_flags.NAT_IS_EXT_HOST_VALID), - ext_host_address=sessions[0].ext_host_address, - ext_host_port=sessions[0].ext_host_port) - - pkts = [] - for port in range(1030, 1100): - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=port, dport=external_port)) - pkts.append(p) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for p in capture: - self.assertEqual(p[IP].dst, backend) - - def test_unknown_proto(self): - """ NAT44 translate packet with unknown protocol """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=20)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.pg1.remote_ip4) - self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_unknown_proto(self): - """ NAT44 translate packet with unknown protocol - hairpinning """ - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - host_in_port = 1234 - server_out_port = 8765 - server_nat_ip = "10.0.0.11" - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # add static mapping for server - self.nat44_add_static_mapping(server.ip4, server_nat_ip) - - # host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=server_nat_ip) / - TCP(sport=host_in_port, dport=server_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=host.mac) / - IP(src=host.ip4, dst=server_nat_ip) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, server.ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to host - p = (Ether(dst=self.pg0.local_mac, src=server.mac) / - IP(src=server.ip4, dst=self.nat_addr) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, server_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - self.assertEqual(packet.haslayer(GRE), 1) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_output_feature_and_service(self): - """ NAT44 interface output feature and services """ - external_addr = '1.2.3.4' - external_port = 80 - local_port = 8080 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_identity_mapping( - ip_address=self.pg1.remote_ip4, sw_if_index=0xFFFFFFFF, - flags=flags, is_add=1) - flags = self.config_flags.NAT_IS_OUT2IN_ONLY - self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - - # from client to service - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=external_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] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_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, external_addr) - self.assertEqual(tcp.sport, external_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from local network host to external network - 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, ignore_port=True) - 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, ignore_port=True) - - # from external network back to local network host - 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_and_service2(self): - """ NAT44 interface output feature and service host direct access """ - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - - # session initiated from service host - translate - 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, ignore_port=True) - - 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) - - # session initiated from remote host - do not translate - self.tcp_port_in = 60303 - self.udp_port_in = 60304 - self.icmp_id_in = 60305 - pkts = self.create_stream_out(self.pg1, - 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) - - def test_output_feature_and_service3(self): - """ NAT44 interface output feature and DST NAT """ - external_addr = '1.2.3.4' - external_port = 80 - local_port = 8080 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_OUT2IN_ONLY - self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=external_addr) / - TCP(sport=12345, dport=external_port)) - 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.pg0.remote_ip4) - self.assertEqual(tcp.sport, 12345) - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=local_port, dport=12345)) - self.pg1.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, external_addr) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_next_src_nat(self): - """ On way back forward packet to nat44-in2out node. """ - twice_nat_addr = '10.0.1.3' - external_port = 80 - local_port = 8080 - post_twice_nat_port = 0 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(twice_nat_addr, twice_nat=1) - flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | - self.config_flags.NAT_IS_SELF_TWICE_NAT) - self.nat44_add_static_mapping(self.pg6.remote_ip4, self.pg1.remote_ip4, - local_port, external_port, - proto=IP_PROTOS.tcp, vrf_id=1, - flags=flags) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg6.sw_if_index, - is_add=1) - - p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / - IP(src=self.pg6.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=12345, dport=external_port)) - self.pg6.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, twice_nat_addr) - self.assertNotEqual(tcp.sport, 12345) - post_twice_nat_port = tcp.sport - self.assertEqual(ip.dst, self.pg6.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / - IP(src=self.pg6.remote_ip4, dst=twice_nat_addr) / - TCP(sport=local_port, dport=post_twice_nat_port)) - self.pg6.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg1.remote_ip4) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(ip.dst, self.pg6.remote_ip4) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def twice_nat_common(self, self_twice_nat=False, same_pg=False, lb=False, - client_id=None): - twice_nat_addr = '10.0.1.3' - - port_in = 8080 - if lb: - if not same_pg: - port_in1 = port_in - port_in2 = port_in - else: - port_in1 = port_in + 1 - port_in2 = port_in + 2 - - port_out = 80 - eh_port_out = 4567 - - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] - if lb and same_pg: - server2 = server1 - if not lb: - server = server1 - - pg0 = self.pg0 - if same_pg: - pg1 = self.pg0 - else: - pg1 = self.pg1 - - eh_translate = ((not self_twice_nat) or (not lb and same_pg) or - client_id == 1) - - self.nat44_add_address(self.nat_addr) - self.nat44_add_address(twice_nat_addr, twice_nat=1) - - flags = 0 - if self_twice_nat: - flags |= self.config_flags.NAT_IS_SELF_TWICE_NAT - else: - flags |= self.config_flags.NAT_IS_TWICE_NAT - - if not lb: - self.nat44_add_static_mapping(pg0.remote_ip4, self.nat_addr, - port_in, port_out, - proto=IP_PROTOS.tcp, - flags=flags) - else: - locals = [{'addr': server1.ip4, - 'port': port_in1, - 'probability': 50, - 'vrf_id': 0}, - {'addr': server2.ip4, - 'port': port_in2, - 'probability': 50, - 'vrf_id': 0}] - out_addr = self.nat_addr - - self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags, - external_addr=out_addr, - external_port=port_out, - protocol=IP_PROTOS.tcp, - local_num=len(locals), - locals=locals) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=pg1.sw_if_index, - is_add=1) - - if same_pg: - if not lb: - client = server - else: - assert client_id is not None - if client_id == 1: - client = self.pg0.remote_hosts[0] - elif client_id == 2: - client = self.pg0.remote_hosts[1] - else: - client = pg1.remote_hosts[0] - p = (Ether(src=pg1.remote_mac, dst=pg1.local_mac) / - IP(src=client.ip4, dst=self.nat_addr) / - TCP(sport=eh_port_out, dport=port_out)) - pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - if lb: - if ip.dst == server1.ip4: - server = server1 - port_in = port_in1 - else: - server = server2 - port_in = port_in2 - self.assertEqual(ip.dst, server.ip4) - if lb and same_pg: - self.assertIn(tcp.dport, [port_in1, port_in2]) - else: - self.assertEqual(tcp.dport, port_in) - if eh_translate: - self.assertEqual(ip.src, twice_nat_addr) - self.assertNotEqual(tcp.sport, eh_port_out) - else: - self.assertEqual(ip.src, client.ip4) - self.assertEqual(tcp.sport, eh_port_out) - eh_addr_in = ip.src - eh_port_in = tcp.sport - saved_port_in = tcp.dport - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - p = (Ether(src=server.mac, dst=pg0.local_mac) / - IP(src=server.ip4, dst=eh_addr_in) / - TCP(sport=saved_port_in, dport=eh_port_in)) - pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = pg1.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, client.ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, eh_port_out) - self.assertEqual(tcp.sport, port_out) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - if eh_translate: - sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) - self.assertEqual(len(sessions), 1) - self.assertTrue(sessions[0].flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) - self.assertTrue(sessions[0].flags & - self.config_flags.NAT_IS_TWICE_NAT) - self.logger.info(self.vapi.cli("show nat44 sessions detail")) - self.vapi.nat44_del_session( - address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=(self.config_flags.NAT_IS_INSIDE | - self.config_flags.NAT_IS_EXT_HOST_VALID), - ext_host_address=sessions[0].ext_host_nat_address, - ext_host_port=sessions[0].ext_host_nat_port) - sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) - self.assertEqual(len(sessions), 0) - - def test_twice_nat(self): - """ Twice NAT44 """ - self.twice_nat_common() - - def test_self_twice_nat_positive(self): - """ Self Twice NAT44 (positive test) """ - self.twice_nat_common(self_twice_nat=True, same_pg=True) - - def test_self_twice_nat_negative(self): - """ Self Twice NAT44 (negative test) """ - self.twice_nat_common(self_twice_nat=True) - - def test_twice_nat_lb(self): - """ Twice NAT44 local service load balancing """ - self.twice_nat_common(lb=True) - - def test_self_twice_nat_lb_positive(self): - """ Self Twice NAT44 local service load balancing (positive test) """ - self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, - client_id=1) - - def test_self_twice_nat_lb_negative(self): - """ Self Twice NAT44 local service load balancing (negative test) """ - self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, - client_id=2) - - def test_twice_nat_interface_addr(self): - """ Acquire twice NAT44 addresses from interface """ - flags = self.config_flags.NAT_IS_TWICE_NAT - self.vapi.nat44_add_del_interface_addr( - is_add=1, - sw_if_index=self.pg3.sw_if_index, - flags=flags) - - # no address in NAT pool - adresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(adresses)) - - # configure interface address and check NAT address pool - self.pg3.config_ip4() - adresses = self.vapi.nat44_address_dump() - self.assertEqual(1, len(adresses)) - self.assertEqual(str(adresses[0].ip_address), - self.pg3.local_ip4) - self.assertEqual(adresses[0].flags, flags) - - # remove interface address and check NAT address pool - self.pg3.unconfig_ip4() - adresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(adresses)) - - def test_tcp_close(self): - """ Close TCP session from inside network - output feature """ - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.pg1.local_ip4) - twice_nat_addr = '10.0.1.3' - service_ip = '192.168.16.150' - self.nat44_add_address(twice_nat_addr, twice_nat=1) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | - self.config_flags.NAT_IS_TWICE_NAT) - self.nat44_add_static_mapping(self.pg0.remote_ip4, - service_ip, - 80, - 80, - proto=IP_PROTOS.tcp, - flags=flags) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - start_sessnum = len(sessions) - - # SYN packet out->in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=service_ip) / - TCP(sport=33898, dport=80, flags="S")) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - tcp_port = p[TCP].sport - - # SYN + ACK packet in->out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / - TCP(sport=80, dport=tcp_port, flags="SA")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # ACK packet out->in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=service_ip) / - TCP(sport=33898, dport=80, flags="A")) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - # FIN packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / - TCP(sport=80, dport=tcp_port, flags="FA", seq=100, ack=300)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # FIN+ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=service_ip) / - TCP(sport=33898, dport=80, flags="FA", seq=300, ack=101)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / - TCP(sport=80, dport=tcp_port, flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, - 0) - self.assertEqual(len(sessions) - start_sessnum, 0) - - def test_tcp_session_close_in(self): - """ Close TCP session from inside network """ - self.tcp_port_out = 10505 - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_TWICE_NAT - self.nat44_add_static_mapping(self.pg0.remote_ip4, - self.nat_addr, - self.tcp_port_in, - self.tcp_port_out, - proto=IP_PROTOS.tcp, - flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - start_sessnum = len(sessions) - - self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, - tcp_transitory=2, icmp=5) - - self.initiate_tcp_session(self.pg0, self.pg1) - - # FIN packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="FA", seq=100, ack=300)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - pkts = [] - - # ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="A", seq=300, ack=101)) - pkts.append(p) - - # FIN packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=300, ack=101)) - pkts.append(p) - - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(2) - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 1) - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - out2in_drops = stats[0] - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - in2out_drops = stats[0] - - # extra FIN packet out -> in - this should be dropped - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=300, ack=101)) - - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.assert_nothing_captured() - - # extra ACK packet in -> out - this should be dropped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - out2in_drops, 1) - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - in2out_drops, 1) - - self.sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 0) - - def test_tcp_session_close_out(self): - """ Close TCP session from outside network """ - self.tcp_port_out = 10505 - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_TWICE_NAT - self.nat44_add_static_mapping(self.pg0.remote_ip4, - self.nat_addr, - self.tcp_port_in, - self.tcp_port_out, - proto=IP_PROTOS.tcp, - flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - start_sessnum = len(sessions) - - self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, - tcp_transitory=2, icmp=5) - - self.initiate_tcp_session(self.pg0, self.pg1) - - # FIN packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=100, ack=300)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - # FIN+ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="FA", seq=300, ack=101)) - - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="A", seq=101, ack=301)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 1) - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - out2in_drops = stats[0] - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - in2out_drops = stats[0] - - # extra FIN packet out -> in - this should be dropped - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=300, ack=101)) - - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.assert_nothing_captured() - - # extra ACK packet in -> out - this should be dropped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - out2in_drops, 1) - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - in2out_drops, 1) - - self.sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 0) - - def test_tcp_session_close_simultaneous(self): - """ Close TCP session from inside network """ - self.tcp_port_out = 10505 - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_TWICE_NAT - self.nat44_add_static_mapping(self.pg0.remote_ip4, - self.nat_addr, - self.tcp_port_in, - self.tcp_port_out, - proto=IP_PROTOS.tcp, - flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - start_sessnum = len(sessions) - - self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, - tcp_transitory=2, icmp=5) - - self.initiate_tcp_session(self.pg0, self.pg1) - - # FIN packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="FA", seq=100, ack=300)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # FIN packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=300, ack=100)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="A", seq=301, ack=101)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 1) - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - out2in_drops = stats[0] - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - in2out_drops = stats[0] - - # extra FIN packet out -> in - this should be dropped - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="FA", seq=300, ack=101)) - - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.assert_nothing_captured() - - # extra ACK packet in -> out - this should be dropped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - - stats = self.statistics.get_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - out2in_drops, 1) - stats = self.statistics.get_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats[0] - in2out_drops, 1) - - self.sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A", seq=101, ack=301)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) - self.assertEqual(len(sessions) - start_sessnum, 0) - - def test_one_armed_nat44_static(self): - """ One armed NAT44 and 1:1 NAPT asymmetrical rule """ - remote_host = self.pg4.remote_hosts[0] - local_host = self.pg4.remote_hosts[1] - external_port = 80 - local_port = 8080 - eh_port_in = 0 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr, twice_nat=1) - flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | - self.config_flags.NAT_IS_TWICE_NAT) - self.nat44_add_static_mapping(local_host.ip4, self.nat_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg4.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg4.sw_if_index, - flags=flags, is_add=1) - - # from client to service - p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / - IP(src=remote_host.ip4, dst=self.nat_addr) / - TCP(sport=12345, dport=external_port)) - self.pg4.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg4.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, local_host.ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, local_port) - self.assertNotEqual(tcp.sport, 12345) - eh_port_in = tcp.sport - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client - p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / - IP(src=local_host.ip4, dst=self.nat_addr) / - TCP(sport=local_port, dport=eh_port_in)) - self.pg4.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg4.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, remote_host.ip4) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_static_with_port_out2(self): - """ 1:1 NAPT asymmetrical rule """ - - external_port = 80 - local_port = 8080 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - flags = self.config_flags.NAT_IS_OUT2IN_ONLY - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, flags=flags) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - # 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] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # ICMP error - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - ICMP(type=11) / capture[0][IP]) - 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: - self.assertEqual(p[IP].src, self.nat_addr) - inner = p[IPerror] - self.assertEqual(inner.dst, self.nat_addr) - self.assertEqual(inner[TCPerror].dport, external_port) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # ICMP error - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - ICMP(type=11) / capture[0][IP]) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - self.assertEqual(p[IP].dst, self.pg0.remote_ip4) - inner = p[IPerror] - self.assertEqual(inner.src, self.pg0.remote_ip4) - self.assertEqual(inner[TCPerror].sport, local_port) - 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=self.pg0.remote_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] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client (no translation) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_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, self.pg0.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_output_feature(self): - """ NAT44 interface output feature (in2out postrouting) """ - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - is_add=1, - sw_if_index=self.pg1.sw_if_index) - - # 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, ignore_port=True) - - # 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_stateful_acl(self): - """ NAT44 endpoint-dependent output feature works with stateful ACL """ - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_output_feature( - sw_if_index=self.pg0.sw_if_index, - flags=self.config_flags.NAT_IS_INSIDE, - is_add=1) - self.vapi.nat44_interface_add_del_output_feature( - sw_if_index=self.pg1.sw_if_index, - flags=self.config_flags.NAT_IS_OUTSIDE, - is_add=1) - - # First ensure that the NAT is working sans ACL - - # send packets out2in, no sessions yet so packets should drop - pkts_out2in = self.create_stream_out(self.pg1) - self.send_and_assert_no_replies(self.pg1, pkts_out2in) - - # send packets into inside intf, ensure received via outside intf - pkts_in2out = self.create_stream_in(self.pg0, self.pg1) - capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1, - len(pkts_in2out)) - self.verify_capture_out(capture, ignore_port=True) - - # send out2in again, with sessions created it should work now - pkts_out2in = self.create_stream_out(self.pg1) - capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0, - len(pkts_out2in)) - self.verify_capture_in(capture, self.pg0) - - # Create an ACL blocking everything - out2in_deny_rule = AclRule(is_permit=0) - out2in_acl = VppAcl(self, rules=[out2in_deny_rule]) - out2in_acl.add_vpp_config() - - # create an ACL to permit/reflect everything - in2out_reflect_rule = AclRule(is_permit=2) - in2out_acl = VppAcl(self, rules=[in2out_reflect_rule]) - in2out_acl.add_vpp_config() - - # apply as input acl on interface and confirm it blocks everything - acl_if = VppAclInterface(self, sw_if_index=self.pg1.sw_if_index, - n_input=1, acls=[out2in_acl]) - acl_if.add_vpp_config() - self.send_and_assert_no_replies(self.pg1, pkts_out2in) - - # apply output acl - acl_if.acls = [out2in_acl, in2out_acl] - acl_if.add_vpp_config() - # send in2out to generate ACL state (NAT state was created earlier) - capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1, - len(pkts_in2out)) - self.verify_capture_out(capture, ignore_port=True) - - # send out2in again. ACL state exists so it should work now. - # TCP packets with the syn flag set also need the ack flag - for p in pkts_out2in: - if p.haslayer(TCP) and p[TCP].flags & 0x02: - p[TCP].flags |= 0x10 - capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0, - len(pkts_out2in)) - self.verify_capture_in(capture, self.pg0) - self.logger.info(self.vapi.cli("show trace")) - - def test_multiple_vrf(self): - """ Multiple VRF setup """ - external_addr = '1.2.3.4' - external_port = 80 - local_port = 8080 - port = 0 - - self.vapi.nat44_forwarding_enable_disable(enable=1) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - is_add=1, flags=flags) - self.vapi.nat44_interface_add_del_output_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg5.sw_if_index, - is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg5.sw_if_index, - is_add=1, flags=flags) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg6.sw_if_index, - is_add=1) - flags = self.config_flags.NAT_IS_OUT2IN_ONLY - self.nat44_add_static_mapping(self.pg5.remote_ip4, external_addr, - local_port, external_port, vrf_id=1, - proto=IP_PROTOS.tcp, flags=flags) - self.nat44_add_static_mapping( - self.pg0.remote_ip4, - external_sw_if_index=self.pg0.sw_if_index, - local_port=local_port, - vrf_id=0, - external_port=external_port, - proto=IP_PROTOS.tcp, - flags=flags - ) - - # from client to service (both VRF1) - p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / - IP(src=self.pg6.remote_ip4, dst=external_addr) / - TCP(sport=12345, dport=external_port)) - self.pg6.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg5.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client (both VRF1) - p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / - IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) / - TCP(sport=local_port, dport=12345)) - self.pg5.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, external_addr) - self.assertEqual(tcp.sport, external_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # dynamic NAT from VRF1 to VRF0 (output-feature) - p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / - IP(src=self.pg5.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=2345, dport=22)) - self.pg5.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.assert_packet_checksums_valid(p) - port = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=22, dport=port)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg5.remote_ip4) - self.assertEqual(tcp.dport, 2345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client VRF1 to service VRF0 - p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / - IP(src=self.pg6.remote_ip4, dst=self.pg0.local_ip4) / - TCP(sport=12346, dport=external_port)) - self.pg6.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.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service VRF0 back to client VRF1 - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg6.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.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg0.local_ip4) - self.assertEqual(tcp.sport, external_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client VRF0 to service VRF1 - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=external_addr) / - TCP(sport=12347, dport=external_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg5.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service VRF1 back to client VRF0 - p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / - IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=local_port, dport=12347)) - self.pg5.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, external_addr) - self.assertEqual(tcp.sport, external_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client to server (both VRF1, no translation) - p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / - IP(src=self.pg6.remote_ip4, dst=self.pg5.remote_ip4) / - TCP(sport=12348, dport=local_port)) - self.pg6.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg5.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from server back to client (both VRF1, no translation) - p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / - IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) / - TCP(sport=local_port, dport=12348)) - self.pg5.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg5.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client VRF1 to server VRF0 (no translation) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) / - TCP(sport=local_port, dport=12349)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg0.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from server VRF0 back to client VRF1 (no translation) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) / - TCP(sport=local_port, dport=12349)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg0.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client VRF0 to server VRF1 (no translation) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg5.remote_ip4) / - TCP(sport=12344, dport=local_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg5.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from server VRF1 back to client VRF0 (no translation) - p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / - IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=local_port, dport=12344)) - self.pg5.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.pg5.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_session_timeout(self): - """ NAT44 session timeouts """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, - tcp_transitory=240, icmp=5) - - max_sessions = 1000 - pkts = [] - for i in range(0, max_sessions): - src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=src, dst=self.pg1.remote_ip4) / - ICMP(id=1025, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(max_sessions) - - sleep(10) - - pkts = [] - for i in range(0, max_sessions): - src = "10.11.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=src, dst=self.pg1.remote_ip4) / - ICMP(id=1026, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(max_sessions) - - nsessions = 0 - users = self.vapi.nat44_user_dump() - for user in users: - nsessions = nsessions + user.nsessions - self.assertLess(nsessions, 2 * max_sessions) - - @unittest.skipUnless(running_extended_tests, "part of extended tests") - def test_session_rst_timeout(self): - """ NAT44 session RST timeouts """ - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, - tcp_transitory=5, icmp=60) - - self.initiate_tcp_session(self.pg0, self.pg1) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="R")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - sleep(6) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in + 1, dport=self.tcp_external_port + 1, - flags="S")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - def test_syslog_sess(self): - """ Test syslog session creation and deletion """ - self.vapi.syslog_set_filter( - self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO) - self.vapi.syslog_set_sender(self.pg2.local_ip4, self.pg2.remote_ip4) - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.tcp_port_out = capture[0][TCP].sport - capture = self.pg2.get_capture(1) - self.verify_syslog_sess(capture[0][Raw].load) - - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.nat44_add_address(self.nat_addr, is_add=0) - capture = self.pg2.get_capture(1) - self.verify_syslog_sess(capture[0][Raw].load, False) - - def test_ed_users_dump(self): - """ API test - nat44_user_dump """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, - is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) - - real_ip = self.pg0.remote_ip4 - alias_ip = self.nat_addr - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping(is_add=1, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 0) - 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) - - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 1) - static_user = users[0] - self.assertEqual(static_user.nstaticsessions, 3) - self.assertEqual(static_user.nsessions, 0) - - # 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 - - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 2) - if str(users[0].ip_address) == self.pg0.remote_hosts[0].ip4: - non_static_user = users[1] - static_user = users[0] - else: - non_static_user = users[0] - static_user = users[1] - self.assertEqual(static_user.nstaticsessions, 3) - self.assertEqual(static_user.nsessions, 0) - self.assertEqual(non_static_user.nstaticsessions, 0) - self.assertEqual(non_static_user.nsessions, 3) - - users = self.vapi.nat44_user_dump() - self.assertEqual(len(users), 2) - if str(users[0].ip_address) == self.pg0.remote_hosts[0].ip4: - non_static_user = users[1] - static_user = users[0] - else: - non_static_user = users[0] - static_user = users[1] - self.assertEqual(static_user.nstaticsessions, 3) - self.assertEqual(static_user.nsessions, 0) - self.assertEqual(non_static_user.nstaticsessions, 0) - self.assertEqual(non_static_user.nsessions, 3) - - finally: - self.vapi.nat44_forwarding_enable_disable(enable=0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping( - is_add=0, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) - - def show_commands_at_teardown(self): - self.logger.info(self.vapi.cli("show errors")) - self.logger.info(self.vapi.cli("show nat44 addresses")) - self.logger.info(self.vapi.cli("show nat44 interfaces")) - self.logger.info(self.vapi.cli("show nat44 static mappings")) - self.logger.info(self.vapi.cli("show nat44 interface address")) - self.logger.info(self.vapi.cli("show nat44 sessions detail")) - self.logger.info(self.vapi.cli("show nat44 hash tables detail")) - self.logger.info(self.vapi.cli("show nat timeouts")) - self.logger.info(self.vapi.cli("debug nat44 fib registration")) - - -class TestNAT44EndpointDependent3(MethodHolder): - """ Endpoint-Dependent mapping and filtering extra test cases """ - - max_translations = 50 - - @classmethod - def setUpClass(cls): - super(TestNAT44EndpointDependent3, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") - - cls.nat_addr = '10.0.0.3' - - cls.create_pg_interfaces(range(2)) - - for i in cls.pg_interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - def setUp(self): - super(TestNAT44EndpointDependent3, self).setUp() - flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT - self.vapi.nat44_plugin_enable_disable( - sessions=self.max_translations, - flags=flags, enable=1) - self.vapi.nat_set_timeouts( - udp=1, tcp_established=7440, tcp_transitory=30, icmp=1) - - self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg1.sw_if_index, is_add=1) - - @classmethod - def tearDownClass(cls): - super(TestNAT44EndpointDependent3, cls).tearDownClass() - - def tearDown(self): - super(TestNAT44EndpointDependent3, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def init_tcp_session(self, in_if, out_if, sport, ext_dport): - # SYN packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=sport, dport=ext_dport, flags="S")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = out_if.get_capture(1) - p = capture[0] - tcp_port_out = p[TCP].sport - - # SYN + ACK packet out->in - p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / - IP(src=out_if.remote_ip4, dst=self.nat_addr) / - TCP(sport=ext_dport, dport=tcp_port_out, flags="SA")) - out_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - in_if.get_capture(1) - - # ACK packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=sport, dport=ext_dport, flags="A")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - out_if.get_capture(1) - - return tcp_port_out - - def test_lru_cleanup(self): - """ LRU cleanup algorithm """ - tcp_port_out = self.init_tcp_session(self.pg0, self.pg1, 2000, 80) - pkts = [] - for i in range(0, self.max_translations - 1): - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / - UDP(sport=7000+i, dport=80)) - pkts.append(p) - - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(len(pkts)) - self.sleep(1.5, "wait for timeouts") - - pkts = [] - for i in range(0, self.max_translations - 1): - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / - ICMP(id=8000+i, type='echo-request')) - pkts.append(p) - - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(len(pkts)) - - -class TestNAT44Out2InDPO(MethodHolder): - """ NAT44 Test Cases using out2in DPO """ - - @classmethod - def setUpClass(cls): - super(TestNAT44Out2InDPO, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") - - cls.tcp_port_in = 6303 - cls.tcp_port_out = 6303 - cls.udp_port_in = 6304 - cls.udp_port_out = 6304 - cls.icmp_id_in = 6305 - cls.icmp_id_out = 6305 - cls.nat_addr = '10.0.0.3' - cls.dst_ip4 = '192.168.70.1' - - cls.create_pg_interfaces(range(2)) - - cls.pg0.admin_up() - cls.pg0.config_ip4() - cls.pg0.resolve_arp() - - cls.pg1.admin_up() - cls.pg1.config_ip6() - cls.pg1.resolve_ndp() - - r1 = VppIpRoute(cls, "::", 0, - [VppRoutePath(cls.pg1.remote_ip6, - cls.pg1.sw_if_index)], - register=False) - r1.add_vpp_config() - - @classmethod - def tearDownClass(cls): - super(TestNAT44Out2InDPO, cls).tearDownClass() - - def setUp(self): - super(TestNAT44Out2InDPO, self).setUp() - flags = self.nat44_config_flags.NAT44_API_IS_OUT2IN_DPO - self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) - - def tearDown(self): - super(TestNAT44Out2InDPO, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def configure_xlat(self): - self.dst_ip6_pfx = '1:2:3::' - self.dst_ip6_pfx_n = socket.inet_pton(socket.AF_INET6, - self.dst_ip6_pfx) - self.dst_ip6_pfx_len = 96 - self.src_ip6_pfx = '4:5:6::' - self.src_ip6_pfx_n = socket.inet_pton(socket.AF_INET6, - self.src_ip6_pfx) - self.src_ip6_pfx_len = 96 - self.vapi.map_add_domain(self.dst_ip6_pfx_n, self.dst_ip6_pfx_len, - self.src_ip6_pfx_n, self.src_ip6_pfx_len, - '\x00\x00\x00\x00', 0) - - @unittest.skip('Temporary disabled') - def test_464xlat_ce(self): - """ Test 464XLAT CE with NAT44 """ - - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.out2in_dpo) - - self.configure_xlat() - - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags, is_add=1) - self.vapi.nat44_add_del_address_range(first_ip_address=self.nat_addr_n, - last_ip_address=self.nat_addr_n, - vrf_id=0xFFFFFFFF, is_add=1) - - out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx, - self.dst_ip6_pfx_len) - out_dst_ip6 = self.compose_ip6(self.nat_addr, self.src_ip6_pfx, - self.src_ip6_pfx_len) - - try: - pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4) - 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_ip6(capture, nat_ip=out_dst_ip6, - dst_ip=out_src_ip6) - - pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6, - out_dst_ip6) - 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) - finally: - self.vapi.nat44_interface_add_del_feature( - sw_if_index=self.pg0.sw_if_index, - flags=flags) - self.vapi.nat44_add_del_address_range( - first_ip_address=self.nat_addr_n, - last_ip_address=self.nat_addr_n, - vrf_id=0xFFFFFFFF) - - @unittest.skip('Temporary disabled') - def test_464xlat_ce_no_nat(self): - """ Test 464XLAT CE without NAT44 """ - - self.configure_xlat() - - out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx, - self.dst_ip6_pfx_len) - out_dst_ip6 = self.compose_ip6(self.pg0.remote_ip4, self.src_ip6_pfx, - self.src_ip6_pfx_len) - - pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4) - 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_ip6(capture, dst_ip=out_src_ip6, - nat_ip=out_dst_ip6, same_port=True) - - pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6, out_dst_ip6) - 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) - - -if __name__ == '__main__': - unittest.main(testRunner=VppTestRunner) diff --git a/src/plugins/nat/test/test_nat44.py b/src/plugins/nat/test/test_nat44.py new file mode 100644 index 00000000000..d635abf9825 --- /dev/null +++ b/src/plugins/nat/test/test_nat44.py @@ -0,0 +1,7276 @@ +#!/usr/bin/env python3 + +import ipaddress +import random +import socket +import struct +import unittest +from io import BytesIO +from time import sleep + +import scapy.compat +from framework import VppTestCase, VppTestRunner, running_extended_tests +from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder +from scapy.all import bind_layers, Packet, ByteEnumField, ShortField, \ + IPField, IntField, LongField, XByteField, FlagsField, FieldLenField, \ + PacketListField +from scapy.data import IP_PROTOS +from scapy.layers.inet import IP, TCP, UDP, ICMP +from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror +from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment +from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply, \ + ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, fragment6 +from scapy.layers.l2 import Ether, ARP, GRE +from scapy.packet import Raw +from syslog_rfc5424_parser import SyslogMessage, ParseError +from syslog_rfc5424_parser.constants import SyslogSeverity +from util import ip4_range +from util import ppc, ppp +from vpp_acl import AclRule, VppAcl, VppAclInterface +from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_neighbor import VppNeighbor +from vpp_papi import VppEnum + + +# NAT HA protocol event data +class Event(Packet): + name = "Event" + fields_desc = [ByteEnumField("event_type", None, + {1: "add", 2: "del", 3: "refresh"}), + ByteEnumField("protocol", None, + {0: "other", 1: "udp", 2: "tcp", 3: "icmp"}), + ShortField("flags", 0), + IPField("in_addr", None), + IPField("out_addr", None), + ShortField("in_port", None), + ShortField("out_port", None), + IPField("eh_addr", None), + IPField("ehn_addr", None), + ShortField("eh_port", None), + ShortField("ehn_port", None), + IntField("fib_index", None), + IntField("total_pkts", 0), + LongField("total_bytes", 0)] + + def extract_padding(self, s): + return "", s + + +# NAT HA protocol header +class HANATStateSync(Packet): + name = "HA NAT state sync" + fields_desc = [XByteField("version", 1), + FlagsField("flags", 0, 8, ['ACK']), + FieldLenField("count", None, count_of="events"), + IntField("sequence_number", 1), + IntField("thread_index", 0), + PacketListField("events", [], Event, + count_from=lambda pkt: pkt.count)] + + +class MethodHolder(VppTestCase): + """ NAT create capture and verify method holder """ + + @property + def config_flags(self): + return VppEnum.vl_api_nat_config_flags_t + + @property + def nat44_config_flags(self): + return VppEnum.vl_api_nat44_config_flags_t + + @property + def SYSLOG_SEVERITY(self): + return VppEnum.vl_api_syslog_severity_t + + def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', + local_port=0, external_port=0, vrf_id=0, + is_add=1, external_sw_if_index=0xFFFFFFFF, + proto=0, tag="", flags=0): + """ + Add/delete NAT44 static mapping + + :param local_ip: Local IP address + :param external_ip: External IP address + :param local_port: Local port number (Optional) + :param external_port: External port number (Optional) + :param vrf_id: VRF ID (Default 0) + :param is_add: 1 if add, 0 if delete (Default add) + :param external_sw_if_index: External interface instead of IP address + :param proto: IP protocol (Mandatory if port specified) + :param tag: Opaque string tag + :param flags: NAT configuration flags + """ + + if not (local_port and external_port): + flags |= self.config_flags.NAT_IS_ADDR_ONLY + + self.vapi.nat44_add_del_static_mapping( + is_add=is_add, + local_ip_address=local_ip, + external_ip_address=external_ip, + external_sw_if_index=external_sw_if_index, + local_port=local_port, + external_port=external_port, + vrf_id=vrf_id, protocol=proto, + flags=flags, + tag=tag) + + def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): + """ + Add/delete NAT44 address + + :param ip: IP address + :param is_add: 1 if add, 0 if delete (Default add) + :param twice_nat: twice NAT address for external hosts + """ + flags = self.config_flags.NAT_IS_TWICE_NAT if twice_nat else 0 + self.vapi.nat44_add_del_address_range(first_ip_address=ip, + last_ip_address=ip, + vrf_id=vrf_id, + is_add=is_add, + flags=flags) + + def create_stream_in(self, in_if, out_if, dst_ip=None, ttl=64): + """ + Create packet stream for inside network + + :param in_if: Inside interface + :param out_if: Outside interface + :param dst_ip: Destination address + :param ttl: TTL of generated packets + """ + if dst_ip is None: + dst_ip = out_if.remote_ip4 + + pkts = [] + # TCP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / + TCP(sport=self.tcp_port_in, dport=20)) + pkts.extend([p, p]) + + # UDP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / + UDP(sport=self.udp_port_in, dport=20)) + pkts.append(p) + + # ICMP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + + return pkts + + def compose_ip6(self, ip4, pref, plen): + """ + Compose IPv4-embedded IPv6 addresses + + :param ip4: IPv4 address + :param pref: IPv6 prefix + :param plen: IPv6 prefix length + :returns: IPv4-embedded IPv6 addresses + """ + pref_n = list(socket.inet_pton(socket.AF_INET6, pref)) + ip4_n = list(socket.inet_pton(socket.AF_INET, ip4)) + if plen == 32: + pref_n[4] = ip4_n[0] + pref_n[5] = ip4_n[1] + pref_n[6] = ip4_n[2] + pref_n[7] = ip4_n[3] + elif plen == 40: + pref_n[5] = ip4_n[0] + pref_n[6] = ip4_n[1] + pref_n[7] = ip4_n[2] + pref_n[9] = ip4_n[3] + elif plen == 48: + pref_n[6] = ip4_n[0] + pref_n[7] = ip4_n[1] + pref_n[9] = ip4_n[2] + pref_n[10] = ip4_n[3] + elif plen == 56: + pref_n[7] = ip4_n[0] + pref_n[9] = ip4_n[1] + pref_n[10] = ip4_n[2] + pref_n[11] = ip4_n[3] + elif plen == 64: + pref_n[9] = ip4_n[0] + pref_n[10] = ip4_n[1] + pref_n[11] = ip4_n[2] + pref_n[12] = ip4_n[3] + elif plen == 96: + pref_n[12] = ip4_n[0] + pref_n[13] = ip4_n[1] + pref_n[14] = ip4_n[2] + pref_n[15] = ip4_n[3] + packed_pref_n = b''.join([scapy.compat.chb(x) for x in pref_n]) + return socket.inet_ntop(socket.AF_INET6, packed_pref_n) + + def extract_ip4(self, ip6, plen): + """ + Extract IPv4 address embedded in IPv6 addresses + + :param ip6: IPv6 address + :param plen: IPv6 prefix length + :returns: extracted IPv4 address + """ + ip6_n = list(socket.inet_pton(socket.AF_INET6, ip6)) + ip4_n = [None] * 4 + if plen == 32: + ip4_n[0] = ip6_n[4] + ip4_n[1] = ip6_n[5] + ip4_n[2] = ip6_n[6] + ip4_n[3] = ip6_n[7] + elif plen == 40: + ip4_n[0] = ip6_n[5] + ip4_n[1] = ip6_n[6] + ip4_n[2] = ip6_n[7] + ip4_n[3] = ip6_n[9] + elif plen == 48: + ip4_n[0] = ip6_n[6] + ip4_n[1] = ip6_n[7] + ip4_n[2] = ip6_n[9] + ip4_n[3] = ip6_n[10] + elif plen == 56: + ip4_n[0] = ip6_n[7] + ip4_n[1] = ip6_n[9] + ip4_n[2] = ip6_n[10] + ip4_n[3] = ip6_n[11] + elif plen == 64: + ip4_n[0] = ip6_n[9] + ip4_n[1] = ip6_n[10] + ip4_n[2] = ip6_n[11] + ip4_n[3] = ip6_n[12] + elif plen == 96: + ip4_n[0] = ip6_n[12] + ip4_n[1] = ip6_n[13] + ip4_n[2] = ip6_n[14] + ip4_n[3] = ip6_n[15] + return socket.inet_ntop(socket.AF_INET, ''.join(ip4_n)) + + 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=tcp_port, sport=20)) + pkts.extend([p, 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=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=icmp_id, type='echo-reply')) + pkts.append(p) + + return pkts + + def create_stream_out_ip6(self, out_if, src_ip, dst_ip, hl=64): + """ + Create packet stream for outside network + + :param out_if: Outside interface + :param dst_ip: Destination IP address (Default use global NAT address) + :param hl: HL of generated packets + """ + pkts = [] + # TCP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IPv6(src=src_ip, dst=dst_ip, hlim=hl) / + TCP(dport=self.tcp_port_out, sport=20)) + pkts.append(p) + + # UDP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IPv6(src=src_ip, dst=dst_ip, hlim=hl) / + UDP(dport=self.udp_port_out, sport=20)) + pkts.append(p) + + # ICMP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IPv6(src=src_ip, dst=dst_ip, hlim=hl) / + ICMPv6EchoReply(id=self.icmp_id_out)) + pkts.append(p) + + return pkts + + def verify_capture_out(self, capture, nat_ip=None, same_port=False, + dst_ip=None, is_ip6=False, ignore_port=False): + """ + Verify captured packets on outside network + + :param capture: Captured packets + :param nat_ip: Translated IP address (Default use global NAT address) + :param same_port: Source port number is not translated (Default False) + :param dst_ip: Destination IP address (Default do not verify) + :param is_ip6: If L3 protocol is IPv6 (Default False) + """ + if is_ip6: + IP46 = IPv6 + ICMP46 = ICMPv6EchoRequest + else: + IP46 = IP + ICMP46 = ICMP + if nat_ip is None: + nat_ip = self.nat_addr + for packet in capture: + try: + if not is_ip6: + self.assert_packet_checksums_valid(packet) + self.assertEqual(packet[IP46].src, nat_ip) + if dst_ip is not None: + self.assertEqual(packet[IP46].dst, dst_ip) + if packet.haslayer(TCP): + if not ignore_port: + if same_port: + self.assertEqual( + packet[TCP].sport, self.tcp_port_in) + else: + self.assertNotEqual( + packet[TCP].sport, self.tcp_port_in) + self.tcp_port_out = packet[TCP].sport + self.assert_packet_checksums_valid(packet) + elif packet.haslayer(UDP): + if not ignore_port: + if same_port: + self.assertEqual( + packet[UDP].sport, self.udp_port_in) + else: + self.assertNotEqual( + packet[UDP].sport, self.udp_port_in) + self.udp_port_out = packet[UDP].sport + else: + if not ignore_port: + if same_port: + self.assertEqual( + packet[ICMP46].id, self.icmp_id_in) + else: + self.assertNotEqual( + packet[ICMP46].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP46].id + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def verify_capture_out_ip6(self, capture, nat_ip, same_port=False, + dst_ip=None): + """ + Verify captured packets on outside network + + :param capture: Captured packets + :param nat_ip: Translated IP address + :param same_port: Source port number is not translated (Default False) + :param dst_ip: Destination IP address (Default do not verify) + """ + return self.verify_capture_out(capture, nat_ip, same_port, dst_ip, + True) + + def verify_capture_in(self, capture, in_if): + """ + Verify captured packets on inside network + + :param capture: Captured packets + :param in_if: Inside interface + """ + for packet in capture: + try: + self.assert_packet_checksums_valid(packet) + self.assertEqual(packet[IP].dst, in_if.remote_ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_in_ip6(self, capture, src_ip, dst_ip): + """ + Verify captured IPv6 packets on inside network + + :param capture: Captured packets + :param src_ip: Source IP + :param dst_ip: Destination IP address + """ + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, src_ip) + self.assertEqual(packet[IPv6].dst, dst_ip) + self.assert_packet_checksums_valid(packet) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + else: + self.assertEqual(packet[ICMPv6EchoReply].id, + self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_no_translation(self, capture, ingress_if, egress_if): + """ + Verify captured packet that don't have to be translated + + :param capture: Captured packets + :param ingress_if: Ingress interface + :param egress_if: Egress interface + """ + for packet in capture: + try: + self.assertEqual(packet[IP].src, ingress_if.remote_ip4) + self.assertEqual(packet[IP].dst, egress_if.remote_ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, self.tcp_port_in) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].sport, self.udp_port_in) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_out_with_icmp_errors(self, capture, src_ip=None, + icmp_type=11): + """ + Verify captured packets with ICMP errors on outside network + + :param capture: Captured packets + :param src_ip: Translated IP address or IP address of VPP + (Default use global NAT address) + :param icmp_type: Type of error ICMP packet + we are expecting (Default 11) + """ + if src_ip is None: + src_ip = self.nat_addr + for packet in capture: + try: + self.assertEqual(packet[IP].src, src_ip) + self.assertEqual(packet.haslayer(ICMP), 1) + icmp = packet[ICMP] + self.assertEqual(icmp.type, icmp_type) + self.assertTrue(icmp.haslayer(IPerror)) + inner_ip = icmp[IPerror] + if inner_ip.haslayer(TCPerror): + self.assertEqual(inner_ip[TCPerror].dport, + self.tcp_port_out) + elif inner_ip.haslayer(UDPerror): + self.assertEqual(inner_ip[UDPerror].dport, + self.udp_port_out) + else: + self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_out) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def verify_capture_in_with_icmp_errors(self, capture, in_if, icmp_type=11): + """ + Verify captured packets with ICMP errors on inside network + + :param capture: Captured packets + :param in_if: Inside interface + :param icmp_type: Type of error ICMP packet + we are expecting (Default 11) + """ + for packet in capture: + try: + self.assertEqual(packet[IP].dst, in_if.remote_ip4) + self.assertEqual(packet.haslayer(ICMP), 1) + icmp = packet[ICMP] + self.assertEqual(icmp.type, icmp_type) + self.assertTrue(icmp.haslayer(IPerror)) + inner_ip = icmp[IPerror] + if inner_ip.haslayer(TCPerror): + self.assertEqual(inner_ip[TCPerror].sport, + self.tcp_port_in) + elif inner_ip.haslayer(UDPerror): + self.assertEqual(inner_ip[UDPerror].sport, + self.udp_port_in) + else: + self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def create_stream_frag(self, src_if, dst, sport, dport, data, + proto=IP_PROTOS.tcp, echo_reply=False): + """ + Create fragmented packet stream + + :param src_if: Source interface + :param dst: Destination IPv4 address + :param sport: Source port + :param dport: Destination port + :param data: Payload data + :param proto: protocol (TCP, UDP, ICMP) + :param echo_reply: use echo_reply if protocol is ICMP + :returns: Fragments + """ + if proto == IP_PROTOS.tcp: + p = (IP(src=src_if.remote_ip4, dst=dst) / + TCP(sport=sport, dport=dport) / + Raw(data)) + p = p.__class__(scapy.compat.raw(p)) + chksum = p[TCP].chksum + proto_header = TCP(sport=sport, dport=dport, chksum=chksum) + elif proto == IP_PROTOS.udp: + proto_header = UDP(sport=sport, dport=dport) + elif proto == IP_PROTOS.icmp: + if not echo_reply: + proto_header = ICMP(id=sport, type='echo-request') + else: + proto_header = ICMP(id=sport, type='echo-reply') + else: + raise Exception("Unsupported protocol") + id = random.randint(0, 65535) + pkts = [] + if proto == IP_PROTOS.tcp: + raw = Raw(data[0:4]) + else: + raw = Raw(data[0:16]) + p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / + IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=0, id=id) / + proto_header / + raw) + pkts.append(p) + if proto == IP_PROTOS.tcp: + raw = Raw(data[4:20]) + else: + raw = Raw(data[16:32]) + p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / + IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=3, id=id, + proto=proto) / + raw) + pkts.append(p) + if proto == IP_PROTOS.tcp: + raw = Raw(data[20:]) + else: + raw = Raw(data[32:]) + p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / + IP(src=src_if.remote_ip4, dst=dst, frag=5, proto=proto, + id=id) / + raw) + pkts.append(p) + return pkts + + def reass_frags_and_verify(self, frags, src, dst): + """ + Reassemble and verify fragmented packet + + :param frags: Captured fragments + :param src: Source IPv4 address to verify + :param dst: Destination IPv4 address to verify + + :returns: Reassembled IPv4 packet + """ + buffer = BytesIO() + for p in frags: + self.assertEqual(p[IP].src, src) + self.assertEqual(p[IP].dst, dst) + self.assert_ip_checksum_valid(p) + buffer.seek(p[IP].frag * 8) + buffer.write(bytes(p[IP].payload)) + ip = IP(src=frags[0][IP].src, dst=frags[0][IP].dst, + proto=frags[0][IP].proto) + if ip.proto == IP_PROTOS.tcp: + p = (ip / TCP(buffer.getvalue())) + self.logger.debug(ppp("Reassembled:", p)) + self.assert_tcp_checksum_valid(p) + elif ip.proto == IP_PROTOS.udp: + p = (ip / UDP(buffer.getvalue()[:8]) / + Raw(buffer.getvalue()[8:])) + elif ip.proto == IP_PROTOS.icmp: + p = (ip / ICMP(buffer.getvalue())) + return p + + def reass_frags_and_verify_ip6(self, frags, src, dst): + """ + Reassemble and verify fragmented packet + + :param frags: Captured fragments + :param src: Source IPv6 address to verify + :param dst: Destination IPv6 address to verify + + :returns: Reassembled IPv6 packet + """ + buffer = BytesIO() + for p in frags: + self.assertEqual(p[IPv6].src, src) + self.assertEqual(p[IPv6].dst, dst) + buffer.seek(p[IPv6ExtHdrFragment].offset * 8) + buffer.write(bytes(p[IPv6ExtHdrFragment].payload)) + ip = IPv6(src=frags[0][IPv6].src, dst=frags[0][IPv6].dst, + nh=frags[0][IPv6ExtHdrFragment].nh) + if ip.nh == IP_PROTOS.tcp: + p = (ip / TCP(buffer.getvalue())) + elif ip.nh == IP_PROTOS.udp: + p = (ip / UDP(buffer.getvalue())) + self.logger.debug(ppp("Reassembled:", p)) + self.assert_packet_checksums_valid(p) + return p + + def initiate_tcp_session(self, in_if, out_if): + """ + Initiates TCP session + + :param in_if: Inside interface + :param out_if: Outside interface + """ + try: + # SYN packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="S")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = out_if.get_capture(1) + p = capture[0] + self.tcp_port_out = p[TCP].sport + + # SYN + ACK packet out->in + p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / + IP(src=out_if.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="SA")) + out_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + in_if.get_capture(1) + + # ACK packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + out_if.get_capture(1) + + except: + self.logger.error("TCP 3 way handshake failed") + raise + + def verify_ipfix_nat44_ses(self, data): + """ + Verify IPFIX NAT44 session create/delete event + + :param data: Decoded IPFIX data records + """ + nat44_ses_create_num = 0 + nat44_ses_delete_num = 0 + self.assertEqual(6, len(data)) + for record in data: + # natEvent + self.assertIn(scapy.compat.orb(record[230]), [4, 5]) + if scapy.compat.orb(record[230]) == 4: + nat44_ses_create_num += 1 + else: + nat44_ses_delete_num += 1 + # sourceIPv4Address + self.assertEqual(self.pg0.remote_ip4, + str(ipaddress.IPv4Address(record[8]))) + # postNATSourceIPv4Address + self.assertEqual(socket.inet_pton(socket.AF_INET, self.nat_addr), + record[225]) + # ingressVRFID + self.assertEqual(struct.pack("!I", 0), record[234]) + # protocolIdentifier/sourceTransportPort + # /postNAPTSourceTransportPort + if IP_PROTOS.icmp == scapy.compat.orb(record[4]): + self.assertEqual(struct.pack("!H", self.icmp_id_in), record[7]) + self.assertEqual(struct.pack("!H", self.icmp_id_out), + record[227]) + elif IP_PROTOS.tcp == scapy.compat.orb(record[4]): + self.assertEqual(struct.pack("!H", self.tcp_port_in), + record[7]) + self.assertEqual(struct.pack("!H", self.tcp_port_out), + record[227]) + elif IP_PROTOS.udp == scapy.compat.orb(record[4]): + self.assertEqual(struct.pack("!H", self.udp_port_in), + record[7]) + self.assertEqual(struct.pack("!H", self.udp_port_out), + record[227]) + else: + self.fail("Invalid protocol") + self.assertEqual(3, nat44_ses_create_num) + self.assertEqual(3, nat44_ses_delete_num) + + def verify_ipfix_addr_exhausted(self, data): + """ + Verify IPFIX NAT addresses event + + :param data: Decoded IPFIX data records + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(scapy.compat.orb(record[230]), 3) + # natPoolID + self.assertEqual(struct.pack("!I", 0), record[283]) + + def verify_ipfix_max_sessions(self, data, limit): + """ + Verify IPFIX maximum session entries exceeded event + + :param data: Decoded IPFIX data records + :param limit: Number of maximum session entries that can be created. + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(scapy.compat.orb(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual(struct.pack("I", 1), record[466]) + # maxSessionEntries + self.assertEqual(struct.pack("I", limit), record[471]) + + def verify_ipfix_max_bibs(self, data, limit): + """ + Verify IPFIX maximum BIB entries exceeded event + + :param data: Decoded IPFIX data records + :param limit: Number of maximum BIB entries that can be created. + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(scapy.compat.orb(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual(struct.pack("I", 2), record[466]) + # maxBIBEntries + self.assertEqual(struct.pack("I", limit), record[472]) + + def verify_no_nat44_user(self): + """ Verify that there is no NAT44 user """ + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 0) + users = self.statistics.get_counter('/nat44/total-users') + self.assertEqual(users[0][0], 0) + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 0) + + def verify_ipfix_max_entries_per_user(self, data, limit, src_addr): + """ + Verify IPFIX maximum entries per user exceeded event + + :param data: Decoded IPFIX data records + :param limit: Number of maximum entries per user + :param src_addr: IPv4 source address + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(scapy.compat.orb(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual(struct.pack("I", 3), record[466]) + # maxEntriesPerUser + self.assertEqual(struct.pack("I", limit), record[473]) + # sourceIPv4Address + self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8]) + + def verify_syslog_apmap(self, data, is_add=True): + message = data.decode('utf-8') + try: + message = SyslogMessage.parse(message) + except ParseError as e: + self.logger.error(e) + raise + else: + self.assertEqual(message.severity, SyslogSeverity.info) + self.assertEqual(message.appname, 'NAT') + self.assertEqual(message.msgid, 'APMADD' if is_add else 'APMDEL') + sd_params = message.sd.get('napmap') + self.assertTrue(sd_params is not None) + self.assertEqual(sd_params.get('IATYP'), 'IPv4') + self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4) + self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in) + self.assertEqual(sd_params.get('XATYP'), 'IPv4') + self.assertEqual(sd_params.get('XSADDR'), self.nat_addr) + self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out) + self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp) + self.assertTrue(sd_params.get('SSUBIX') is not None) + self.assertEqual(sd_params.get('SVLAN'), '0') + + def verify_syslog_sess(self, data, is_add=True, is_ip6=False): + message = data.decode('utf-8') + try: + message = SyslogMessage.parse(message) + except ParseError as e: + self.logger.error(e) + raise + else: + self.assertEqual(message.severity, SyslogSeverity.info) + self.assertEqual(message.appname, 'NAT') + self.assertEqual(message.msgid, 'SADD' if is_add else 'SDEL') + sd_params = message.sd.get('nsess') + self.assertTrue(sd_params is not None) + if is_ip6: + self.assertEqual(sd_params.get('IATYP'), 'IPv6') + self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip6) + else: + self.assertEqual(sd_params.get('IATYP'), 'IPv4') + self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4) + self.assertTrue(sd_params.get('SSUBIX') is not None) + self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in) + self.assertEqual(sd_params.get('XATYP'), 'IPv4') + self.assertEqual(sd_params.get('XSADDR'), self.nat_addr) + self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out) + self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp) + self.assertEqual(sd_params.get('SVLAN'), '0') + self.assertEqual(sd_params.get('XDADDR'), self.pg1.remote_ip4) + self.assertEqual(sd_params.get('XDPORT'), + "%d" % self.tcp_external_port) + + def verify_mss_value(self, pkt, mss): + """ + Verify TCP MSS value + + :param pkt: + :param mss: + """ + if not pkt.haslayer(IP) or not pkt.haslayer(TCP): + raise TypeError("Not a TCP/IP packet") + + for option in pkt[TCP].options: + if option[0] == 'MSS': + self.assertEqual(option[1], mss) + self.assert_tcp_checksum_valid(pkt) + + @staticmethod + def proto2layer(proto): + if proto == IP_PROTOS.tcp: + return TCP + elif proto == IP_PROTOS.udp: + return UDP + elif proto == IP_PROTOS.icmp: + return ICMP + else: + raise Exception("Unsupported protocol") + + def frag_in_order(self, proto=IP_PROTOS.tcp, dont_translate=False, + ignore_port=False): + layer = self.proto2layer(proto) + + if proto == IP_PROTOS.tcp: + data = b"A" * 4 + b"B" * 16 + b"C" * 3 + else: + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + self.port_in = random.randint(1025, 65535) + + # in2out + pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4, + self.port_in, 20, data, proto) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg1.get_capture(len(pkts)) + if not dont_translate: + p = self.reass_frags_and_verify(frags, + self.nat_addr, + self.pg1.remote_ip4) + else: + p = self.reass_frags_and_verify(frags, + self.pg0.remote_ip4, + self.pg1.remote_ip4) + if proto != IP_PROTOS.icmp: + if not dont_translate: + self.assertEqual(p[layer].dport, 20) + if not ignore_port: + self.assertNotEqual(p[layer].sport, self.port_in) + else: + self.assertEqual(p[layer].sport, self.port_in) + else: + if not ignore_port: + if not dont_translate: + self.assertNotEqual(p[layer].id, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + # out2in + if not dont_translate: + dst_addr = self.nat_addr + else: + dst_addr = self.pg0.remote_ip4 + if proto != IP_PROTOS.icmp: + sport = 20 + dport = p[layer].sport + else: + sport = p[layer].id + dport = 0 + pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, data, + proto, echo_reply=True) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.pg1.remote_ip4, + self.pg0.remote_ip4) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].sport, 20) + self.assertEqual(p[layer].dport, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + def frag_in_order_in_plus_out(self, proto=IP_PROTOS.tcp): + layer = self.proto2layer(proto) + + if proto == IP_PROTOS.tcp: + data = b"A" * 4 + b"B" * 16 + b"C" * 3 + else: + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + self.port_in = random.randint(1025, 65535) + + for i in range(2): + # out2in + pkts = self.create_stream_frag(self.pg0, self.server_out_addr, + self.port_in, self.server_out_port, + data, proto) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg1.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.pg0.remote_ip4, + self.server_in_addr) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].sport, self.port_in) + self.assertEqual(p[layer].dport, self.server_in_port) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + # in2out + if proto != IP_PROTOS.icmp: + pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, + self.server_in_port, + p[layer].sport, data, proto) + else: + pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, + p[layer].id, 0, data, proto, + echo_reply=True) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.server_out_addr, + self.pg0.remote_ip4) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].sport, self.server_out_port) + self.assertEqual(p[layer].dport, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + def reass_hairpinning(self, proto=IP_PROTOS.tcp, ignore_port=False): + layer = self.proto2layer(proto) + + if proto == IP_PROTOS.tcp: + data = b"A" * 4 + b"B" * 16 + b"C" * 3 + else: + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + + # send packet from host to server + pkts = self.create_stream_frag(self.pg0, + self.nat_addr, + self.host_in_port, + self.server_out_port, + data, + proto) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.nat_addr, + self.server.ip4) + if proto != IP_PROTOS.icmp: + if not ignore_port: + self.assertNotEqual(p[layer].sport, self.host_in_port) + self.assertEqual(p[layer].dport, self.server_in_port) + else: + if not ignore_port: + self.assertNotEqual(p[layer].id, self.host_in_port) + self.assertEqual(data, p[Raw].load) + + def frag_out_of_order(self, proto=IP_PROTOS.tcp, dont_translate=False, + ignore_port=False): + layer = self.proto2layer(proto) + + if proto == IP_PROTOS.tcp: + data = b"A" * 4 + b"B" * 16 + b"C" * 3 + else: + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + self.port_in = random.randint(1025, 65535) + + for i in range(2): + # in2out + pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4, + self.port_in, 20, data, proto) + pkts.reverse() + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg1.get_capture(len(pkts)) + if not dont_translate: + p = self.reass_frags_and_verify(frags, + self.nat_addr, + self.pg1.remote_ip4) + else: + p = self.reass_frags_and_verify(frags, + self.pg0.remote_ip4, + self.pg1.remote_ip4) + if proto != IP_PROTOS.icmp: + if not dont_translate: + self.assertEqual(p[layer].dport, 20) + if not ignore_port: + self.assertNotEqual(p[layer].sport, self.port_in) + else: + self.assertEqual(p[layer].sport, self.port_in) + else: + if not ignore_port: + if not dont_translate: + self.assertNotEqual(p[layer].id, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + # out2in + if not dont_translate: + dst_addr = self.nat_addr + else: + dst_addr = self.pg0.remote_ip4 + if proto != IP_PROTOS.icmp: + sport = 20 + dport = p[layer].sport + else: + sport = p[layer].id + dport = 0 + pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, + data, proto, echo_reply=True) + pkts.reverse() + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.pg1.remote_ip4, + self.pg0.remote_ip4) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].sport, 20) + self.assertEqual(p[layer].dport, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + def frag_out_of_order_in_plus_out(self, proto=IP_PROTOS.tcp): + layer = self.proto2layer(proto) + + if proto == IP_PROTOS.tcp: + data = b"A" * 4 + b"B" * 16 + b"C" * 3 + else: + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + self.port_in = random.randint(1025, 65535) + + for i in range(2): + # out2in + pkts = self.create_stream_frag(self.pg0, self.server_out_addr, + self.port_in, self.server_out_port, + data, proto) + pkts.reverse() + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg1.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.pg0.remote_ip4, + self.server_in_addr) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].dport, self.server_in_port) + self.assertEqual(p[layer].sport, self.port_in) + self.assertEqual(p[layer].dport, self.server_in_port) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + # in2out + if proto != IP_PROTOS.icmp: + pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, + self.server_in_port, + p[layer].sport, data, proto) + else: + pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4, + p[layer].id, 0, data, proto, + echo_reply=True) + pkts.reverse() + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.server_out_addr, + self.pg0.remote_ip4) + if proto != IP_PROTOS.icmp: + self.assertEqual(p[layer].sport, self.server_out_port) + self.assertEqual(p[layer].dport, self.port_in) + else: + self.assertEqual(p[layer].id, self.port_in) + self.assertEqual(data, p[Raw].load) + + +class TestNATMisc(MethodHolder): + """ NAT misc Test Cases """ + + max_translations = 10240 + max_users = 10240 + + def setUp(self): + super(TestNATMisc, self).setUp() + self.vapi.nat44_plugin_enable_disable( + sessions=self.max_translations, + users=self.max_users, enable=1) + + def tearDown(self): + super(TestNATMisc, self).tearDown() + if not self.vpp_dead: + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def test_show_max_translations(self): + """ API test - max translations per thread """ + nat_config = self.vapi.nat_show_config_2() + self.assertEqual(self.max_translations, + nat_config.max_translations_per_thread) + + +class TestNAT44(MethodHolder): + """ NAT44 Test Cases """ + + max_translations = 10240 + max_users = 10240 + + @classmethod + def setUpClass(cls): + super(TestNAT44, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.ipfix_src_port = 4739 + cls.ipfix_domain_id = 1 + cls.tcp_external_port = 80 + cls.udp_external_port = 69 + + cls.create_pg_interfaces(range(10)) + cls.interfaces = list(cls.pg_interfaces[0:4]) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.pg0.generate_remote_hosts(3) + cls.pg0.configure_ipv4_neighbors() + + cls.pg1.generate_remote_hosts(1) + cls.pg1.configure_ipv4_neighbors() + + cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7])) + cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 10}) + cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 20}) + + cls.pg4._local_ip4 = "172.16.255.1" + cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2" + cls.pg4.set_table_ip4(10) + cls.pg5._local_ip4 = "172.17.255.3" + cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4" + cls.pg5.set_table_ip4(10) + cls.pg6._local_ip4 = "172.16.255.1" + cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2" + cls.pg6.set_table_ip4(20) + for i in cls.overlapping_interfaces: + i.config_ip4() + i.admin_up() + i.resolve_arp() + + cls.pg7.admin_up() + cls.pg8.admin_up() + + cls.pg9.generate_remote_hosts(2) + cls.pg9.config_ip4() + cls.vapi.sw_interface_add_del_address( + sw_if_index=cls.pg9.sw_if_index, + prefix="10.0.0.1/24") + + cls.pg9.admin_up() + cls.pg9.resolve_arp() + cls.pg9._remote_hosts[1]._ip4 = cls.pg9._remote_hosts[0]._ip4 + cls.pg4._remote_ip4 = cls.pg9._remote_hosts[0]._ip4 = "10.0.0.2" + cls.pg9.resolve_arp() + + def setUp(self): + super(TestNAT44, self).setUp() + self.vapi.nat44_plugin_enable_disable( + sessions=self.max_translations, + users=self.max_users, enable=1) + + @classmethod + def tearDownClass(cls): + super(TestNAT44, cls).tearDownClass() + + def tearDown(self): + super(TestNAT44, self).tearDown() + if not self.vpp_dead: + self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=0) + self.ipfix_src_port = 4739 + self.ipfix_domain_id = 1 + + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def test_clear_sessions(self): + """ NAT44 session clearing test """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + nat_config = self.vapi.nat_show_config() + self.assertEqual(0, nat_config.endpoint_dependent) + + 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) + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertTrue(sessions[0][0] > 0) + self.logger.info("sessions before clearing: %s" % sessions[0][0]) + + self.vapi.cli("clear nat44 sessions") + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 0) + self.logger.info("sessions after clearing: %s" % sessions[0][0]) + + def test_dynamic(self): + """ NAT44 dynamic translation test """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # in2out + tcpn = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] + icmpn = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] + drops = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] + + 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) + + if_idx = self.pg0.sw_if_index + cnt = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + # out2in + tcpn = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] + icmpn = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] + drops = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] + + 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) + + if_idx = self.pg1.sw_if_index + cnt = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + users = self.statistics.get_counter('/nat44/total-users') + self.assertEqual(users[0][0], 1) + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 3) + + def test_dynamic_icmp_errors_in2out_ttl_1(self): + """ NAT44 handling of client packets with TTL=1 """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # Client side - generate traffic + pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - verify ICMP type 11 packets + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_with_icmp_errors(capture, self.pg0) + + def test_dynamic_icmp_errors_out2in_ttl_1(self): + """ NAT44 handling of server packets with TTL=1 """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # Client side - create sessions + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - generate traffic + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + pkts = self.create_stream_out(self.pg1, ttl=1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - verify ICMP type 11 packets + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out_with_icmp_errors(capture, + src_ip=self.pg1.local_ip4) + + def test_dynamic_icmp_errors_in2out_ttl_2(self): + """ NAT44 handling of error responses to client packets with TTL=2 """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # Client side - generate traffic + pkts = self.create_stream_in(self.pg0, self.pg1, ttl=2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - simulate ICMP type 11 response + capture = self.pg1.get_capture(len(pkts)) + pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=11) / packet[IP] for packet in capture] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - verify ICMP type 11 packets + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_with_icmp_errors(capture, self.pg0) + + def test_dynamic_icmp_errors_out2in_ttl_2(self): + """ NAT44 handling of error responses to server packets with TTL=2 """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # Client side - create sessions + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - generate traffic + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + pkts = self.create_stream_out(self.pg1, ttl=2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - simulate ICMP type 11 response + capture = self.pg0.get_capture(len(pkts)) + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + ICMP(type=11) / packet[IP] for packet in capture] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - verify ICMP type 11 packets + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out_with_icmp_errors(capture) + + def test_ping_out_interface_from_outside(self): + """ Ping NAT44 out interface from outside network """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / + ICMP(id=self.icmp_id_out, type='echo-request')) + pkts = [p] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + packet = capture[0] + try: + self.assertEqual(packet[IP].src, self.pg1.local_ip4) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.assertEqual(packet[ICMP].type, 0) # echo reply + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def test_ping_internal_host_from_outside(self): + """ Ping internal host from outside network """ + + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # out2in + pkt = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr, ttl=64) / + ICMP(id=self.icmp_id_out, type='echo-request')) + self.pg1.add_stream(pkt) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_capture_in(capture, self.pg0) + self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) + + # in2out + pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + ICMP(id=self.icmp_id_in, type='echo-reply')) + self.pg0.add_stream(pkt) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.verify_capture_out(capture, same_port=True) + self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) + + def test_forwarding(self): + """ NAT44 forwarding test """ + + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=1) + + real_ip = self.pg0.remote_ip4 + alias_ip = self.nat_addr + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping(is_add=1, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + try: + # 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) + + # 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(enable=0) + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping( + is_add=0, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + def test_static_in(self): + """ 1:1 NAT initialized from inside network """ + + nat_ip = "10.0.0.10" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + sm = self.vapi.nat44_static_mapping_dump() + self.assertEqual(len(sm), 1) + self.assertEqual(sm[0].tag, '') + self.assertEqual(sm[0].protocol, 0) + self.assertEqual(sm[0].local_port, 0) + self.assertEqual(sm[0].external_port, 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, nat_ip, True) + + # out2in + pkts = self.create_stream_out(self.pg1, nat_ip) + 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_static_out(self): + """ 1:1 NAT initialized from outside network """ + + nat_ip = "10.0.0.20" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + tag = "testTAG" + + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip, tag=tag) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + sm = self.vapi.nat44_static_mapping_dump() + self.assertEqual(len(sm), 1) + self.assertEqual(sm[0].tag, tag) + + # out2in + pkts = self.create_stream_out(self.pg1, nat_ip) + 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) + + # 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, nat_ip, True) + + def test_static_with_port_in(self): + """ 1:1 NAPT initialized from inside network """ + + self.tcp_port_out = 3606 + self.udp_port_out = 3607 + self.icmp_id_out = 3608 + + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # 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_static_with_port_out(self): + """ 1:1 NAPT initialized from outside network """ + + self.tcp_port_out = 30606 + self.udp_port_out = 30607 + self.icmp_id_out = 30608 + + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # 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) + + # 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) + + def test_static_vrf_aware(self): + """ 1:1 NAT VRF awareness """ + + nat_ip1 = "10.0.0.30" + nat_ip2 = "10.0.0.40" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_static_mapping(self.pg4.remote_ip4, nat_ip1, + vrf_id=10) + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip2, + vrf_id=10) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg3.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg4.sw_if_index, + flags=flags, is_add=1) + + # inside interface VRF match NAT44 static mapping VRF + 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_ip1, True) + + # inside interface VRF don't match NAT44 static mapping VRF (packets + # are dropped) + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg3.assert_nothing_captured() + + def test_dynamic_to_static(self): + """ Switch from dynamic translation to 1:1NAT """ + nat_ip = "10.0.0.10" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # dynamic + 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) + + # 1:1NAT + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions), 0) + 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, True) + + def test_identity_nat(self): + """ Identity NAT """ + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_identity_mapping( + ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, + flags=flags, is_add=1) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=12345, dport=56789)) + self.pg1.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.dst, self.pg0.remote_ip4) + self.assertEqual(ip.src, self.pg1.remote_ip4) + self.assertEqual(tcp.dport, 56789) + self.assertEqual(tcp.sport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions), 0) + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_identity_mapping( + ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, + flags=flags, vrf_id=1, is_add=1) + identity_mappings = self.vapi.nat44_identity_mapping_dump() + self.assertEqual(len(identity_mappings), 2) + + def test_multiple_inside_interfaces(self): + """ NAT44 multiple non-overlapping address space inside interfaces """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg3.sw_if_index, + is_add=1) + + # between two NAT44 inside interfaces (no translation) + 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_no_translation(capture, self.pg0, self.pg1) + + # from NAT44 inside to interface without NAT44 feature (no translation) + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg0, self.pg2) + + # in2out 1st interface + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.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) + + # out2in 1st interface + pkts = self.create_stream_out(self.pg3) + self.pg3.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) + + # in2out 2nd interface + pkts = self.create_stream_in(self.pg1, self.pg3) + self.pg1.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) + + # out2in 2nd interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg1) + + def test_inside_overlapping_interfaces(self): + """ NAT44 multiple inside interfaces with overlapping address space """ + + static_nat_ip = "10.0.0.10" + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg3.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg4.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg5.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg6.sw_if_index, + flags=flags, is_add=1) + self.nat44_add_static_mapping(self.pg6.remote_ip4, static_nat_ip, + vrf_id=20) + + # between NAT44 inside interfaces with same VRF (no translation) + pkts = self.create_stream_in(self.pg4, self.pg5) + self.pg4.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg4, self.pg5) + + # between NAT44 inside interfaces with different VRF (hairpinning) + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=self.pg4.remote_ip4, dst=static_nat_ip) / + TCP(sport=1234, dport=5678)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, self.pg6.remote_ip4) + self.assertNotEqual(tcp.sport, 1234) + self.assertEqual(tcp.dport, 5678) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # in2out 1st interface + 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) + + # out2in 1st interface + pkts = self.create_stream_out(self.pg3) + 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 2nd interface + pkts = self.create_stream_in(self.pg5, self.pg3) + self.pg5.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) + + # out2in 2nd interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg5) + + # pg5 session dump + addresses = self.vapi.nat44_address_dump() + self.assertEqual(len(addresses), 1) + sessions = self.vapi.nat44_user_session_dump(self.pg5.remote_ip4, 10) + self.assertEqual(len(sessions), 3) + for session in sessions: + self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertEqual(str(session.inside_ip_address), + self.pg5.remote_ip4) + self.assertEqual(session.outside_ip_address, + addresses[0].ip_address) + self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp) + self.assertEqual(sessions[1].protocol, IP_PROTOS.udp) + self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp) + self.assertEqual(sessions[0].inside_port, self.tcp_port_in) + self.assertEqual(sessions[1].inside_port, self.udp_port_in) + self.assertEqual(sessions[2].inside_port, self.icmp_id_in) + self.assertEqual(sessions[0].outside_port, self.tcp_port_out) + self.assertEqual(sessions[1].outside_port, self.udp_port_out) + self.assertEqual(sessions[2].outside_port, self.icmp_id_out) + + # in2out 3rd interface + 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, static_nat_ip, True) + + # out2in 3rd interface + pkts = self.create_stream_out(self.pg3, static_nat_ip) + 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) + + # general user and session dump verifications + users = self.vapi.nat44_user_dump() + self.assertGreaterEqual(len(users), 3) + addresses = self.vapi.nat44_address_dump() + self.assertEqual(len(addresses), 1) + for user in users: + sessions = self.vapi.nat44_user_session_dump(user.ip_address, + user.vrf_id) + for session in sessions: + self.assertEqual(user.ip_address, session.inside_ip_address) + self.assertTrue(session.total_bytes > session.total_pkts > 0) + self.assertTrue(session.protocol in + [IP_PROTOS.tcp, IP_PROTOS.udp, + IP_PROTOS.icmp]) + self.assertFalse(session.flags & + self.config_flags.NAT_IS_EXT_HOST_VALID) + + # pg4 session dump + sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4, 10) + self.assertGreaterEqual(len(sessions), 4) + for session in sessions: + self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertEqual(str(session.inside_ip_address), + self.pg4.remote_ip4) + self.assertEqual(session.outside_ip_address, + addresses[0].ip_address) + + # pg6 session dump + sessions = self.vapi.nat44_user_session_dump(self.pg6.remote_ip4, 20) + self.assertGreaterEqual(len(sessions), 3) + for session in sessions: + self.assertTrue(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertEqual(str(session.inside_ip_address), + self.pg6.remote_ip4) + self.assertEqual(str(session.outside_ip_address), + static_nat_ip) + self.assertTrue(session.inside_port in + [self.tcp_port_in, self.udp_port_in, + self.icmp_id_in]) + + def test_hairpinning(self): + """ NAT44 hairpinning - 1:1 NAPT """ + + 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.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, self.nat_addr, + server_in_port, server_out_port, + proto=IP_PROTOS.tcp) + + cnt = self.statistics.get_counter('/nat44/hairpinning')[0] + # send packet from host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=self.nat_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.nat_addr) + self.assertEqual(ip.dst, server.ip4) + self.assertNotEqual(tcp.sport, host_in_port) + self.assertEqual(tcp.dport, server_in_port) + self.assert_packet_checksums_valid(p) + host_out_port = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + after = self.statistics.get_counter('/nat44/hairpinning')[0] + if_idx = self.pg0.sw_if_index + self.assertEqual(after[if_idx] - cnt[if_idx], 1) + + # send reply from server to host + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.nat_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.nat_addr) + self.assertEqual(ip.dst, host.ip4) + self.assertEqual(tcp.sport, server_out_port) + self.assertEqual(tcp.dport, host_in_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + after = self.statistics.get_counter('/nat44/hairpinning')[0] + if_idx = self.pg0.sw_if_index + self.assertEqual(after[if_idx] - cnt[if_idx], 2) + + def test_hairpinning2(self): + """ NAT44 hairpinning - 1:1 NAT""" + + server1_nat_ip = "10.0.0.10" + server2_nat_ip = "10.0.0.11" + host = self.pg0.remote_hosts[0] + server1 = self.pg0.remote_hosts[1] + server2 = self.pg0.remote_hosts[2] + server_tcp_port = 22 + server_udp_port = 20 + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # add static mapping for servers + self.nat44_add_static_mapping(server1.ip4, server1_nat_ip) + self.nat44_add_static_mapping(server2.ip4, server2_nat_ip) + + # host to server1 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + TCP(sport=self.tcp_port_in, dport=server_tcp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + UDP(sport=self.udp_port_in, dport=server_udp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, server1.ip4) + if packet.haslayer(TCP): + self.assertNotEqual(packet[TCP].sport, self.tcp_port_in) + self.assertEqual(packet[TCP].dport, server_tcp_port) + self.tcp_port_out = packet[TCP].sport + self.assert_packet_checksums_valid(packet) + elif packet.haslayer(UDP): + self.assertNotEqual(packet[UDP].sport, self.udp_port_in) + self.assertEqual(packet[UDP].dport, server_udp_port) + self.udp_port_out = packet[UDP].sport + else: + self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP].id + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server1 to host + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + TCP(sport=server_tcp_port, dport=self.tcp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + UDP(sport=server_udp_port, dport=self.udp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + ICMP(id=self.icmp_id_out, type='echo-reply')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server1_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.assertEqual(packet[TCP].sport, server_tcp_port) + self.assert_packet_checksums_valid(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.assertEqual(packet[UDP].sport, server_udp_port) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server2 to server1 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + TCP(sport=self.tcp_port_in, dport=server_tcp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + UDP(sport=self.udp_port_in, dport=server_udp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server2_nat_ip) + self.assertEqual(packet[IP].dst, server1.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, self.tcp_port_in) + self.assertEqual(packet[TCP].dport, server_tcp_port) + self.tcp_port_out = packet[TCP].sport + self.assert_packet_checksums_valid(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].sport, self.udp_port_in) + self.assertEqual(packet[UDP].dport, server_udp_port) + self.udp_port_out = packet[UDP].sport + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP].id + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server1 to server2 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + TCP(sport=server_tcp_port, dport=self.tcp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + UDP(sport=server_udp_port, dport=self.udp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + ICMP(id=self.icmp_id_out, type='echo-reply')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server1_nat_ip) + self.assertEqual(packet[IP].dst, server2.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.assertEqual(packet[TCP].sport, server_tcp_port) + self.assert_packet_checksums_valid(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.assertEqual(packet[UDP].sport, server_udp_port) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_interface_addr(self): + """ Acquire NAT44 addresses from interface """ + self.vapi.nat44_add_del_interface_addr( + is_add=1, + sw_if_index=self.pg7.sw_if_index) + + # no address in NAT pool + addresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(addresses)) + + # configure interface address and check NAT address pool + self.pg7.config_ip4() + addresses = self.vapi.nat44_address_dump() + self.assertEqual(1, len(addresses)) + self.assertEqual(str(addresses[0].ip_address), self.pg7.local_ip4) + + # remove interface address and check NAT address pool + self.pg7.unconfig_ip4() + addresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(addresses)) + + def test_interface_addr_static_mapping(self): + """ Static mapping with addresses from interface """ + tag = "testTAG" + + self.vapi.nat44_add_del_interface_addr( + is_add=1, + sw_if_index=self.pg7.sw_if_index) + self.nat44_add_static_mapping( + '1.2.3.4', + external_sw_if_index=self.pg7.sw_if_index, + tag=tag) + + # static mappings with external interface + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(1, len(static_mappings)) + self.assertEqual(self.pg7.sw_if_index, + static_mappings[0].external_sw_if_index) + self.assertEqual(static_mappings[0].tag, tag) + + # configure interface address and check static mappings + self.pg7.config_ip4() + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(2, len(static_mappings)) + resolved = False + for sm in static_mappings: + if sm.external_sw_if_index == 0xFFFFFFFF: + self.assertEqual(str(sm.external_ip_address), + self.pg7.local_ip4) + self.assertEqual(sm.tag, tag) + resolved = True + self.assertTrue(resolved) + + # remove interface address and check static mappings + self.pg7.unconfig_ip4() + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(1, len(static_mappings)) + self.assertEqual(self.pg7.sw_if_index, + static_mappings[0].external_sw_if_index) + self.assertEqual(static_mappings[0].tag, tag) + + # configure interface address again and check static mappings + self.pg7.config_ip4() + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(2, len(static_mappings)) + resolved = False + for sm in static_mappings: + if sm.external_sw_if_index == 0xFFFFFFFF: + self.assertEqual(str(sm.external_ip_address), + self.pg7.local_ip4) + self.assertEqual(sm.tag, tag) + resolved = True + self.assertTrue(resolved) + + # remove static mapping + self.nat44_add_static_mapping( + '1.2.3.4', + external_sw_if_index=self.pg7.sw_if_index, + tag=tag, + is_add=0) + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(0, len(static_mappings)) + + def test_interface_addr_identity_nat(self): + """ Identity NAT with addresses from interface """ + + port = 53053 + self.vapi.nat44_add_del_interface_addr( + is_add=1, + sw_if_index=self.pg7.sw_if_index) + self.vapi.nat44_add_del_identity_mapping( + ip_address=b'0', + sw_if_index=self.pg7.sw_if_index, + port=port, + protocol=IP_PROTOS.tcp, + is_add=1) + + # identity mappings with external interface + identity_mappings = self.vapi.nat44_identity_mapping_dump() + self.assertEqual(1, len(identity_mappings)) + self.assertEqual(self.pg7.sw_if_index, + identity_mappings[0].sw_if_index) + + # configure interface address and check identity mappings + self.pg7.config_ip4() + identity_mappings = self.vapi.nat44_identity_mapping_dump() + resolved = False + self.assertEqual(2, len(identity_mappings)) + for sm in identity_mappings: + if sm.sw_if_index == 0xFFFFFFFF: + self.assertEqual(str(identity_mappings[0].ip_address), + self.pg7.local_ip4) + self.assertEqual(port, identity_mappings[0].port) + self.assertEqual(IP_PROTOS.tcp, identity_mappings[0].protocol) + resolved = True + self.assertTrue(resolved) + + # remove interface address and check identity mappings + self.pg7.unconfig_ip4() + identity_mappings = self.vapi.nat44_identity_mapping_dump() + self.assertEqual(1, len(identity_mappings)) + self.assertEqual(self.pg7.sw_if_index, + identity_mappings[0].sw_if_index) + + def test_ipfix_nat44_sess(self): + """ IPFIX logging NAT44 session created/deleted """ + self.ipfix_domain_id = 10 + self.ipfix_src_port = 20202 + collector_port = 30303 + bind_layers(UDP, IPFIX, dport=30303) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, + src_address=self.pg3.local_ip4, + path_mtu=512, + template_interval=10, + collector_port=collector_port) + self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) + + 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) + self.nat44_add_address(self.nat_addr, is_add=0) + self.vapi.ipfix_flush() + capture = self.pg3.get_capture(7) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, collector_port) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_nat44_ses(data) + + def test_ipfix_addr_exhausted(self): + """ IPFIX logging NAT addresses exhausted """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, + src_address=self.pg3.local_ip4, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=3025)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sleep(1) + self.vapi.ipfix_flush() + capture = self.pg3.get_capture(7) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, 4739) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_addr_exhausted(data) + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_ipfix_max_sessions(self): + """ IPFIX logging maximum session entries exceeded """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + max_sessions = self.max_translations + + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + TCP(sport=1025)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + self.pg1.get_capture(max_sessions) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, + src_address=self.pg3.local_ip4, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=1025)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sleep(1) + self.vapi.ipfix_flush() + capture = self.pg3.get_capture(7) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, 4739) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_max_sessions(data, max_sessions) + + def test_syslog_apmap(self): + """ Test syslog address and port mapping creation and deletion """ + self.vapi.syslog_set_filter( + self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO) + self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=20)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.tcp_port_out = capture[0][TCP].sport + capture = self.pg3.get_capture(1) + self.verify_syslog_apmap(capture[0][Raw].load) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.nat44_add_address(self.nat_addr, is_add=0) + capture = self.pg3.get_capture(1) + self.verify_syslog_apmap(capture[0][Raw].load, False) + + def test_pool_addr_fib(self): + """ NAT44 add pool addresses to FIB """ + static_addr = '10.0.0.10' + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr) + + # NAT44 address + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.assertTrue(capture[0].haslayer(ARP)) + self.assertTrue(capture[0][ARP].op, ARP.is_at) + + # 1:1 NAT address + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=static_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.assertTrue(capture[0].haslayer(ARP)) + self.assertTrue(capture[0][ARP].op, ARP.is_at) + + # send ARP to non-NAT44 interface + p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac)) + self.pg2.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + # remove addresses and verify + self.nat44_add_address(self.nat_addr, is_add=0) + self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr, + is_add=0) + + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=static_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + def test_vrf_mode(self): + """ NAT44 tenant VRF aware address pool mode """ + + vrf_id1 = 1 + vrf_id2 = 2 + nat_ip1 = "10.0.0.10" + nat_ip2 = "10.0.0.11" + + self.pg0.unconfig_ip4() + self.pg1.unconfig_ip4() + self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1}) + self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2}) + self.pg0.set_table_ip4(vrf_id1) + self.pg1.set_table_ip4(vrf_id2) + self.pg0.config_ip4() + self.pg1.config_ip4() + self.pg0.resolve_arp() + self.pg1.resolve_arp() + + self.nat44_add_address(nat_ip1, vrf_id=vrf_id1) + self.nat44_add_address(nat_ip2, vrf_id=vrf_id2) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg2.sw_if_index, + is_add=1) + + try: + # first VRF + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + # second VRF + pkts = self.create_stream_in(self.pg1, self.pg2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip2) + + finally: + self.pg0.unconfig_ip4() + self.pg1.unconfig_ip4() + self.pg0.set_table_ip4(0) + self.pg1.set_table_ip4(0) + self.pg0.config_ip4() + self.pg1.config_ip4() + self.pg0.resolve_arp() + self.pg1.resolve_arp() + self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id1}) + self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id2}) + + def test_vrf_feature_independent(self): + """ NAT44 tenant VRF independent address pool mode """ + + nat_ip1 = "10.0.0.10" + nat_ip2 = "10.0.0.11" + + self.nat44_add_address(nat_ip1) + self.nat44_add_address(nat_ip2, vrf_id=99) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg2.sw_if_index, + is_add=1) + + # first VRF + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + # second VRF + pkts = self.create_stream_in(self.pg1, self.pg2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + def create_routes_and_neigbors(self): + r1 = VppIpRoute(self, self.pg7.remote_ip4, 32, + [VppRoutePath(self.pg7.remote_ip4, + self.pg7.sw_if_index)]) + r2 = VppIpRoute(self, self.pg8.remote_ip4, 32, + [VppRoutePath(self.pg8.remote_ip4, + self.pg8.sw_if_index)]) + r1.add_vpp_config() + r2.add_vpp_config() + + n1 = VppNeighbor(self, + self.pg7.sw_if_index, + self.pg7.remote_mac, + self.pg7.remote_ip4, + is_static=1) + n2 = VppNeighbor(self, + self.pg8.sw_if_index, + self.pg8.remote_mac, + self.pg8.remote_ip4, + is_static=1) + n1.add_vpp_config() + n2.add_vpp_config() + + def test_dynamic_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address """ + self.create_routes_and_neigbors() + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg7.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg8.sw_if_index, + is_add=1) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg8, self.nat_addr) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + def test_static_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address - 1:1 NAT """ + + self.create_routes_and_neigbors() + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg7.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg8.sw_if_index, + is_add=1) + + # out2in + pkts = self.create_stream_out(self.pg8) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture, self.nat_addr, True) + + def test_static_with_port_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address - 1:1 NAPT """ + + self.tcp_port_out = 30606 + self.udp_port_out = 30607 + self.icmp_id_out = 30608 + + self.create_routes_and_neigbors() + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg7.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg8.sw_if_index, + is_add=1) + + # out2in + pkts = self.create_stream_out(self.pg8) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture) + + def test_static_unknown_proto(self): + """ 1:1 NAT translate packet with unknown protocol """ + nat_ip = "10.0.0.10" + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, nat_ip) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=nat_ip) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.pg1.remote_ip4) + self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_static_unknown_proto(self): + """ 1:1 NAT translate packet with unknown protocol - hairpinning """ + + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + + host_nat_ip = "10.0.0.10" + server_nat_ip = "10.0.0.11" + + self.nat44_add_static_mapping(host.ip4, host_nat_ip) + self.nat44_add_static_mapping(server.ip4, server_nat_ip) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # host to server + p = (Ether(dst=self.pg0.local_mac, src=host.mac) / + IP(src=host.ip4, dst=server_nat_ip) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, host_nat_ip) + self.assertEqual(packet[IP].dst, server.ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to host + p = (Ether(dst=self.pg0.local_mac, src=server.mac) / + IP(src=server.ip4, dst=host_nat_ip) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, server_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_output_feature(self): + """ NAT44 interface output feature (in2out postrouting) """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, flags=flags, + sw_if_index=self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, flags=flags, + sw_if_index=self.pg1.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg3.sw_if_index) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.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) + + # out2in + pkts = self.create_stream_out(self.pg3) + self.pg3.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) + + # from non-NAT interface to NAT inside interface + pkts = self.create_stream_in(self.pg2, self.pg0) + self.pg2.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg2, self.pg0) + + def test_output_feature_vrf_aware(self): + """ NAT44 interface output feature VRF aware (in2out postrouting) """ + nat_ip_vrf10 = "10.0.0.10" + nat_ip_vrf20 = "10.0.0.20" + + r1 = VppIpRoute(self, self.pg3.remote_ip4, 32, + [VppRoutePath(self.pg3.remote_ip4, + self.pg3.sw_if_index)], + table_id=10) + r2 = VppIpRoute(self, self.pg3.remote_ip4, 32, + [VppRoutePath(self.pg3.remote_ip4, + self.pg3.sw_if_index)], + table_id=20) + r1.add_vpp_config() + r2.add_vpp_config() + + self.nat44_add_address(nat_ip_vrf10, vrf_id=10) + self.nat44_add_address(nat_ip_vrf20, vrf_id=20) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, flags=flags, + sw_if_index=self.pg4.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, flags=flags, + sw_if_index=self.pg6.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg3.sw_if_index) + + # 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): + """ NAT44 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.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, flags=flags, + sw_if_index=self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, self.nat_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.nat_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.nat_addr) + self.assertEqual(ip.dst, server.ip4) + self.assertNotEqual(tcp.sport, host_in_port) + self.assertEqual(tcp.dport, server_in_port) + self.assert_packet_checksums_valid(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.nat_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.nat_addr) + self.assertEqual(ip.dst, host.ip4) + self.assertEqual(tcp.sport, server_out_port) + self.assertEqual(tcp.dport, host_in_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_one_armed_nat44(self): + """ One armed NAT44 """ + remote_host = self.pg9.remote_hosts[0] + local_host = self.pg9.remote_hosts[1] + external_port = 0 + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg9.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg9.sw_if_index, + flags=flags, is_add=1) + + # in2out + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=local_host.ip4, dst=remote_host.ip4) / + TCP(sport=12345, dport=80)) + self.pg9.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg9.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, remote_host.ip4) + self.assertNotEqual(tcp.sport, 12345) + external_port = tcp.sport + self.assertEqual(tcp.dport, 80) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # out2in + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=remote_host.ip4, dst=self.nat_addr) / + TCP(sport=80, dport=external_port)) + self.pg9.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg9.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, remote_host.ip4) + self.assertEqual(ip.dst, local_host.ip4) + self.assertEqual(tcp.sport, 80) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + err = self.statistics.get_err_counter( + '/err/nat44-classify/next in2out') + self.assertEqual(err, 1) + err = self.statistics.get_err_counter( + '/err/nat44-classify/next out2in') + self.assertEqual(err, 1) + + def test_del_session(self): + """ Delete NAT44 session """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(len(pkts)) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + nsessions = len(sessions) + + self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=self.config_flags.NAT_IS_INSIDE) + self.vapi.nat44_del_session(address=sessions[1].outside_ip_address, + port=sessions[1].outside_port, + protocol=sessions[1].protocol) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(nsessions - len(sessions), 2) + + self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=self.config_flags.NAT_IS_INSIDE) + + self.verify_no_nat44_user() + + def test_frag_in_order(self): + """ NAT44 translate fragments arriving in order """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + self.frag_in_order(proto=IP_PROTOS.tcp) + self.frag_in_order(proto=IP_PROTOS.udp) + self.frag_in_order(proto=IP_PROTOS.icmp) + + def test_frag_forwarding(self): + """ NAT44 forwarding fragment test """ + self.vapi.nat44_add_del_interface_addr( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=1) + + data = b"A" * 16 + b"B" * 16 + b"C" * 3 + pkts = self.create_stream_frag(self.pg1, + self.pg0.remote_ip4, + 4789, + 4789, + data, + proto=IP_PROTOS.udp) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + frags = self.pg0.get_capture(len(pkts)) + p = self.reass_frags_and_verify(frags, + self.pg1.remote_ip4, + self.pg0.remote_ip4) + self.assertEqual(p[UDP].sport, 4789) + self.assertEqual(p[UDP].dport, 4789) + self.assertEqual(data, p[Raw].load) + + def test_reass_hairpinning(self): + """ NAT44 fragments hairpinning """ + + self.server = self.pg0.remote_hosts[1] + self.host_in_port = random.randint(1025, 65535) + self.server_in_port = random.randint(1025, 65535) + self.server_out_port = random.randint(1025, 65535) + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + # add static mapping for server + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr) + + self.reass_hairpinning(proto=IP_PROTOS.tcp) + self.reass_hairpinning(proto=IP_PROTOS.udp) + self.reass_hairpinning(proto=IP_PROTOS.icmp) + + def test_frag_out_of_order(self): + """ NAT44 translate fragments arriving out of order """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + self.frag_out_of_order(proto=IP_PROTOS.tcp) + self.frag_out_of_order(proto=IP_PROTOS.udp) + self.frag_out_of_order(proto=IP_PROTOS.icmp) + + def test_port_restricted(self): + """ Port restricted NAT44 (MAP-E CE) """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_set_addr_and_port_alloc_alg(alg=1, + psid_offset=6, + psid_length=6, + psid=10) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=4567, dport=22)) + 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.dst, self.pg1.remote_ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, 22) + self.assertNotEqual(tcp.sport, 4567) + self.assertEqual((tcp.sport >> 6) & 63, 10) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_port_range(self): + """ External address port range """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_set_addr_and_port_alloc_alg(alg=2, + start_port=1025, + end_port=1027) + + pkts = [] + for port in range(0, 5): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=1125 + port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(3) + for p in capture: + tcp = p[TCP] + self.assertGreaterEqual(tcp.sport, 1025) + self.assertLessEqual(tcp.sport, 1027) + + def test_multiple_outside_vrf(self): + """ Multiple outside VRF """ + vrf_id1 = 1 + vrf_id2 = 2 + + self.pg1.unconfig_ip4() + self.pg2.unconfig_ip4() + self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1}) + self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2}) + self.pg1.set_table_ip4(vrf_id1) + self.pg2.set_table_ip4(vrf_id2) + self.pg1.config_ip4() + self.pg2.config_ip4() + self.pg1.resolve_arp() + self.pg2.resolve_arp() + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg2.sw_if_index, + is_add=1) + + try: + # first VRF + 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, self.nat_addr) + + pkts = self.create_stream_out(self.pg1, self.nat_addr) + 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) + + self.tcp_port_in = 60303 + self.udp_port_in = 60304 + self.icmp_id_in = 60305 + + # second VRF + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, self.nat_addr) + + pkts = self.create_stream_out(self.pg2, self.nat_addr) + self.pg2.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) + + finally: + self.nat44_add_address(self.nat_addr, is_add=0) + self.pg1.unconfig_ip4() + self.pg2.unconfig_ip4() + self.pg1.set_table_ip4(0) + self.pg2.set_table_ip4(0) + self.pg1.config_ip4() + self.pg2.config_ip4() + self.pg1.resolve_arp() + self.pg2.resolve_arp() + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_session_timeout(self): + """ NAT44 session timeouts """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_set_timeouts(udp=5, tcp_established=7440, + tcp_transitory=240, icmp=60) + + max_sessions = 1000 + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + UDP(sport=1025, dport=53)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + sleep(6) + + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + UDP(sport=1026, dport=53)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + nsessions = 0 + users = self.vapi.nat44_user_dump() + for user in users: + nsessions = nsessions + user.nsessions + self.assertLess(nsessions, 2 * max_sessions) + + def test_mss_clamping(self): + """ TCP MSS clamping """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="S", options=[('MSS', 1400)])) + + self.vapi.nat_set_mss_clamping(enable=1, mss_value=1000) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + # Negotiated MSS value greater than configured - changed + self.verify_mss_value(capture[0], 1000) + + self.vapi.nat_set_mss_clamping(enable=0, mss_value=1500) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + # MSS clamping disabled - negotiated MSS unchanged + self.verify_mss_value(capture[0], 1400) + + self.vapi.nat_set_mss_clamping(enable=1, mss_value=1500) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + # Negotiated MSS value smaller than configured - unchanged + self.verify_mss_value(capture[0], 1400) + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_ha_send(self): + """ Send HA session synchronization events (active) """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, + port=12345, + path_mtu=512) + self.vapi.nat_ha_set_failover(ip_address=self.pg3.remote_ip4, + port=12346, session_refresh_interval=10) + bind_layers(UDP, HANATStateSync, sport=12345) + + # create sessions + 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) + # active send HA events + self.vapi.nat_ha_flush() + stats = self.statistics.get_counter('/nat44/ha/add-event-send') + self.assertEqual(stats[0][0], 3) + capture = self.pg3.get_capture(1) + p = capture[0] + self.assert_packet_checksums_valid(p) + try: + ip = p[IP] + udp = p[UDP] + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(ip.src, self.pg3.local_ip4) + self.assertEqual(ip.dst, self.pg3.remote_ip4) + self.assertEqual(udp.sport, 12345) + self.assertEqual(udp.dport, 12346) + self.assertEqual(hanat.version, 1) + self.assertEqual(hanat.thread_index, 0) + self.assertEqual(hanat.count, 3) + seq = hanat.sequence_number + for event in hanat.events: + self.assertEqual(event.event_type, 1) + self.assertEqual(event.in_addr, self.pg0.remote_ip4) + self.assertEqual(event.out_addr, self.nat_addr) + self.assertEqual(event.fib_index, 0) + + # ACK received events + ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=12346, dport=12345) / + HANATStateSync(sequence_number=seq, flags='ACK')) + self.pg3.add_stream(ack) + self.pg_start() + stats = self.statistics.get_counter('/nat44/ha/ack-recv') + self.assertEqual(stats[0][0], 1) + + # delete one session + self.pg_enable_capture(self.pg_interfaces) + self.vapi.nat44_del_session(address=self.pg0.remote_ip4, + port=self.tcp_port_in, + protocol=IP_PROTOS.tcp, + flags=self.config_flags.NAT_IS_INSIDE) + self.vapi.nat_ha_flush() + stats = self.statistics.get_counter('/nat44/ha/del-event-send') + self.assertEqual(stats[0][0], 1) + capture = self.pg3.get_capture(1) + p = capture[0] + try: + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertGreater(hanat.sequence_number, seq) + + # do not send ACK, active retry send HA event again + self.pg_enable_capture(self.pg_interfaces) + sleep(12) + stats = self.statistics.get_counter('/nat44/ha/retry-count') + self.assertEqual(stats[0][0], 3) + stats = self.statistics.get_counter('/nat44/ha/missed-count') + self.assertEqual(stats[0][0], 1) + capture = self.pg3.get_capture(3) + for packet in capture: + self.assertEqual(packet, p) + + # session counters refresh + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(2) + self.vapi.nat_ha_flush() + stats = self.statistics.get_counter('/nat44/ha/refresh-event-send') + self.assertEqual(stats[0][0], 2) + capture = self.pg3.get_capture(1) + p = capture[0] + self.assert_packet_checksums_valid(p) + try: + ip = p[IP] + udp = p[UDP] + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(ip.src, self.pg3.local_ip4) + self.assertEqual(ip.dst, self.pg3.remote_ip4) + self.assertEqual(udp.sport, 12345) + self.assertEqual(udp.dport, 12346) + self.assertEqual(hanat.version, 1) + self.assertEqual(hanat.count, 2) + seq = hanat.sequence_number + for event in hanat.events: + self.assertEqual(event.event_type, 3) + self.assertEqual(event.out_addr, self.nat_addr) + self.assertEqual(event.fib_index, 0) + self.assertEqual(event.total_pkts, 2) + self.assertGreater(event.total_bytes, 0) + + ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=12346, dport=12345) / + HANATStateSync(sequence_number=seq, flags='ACK')) + self.pg3.add_stream(ack) + self.pg_start() + stats = self.statistics.get_counter('/nat44/ha/ack-recv') + self.assertEqual(stats[0][0], 2) + + def test_ha_recv(self): + """ Receive HA session synchronization events (passive) """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, + port=12345, + path_mtu=512) + bind_layers(UDP, HANATStateSync, sport=12345) + + self.tcp_port_out = random.randint(1025, 65535) + self.udp_port_out = random.randint(1025, 65535) + + # send HA session add events to failover/passive + p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=12346, dport=12345) / + HANATStateSync(sequence_number=1, events=[ + Event(event_type='add', protocol='tcp', + in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, + in_port=self.tcp_port_in, out_port=self.tcp_port_out, + eh_addr=self.pg1.remote_ip4, + ehn_addr=self.pg1.remote_ip4, + eh_port=self.tcp_external_port, + ehn_port=self.tcp_external_port, fib_index=0), + Event(event_type='add', protocol='udp', + in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, + in_port=self.udp_port_in, out_port=self.udp_port_out, + eh_addr=self.pg1.remote_ip4, + ehn_addr=self.pg1.remote_ip4, + eh_port=self.udp_external_port, + ehn_port=self.udp_external_port, fib_index=0)])) + + self.pg3.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # receive ACK + capture = self.pg3.get_capture(1) + p = capture[0] + try: + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(hanat.sequence_number, 1) + self.assertEqual(hanat.flags, 'ACK') + self.assertEqual(hanat.version, 1) + self.assertEqual(hanat.thread_index, 0) + stats = self.statistics.get_counter('/nat44/ha/ack-send') + self.assertEqual(stats[0][0], 1) + stats = self.statistics.get_counter('/nat44/ha/add-event-recv') + self.assertEqual(stats[0][0], 2) + users = self.statistics.get_counter('/nat44/total-users') + self.assertEqual(users[0][0], 1) + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 2) + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 1) + self.assertEqual(str(users[0].ip_address), + self.pg0.remote_ip4) + # there should be 2 sessions created by HA + sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, + users[0].vrf_id) + self.assertEqual(len(sessions), 2) + for session in sessions: + self.assertEqual(str(session.inside_ip_address), + self.pg0.remote_ip4) + self.assertEqual(str(session.outside_ip_address), + self.nat_addr) + self.assertIn(session.inside_port, + [self.tcp_port_in, self.udp_port_in]) + self.assertIn(session.outside_port, + [self.tcp_port_out, self.udp_port_out]) + self.assertIn(session.protocol, [IP_PROTOS.tcp, IP_PROTOS.udp]) + + # send HA session delete event to failover/passive + p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=12346, dport=12345) / + HANATStateSync(sequence_number=2, events=[ + Event(event_type='del', protocol='udp', + in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, + in_port=self.udp_port_in, out_port=self.udp_port_out, + eh_addr=self.pg1.remote_ip4, + ehn_addr=self.pg1.remote_ip4, + eh_port=self.udp_external_port, + ehn_port=self.udp_external_port, fib_index=0)])) + + self.pg3.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # receive ACK + capture = self.pg3.get_capture(1) + p = capture[0] + try: + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(hanat.sequence_number, 2) + self.assertEqual(hanat.flags, 'ACK') + self.assertEqual(hanat.version, 1) + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 1) + self.assertEqual(str(users[0].ip_address), + self.pg0.remote_ip4) + # now we should have only 1 session, 1 deleted by HA + sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, + users[0].vrf_id) + self.assertEqual(len(sessions), 1) + stats = self.statistics.get_counter('/nat44/ha/del-event-recv') + self.assertEqual(stats[0][0], 1) + + stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') + self.assertEqual(stats, 2) + + # send HA session refresh event to failover/passive + p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=12346, dport=12345) / + HANATStateSync(sequence_number=3, events=[ + Event(event_type='refresh', protocol='tcp', + in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr, + in_port=self.tcp_port_in, out_port=self.tcp_port_out, + eh_addr=self.pg1.remote_ip4, + ehn_addr=self.pg1.remote_ip4, + eh_port=self.tcp_external_port, + ehn_port=self.tcp_external_port, fib_index=0, + total_bytes=1024, total_pkts=2)])) + self.pg3.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # receive ACK + capture = self.pg3.get_capture(1) + p = capture[0] + try: + hanat = p[HANATStateSync] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(hanat.sequence_number, 3) + self.assertEqual(hanat.flags, 'ACK') + self.assertEqual(hanat.version, 1) + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 1) + self.assertEqual(str(users[0].ip_address), + self.pg0.remote_ip4) + sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, + users[0].vrf_id) + self.assertEqual(len(sessions), 1) + session = sessions[0] + self.assertEqual(session.total_bytes, 1024) + self.assertEqual(session.total_pkts, 2) + stats = self.statistics.get_counter('/nat44/ha/refresh-event-recv') + self.assertEqual(stats[0][0], 1) + + stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') + self.assertEqual(stats, 3) + + # send packet to test session created by HA + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out)) + self.pg1.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] + except IndexError: + self.logger.error(ppp("Invalid packet:", p)) + raise + else: + self.assertEqual(ip.src, self.pg1.remote_ip4) + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.sport, self.tcp_external_port) + self.assertEqual(tcp.dport, self.tcp_port_in) + + def show_commands_at_teardown(self): + self.logger.info(self.vapi.cli("show nat44 addresses")) + self.logger.info(self.vapi.cli("show nat44 interfaces")) + self.logger.info(self.vapi.cli("show nat44 static mappings")) + self.logger.info(self.vapi.cli("show nat44 interface address")) + self.logger.info(self.vapi.cli("show nat44 sessions detail")) + self.logger.info(self.vapi.cli("show nat44 hash tables detail")) + self.logger.info(self.vapi.cli("show nat timeouts")) + self.logger.info( + self.vapi.cli("show nat addr-port-assignment-alg")) + self.logger.info(self.vapi.cli("show nat ha")) + + +class TestNAT44EndpointDependent2(MethodHolder): + """ Endpoint-Dependent mapping and filtering test cases """ + + @classmethod + def tearDownClass(cls): + super(TestNAT44EndpointDependent2, cls).tearDownClass() + + def tearDown(self): + super(TestNAT44EndpointDependent2, self).tearDown() + + @classmethod + def create_and_add_ip4_table(cls, i, table_id): + cls.vapi.ip_table_add_del(is_add=1, table={'table_id': table_id}) + i.set_table_ip4(table_id) + + @classmethod + def setUpClass(cls): + super(TestNAT44EndpointDependent2, cls).setUpClass() + + cls.create_pg_interfaces(range(3)) + cls.interfaces = list(cls.pg_interfaces) + + cls.create_and_add_ip4_table(cls.pg1, 10) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + i.generate_remote_hosts(1) + i.configure_ipv4_neighbors() + + def setUp(self): + super(TestNAT44EndpointDependent2, self).setUp() + flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT + self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) + + def tearDown(self): + super(TestNAT44EndpointDependent2, self).tearDown() + if not self.vpp_dead: + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def nat_add_inside_interface(self, i): + self.vapi.nat44_interface_add_del_feature( + flags=self.config_flags.NAT_IS_INSIDE, + sw_if_index=i.sw_if_index, is_add=1) + + def nat_add_outside_interface(self, i): + self.vapi.nat44_interface_add_del_feature( + flags=self.config_flags.NAT_IS_OUTSIDE, + sw_if_index=i.sw_if_index, is_add=1) + + def nat_add_interface_address(self, i): + self.nat_addr = i.local_ip4 + self.vapi.nat44_add_del_interface_addr( + sw_if_index=i.sw_if_index, is_add=1) + + def nat_add_address(self, address, vrf_id=0xFFFFFFFF): + self.nat_addr = address + self.nat44_add_address(address, vrf_id=vrf_id) + + def cli(self, command): + result = self.vapi.cli(command) + self.logger.info(result) + # print(result) + + def show_configuration(self): + self.cli("show interface") + self.cli("show interface address") + self.cli("show nat44 addresses") + self.cli("show nat44 interfaces") + + def create_tcp_stream(self, in_if, out_if, count): + """ + Create tcp packet stream + + :param in_if: Inside interface + :param out_if: Outside interface + :param count: count of packets to generate + """ + pkts = [] + port = 6303 + + for i in range(count): + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=64) / + TCP(sport=port + i, dport=20)) + pkts.append(p) + + return pkts + + def test_session_limit_per_vrf(self): + + inside = self.pg0 + inside_vrf10 = self.pg1 + outside = self.pg2 + + limit = 5 + + # 2 interfaces pg0, pg1 (vrf10, limit 1 tcp session) + # non existing vrf_id makes process core dump + self.vapi.nat44_set_session_limit(session_limit=limit, vrf_id=10) + + self.nat_add_inside_interface(inside) + self.nat_add_inside_interface(inside_vrf10) + self.nat_add_outside_interface(outside) + + # vrf independent + self.nat_add_interface_address(outside) + + # BUG: causing core dump - when bad vrf_id is specified + # self.nat44_add_address(outside.local_ip4, vrf_id=20) + + self.show_configuration() + + stream = self.create_tcp_stream(inside_vrf10, outside, limit * 2) + inside_vrf10.add_stream(stream) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + capture = outside.get_capture(limit) + + stream = self.create_tcp_stream(inside, outside, limit * 2) + inside.add_stream(stream) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + capture = outside.get_capture(len(stream)) + + +class TestNAT44EndpointDependent(MethodHolder): + """ Endpoint-Dependent mapping and filtering test cases """ + + @classmethod + def setUpClass(cls): + super(TestNAT44EndpointDependent, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.ipfix_src_port = 4739 + cls.ipfix_domain_id = 1 + cls.tcp_external_port = 80 + + cls.create_pg_interfaces(range(9)) + cls.interfaces = list(cls.pg_interfaces[0:3]) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.pg0.generate_remote_hosts(3) + cls.pg0.configure_ipv4_neighbors() + + cls.pg3.admin_up() + + cls.pg4.generate_remote_hosts(2) + cls.pg4.config_ip4() + cls.vapi.sw_interface_add_del_address( + sw_if_index=cls.pg4.sw_if_index, + prefix="10.0.0.1/24") + + cls.pg4.admin_up() + cls.pg4.resolve_arp() + cls.pg4._remote_hosts[1]._ip4 = cls.pg4._remote_hosts[0]._ip4 + cls.pg4.resolve_arp() + + zero_ip4 = socket.inet_pton(socket.AF_INET, "0.0.0.0") + cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 1}) + + cls.pg5._local_ip4 = "10.1.1.1" + cls.pg5._remote_hosts[0]._ip4 = "10.1.1.2" + cls.pg5.set_table_ip4(1) + cls.pg5.config_ip4() + cls.pg5.admin_up() + r1 = VppIpRoute(cls, cls.pg5.remote_ip4, 32, + [VppRoutePath("0.0.0.0", + cls.pg5.sw_if_index)], + table_id=1, + register=False) + r1.add_vpp_config() + + cls.pg6._local_ip4 = "10.1.2.1" + cls.pg6._remote_hosts[0]._ip4 = "10.1.2.2" + cls.pg6.set_table_ip4(1) + cls.pg6.config_ip4() + cls.pg6.admin_up() + + r2 = VppIpRoute(cls, cls.pg6.remote_ip4, 32, + [VppRoutePath("0.0.0.0", + cls.pg6.sw_if_index)], + table_id=1, + register=False) + r3 = VppIpRoute(cls, cls.pg6.remote_ip4, 16, + [VppRoutePath("0.0.0.0", + 0xffffffff, + nh_table_id=1)], + table_id=0, + register=False) + r4 = VppIpRoute(cls, "0.0.0.0", 0, + [VppRoutePath("0.0.0.0", 0xffffffff, + nh_table_id=0)], + table_id=1, + register=False) + r5 = VppIpRoute(cls, "0.0.0.0", 0, + [VppRoutePath(cls.pg1.local_ip4, + cls.pg1.sw_if_index)], + register=False) + r2.add_vpp_config() + r3.add_vpp_config() + r4.add_vpp_config() + r5.add_vpp_config() + + cls.pg5.resolve_arp() + cls.pg6.resolve_arp() + + cls.pg7.admin_up() + cls.pg7.config_ip4() + cls.pg7.resolve_arp() + cls.pg7.generate_remote_hosts(3) + cls.pg7.configure_ipv4_neighbors() + + cls.pg8.admin_up() + cls.pg8.config_ip4() + cls.pg8.resolve_arp() + + @classmethod + def tearDownClass(cls): + super(TestNAT44EndpointDependent, cls).tearDownClass() + + def setUp(self): + super(TestNAT44EndpointDependent, self).setUp() + flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT + self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) + self.vapi.nat_set_timeouts( + udp=300, tcp_established=7440, + tcp_transitory=240, icmp=60) + + def tearDown(self): + super(TestNAT44EndpointDependent, self).tearDown() + if not self.vpp_dead: + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def test_frag_in_order(self): + """ NAT44 translate fragments arriving in order """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.frag_in_order(proto=IP_PROTOS.tcp, ignore_port=True) + self.frag_in_order(proto=IP_PROTOS.udp, ignore_port=True) + self.frag_in_order(proto=IP_PROTOS.icmp, ignore_port=True) + + def test_frag_in_order_dont_translate(self): + """ NAT44 don't translate fragments arriving in order """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=True) + self.frag_in_order(proto=IP_PROTOS.tcp, dont_translate=True) + + def test_frag_out_of_order(self): + """ NAT44 translate fragments arriving out of order """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.frag_out_of_order(proto=IP_PROTOS.tcp, ignore_port=True) + self.frag_out_of_order(proto=IP_PROTOS.udp, ignore_port=True) + self.frag_out_of_order(proto=IP_PROTOS.icmp, ignore_port=True) + + def test_frag_out_of_order_dont_translate(self): + """ NAT44 don't translate fragments arriving out of order """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=True) + self.frag_out_of_order(proto=IP_PROTOS.tcp, dont_translate=True) + + def test_frag_in_order_in_plus_out(self): + """ in+out interface fragments in order """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + flags=flags, is_add=1) + + self.server = self.pg1.remote_hosts[0] + + self.server_in_addr = self.server.ip4 + self.server_out_addr = '11.11.11.11' + self.server_in_port = random.randint(1025, 65535) + self.server_out_port = random.randint(1025, 65535) + + self.nat44_add_address(self.server_out_addr) + + # add static mappings for server + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + proto=IP_PROTOS.icmp) + + self.frag_in_order_in_plus_out(proto=IP_PROTOS.tcp) + self.frag_in_order_in_plus_out(proto=IP_PROTOS.udp) + self.frag_in_order_in_plus_out(proto=IP_PROTOS.icmp) + + def test_frag_out_of_order_in_plus_out(self): + """ in+out interface fragments out of order """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + flags=flags, is_add=1) + + self.server = self.pg1.remote_hosts[0] + + self.server_in_addr = self.server.ip4 + self.server_out_addr = '11.11.11.11' + self.server_in_port = random.randint(1025, 65535) + self.server_out_port = random.randint(1025, 65535) + + self.nat44_add_address(self.server_out_addr) + + # add static mappings for server + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.server_in_addr, + self.server_out_addr, + proto=IP_PROTOS.icmp) + + self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.tcp) + self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.udp) + self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.icmp) + + def test_reass_hairpinning(self): + """ NAT44 fragments hairpinning """ + self.server = self.pg0.remote_hosts[1] + self.host_in_port = random.randint(1025, 65535) + self.server_in_port = random.randint(1025, 65535) + self.server_out_port = random.randint(1025, 65535) + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + # add static mapping for server + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr, + self.server_in_port, + self.server_out_port, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.server.ip4, self.nat_addr) + + self.reass_hairpinning(proto=IP_PROTOS.tcp, ignore_port=True) + self.reass_hairpinning(proto=IP_PROTOS.udp, ignore_port=True) + self.reass_hairpinning(proto=IP_PROTOS.icmp, ignore_port=True) + + def test_clear_sessions(self): + """ NAT44 ED session clearing test """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.endpoint_dependent) + + 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, ignore_port=True) + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertTrue(sessions[0][0] > 0) + self.logger.info("sessions before clearing: %s" % sessions[0][0]) + + # just for testing purposes + self.logger.info(self.vapi.cli("show nat44 summary")) + + self.vapi.cli("clear nat44 sessions") + + self.logger.info(self.vapi.cli("show nat44 summary")) + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 0) + self.logger.info("sessions after clearing: %s" % sessions[0][0]) + + def test_dynamic(self): + """ NAT44 dynamic translation test """ + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.endpoint_dependent) + + # in2out + tcpn = self.statistics.get_counter('/nat44/ed/in2out/slowpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44/ed/in2out/slowpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/drops')[0] + + 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, ignore_port=True) + + if_idx = self.pg0.sw_if_index + cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/ed/in2out/slowpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + # out2in + tcpn = self.statistics.get_counter('/nat44/ed/out2in/fastpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44/ed/out2in/fastpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44/ed/out2in/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/drops')[0] + + 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) + + if_idx = self.pg1.sw_if_index + cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/ed/out2in/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter('/nat44/ed/out2in/fastpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 3) + + def test_dynamic_out_of_ports(self): + """ NAT44 dynamic translation test: out of ports """ + + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.endpoint_dependent) + + # in2out and no NAT addresses added + err_old = self.statistics.get_err_counter( + '/err/nat44-ed-in2out-slowpath/out of ports') + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(0, timeout=1) + + err_new = self.statistics.get_err_counter( + '/err/nat44-ed-in2out-slowpath/out of ports') + + self.assertEqual(err_new - err_old, len(pkts)) + + # in2out after NAT addresses added + self.nat44_add_address(self.nat_addr) + + err_old = self.statistics.get_err_counter( + '/err/nat44-ed-in2out-slowpath/out of ports') + + 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, ignore_port=True) + + err_new = self.statistics.get_err_counter( + '/err/nat44-ed-in2out-slowpath/out of ports') + + self.assertEqual(err_new, err_old) + + def test_dynamic_output_feature_vrf(self): + """ NAT44 dynamic translation test: output-feature, VRF""" + + # other then default (0) + new_vrf_id = 22 + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_output_feature( + sw_if_index=self.pg7.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + sw_if_index=self.pg8.sw_if_index, + is_add=1) + + try: + self.vapi.ip_table_add_del(is_add=1, + table={'table_id': new_vrf_id}) + + self.pg7.unconfig_ip4() + self.pg7.set_table_ip4(new_vrf_id) + self.pg7.config_ip4() + self.pg7.resolve_arp() + + self.pg8.unconfig_ip4() + self.pg8.set_table_ip4(new_vrf_id) + self.pg8.config_ip4() + self.pg8.resolve_arp() + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.endpoint_dependent) + + # in2out + tcpn = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/tcp')[0] + udpn = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/drops')[0] + + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture, ignore_port=True) + + if_idx = self.pg7.sw_if_index + cnt = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter( + '/nat44/ed/in2out/slowpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + # out2in + tcpn = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/tcp')[0] + udpn = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44/ed/out2in/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/drops')[0] + + pkts = self.create_stream_out(self.pg8) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + if_idx = self.pg8.sw_if_index + cnt = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/tcp')[0] + self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) + cnt = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/udp')[0] + self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) + cnt = self.statistics.get_counter( + '/nat44/ed/out2in/slowpath/icmp')[0] + self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) + cnt = self.statistics.get_counter( + '/nat44/ed/out2in/fastpath/drops')[0] + self.assertEqual(cnt[if_idx] - drops[if_idx], 0) + + sessions = self.statistics.get_counter('/nat44/total-sessions') + self.assertEqual(sessions[0][0], 3) + + finally: + self.pg7.unconfig_ip4() + self.pg7.set_table_ip4(1) + self.pg7.config_ip4() + self.pg7.resolve_arp() + + self.pg8.unconfig_ip4() + self.pg8.set_table_ip4(1) + self.pg8.config_ip4() + self.pg8.resolve_arp() + + self.vapi.ip_table_add_del(is_add=0, + table={'table_id': new_vrf_id}) + + def test_forwarding(self): + """ NAT44 forwarding test """ + + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=1) + + real_ip = self.pg0.remote_ip4 + alias_ip = self.nat_addr + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping(is_add=1, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + 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 + + user = self.pg0.remote_hosts[1] + sessions = self.vapi.nat44_user_session_dump(user.ip4, 0) + self.assertEqual(len(sessions), 3) + self.assertTrue(sessions[0].flags & + self.config_flags.NAT_IS_EXT_HOST_VALID) + self.vapi.nat44_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=(self.config_flags.NAT_IS_INSIDE | + self.config_flags.NAT_IS_EXT_HOST_VALID), + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(user.ip4, 0) + self.assertEqual(len(sessions), 2) + + finally: + self.vapi.nat44_forwarding_enable_disable(enable=0) + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping( + is_add=0, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + def test_static_lb(self): + """ NAT44 local service load balancing """ + external_addr_n = self.nat_addr + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + + locals = [{'addr': server1.ip4, + 'port': local_port, + 'probability': 70, + 'vrf_id': 0}, + {'addr': server2.ip4, + 'port': local_port, + 'probability': 30, + 'vrf_id': 0}] + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping( + is_add=1, + external_addr=external_addr_n, + external_port=external_port, + protocol=IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].flags & + self.config_flags.NAT_IS_EXT_HOST_VALID) + self.vapi.nat44_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=(self.config_flags.NAT_IS_INSIDE | + self.config_flags.NAT_IS_EXT_HOST_VALID), + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) + self.assertEqual(len(sessions), 0) + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_static_lb_multi_clients(self): + """ NAT44 local service load balancing - multiple clients""" + + external_addr = self.nat_addr + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + server3 = self.pg0.remote_hosts[2] + + locals = [{'addr': server1.ip4, + 'port': local_port, + 'probability': 90, + 'vrf_id': 0}, + {'addr': server2.ip4, + 'port': local_port, + 'probability': 10, + 'vrf_id': 0}] + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping(is_add=1, + external_addr=external_addr, + external_port=external_port, + protocol=IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + server1_n = 0 + server2_n = 0 + clients = ip4_range(self.pg1.remote_ip4, 10, 50) + pkts = [] + for client in clients: + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=client, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + pkts.append(p) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + else: + server2_n += 1 + self.assertGreater(server1_n, server2_n) + + local = { + 'addr': server3.ip4, + 'port': local_port, + 'probability': 20, + 'vrf_id': 0 + } + + # add new back-end + self.vapi.nat44_lb_static_mapping_add_del_local( + is_add=1, + external_addr=external_addr, + external_port=external_port, + local=local, + protocol=IP_PROTOS.tcp) + server1_n = 0 + server2_n = 0 + server3_n = 0 + clients = ip4_range(self.pg1.remote_ip4, 60, 110) + pkts = [] + for client in clients: + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=client, dst=self.nat_addr) / + TCP(sport=12346, dport=external_port)) + pkts.append(p) + self.assertGreater(len(pkts), 0) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + elif p[IP].dst == server2.ip4: + server2_n += 1 + else: + server3_n += 1 + self.assertGreater(server1_n, 0) + self.assertGreater(server2_n, 0) + self.assertGreater(server3_n, 0) + + local = { + 'addr': server2.ip4, + 'port': local_port, + 'probability': 10, + 'vrf_id': 0 + } + + # remove one back-end + self.vapi.nat44_lb_static_mapping_add_del_local( + is_add=0, + external_addr=external_addr, + external_port=external_port, + local=local, + protocol=IP_PROTOS.tcp) + server1_n = 0 + server2_n = 0 + server3_n = 0 + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + elif p[IP].dst == server2.ip4: + server2_n += 1 + else: + server3_n += 1 + self.assertGreater(server1_n, 0) + self.assertEqual(server2_n, 0) + self.assertGreater(server3_n, 0) + + def test_static_lb_2(self): + """ NAT44 local service load balancing (asymmetrical rule) """ + external_addr = self.nat_addr + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + + locals = [{'addr': server1.ip4, + 'port': local_port, + 'probability': 70, + 'vrf_id': 0}, + {'addr': server2.ip4, + 'port': local_port, + 'probability': 30, + 'vrf_id': 0}] + + self.vapi.nat44_forwarding_enable_disable(enable=1) + flags = self.config_flags.NAT_IS_OUT2IN_ONLY + self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags, + external_addr=external_addr, + external_port=external_port, + protocol=IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_lb_affinity(self): + """ NAT44 local service load balancing affinity """ + external_addr = self.nat_addr + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + + locals = [{'addr': server1.ip4, + 'port': local_port, + 'probability': 50, + 'vrf_id': 0}, + {'addr': server2.ip4, + 'port': local_port, + 'probability': 50, + 'vrf_id': 0}] + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping(is_add=1, + external_addr=external_addr, + external_port=external_port, + protocol=IP_PROTOS.tcp, + affinity=10800, + local_num=len(locals), + locals=locals) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=1025, dport=external_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + backend = capture[0][IP].dst + + sessions = self.vapi.nat44_user_session_dump(backend, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].flags & + self.config_flags.NAT_IS_EXT_HOST_VALID) + self.vapi.nat44_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=(self.config_flags.NAT_IS_INSIDE | + self.config_flags.NAT_IS_EXT_HOST_VALID), + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + + pkts = [] + for port in range(1030, 1100): + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=port, dport=external_port)) + pkts.append(p) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + self.assertEqual(p[IP].dst, backend) + + def test_unknown_proto(self): + """ NAT44 translate packet with unknown protocol """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=20)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.pg1.remote_ip4) + self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_unknown_proto(self): + """ NAT44 translate packet with unknown protocol - hairpinning """ + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + server_out_port = 8765 + server_nat_ip = "10.0.0.11" + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, server_nat_ip) + + # host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=server_nat_ip) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=host.mac) / + IP(src=host.ip4, dst=server_nat_ip) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, server.ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to host + p = (Ether(dst=self.pg0.local_mac, src=server.mac) / + IP(src=server.ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, server_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + self.assertEqual(packet.haslayer(GRE), 1) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_output_feature_and_service(self): + """ NAT44 interface output feature and services """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_identity_mapping( + ip_address=self.pg1.remote_ip4, sw_if_index=0xFFFFFFFF, + flags=flags, is_add=1) + flags = self.config_flags.NAT_IS_OUT2IN_ONLY + self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + + # from client to service + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=external_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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_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, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from local network host to external network + 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, ignore_port=True) + 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, ignore_port=True) + + # from external network back to local network host + 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_and_service2(self): + """ NAT44 interface output feature and service host direct access """ + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + + # session initiated from service host - translate + 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, ignore_port=True) + + 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) + + # session initiated from remote host - do not translate + self.tcp_port_in = 60303 + self.udp_port_in = 60304 + self.icmp_id_in = 60305 + pkts = self.create_stream_out(self.pg1, + 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) + + def test_output_feature_and_service3(self): + """ NAT44 interface output feature and DST NAT """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_OUT2IN_ONLY + self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=external_addr) / + TCP(sport=12345, dport=external_port)) + 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.pg0.remote_ip4) + self.assertEqual(tcp.sport, 12345) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg1.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, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_next_src_nat(self): + """ On way back forward packet to nat44-in2out node. """ + twice_nat_addr = '10.0.1.3' + external_port = 80 + local_port = 8080 + post_twice_nat_port = 0 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(twice_nat_addr, twice_nat=1) + flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | + self.config_flags.NAT_IS_SELF_TWICE_NAT) + self.nat44_add_static_mapping(self.pg6.remote_ip4, self.pg1.remote_ip4, + local_port, external_port, + proto=IP_PROTOS.tcp, vrf_id=1, + flags=flags) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg6.sw_if_index, + is_add=1) + + p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / + IP(src=self.pg6.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=12345, dport=external_port)) + self.pg6.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, twice_nat_addr) + self.assertNotEqual(tcp.sport, 12345) + post_twice_nat_port = tcp.sport + self.assertEqual(ip.dst, self.pg6.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / + IP(src=self.pg6.remote_ip4, dst=twice_nat_addr) / + TCP(sport=local_port, dport=post_twice_nat_port)) + self.pg6.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg1.remote_ip4) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(ip.dst, self.pg6.remote_ip4) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def twice_nat_common(self, self_twice_nat=False, same_pg=False, lb=False, + client_id=None): + twice_nat_addr = '10.0.1.3' + + port_in = 8080 + if lb: + if not same_pg: + port_in1 = port_in + port_in2 = port_in + else: + port_in1 = port_in + 1 + port_in2 = port_in + 2 + + port_out = 80 + eh_port_out = 4567 + + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + if lb and same_pg: + server2 = server1 + if not lb: + server = server1 + + pg0 = self.pg0 + if same_pg: + pg1 = self.pg0 + else: + pg1 = self.pg1 + + eh_translate = ((not self_twice_nat) or (not lb and same_pg) or + client_id == 1) + + self.nat44_add_address(self.nat_addr) + self.nat44_add_address(twice_nat_addr, twice_nat=1) + + flags = 0 + if self_twice_nat: + flags |= self.config_flags.NAT_IS_SELF_TWICE_NAT + else: + flags |= self.config_flags.NAT_IS_TWICE_NAT + + if not lb: + self.nat44_add_static_mapping(pg0.remote_ip4, self.nat_addr, + port_in, port_out, + proto=IP_PROTOS.tcp, + flags=flags) + else: + locals = [{'addr': server1.ip4, + 'port': port_in1, + 'probability': 50, + 'vrf_id': 0}, + {'addr': server2.ip4, + 'port': port_in2, + 'probability': 50, + 'vrf_id': 0}] + out_addr = self.nat_addr + + self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags, + external_addr=out_addr, + external_port=port_out, + protocol=IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=pg1.sw_if_index, + is_add=1) + + if same_pg: + if not lb: + client = server + else: + assert client_id is not None + if client_id == 1: + client = self.pg0.remote_hosts[0] + elif client_id == 2: + client = self.pg0.remote_hosts[1] + else: + client = pg1.remote_hosts[0] + p = (Ether(src=pg1.remote_mac, dst=pg1.local_mac) / + IP(src=client.ip4, dst=self.nat_addr) / + TCP(sport=eh_port_out, dport=port_out)) + pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + if lb: + if ip.dst == server1.ip4: + server = server1 + port_in = port_in1 + else: + server = server2 + port_in = port_in2 + self.assertEqual(ip.dst, server.ip4) + if lb and same_pg: + self.assertIn(tcp.dport, [port_in1, port_in2]) + else: + self.assertEqual(tcp.dport, port_in) + if eh_translate: + self.assertEqual(ip.src, twice_nat_addr) + self.assertNotEqual(tcp.sport, eh_port_out) + else: + self.assertEqual(ip.src, client.ip4) + self.assertEqual(tcp.sport, eh_port_out) + eh_addr_in = ip.src + eh_port_in = tcp.sport + saved_port_in = tcp.dport + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=server.mac, dst=pg0.local_mac) / + IP(src=server.ip4, dst=eh_addr_in) / + TCP(sport=saved_port_in, dport=eh_port_in)) + pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, client.ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, eh_port_out) + self.assertEqual(tcp.sport, port_out) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + if eh_translate: + sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].flags & + self.config_flags.NAT_IS_EXT_HOST_VALID) + self.assertTrue(sessions[0].flags & + self.config_flags.NAT_IS_TWICE_NAT) + self.logger.info(self.vapi.cli("show nat44 sessions detail")) + self.vapi.nat44_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=(self.config_flags.NAT_IS_INSIDE | + self.config_flags.NAT_IS_EXT_HOST_VALID), + ext_host_address=sessions[0].ext_host_nat_address, + ext_host_port=sessions[0].ext_host_nat_port) + sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) + self.assertEqual(len(sessions), 0) + + def test_twice_nat(self): + """ Twice NAT44 """ + self.twice_nat_common() + + def test_self_twice_nat_positive(self): + """ Self Twice NAT44 (positive test) """ + self.twice_nat_common(self_twice_nat=True, same_pg=True) + + def test_self_twice_nat_negative(self): + """ Self Twice NAT44 (negative test) """ + self.twice_nat_common(self_twice_nat=True) + + def test_twice_nat_lb(self): + """ Twice NAT44 local service load balancing """ + self.twice_nat_common(lb=True) + + def test_self_twice_nat_lb_positive(self): + """ Self Twice NAT44 local service load balancing (positive test) """ + self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, + client_id=1) + + def test_self_twice_nat_lb_negative(self): + """ Self Twice NAT44 local service load balancing (negative test) """ + self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, + client_id=2) + + def test_twice_nat_interface_addr(self): + """ Acquire twice NAT44 addresses from interface """ + flags = self.config_flags.NAT_IS_TWICE_NAT + self.vapi.nat44_add_del_interface_addr( + is_add=1, + sw_if_index=self.pg3.sw_if_index, + flags=flags) + + # no address in NAT pool + adresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(adresses)) + + # configure interface address and check NAT address pool + self.pg3.config_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(1, len(adresses)) + self.assertEqual(str(adresses[0].ip_address), + self.pg3.local_ip4) + self.assertEqual(adresses[0].flags, flags) + + # remove interface address and check NAT address pool + self.pg3.unconfig_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(adresses)) + + def test_tcp_close(self): + """ Close TCP session from inside network - output feature """ + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.pg1.local_ip4) + twice_nat_addr = '10.0.1.3' + service_ip = '192.168.16.150' + self.nat44_add_address(twice_nat_addr, twice_nat=1) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | + self.config_flags.NAT_IS_TWICE_NAT) + self.nat44_add_static_mapping(self.pg0.remote_ip4, + service_ip, + 80, + 80, + proto=IP_PROTOS.tcp, + flags=flags) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + start_sessnum = len(sessions) + + # SYN packet out->in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=service_ip) / + TCP(sport=33898, dport=80, flags="S")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + tcp_port = p[TCP].sport + + # SYN + ACK packet in->out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / + TCP(sport=80, dport=tcp_port, flags="SA")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # ACK packet out->in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=service_ip) / + TCP(sport=33898, dport=80, flags="A")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / + TCP(sport=80, dport=tcp_port, flags="FA", seq=100, ack=300)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # FIN+ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=service_ip) / + TCP(sport=33898, dport=80, flags="FA", seq=300, ack=101)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / + TCP(sport=80, dport=tcp_port, flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, + 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_tcp_session_close_in(self): + """ Close TCP session from inside network """ + self.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_TWICE_NAT + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_port_out, + proto=IP_PROTOS.tcp, + flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + start_sessnum = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=100, ack=300)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + pkts = [] + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=300, ack=101)) + pkts.append(p) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=101)) + pkts.append(p) + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(2) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 1) + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + out2in_drops = stats[0] + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + in2out_drops = stats[0] + + # extra FIN packet out -> in - this should be dropped + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=101)) + + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.assert_nothing_captured() + + # extra ACK packet in -> out - this should be dropped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - out2in_drops, 1) + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - in2out_drops, 1) + + self.sleep(3) + # extra ACK packet in -> out - this will cause session to be wiped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_tcp_session_close_out(self): + """ Close TCP session from outside network """ + self.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_TWICE_NAT + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_port_out, + proto=IP_PROTOS.tcp, + flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + start_sessnum = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=100, ack=300)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # FIN+ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=300, ack=101)) + + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=101, ack=301)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 1) + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + out2in_drops = stats[0] + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + in2out_drops = stats[0] + + # extra FIN packet out -> in - this should be dropped + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=101)) + + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.assert_nothing_captured() + + # extra ACK packet in -> out - this should be dropped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - out2in_drops, 1) + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - in2out_drops, 1) + + self.sleep(3) + # extra ACK packet in -> out - this will cause session to be wiped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_tcp_session_close_simultaneous(self): + """ Close TCP session from inside network """ + self.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_TWICE_NAT + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_port_out, + proto=IP_PROTOS.tcp, + flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + start_sessnum = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=100, ack=300)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=100)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=301, ack=101)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 1) + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + out2in_drops = stats[0] + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + in2out_drops = stats[0] + + # extra FIN packet out -> in - this should be dropped + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=101)) + + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.assert_nothing_captured() + + # extra ACK packet in -> out - this should be dropped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + + stats = self.statistics.get_counter( + '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - out2in_drops, 1) + stats = self.statistics.get_counter( + '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') + self.assertEqual(stats[0] - in2out_drops, 1) + + self.sleep(3) + # extra ACK packet in -> out - this will cause session to be wiped + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_one_armed_nat44_static(self): + """ One armed NAT44 and 1:1 NAPT asymmetrical rule """ + remote_host = self.pg4.remote_hosts[0] + local_host = self.pg4.remote_hosts[1] + external_port = 80 + local_port = 8080 + eh_port_in = 0 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr, twice_nat=1) + flags = (self.config_flags.NAT_IS_OUT2IN_ONLY | + self.config_flags.NAT_IS_TWICE_NAT) + self.nat44_add_static_mapping(local_host.ip4, self.nat_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg4.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg4.sw_if_index, + flags=flags, is_add=1) + + # from client to service + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=remote_host.ip4, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, local_host.ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, local_port) + self.assertNotEqual(tcp.sport, 12345) + eh_port_in = tcp.sport + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=local_host.ip4, dst=self.nat_addr) / + TCP(sport=local_port, dport=eh_port_in)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, remote_host.ip4) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_static_with_port_out2(self): + """ 1:1 NAPT asymmetrical rule """ + + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + flags = self.config_flags.NAT_IS_OUT2IN_ONLY + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, flags=flags) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + # 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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + ICMP(type=11) / capture[0][IP]) + 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: + self.assertEqual(p[IP].src, self.nat_addr) + inner = p[IPerror] + self.assertEqual(inner.dst, self.nat_addr) + self.assertEqual(inner[TCPerror].dport, external_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=11) / capture[0][IP]) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + self.assertEqual(p[IP].dst, self.pg0.remote_ip4) + inner = p[IPerror] + self.assertEqual(inner.src, self.pg0.remote_ip4) + self.assertEqual(inner[TCPerror].sport, local_port) + 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=self.pg0.remote_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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_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, self.pg0.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_output_feature(self): + """ NAT44 interface output feature (in2out postrouting) """ + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + is_add=1, + sw_if_index=self.pg1.sw_if_index) + + # 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, ignore_port=True) + + # 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_stateful_acl(self): + """ NAT44 endpoint-dependent output feature works with stateful ACL """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_output_feature( + sw_if_index=self.pg0.sw_if_index, + flags=self.config_flags.NAT_IS_INSIDE, + is_add=1) + self.vapi.nat44_interface_add_del_output_feature( + sw_if_index=self.pg1.sw_if_index, + flags=self.config_flags.NAT_IS_OUTSIDE, + is_add=1) + + # First ensure that the NAT is working sans ACL + + # send packets out2in, no sessions yet so packets should drop + pkts_out2in = self.create_stream_out(self.pg1) + self.send_and_assert_no_replies(self.pg1, pkts_out2in) + + # send packets into inside intf, ensure received via outside intf + pkts_in2out = self.create_stream_in(self.pg0, self.pg1) + capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1, + len(pkts_in2out)) + self.verify_capture_out(capture, ignore_port=True) + + # send out2in again, with sessions created it should work now + pkts_out2in = self.create_stream_out(self.pg1) + capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0, + len(pkts_out2in)) + self.verify_capture_in(capture, self.pg0) + + # Create an ACL blocking everything + out2in_deny_rule = AclRule(is_permit=0) + out2in_acl = VppAcl(self, rules=[out2in_deny_rule]) + out2in_acl.add_vpp_config() + + # create an ACL to permit/reflect everything + in2out_reflect_rule = AclRule(is_permit=2) + in2out_acl = VppAcl(self, rules=[in2out_reflect_rule]) + in2out_acl.add_vpp_config() + + # apply as input acl on interface and confirm it blocks everything + acl_if = VppAclInterface(self, sw_if_index=self.pg1.sw_if_index, + n_input=1, acls=[out2in_acl]) + acl_if.add_vpp_config() + self.send_and_assert_no_replies(self.pg1, pkts_out2in) + + # apply output acl + acl_if.acls = [out2in_acl, in2out_acl] + acl_if.add_vpp_config() + # send in2out to generate ACL state (NAT state was created earlier) + capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1, + len(pkts_in2out)) + self.verify_capture_out(capture, ignore_port=True) + + # send out2in again. ACL state exists so it should work now. + # TCP packets with the syn flag set also need the ack flag + for p in pkts_out2in: + if p.haslayer(TCP) and p[TCP].flags & 0x02: + p[TCP].flags |= 0x10 + capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0, + len(pkts_out2in)) + self.verify_capture_in(capture, self.pg0) + self.logger.info(self.vapi.cli("show trace")) + + def test_multiple_vrf(self): + """ Multiple VRF setup """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + port = 0 + + self.vapi.nat44_forwarding_enable_disable(enable=1) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + is_add=1, flags=flags) + self.vapi.nat44_interface_add_del_output_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg5.sw_if_index, + is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg5.sw_if_index, + is_add=1, flags=flags) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg6.sw_if_index, + is_add=1) + flags = self.config_flags.NAT_IS_OUT2IN_ONLY + self.nat44_add_static_mapping(self.pg5.remote_ip4, external_addr, + local_port, external_port, vrf_id=1, + proto=IP_PROTOS.tcp, flags=flags) + self.nat44_add_static_mapping( + self.pg0.remote_ip4, + external_sw_if_index=self.pg0.sw_if_index, + local_port=local_port, + vrf_id=0, + external_port=external_port, + proto=IP_PROTOS.tcp, + flags=flags + ) + + # from client to service (both VRF1) + p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / + IP(src=self.pg6.remote_ip4, dst=external_addr) / + TCP(sport=12345, dport=external_port)) + self.pg6.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg5.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (both VRF1) + p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / + IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg5.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # dynamic NAT from VRF1 to VRF0 (output-feature) + p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / + IP(src=self.pg5.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=2345, dport=22)) + self.pg5.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.assert_packet_checksums_valid(p) + port = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=22, dport=port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg5.remote_ip4) + self.assertEqual(tcp.dport, 2345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client VRF1 to service VRF0 + p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / + IP(src=self.pg6.remote_ip4, dst=self.pg0.local_ip4) / + TCP(sport=12346, dport=external_port)) + self.pg6.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.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service VRF0 back to client VRF1 + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg6.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.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg0.local_ip4) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client VRF0 to service VRF1 + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=external_addr) / + TCP(sport=12347, dport=external_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg5.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service VRF1 back to client VRF0 + p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / + IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=local_port, dport=12347)) + self.pg5.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, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client to server (both VRF1, no translation) + p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) / + IP(src=self.pg6.remote_ip4, dst=self.pg5.remote_ip4) / + TCP(sport=12348, dport=local_port)) + self.pg6.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg5.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from server back to client (both VRF1, no translation) + p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / + IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) / + TCP(sport=local_port, dport=12348)) + self.pg5.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg5.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client VRF1 to server VRF0 (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) / + TCP(sport=local_port, dport=12349)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg0.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from server VRF0 back to client VRF1 (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) / + TCP(sport=local_port, dport=12349)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg0.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client VRF0 to server VRF1 (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg5.remote_ip4) / + TCP(sport=12344, dport=local_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg5.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from server VRF1 back to client VRF0 (no translation) + p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) / + IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=local_port, dport=12344)) + self.pg5.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.pg5.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_session_timeout(self): + """ NAT44 session timeouts """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=240, icmp=5) + + max_sessions = 1000 + pkts = [] + for i in range(0, max_sessions): + src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + ICMP(id=1025, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + sleep(10) + + pkts = [] + for i in range(0, max_sessions): + src = "10.11.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=src, dst=self.pg1.remote_ip4) / + ICMP(id=1026, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(max_sessions) + + nsessions = 0 + users = self.vapi.nat44_user_dump() + for user in users: + nsessions = nsessions + user.nsessions + self.assertLess(nsessions, 2 * max_sessions) + + @unittest.skipUnless(running_extended_tests, "part of extended tests") + def test_session_rst_timeout(self): + """ NAT44 session RST timeouts """ + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=5, icmp=60) + + self.initiate_tcp_session(self.pg0, self.pg1) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="R")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + sleep(6) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in + 1, dport=self.tcp_external_port + 1, + flags="S")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + def test_syslog_sess(self): + """ Test syslog session creation and deletion """ + self.vapi.syslog_set_filter( + self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO) + self.vapi.syslog_set_sender(self.pg2.local_ip4, self.pg2.remote_ip4) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.tcp_port_out = capture[0][TCP].sport + capture = self.pg2.get_capture(1) + self.verify_syslog_sess(capture[0][Raw].load) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.nat44_add_address(self.nat_addr, is_add=0) + capture = self.pg2.get_capture(1) + self.verify_syslog_sess(capture[0][Raw].load, False) + + def test_ed_users_dump(self): + """ API test - nat44_user_dump """ + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, + is_add=1) + self.vapi.nat44_forwarding_enable_disable(enable=1) + + real_ip = self.pg0.remote_ip4 + alias_ip = self.nat_addr + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping(is_add=1, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 0) + 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) + + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 1) + static_user = users[0] + self.assertEqual(static_user.nstaticsessions, 3) + self.assertEqual(static_user.nsessions, 0) + + # 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 + + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 2) + if str(users[0].ip_address) == self.pg0.remote_hosts[0].ip4: + non_static_user = users[1] + static_user = users[0] + else: + non_static_user = users[0] + static_user = users[1] + self.assertEqual(static_user.nstaticsessions, 3) + self.assertEqual(static_user.nsessions, 0) + self.assertEqual(non_static_user.nstaticsessions, 0) + self.assertEqual(non_static_user.nsessions, 3) + + users = self.vapi.nat44_user_dump() + self.assertEqual(len(users), 2) + if str(users[0].ip_address) == self.pg0.remote_hosts[0].ip4: + non_static_user = users[1] + static_user = users[0] + else: + non_static_user = users[0] + static_user = users[1] + self.assertEqual(static_user.nstaticsessions, 3) + self.assertEqual(static_user.nsessions, 0) + self.assertEqual(non_static_user.nstaticsessions, 0) + self.assertEqual(non_static_user.nsessions, 3) + + finally: + self.vapi.nat44_forwarding_enable_disable(enable=0) + flags = self.config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping( + is_add=0, + local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) + + def show_commands_at_teardown(self): + self.logger.info(self.vapi.cli("show errors")) + self.logger.info(self.vapi.cli("show nat44 addresses")) + self.logger.info(self.vapi.cli("show nat44 interfaces")) + self.logger.info(self.vapi.cli("show nat44 static mappings")) + self.logger.info(self.vapi.cli("show nat44 interface address")) + self.logger.info(self.vapi.cli("show nat44 sessions detail")) + self.logger.info(self.vapi.cli("show nat44 hash tables detail")) + self.logger.info(self.vapi.cli("show nat timeouts")) + self.logger.info(self.vapi.cli("debug nat44 fib registration")) + + +class TestNAT44EndpointDependent3(MethodHolder): + """ Endpoint-Dependent mapping and filtering extra test cases """ + + max_translations = 50 + + @classmethod + def setUpClass(cls): + super(TestNAT44EndpointDependent3, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + + cls.nat_addr = '10.0.0.3' + + cls.create_pg_interfaces(range(2)) + + for i in cls.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def setUp(self): + super(TestNAT44EndpointDependent3, self).setUp() + flags = self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT + self.vapi.nat44_plugin_enable_disable( + sessions=self.max_translations, + flags=flags, enable=1) + self.vapi.nat_set_timeouts( + udp=1, tcp_established=7440, tcp_transitory=30, icmp=1) + + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, is_add=1) + + @classmethod + def tearDownClass(cls): + super(TestNAT44EndpointDependent3, cls).tearDownClass() + + def tearDown(self): + super(TestNAT44EndpointDependent3, self).tearDown() + if not self.vpp_dead: + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def init_tcp_session(self, in_if, out_if, sport, ext_dport): + # SYN packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=sport, dport=ext_dport, flags="S")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = out_if.get_capture(1) + p = capture[0] + tcp_port_out = p[TCP].sport + + # SYN + ACK packet out->in + p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / + IP(src=out_if.remote_ip4, dst=self.nat_addr) / + TCP(sport=ext_dport, dport=tcp_port_out, flags="SA")) + out_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + in_if.get_capture(1) + + # ACK packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=sport, dport=ext_dport, flags="A")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + out_if.get_capture(1) + + return tcp_port_out + + def test_lru_cleanup(self): + """ LRU cleanup algorithm """ + tcp_port_out = self.init_tcp_session(self.pg0, self.pg1, 2000, 80) + pkts = [] + for i in range(0, self.max_translations - 1): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + UDP(sport=7000+i, dport=80)) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(len(pkts)) + self.sleep(1.5, "wait for timeouts") + + pkts = [] + for i in range(0, self.max_translations - 1): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + ICMP(id=8000+i, type='echo-request')) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(len(pkts)) + + +class TestNAT44Out2InDPO(MethodHolder): + """ NAT44 Test Cases using out2in DPO """ + + @classmethod + def setUpClass(cls): + super(TestNAT44Out2InDPO, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.dst_ip4 = '192.168.70.1' + + cls.create_pg_interfaces(range(2)) + + cls.pg0.admin_up() + cls.pg0.config_ip4() + cls.pg0.resolve_arp() + + cls.pg1.admin_up() + cls.pg1.config_ip6() + cls.pg1.resolve_ndp() + + r1 = VppIpRoute(cls, "::", 0, + [VppRoutePath(cls.pg1.remote_ip6, + cls.pg1.sw_if_index)], + register=False) + r1.add_vpp_config() + + @classmethod + def tearDownClass(cls): + super(TestNAT44Out2InDPO, cls).tearDownClass() + + def setUp(self): + super(TestNAT44Out2InDPO, self).setUp() + flags = self.nat44_config_flags.NAT44_API_IS_OUT2IN_DPO + self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) + + def tearDown(self): + super(TestNAT44Out2InDPO, self).tearDown() + if not self.vpp_dead: + self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.cli("clear logging") + + def configure_xlat(self): + self.dst_ip6_pfx = '1:2:3::' + self.dst_ip6_pfx_n = socket.inet_pton(socket.AF_INET6, + self.dst_ip6_pfx) + self.dst_ip6_pfx_len = 96 + self.src_ip6_pfx = '4:5:6::' + self.src_ip6_pfx_n = socket.inet_pton(socket.AF_INET6, + self.src_ip6_pfx) + self.src_ip6_pfx_len = 96 + self.vapi.map_add_domain(self.dst_ip6_pfx_n, self.dst_ip6_pfx_len, + self.src_ip6_pfx_n, self.src_ip6_pfx_len, + '\x00\x00\x00\x00', 0) + + @unittest.skip('Temporary disabled') + def test_464xlat_ce(self): + """ Test 464XLAT CE with NAT44 """ + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.out2in_dpo) + + self.configure_xlat() + + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat44_add_del_address_range(first_ip_address=self.nat_addr_n, + last_ip_address=self.nat_addr_n, + vrf_id=0xFFFFFFFF, is_add=1) + + out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx, + self.dst_ip6_pfx_len) + out_dst_ip6 = self.compose_ip6(self.nat_addr, self.src_ip6_pfx, + self.src_ip6_pfx_len) + + try: + pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4) + 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_ip6(capture, nat_ip=out_dst_ip6, + dst_ip=out_src_ip6) + + pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6, + out_dst_ip6) + 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) + finally: + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, + flags=flags) + self.vapi.nat44_add_del_address_range( + first_ip_address=self.nat_addr_n, + last_ip_address=self.nat_addr_n, + vrf_id=0xFFFFFFFF) + + @unittest.skip('Temporary disabled') + def test_464xlat_ce_no_nat(self): + """ Test 464XLAT CE without NAT44 """ + + self.configure_xlat() + + out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx, + self.dst_ip6_pfx_len) + out_dst_ip6 = self.compose_ip6(self.pg0.remote_ip4, self.src_ip6_pfx, + self.src_ip6_pfx_len) + + pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4) + 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_ip6(capture, dst_ip=out_src_ip6, + nat_ip=out_dst_ip6, same_port=True) + + pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6, out_dst_ip6) + 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) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) -- cgit 1.2.3-korg