From ebdf190a9c4a514329de7e5e9b9178c3af055122 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Fri, 4 May 2018 03:57:42 -0700 Subject: [PATCH] NAT44: TCP connection close detection (VPP-1266) Change-Id: Iba1cc1179ee80478e29888790a6476571d1904dc Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 20 +++- src/plugins/nat/nat.api | 4 +- src/plugins/nat/nat.c | 34 ++++-- src/plugins/nat/nat.h | 30 ++++- src/plugins/nat/nat44_cli.c | 44 +++++++ src/plugins/nat/nat_api.c | 2 + src/plugins/nat/out2in.c | 10 ++ test/test_nat.py | 277 +++++++++++++++++++++++++++++++++++++------- 8 files changed, 364 insertions(+), 57 deletions(-) diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 3ec65e80cba..d3369b6b49c 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -532,6 +532,11 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, value.value); if (is_fwd_bypass_session (s)) { + if (ip->protocol == IP_PROTOCOL_TCP) + { + tcp_header_t *tcp = ip4_next_header(ip); + nat44_set_tcp_session_state (sm, s, tcp, thread_index); + } /* Per-user LRU list maintenance */ clib_dlist_remove (tsm->list_pool, s->per_user_index); clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, @@ -1369,7 +1374,15 @@ snat_in2out_lb (snat_main_t *sm, { s = pool_elt_at_index (tsm->sessions, s_value.value); if (is_fwd_bypass_session (s)) - return 0; + { + if (ip->protocol == IP_PROTOCOL_TCP) + nat44_set_tcp_session_state (sm, s, tcp, thread_index); + /* Per-user LRU list maintenance */ + clib_dlist_remove (tsm->list_pool, s->per_user_index); + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + return 0; + } } else { @@ -1457,6 +1470,7 @@ snat_in2out_lb (snat_main_t *sm, ip->dst_address.as_u32 = s->ext_host_addr.as_u32; } tcp->checksum = ip_csum_fold(sum); + nat44_set_tcp_session_state (sm, s, tcp, thread_index); } else { @@ -1715,6 +1729,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { @@ -1907,6 +1922,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp1->checksum = ip_csum_fold(sum1); + nat44_set_tcp_session_state (sm, s1, tcp1, thread_index); } else { @@ -2136,6 +2152,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { @@ -2668,6 +2685,7 @@ nat44_in2out_reass_node_fn (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 23c89adef7a..24aa5d1aa6f 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -13,7 +13,7 @@ * limitations under the License. */ -option version = "2.4.1"; +option version = "2.5.0"; /** * @file nat.api @@ -558,6 +558,7 @@ define nat44_user_session_dump { @param last_heard - last heard timer @param total_bytes - count of bytes sent through session @param total_pkts - count of pakets sent through session + @param is_closed - 1 if TCP session is closed */ define nat44_user_session_details { u32 context; @@ -570,6 +571,7 @@ define nat44_user_session_details { u64 last_heard; u64 total_bytes; u32 total_pkts; + u8 is_closed; }; /** \brief NAT44 load-balancing address and port pair diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 8e4d9df929c..68b43c05ea1 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -162,7 +162,8 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) ed_key.fib_index = 0; ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0) && + s->state != SNAT_SESSION_TCP_CLOSED) clib_warning ("in2out_ed key del failed"); return; } @@ -187,7 +188,8 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0) && + s->state != SNAT_SESSION_TCP_CLOSED) clib_warning ("out2in_ed key del failed"); ed_key.l_addr = s->in2out.addr; @@ -201,7 +203,8 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0) && + s->state != SNAT_SESSION_TCP_CLOSED) clib_warning ("in2out_ed key del failed"); } @@ -217,7 +220,7 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) s->in2out.fib_index); /* Twice NAT address and port for external host */ - if (is_twice_nat_session (s)) + if (is_twice_nat_session (s) && s->state != SNAT_SESSION_TCP_CLOSED) { for (i = 0; i < vec_len (sm->twice_nat_addresses); i++) { @@ -238,16 +241,18 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) /* Session lookup tables */ kv.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0)) + if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0) && + s->state != SNAT_SESSION_TCP_CLOSED) clib_warning ("in2out key del failed"); kv.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0)) + if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0) && + s->state != SNAT_SESSION_TCP_CLOSED) clib_warning ("out2in key del failed"); if (snat_is_session_static (s)) return; - if (s->outside_address_index != ~0) + if (s->outside_address_index != ~0 && s->state != SNAT_SESSION_TCP_CLOSED) snat_free_outside_address_and_port (sm->addresses, thread_index, &s->out2in, s->outside_address_index); } @@ -333,6 +338,11 @@ nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index) s->flags = 0; s->total_bytes = 0; s->total_pkts = 0; + s->state = 0; + s->ext_host_addr.as_u32 = 0; + s->ext_host_port = 0; + s->ext_host_nat_addr.as_u32 = 0; + s->ext_host_nat_port = 0; } else { @@ -2696,12 +2706,18 @@ u8 * format_snat_session (u8 * s, va_list * args) else { if (sess->ext_host_addr.as_u32) - s = format (s, " external host %U\n", - format_ip4_address, &sess->ext_host_addr); + s = format (s, " external host %U:%u\n", + format_ip4_address, &sess->ext_host_addr, + clib_net_to_host_u16 (sess->ext_host_port)); } s = format (s, " last heard %.2f\n", sess->last_heard); s = format (s, " total pkts %d, total bytes %lld\n", sess->total_pkts, sess->total_bytes); + if (sess->in2out.protocol == SNAT_PROTOCOL_TCP) + { + s = format (s, " state %s\n", + sess->state == SNAT_SESSION_TCP_CLOSED ? "closed" : "open"); + } if (snat_is_session_static (sess)) s = format (s, " static translation\n"); else diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 775376fcbab..61d26b2076c 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -115,8 +115,10 @@ typedef enum { _(3, TCP_ESTABLISHED, "tcp-established") \ _(4, TCP_FIN_WAIT, "tcp-fin-wait") \ _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \ - _(6, TCP_LAST_ACK, "tcp-last-ack") \ - _(7, ICMP_ACTIVE, "icmp-active") + _(6, TCP_CLOSING, "tcp-closing") \ + _(7, TCP_LAST_ACK, "tcp-last-ack") \ + _(8, TCP_CLOSED, "tcp-closed") \ + _(9, ICMP_ACTIVE, "icmp-active") typedef enum { #define _(v, N, s) SNAT_SESSION_##N = v, @@ -164,6 +166,9 @@ typedef CLIB_PACKED(struct { /* External hos address and port after translation */ ip4_address_t ext_host_nat_addr; /* 74-77 */ u16 ext_host_nat_port; /* 78-79 */ + + /* TCP session state */ + u8 state; }) snat_session_t; @@ -681,4 +686,25 @@ user_session_increment(snat_main_t *sm, snat_user_t *u, u8 is_static) } } +always_inline void +nat44_set_tcp_session_state(snat_main_t * sm, snat_session_t * ses, + tcp_header_t * tcp, u32 thread_index) +{ + if (tcp->flags & TCP_FLAG_FIN && ses->state == SNAT_SESSION_UNKNOWN) + ses->state = SNAT_SESSION_TCP_FIN_WAIT; + else if (tcp->flags & TCP_FLAG_FIN && ses->state == SNAT_SESSION_TCP_FIN_WAIT) + ses->state = SNAT_SESSION_TCP_CLOSING; + else if (tcp->flags & TCP_FLAG_ACK && ses->state == SNAT_SESSION_TCP_FIN_WAIT) + ses->state = SNAT_SESSION_TCP_CLOSE_WAIT; + else if (tcp->flags & TCP_FLAG_FIN && ses->state == SNAT_SESSION_TCP_CLOSE_WAIT) + ses->state = SNAT_SESSION_TCP_LAST_ACK; + else if (tcp->flags & TCP_FLAG_ACK && ses->state == SNAT_SESSION_TCP_CLOSING) + ses->state = SNAT_SESSION_TCP_LAST_ACK; + else if (tcp->flags & TCP_FLAG_ACK && ses->state == SNAT_SESSION_TCP_LAST_ACK) + { + nat_free_session_data (sm, ses, thread_index); + ses->state = SNAT_SESSION_TCP_CLOSED; + } +} + #endif /* __included_snat_h__ */ diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 2f1e9a019ac..f07b6dde215 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -157,6 +157,38 @@ done: return error; } +static clib_error_t * +nat44_show_hash_commnad_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + int i; + int verbose = 0; + + if (unformat (input, "detail")) + verbose = 1; + else if (unformat (input, "verbose")) + verbose = 2; + + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed, verbose); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->static_mapping_by_local, + verbose); + vlib_cli_output (vm, "%U", + format_bihash_8_8, &sm->static_mapping_by_external, + verbose); + vec_foreach_index (i, sm->per_thread_data) + { + tsm = vec_elt_at_index (sm->per_thread_data, i); + vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->in2out, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->out2in, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->user_hash, verbose); + } + + return 0; +} + static clib_error_t * nat44_set_alloc_addr_and_port_alg_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -1488,6 +1520,18 @@ VLIB_CLI_COMMAND (nat44_set_alloc_addr_and_port_alg_command, static) = { .function = nat44_set_alloc_addr_and_port_alg_command_fn, }; +/*? + * @cliexpar + * @cliexstart{show nat44 hash tables} + * Show NAT44 hash tables + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_hash, static) = { + .path = "show nat44 hash tables", + .short_help = "show nat44 hash tables [detail|verbose]", + .function = nat44_show_hash_commnad_fn, +}; + /*? * @cliexpar * @cliexstart{nat44 add address} diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 4c815f0bc13..a1d70f8d46e 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -1235,6 +1235,8 @@ send_nat44_user_session_details (snat_session_t * s, rmp->inside_port = s->in2out.port; rmp->protocol = ntohs (snat_proto_to_ip_proto (s->in2out.protocol)); } + if (s->in2out.protocol == SNAT_PROTOCOL_TCP) + rmp->is_closed = s->state == SNAT_SESSION_TCP_CLOSED ? 1 : 0; vl_api_send_msg (reg, (u8 *) rmp); } diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index a0128b8d61d..c0f5a3c835a 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -407,6 +407,11 @@ create_bypass_for_fwd(snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index, clib_warning ("in2out_ed key add failed"); } + if (ip->protocol == IP_PROTOCOL_TCP) + { + tcp_header_t *tcp = ip4_next_header(ip); + nat44_set_tcp_session_state (sm, s, tcp, thread_index); + } /* Per-user LRU list maintenance */ clib_dlist_remove (tsm->list_pool, s->per_user_index); clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, @@ -1058,6 +1063,7 @@ snat_out2in_lb (snat_main_t *sm, ip->src_address.as_u32 = s->ext_host_nat_addr.as_u32; } tcp->checksum = ip_csum_fold(sum); + nat44_set_tcp_session_state (sm, s, tcp, thread_index); } else { @@ -1300,6 +1306,7 @@ snat_out2in_node_fn (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { @@ -1478,6 +1485,7 @@ snat_out2in_node_fn (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp1->checksum = ip_csum_fold(sum1); + nat44_set_tcp_session_state (sm, s1, tcp1, thread_index); } else { @@ -1692,6 +1700,7 @@ snat_out2in_node_fn (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { @@ -1960,6 +1969,7 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm, ip4_header_t /* cheat */, length /* changed member */); tcp0->checksum = ip_csum_fold(sum0); + nat44_set_tcp_session_state (sm, s0, tcp0, thread_index); } else { diff --git a/test/test_nat.py b/test/test_nat.py index 51a60d1b56f..c4018cf66d8 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -693,6 +693,50 @@ class MethodHolder(VppTestCase): p = (ip / UDP(buffer.getvalue())) 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 @@ -910,6 +954,7 @@ class TestNAT44(MethodHolder): cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr) cls.ipfix_src_port = 4739 cls.ipfix_domain_id = 1 + cls.tcp_external_port = 80 cls.create_pg_interfaces(range(10)) cls.interfaces = list(cls.pg_interfaces[0:4]) @@ -4080,6 +4125,194 @@ class TestNAT44(MethodHolder): self.verify_ipfix_max_fragments_ip4(data, 0, self.pg0.remote_ip4n) + def test_tcp_session_close_in(self): + """ Close TCP session from inside network """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + start_sessnum = len(sessions) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # close the session from inside + try: + # 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")) + 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")) + 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")) + 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")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + self.initiate_tcp_session(self.pg0, self.pg1) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 2) + except: + self.logger.error("TCP session termination failed") + raise + + def test_tcp_session_close_out(self): + """ Close TCP session from outside network """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + start_sessnum = len(sessions) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # close the session from outside + try: + # 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")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + pkts = [] + + # 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")) + pkts.append(p) + + # 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")) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(2) + + # 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")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + self.initiate_tcp_session(self.pg0, self.pg1) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 2) + except: + self.logger.error("TCP session termination failed") + raise + + def test_tcp_session_close_simultaneous(self): + """ Close TCP session from inside network """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + start_sessnum = len(sessions) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # close the session from inside + try: + # 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")) + 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")) + 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")) + 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")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + self.initiate_tcp_session(self.pg0, self.pg1) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 2) + except: + self.logger.error("TCP session termination failed") + raise + def tearDown(self): super(TestNAT44, self).tearDown() if not self.vpp_dead: @@ -4331,50 +4564,6 @@ class TestDeterministicNAT(MethodHolder): "(outside network):", packet)) raise - 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_max_entries_per_user(self, data): """ Verify IPFIX maximum entries per user exceeded event -- 2.16.6