diff options
author | Klement Sekera <ksekera@cisco.com> | 2022-01-10 21:57:27 +0000 |
---|---|---|
committer | Ole Tr�an <otroan@employees.org> | 2022-01-24 12:59:46 +0000 |
commit | 56c492aa0502751de2dd9d890096a82c5f04776d (patch) | |
tree | a2b8a1c300853070b26f9953a10bd1a4e41f3bdd /test | |
parent | 4634d02501235d3803a17839eeaf076110abcb18 (diff) |
nat: TCP state tracking based on RFC 7857/RFC 6146
Implement proper state machine based on above RFCs. ACKs to SYNs/FINs
are no longer required/tracked. This is more friendly to peers and
accounts for lost packets and retransmits.
This change also means that all traffic is translated and forwarded
while in transitory timeout, which helps delivering e.g. retransmitted
FINs, FINACKs and other messages.
Also support reopening a session in transitory timeout after seeing both
FINs by seeing both SYNs again. This helps quick connection
reestablishment if the peers want to.
Type: improvement
Signed-off-by: Klement Sekera <ksekera@cisco.com>
Signed-off-by: Miklos Tirpak <miklos.tirpak@gmail.com>
Change-Id: Ibf521c79463472db97e593bfa02b32b4a06dfd2a
Diffstat (limited to 'test')
-rw-r--r-- | test/framework.py | 4 | ||||
-rw-r--r-- | test/test_nat44_ed.py | 620 |
2 files changed, 464 insertions, 160 deletions
diff --git a/test/framework.py b/test/framework.py index f4b168b3c75..8c0df282153 100644 --- a/test/framework.py +++ b/test/framework.py @@ -26,7 +26,7 @@ from abc import ABC, abstractmethod from struct import pack, unpack import scapy.compat -from scapy.packet import Raw +from scapy.packet import Raw, Packet import hook as hookmodule from vpp_pg_interface import VppPGInterface from vpp_sub_interface import VppSubInterface @@ -1308,7 +1308,7 @@ class VppTestCase(CPUInterface, unittest.TestCase): def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None, trace=True): if not n_rx: - n_rx = len(pkts) + n_rx = 1 if isinstance(pkts, Packet) else len(pkts) self.pg_send(intf, pkts, worker=worker, trace=trace) rx = output.get_capture(n_rx) if trace: diff --git a/test/test_nat44_ed.py b/test/test_nat44_ed.py index 5684f5921ba..9bb803e4435 100644 --- a/test/test_nat44_ed.py +++ b/test/test_nat44_ed.py @@ -881,7 +881,7 @@ class TestNAT44ED(VppTestCase): sessions = self.vapi.nat44_user_session_dump(server.ip4, 0) self.assertEqual(len(sessions), 0) - def verify_syslog_sess(self, data, is_add=True, is_ip6=False): + def verify_syslog_sess(self, data, msgid, is_ip6=False): message = data.decode('utf-8') try: message = SyslogMessage.parse(message) @@ -891,7 +891,7 @@ class TestNAT44ED(VppTestCase): else: self.assertEqual(message.severity, SyslogSeverity.info) self.assertEqual(message.appname, 'NAT') - self.assertEqual(message.msgid, 'SADD' if is_add else 'SDEL') + self.assertEqual(message.msgid, msgid) sd_params = message.sd.get('nsess') self.assertTrue(sd_params is not None) if is_ip6: @@ -2550,21 +2550,70 @@ class TestNAT44EDMW(TestNAT44ED): 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) + self.send_and_expect(self.pg0, p, self.pg1) self.virtual_sleep(6) + # The session is already closed 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, + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="P")) + self.send_and_assert_no_replies(self.pg0, p, self.pg1) + + # The session can be re-opened + 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")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) + self.send_and_expect(self.pg0, p, self.pg1) + + def test_session_rst_established_timeout(self): + """ NAT44ED session RST timeouts """ + + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=5, icmp=60) + + self.init_tcp_session(self.pg0, self.pg1, self.tcp_port_in, + self.tcp_external_port) + + # Wait at least the transitory time, the session is in established + # state anyway. RST followed by a data packet should keep it + # established. + self.virtual_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, dport=self.tcp_external_port, + flags="R")) + self.send_and_expect(self.pg0, p, 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="P")) + self.send_and_expect(self.pg0, p, self.pg1) + + # State is established, session should be still open after 6 seconds + self.virtual_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, dport=self.tcp_external_port, + flags="R")) + self.send_and_expect(self.pg0, p, self.pg1) + + # State is transitory, session should be closed after 6 seconds + self.virtual_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, dport=self.tcp_external_port, + flags="P")) + self.send_and_assert_no_replies(self.pg0, p, self.pg1) def test_dynamic_out_of_ports(self): """ NAT44ED dynamic translation test: out of ports """ @@ -3000,7 +3049,10 @@ class TestNAT44EDMW(TestNAT44ED): self.assertEqual(server2_n, 0) self.assertGreater(server3_n, 0) - def test_syslog_sess(self): + # put zzz in front of syslog test name so that it runs as a last test + # setting syslog sender cannot be undone and if it is set, it messes + # with self.send_and_assert_no_replies functionality + def test_zzz_syslog_sess(self): """ NAT44ED Test syslog session creation and deletion """ self.vapi.syslog_set_filter( self.syslog_severity.SYSLOG_API_SEVERITY_INFO) @@ -3019,13 +3071,72 @@ class TestNAT44EDMW(TestNAT44ED): capture = self.pg1.get_capture(1) self.tcp_port_out = capture[0][TCP].sport capture = self.pg3.get_capture(1) - self.verify_syslog_sess(capture[0][Raw].load) + self.verify_syslog_sess(capture[0][Raw].load, 'SADD') self.pg_enable_capture(self.pg_interfaces) self.pg_start() self.nat_add_address(self.nat_addr, is_add=0) capture = self.pg3.get_capture(1) - self.verify_syslog_sess(capture[0][Raw].load, False) + self.verify_syslog_sess(capture[0][Raw].load, 'SDEL') + + # put zzz in front of syslog test name so that it runs as a last test + # setting syslog sender cannot be undone and if it is set, it messes + # with self.send_and_assert_no_replies functionality + def test_zzz_syslog_sess_reopen(self): + """ Syslog events for session reopen """ + 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.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + + # SYN 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=self.tcp_external_port)) + capture = self.send_and_expect(self.pg0, p, self.pg1)[0] + self.tcp_port_out = capture[0][TCP].sport + capture = self.pg3.get_capture(1) + self.verify_syslog_sess(capture[0][Raw].load, 'SADD') + + # SYN out2in + 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.send_and_expect(self.pg1, p, self.pg0) + + # FIN 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=self.tcp_external_port, + flags="F")) + self.send_and_expect(self.pg0, p, self.pg1) + + # FIN out2in + 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, + flags="F")) + self.send_and_expect(self.pg1, p, self.pg0) + + # SYN 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=self.tcp_external_port)) + self.send_and_expect(self.pg0, p, self.pg1) + + # SYN out2in + 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.send_and_expect(self.pg1, p, self.pg0) + + # 2 records should be produced - first one del & add + capture = self.pg3.get_capture(2) + self.verify_syslog_sess(capture[0][Raw].load, 'SDEL') + self.verify_syslog_sess(capture[1][Raw].load, 'SADD') def test_twice_nat_interface_addr(self): """ NAT44ED Acquire twice NAT addresses from interface """ @@ -3150,10 +3261,7 @@ class TestNAT44EDMW(TestNAT44ED): 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) + capture = self.send_and_expect(self.pg1, p, self.pg0, n_rx=1) p = capture[0] tcp_port = p[TCP].sport @@ -3161,58 +3269,43 @@ class TestNAT44EDMW(TestNAT44ED): 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) + self.send_and_expect(self.pg0, p, self.pg1, n_rx=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) + self.send_and_expect(self.pg1, p, self.pg0, n_rx=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) + self.send_and_expect(self.pg0, p, self.pg1, n_rx=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) + self.send_and_expect(self.pg1, p, self.pg0, n_rx=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) + self.send_and_expect(self.pg0, p, self.pg1, n_rx=1) - # session now in transitory timeout - # try SYN packet out->in - should be dropped + # session now in transitory timeout, but traffic still flows + # try FIN 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")) + TCP(sport=33898, dport=80, flags="F")) self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() self.virtual_sleep(new_transitory, "wait for transitory timeout") - self.pg0.assert_nothing_captured(0) + self.pg0.get_capture(1) # session should still exist sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) @@ -3223,13 +3316,9 @@ class TestNAT44EDMW(TestNAT44ED): 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.send_and_assert_no_replies(self.pg1, p) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - start_sessnum, 0) - self.pg0.assert_nothing_captured(0) def test_tcp_session_close_in(self): """ NAT44ED Close TCP session from inside network """ @@ -3258,11 +3347,7 @@ class TestNAT44EDMW(TestNAT44ED): IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / TCP(sport=in_port, dport=ext_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) - + self.send_and_expect(self.pg0, p, self.pg1) pkts = [] # ACK packet out -> in @@ -3279,67 +3364,40 @@ class TestNAT44EDMW(TestNAT44ED): 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) + self.send_and_expect(self.pg1, pkts, self.pg0) # 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=in_port, dport=ext_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) + self.send_and_expect(self.pg0, p, self.pg1) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 1) - out2in_drops = self.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - in2out_drops = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - - # extra FIN packet out -> in - this should be dropped + # retransmit 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=ext_port, dport=out_port, 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() + self.send_and_expect(self.pg1, p, self.pg0) - # extra ACK packet in -> out - this should be dropped + # retransmit 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=in_port, dport=ext_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.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats - out2in_drops, 1) - stats = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats - in2out_drops, 1) + self.send_and_expect(self.pg0, p, self.pg1) self.virtual_sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped + # retransmit 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=in_port, dport=ext_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() + self.send_and_assert_no_replies(self.pg0, p) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 0) @@ -3399,54 +3457,32 @@ class TestNAT44EDMW(TestNAT44ED): sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 1) - out2in_drops = self.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - in2out_drops = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - - # extra FIN packet out -> in - this should be dropped + # retransmit 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=ext_port, dport=out_port, flags="FA", seq=300, ack=101)) + self.send_and_expect(self.pg1, p, self.pg0) - 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 + # retransmit 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=in_port, dport=ext_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.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats - out2in_drops, 1) - stats = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats - in2out_drops, 1) + self.send_and_expect(self.pg0, p, self.pg1) self.virtual_sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped + # retransmit 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=in_port, dport=ext_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() + self.send_and_assert_no_replies(self.pg0, p) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 0) def test_tcp_session_close_simultaneous(self): - """ NAT44ED Close TCP session from inside network """ + """ Simultaneous TCP close from both sides """ in_port = self.tcp_port_in ext_port = 10505 @@ -3471,90 +3507,226 @@ class TestNAT44EDMW(TestNAT44ED): IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / TCP(sport=in_port, dport=ext_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) + self.send_and_expect(self.pg0, p, 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=ext_port, dport=out_port, 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) + self.send_and_expect(self.pg1, p, self.pg0) # 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=in_port, dport=ext_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) + self.send_and_expect(self.pg0, p, self.pg1) # 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=ext_port, dport=out_port, 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) + self.send_and_expect(self.pg1, p, self.pg0) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 1) - out2in_drops = self.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - in2out_drops = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - - # extra FIN packet out -> in - this should be dropped + # retransmit 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=ext_port, dport=out_port, flags="FA", seq=300, ack=101)) + self.send_and_expect(self.pg1, p, self.pg0) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.assert_nothing_captured() + # retransmit 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=in_port, dport=ext_port, + flags="A", seq=101, ack=301)) + self.send_and_expect(self.pg0, p, self.pg1) - # extra ACK packet in -> out - this should be dropped + self.virtual_sleep(3) + # retransmit 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=in_port, dport=ext_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() + self.pg_send(self.pg0, p) + self.send_and_assert_no_replies(self.pg0, p) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 0) + + def test_tcp_session_half_reopen_inside(self): + """ TCP session in FIN/FIN state not reopened by in2out SYN only """ + in_port = self.tcp_port_in + ext_port = 10505 - stats = self.get_err_counter( - '/err/nat44-ed-out2in/drops due to TCP in transitory timeout') - self.assertEqual(stats - out2in_drops, 1) - stats = self.get_err_counter( - '/err/nat44-ed-in2out/drops due to TCP in transitory timeout') - self.assertEqual(stats - in2out_drops, 1) + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + in_port, ext_port, proto=IP_PROTOS.tcp, + flags=self.config_flags.NAT_IS_TWICE_NAT) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + session_n = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port) + + # 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=in_port, dport=ext_port, + flags="FA", seq=100, ack=300)) + self.send_and_expect(self.pg0, p, 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=ext_port, dport=out_port, + flags="FA", seq=300, ack=100)) + self.send_and_expect(self.pg1, p, self.pg0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 1) + + # send SYN 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=in_port, dport=ext_port, + flags="S", seq=101, ack=301)) + self.send_and_expect(self.pg0, p, self.pg1) self.virtual_sleep(3) - # extra ACK packet in -> out - this will cause session to be wiped + # send ACK packet in -> out - session should 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=in_port, dport=ext_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() + self.send_and_assert_no_replies(self.pg0, p, self.pg1) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 0) + + def test_tcp_session_half_reopen_outside(self): + """ TCP session in FIN/FIN state not reopened by out2in SYN only """ + in_port = self.tcp_port_in + ext_port = 10505 + + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + in_port, ext_port, proto=IP_PROTOS.tcp, + flags=self.config_flags.NAT_IS_TWICE_NAT) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + session_n = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port) + + # 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=in_port, dport=ext_port, + flags="FA", seq=100, ack=300)) + self.send_and_expect(self.pg0, p, 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=ext_port, dport=out_port, + flags="FA", seq=300, ack=100)) + self.send_and_expect(self.pg1, p, self.pg0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 1) + + # send SYN 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=ext_port, dport=out_port, + flags="S", seq=300, ack=101)) + self.send_and_expect(self.pg1, p, self.pg0) + + self.virtual_sleep(3) + # send ACK packet in -> out - session should 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=in_port, dport=ext_port, + flags="A", seq=101, ack=301)) + self.send_and_assert_no_replies(self.pg0, p, self.pg1) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions) - session_n, 0) + def test_tcp_session_reopen(self): + """ TCP session in FIN/FIN state reopened by SYN from both sides """ + in_port = self.tcp_port_in + ext_port = 10505 + + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + self.nat_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + in_port, ext_port, proto=IP_PROTOS.tcp, + flags=self.config_flags.NAT_IS_TWICE_NAT) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + session_n = len(sessions) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=2, icmp=5) + + out_port = self.init_tcp_session(self.pg0, self.pg1, in_port, ext_port) + + # 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=in_port, dport=ext_port, + flags="FA", seq=100, ack=300)) + self.send_and_expect(self.pg0, p, 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=ext_port, dport=out_port, + flags="FA", seq=300, ack=100)) + self.send_and_expect(self.pg1, p, self.pg0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 1) + + # send SYN 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=ext_port, dport=out_port, + flags="S", seq=300, ack=101)) + self.send_and_expect(self.pg1, p, self.pg0) + + # send SYN 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=in_port, dport=ext_port, + flags="S", seq=101, ack=301)) + self.send_and_expect(self.pg0, p, self.pg1) + + self.virtual_sleep(3) + # send ACK packet in -> out - should be forwarded and session alive + 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=in_port, dport=ext_port, + flags="A", seq=101, ack=301)) + self.send_and_expect(self.pg0, p, self.pg1) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.assertEqual(len(sessions) - session_n, 1) + def test_dynamic_vrf(self): """ NAT44ED dynamic translation test: different VRF""" @@ -3837,6 +4009,138 @@ class TestNAT44EDMW(TestNAT44ED): self.logger.info(ppp("p2 packet:", p2)) self.logger.info(ppp("capture packet:", capture)) + def test_tcp_session_open_retransmit1(self): + """ NAT44ED Open TCP session with SYN,ACK retransmit 1 + + The client does not receive the [SYN,ACK] or the + ACK from the client is lost. Therefore, the [SYN, ACK] + is retransmitted by the server. + """ + + in_port = self.tcp_port_in + ext_port = self.tcp_external_port + payload = "H" * 10 + + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=5, icmp=60) + # SYN 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=in_port, dport=ext_port, flags="S")) + p = self.send_and_expect(self.pg0, p, self.pg1)[0] + out_port = p[TCP].sport + + # SYN + 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=ext_port, dport=out_port, flags="SA")) + self.send_and_expect(self.pg1, p, self.pg0) + + # ACK in->out does not arrive + + # resent SYN + 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=ext_port, dport=out_port, flags="SA")) + self.send_and_expect(self.pg1, p, self.pg0) + + # 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=in_port, dport=ext_port, flags="A")) + self.send_and_expect(self.pg0, p, self.pg1) + + # Verify that the data can be transmitted after the transitory time + self.virtual_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=in_port, dport=ext_port, flags="PA") / + Raw(payload)) + self.send_and_expect(self.pg0, p, self.pg1) + + def test_tcp_session_open_retransmit2(self): + """ NAT44ED Open TCP session with SYN,ACK retransmit 2 + + The ACK is lost to the server after the TCP session is opened. + Data is sent by the client, then the [SYN,ACK] is + retransmitted by the server. + """ + + in_port = self.tcp_port_in + ext_port = self.tcp_external_port + payload = "H" * 10 + + self.nat_add_address(self.nat_addr) + self.nat_add_inside_interface(self.pg0) + self.nat_add_outside_interface(self.pg1) + + self.vapi.nat_set_timeouts(udp=300, tcp_established=7440, + tcp_transitory=5, icmp=60) + # SYN 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=in_port, dport=ext_port, flags="S")) + p = self.send_and_expect(self.pg0, p, self.pg1)[0] + out_port = p[TCP].sport + + # SYN + 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=ext_port, dport=out_port, flags="SA")) + self.send_and_expect(self.pg1, p, self.pg0) + + # ACK packet in->out -- not received by the server + 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=in_port, dport=ext_port, flags="A")) + self.send_and_expect(self.pg0, p, self.pg1) + + # PUSH + 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=in_port, dport=ext_port, flags="PA") / + Raw(payload)) + self.send_and_expect(self.pg0, p, self.pg1) + + # resent SYN + 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=ext_port, dport=out_port, flags="SA")) + self.send_and_expect(self.pg1, p, self.pg0) + + # resent 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=in_port, dport=ext_port, flags="A")) + self.send_and_expect(self.pg0, p, self.pg1) + + # resent PUSH + 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=in_port, dport=ext_port, flags="PA") / + Raw(payload)) + self.send_and_expect(self.pg0, p, self.pg1) + + # 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=ext_port, dport=out_port, flags="A")) + self.send_and_expect(self.pg1, p, self.pg0) + + # Verify that the data can be transmitted after the transitory time + self.virtual_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=in_port, dport=ext_port, flags="PA") / + Raw(payload)) + self.send_and_expect(self.pg0, p, self.pg1) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) |