X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_nat.py;h=c4018cf66d864c9dfc8760bffcc0abe7f381ff95;hb=ebdf190;hp=d2bc4569af0616b85208c4f58e0f1688c0dacbb2;hpb=e82488f10f20f464961c5c7b381b4a419bca0bbc;p=vpp.git diff --git a/test/test_nat.py b/test/test_nat.py index d2bc4569af0..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]) @@ -922,6 +967,9 @@ class TestNAT44(MethodHolder): 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(10, is_add=1) cls.vapi.ip_table_add_del(20, is_add=1) @@ -1029,7 +1077,10 @@ class TestNAT44(MethodHolder): vrf_id=sm.vrf_id, protocol=sm.protocol, twice_nat=sm.twice_nat, + self_twice_nat=sm.self_twice_nat, out2in_only=sm.out2in_only, + tag=sm.tag, + external_sw_if_index=sm.external_sw_if_index, is_add=0) lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump() @@ -1040,7 +1091,9 @@ class TestNAT44(MethodHolder): lb_sm.protocol, vrf_id=lb_sm.vrf_id, twice_nat=lb_sm.twice_nat, + self_twice_nat=lb_sm.self_twice_nat, out2in_only=lb_sm.out2in_only, + tag=lb_sm.tag, is_add=0, local_num=0, locals=[]) @@ -1069,7 +1122,8 @@ class TestNAT44(MethodHolder): 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, twice_nat=0, out2in_only=0): + proto=0, twice_nat=0, self_twice_nat=0, + out2in_only=0, tag=""): """ Add/delete NAT44 static mapping @@ -1082,7 +1136,11 @@ class TestNAT44(MethodHolder): :param external_sw_if_index: External interface instead of IP address :param proto: IP protocol (Mandatory if port specified) :param twice_nat: 1 if translate external host address and port + :param self_twice_nat: 1 if translate external host address and port + whenever external host address equals + local address of internal host :param out2in_only: if 1 rule is matching only out2in direction + :param tag: Opaque string tag """ addr_only = 1 if local_port and external_port: @@ -1099,7 +1157,9 @@ class TestNAT44(MethodHolder): vrf_id, proto, twice_nat, + self_twice_nat, out2in_only, + tag, is_add) def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): @@ -1376,6 +1436,12 @@ class TestNAT44(MethodHolder): 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) + sm = self.vapi.nat44_static_mapping_dump() + self.assertEqual(len(sm), 1) + self.assertEqual((sm[0].tag).split('\0', 1)[0], '') + 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) @@ -1400,11 +1466,15 @@ class TestNAT44(MethodHolder): 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) + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip, tag=tag) 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) + sm = self.vapi.nat44_static_mapping_dump() + self.assertEqual(len(sm), 1) + self.assertEqual((sm[0].tag).split('\0', 1)[0], tag) # out2in pkts = self.create_stream_out(self.pg1, nat_ip) @@ -1531,6 +1601,24 @@ class TestNAT44(MethodHolder): 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) / @@ -1551,6 +1639,24 @@ class TestNAT44(MethodHolder): 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) / @@ -1626,6 +1732,37 @@ class TestNAT44(MethodHolder): 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) + 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) + + # 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_ip4n, 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 """ @@ -1725,10 +1862,36 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - # multiple clients + @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_n = socket.inet_pton(socket.AF_INET, self.nat_addr) + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + + locals = [{'addr': server1.ip4n, + 'port': local_port, + 'probability': 90}, + {'addr': server2.ip4n, + 'port': local_port, + 'probability': 10}] + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, + external_port, + IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + 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) + server1_n = 0 server2_n = 0 - clients = ip4_range(self.pg1.remote_ip4, 10, 20) + 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) / @@ -2338,28 +2501,62 @@ class TestNAT44(MethodHolder): def test_interface_addr_static_mapping(self): """ Static mapping with addresses from interface """ + tag = "testTAG" + self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index) self.nat44_add_static_mapping( '1.2.3.4', - external_sw_if_index=self.pg7.sw_if_index) + 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).split('\0', 1)[0], tag) # configure interface address and check static mappings self.pg7.config_ip4() static_mappings = self.vapi.nat44_static_mapping_dump() - self.assertEqual(1, len(static_mappings)) - self.assertEqual(static_mappings[0].external_ip_address[0:4], - self.pg7.local_ip4n) - self.assertEqual(0xFFFFFFFF, static_mappings[0].external_sw_if_index) + self.assertEqual(2, len(static_mappings)) + resolved = False + for sm in static_mappings: + if sm.external_sw_if_index == 0xFFFFFFFF: + self.assertEqual(sm.external_ip_address[0:4], + self.pg7.local_ip4n) + self.assertEqual((sm.tag).split('\0', 1)[0], 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).split('\0', 1)[0], 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(sm.external_ip_address[0:4], + self.pg7.local_ip4n) + self.assertEqual((sm.tag).split('\0', 1)[0], 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): @@ -2382,17 +2579,23 @@ class TestNAT44(MethodHolder): # configure interface address and check identity mappings self.pg7.config_ip4() identity_mappings = self.vapi.nat44_identity_mapping_dump() - self.assertEqual(1, len(identity_mappings)) - self.assertEqual(identity_mappings[0].ip_address, - self.pg7.local_ip4n) - self.assertEqual(0xFFFFFFFF, identity_mappings[0].sw_if_index) - self.assertEqual(port, identity_mappings[0].port) - self.assertEqual(IP_PROTOS.tcp, identity_mappings[0].protocol) + resolved = False + self.assertEqual(2, len(identity_mappings)) + for sm in identity_mappings: + if sm.sw_if_index == 0xFFFFFFFF: + self.assertEqual(identity_mappings[0].ip_address, + self.pg7.local_ip4n) + 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(0, len(identity_mappings)) + 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/delted """ @@ -3184,98 +3387,346 @@ class TestNAT44(MethodHolder): 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 + 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(1) self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, + self.vapi.nat44_add_del_identity_mapping(ip=self.pg1.remote_ip4n) + self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, is_inside=0) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) - # 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) + # 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.pg9.get_capture(1) + capture = self.pg0.get_capture(1) p = capture[0] + server = None 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.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) self.check_tcp_checksum(p) self.check_ip_checksum(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) + # 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.pg9.get_capture(1) + capture = self.pg1.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.assertEqual(ip.src, external_addr) + self.assertEqual(tcp.sport, external_port) self.check_tcp_checksum(p) self.check_ip_checksum(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - def test_del_session(self): - """ Delete NAT44 session """ + # 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) + 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) + + # 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(1) 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) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + # session initiaded 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) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) - nsessions = len(sessions) - - self.vapi.nat44_del_session(sessions[0].inside_ip_address, - sessions[0].inside_port, - sessions[0].protocol) - self.vapi.nat44_del_session(sessions[1].outside_ip_address, - sessions[1].outside_port, - sessions[1].protocol, - is_in=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) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) - self.assertEqual(nsessions - len(sessions), 2) + tcp_port_out = self.tcp_port_out + udp_port_out = self.udp_port_out + icmp_id_out = self.icmp_id_out - def test_set_get_reass(self): - """ NAT44 set/get virtual fragmentation reassembly """ - reas_cfg1 = self.vapi.nat_get_reass() + # session initiaded from remote host - do not translate + 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) - self.vapi.nat_set_reass(timeout=reas_cfg1.ip4_timeout + 5, - max_reass=reas_cfg1.ip4_max_reass * 2, - max_frag=reas_cfg1.ip4_max_frag * 2) + 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) - reas_cfg2 = self.vapi.nat_get_reass() + 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(1) + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, + is_inside=0) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + + 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.check_tcp_checksum(p) + self.check_ip_checksum(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.check_tcp_checksum(p) + self.check_ip_checksum(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) + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, + is_inside=0) + + # 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.check_tcp_checksum(p) + self.check_ip_checksum(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.check_tcp_checksum(p) + self.check_ip_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_one_armed_nat44_static(self): + """ One armed NAT44 and 1:1 NAPT symmetrical rule """ + remote_host = self.pg9.remote_hosts[0] + local_host = self.pg9.remote_hosts[1] + external_port = 80 + local_port = 8080 + eh_port_in = 0 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr, twice_nat=1) + self.nat44_add_static_mapping(local_host.ip4, self.nat_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1, + twice_nat=1) + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, + is_inside=0) + + # from client to service + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=remote_host.ip4, dst=self.nat_addr) / + TCP(sport=12345, 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] + server = None + 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.check_tcp_checksum(p) + self.check_ip_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=local_host.ip4, dst=self.nat_addr) / + TCP(sport=local_port, dport=eh_port_in)) + 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.assertEqual(tcp.sport, external_port) + self.assertEqual(tcp.dport, 12345) + self.check_tcp_checksum(p) + self.check_ip_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_del_session(self): + """ Delete NAT44 session """ + 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) + + 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)) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + nsessions = len(sessions) + + self.vapi.nat44_del_session(sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol) + self.vapi.nat44_del_session(sessions[1].outside_ip_address, + sessions[1].outside_port, + sessions[1].protocol, + is_in=0) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + self.assertEqual(nsessions - len(sessions), 2) + + def test_set_get_reass(self): + """ NAT44 set/get virtual fragmentation reassembly """ + reas_cfg1 = self.vapi.nat_get_reass() + + self.vapi.nat_set_reass(timeout=reas_cfg1.ip4_timeout + 5, + max_reass=reas_cfg1.ip4_max_reass * 2, + max_frag=reas_cfg1.ip4_max_frag * 2) + + reas_cfg2 = self.vapi.nat_get_reass() self.assertEqual(reas_cfg1.ip4_timeout + 5, reas_cfg2.ip4_timeout) self.assertEqual(reas_cfg1.ip4_max_reass * 2, reas_cfg2.ip4_max_reass) @@ -3451,56 +3902,128 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - def test_twice_nat(self): - """ Twice NAT44 """ + 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 - eh_port_in = 0 + + 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) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - port_in, port_out, proto=IP_PROTOS.tcp, - twice_nat=1) - self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + if not lb: + self.nat44_add_static_mapping(pg0.remote_ip4, self.nat_addr, + port_in, port_out, + proto=IP_PROTOS.tcp, + twice_nat=int(not self_twice_nat), + self_twice_nat=int(self_twice_nat)) + else: + locals = [{'addr': server1.ip4n, + 'port': port_in1, + 'probability': 50}, + {'addr': server2.ip4n, + 'port': port_in2, + 'probability': 50}] + out_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping(out_addr_n, + port_out, + IP_PROTOS.tcp, + twice_nat=int( + not self_twice_nat), + self_twice_nat=int( + self_twice_nat), + local_num=len(locals), + locals=locals) + self.vapi.nat44_interface_add_del_feature(pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(pg1.sw_if_index, is_inside=0) - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + 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)) - self.pg1.add_stream(p) + pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1) + capture = 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, twice_nat_addr) - self.assertEqual(tcp.dport, port_in) - self.assertNotEqual(tcp.sport, eh_port_out) + 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.check_tcp_checksum(p) self.check_ip_checksum(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) / - TCP(sport=port_in, dport=eh_port_in)) - self.pg0.add_stream(p) + 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 = self.pg1.get_capture(1) + capture = 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.dst, client.ip4) self.assertEqual(ip.src, self.nat_addr) self.assertEqual(tcp.dport, eh_port_out) self.assertEqual(tcp.sport, port_out) @@ -3510,155 +4033,294 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise + 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 """ - external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr) - twice_nat_addr = '10.0.1.3' - local_port = 8080 - external_port = 80 - eh_port_out = 4567 - eh_port_in = 0 - server1 = self.pg0.remote_hosts[0] - server2 = self.pg0.remote_hosts[1] + self.twice_nat_common(lb=True) - locals = [{'addr': server1.ip4n, - 'port': local_port, - 'probability': 50}, - {'addr': server2.ip4n, - 'port': local_port, - 'probability': 50}] + 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) - self.nat44_add_address(self.nat_addr) - self.nat44_add_address(twice_nat_addr, twice_nat=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) - self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, - external_port, - IP_PROTOS.tcp, - twice_nat=1, - local_num=len(locals), - locals=locals) + def test_twice_nat_interface_addr(self): + """ Acquire twice NAT44 addresses from interface """ + self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index, twice_nat=1) + + # 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.pg7.config_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(1, len(adresses)) + self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n) + self.assertEqual(adresses[0].twice_nat, 1) + + # remove interface address and check NAT address pool + self.pg7.unconfig_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(adresses)) + + def test_ipfix_max_frags(self): + """ IPFIX logging maximum fragments pending reassembly exceeded """ + 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) + self.vapi.nat_set_reass(max_frag=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, + src_address=self.pg3.local_ip4n, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port) - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - TCP(sport=eh_port_out, dport=external_port)) - self.pg1.add_stream(p) + data = "A" * 4 + "B" * 16 + "C" * 3 + self.tcp_port_in = random.randint(1025, 65535) + pkts = self.create_stream_frag(self.pg0, + self.pg1.remote_ip4, + self.tcp_port_in, + 20, + data) + self.pg0.add_stream(pkts[-1]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - server = None + frags = self.pg1.get_capture(0) + self.vapi.cli("ipfix flush") # FIXME this should be an API call + capture = self.pg3.get_capture(9) + 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_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: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, twice_nat_addr) - self.assertIn(ip.dst, [server1.ip4, server2.ip4]) - if ip.dst == server1.ip4: - server = server1 - else: - server = server2 - self.assertNotEqual(tcp.sport, eh_port_out) - eh_port_in = tcp.sport - self.assertEqual(tcp.dport, local_port) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + # 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(ppp("Unexpected or invalid packet:", p)) + self.logger.error("TCP session termination failed") raise - p = (Ether(src=server.mac, dst=self.pg0.local_mac) / - IP(src=server.ip4, dst=twice_nat_addr) / - TCP(sport=local_port, dport=eh_port_in)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - p = capture[0] + 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: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(tcp.dport, eh_port_out) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise + # 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) - def test_twice_nat_interface_addr(self): - """ Acquire twice NAT44 addresses from interface """ - self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index, twice_nat=1) + pkts = [] - # no address in NAT pool - adresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(adresses)) + # 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) - # configure interface address and check NAT address pool - self.pg7.config_ip4() - adresses = self.vapi.nat44_address_dump() - self.assertEqual(1, len(adresses)) - self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n) - self.assertEqual(adresses[0].twice_nat, 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) - # remove interface address and check NAT address pool - self.pg7.unconfig_ip4() - adresses = self.vapi.nat44_address_dump() - self.assertEqual(0, len(adresses)) + 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_ipfix_max_frags(self): - """ IPFIX logging maximum fragments pending reassembly exceeded """ + 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) - self.vapi.nat_set_reass(max_frag=0) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, - src_address=self.pg3.local_ip4n, - path_mtu=512, - template_interval=10) - self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port) - data = "A" * 4 + "B" * 16 + "C" * 3 - self.tcp_port_in = random.randint(1025, 65535) - pkts = self.create_stream_frag(self.pg0, - self.pg1.remote_ip4, - self.tcp_port_in, - 20, - data) - self.pg0.add_stream(pkts[-1]) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - frags = self.pg1.get_capture(0) - self.vapi.cli("ipfix flush") # FIXME this should be an API call - capture = self.pg3.get_capture(9) - 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_fragments_ip4(data, 0, - self.pg0.remote_ip4n) + 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: - self.logger.info(self.vapi.cli("show nat44 verbose")) + 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 nat virtual-reassembly")) self.vapi.cli("nat addr-port-assignment-alg default") self.clear_nat44() @@ -3902,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 @@ -4422,7 +5040,13 @@ class TestDeterministicNAT(MethodHolder): def tearDown(self): super(TestDeterministicNAT, self).tearDown() if not self.vpp_dead: - self.logger.info(self.vapi.cli("show nat44 detail")) + self.logger.info(self.vapi.cli("show nat44 interfaces")) + self.logger.info( + self.vapi.cli("show nat44 deterministic mappings")) + self.logger.info( + self.vapi.cli("show nat44 deterministic timeouts")) + self.logger.info( + self.vapi.cli("show nat44 deterministic sessions")) self.clear_nat_det() @@ -5926,5 +6550,159 @@ class TestDSliteCE(MethodHolder): self.logger.info( self.vapi.cli("show dslite b4-tunnel-endpoint-address")) + +class TestNAT66(MethodHolder): + """ NAT66 Test Cases """ + + @classmethod + def setUpClass(cls): + super(TestNAT66, cls).setUpClass() + + try: + cls.nat_addr = 'fd01:ff::2' + cls.nat_addr_n = socket.inet_pton(socket.AF_INET6, cls.nat_addr) + + cls.create_pg_interfaces(range(2)) + cls.interfaces = list(cls.pg_interfaces) + + for i in cls.interfaces: + i.admin_up() + i.config_ip6() + i.configure_ipv6_neighbors() + + except Exception: + super(TestNAT66, cls).tearDownClass() + raise + + def test_static(self): + """ 1:1 NAT66 test """ + self.vapi.nat66_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat66_add_del_interface(self.pg1.sw_if_index, is_inside=0) + self.vapi.nat66_add_del_static_mapping(self.pg0.remote_ip6n, + self.nat_addr_n) + + # in2out + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) / + TCP()) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) / + UDP()) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) / + ICMPv6EchoRequest()) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) / + GRE() / IP() / TCP()) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, self.nat_addr) + self.assertEqual(packet[IPv6].dst, self.pg1.remote_ip6) + if packet.haslayer(TCP): + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.check_udp_checksum(packet) + elif packet.haslayer(ICMPv6EchoRequest): + self.check_icmpv6_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + pkts = [] + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) / + TCP()) + pkts.append(p) + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) / + UDP()) + pkts.append(p) + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) / + ICMPv6EchoReply()) + pkts.append(p) + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(src=self.pg1.remote_ip6, dst=self.nat_addr) / + GRE() / IP() / TCP()) + 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 packet in capture: + try: + self.assertEqual(packet[IPv6].src, self.pg1.remote_ip6) + self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) + if packet.haslayer(TCP): + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.check_udp_checksum(packet) + elif packet.haslayer(ICMPv6EchoReply): + self.check_icmpv6_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + sm = self.vapi.nat66_static_mapping_dump() + self.assertEqual(len(sm), 1) + self.assertEqual(sm[0].total_pkts, 8) + + def test_check_no_translate(self): + """ NAT66 translate only when egress interface is outside interface """ + self.vapi.nat66_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat66_add_del_interface(self.pg1.sw_if_index) + self.vapi.nat66_add_del_static_mapping(self.pg0.remote_ip6n, + self.nat_addr_n) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6) / + UDP()) + self.pg0.add_stream([p]) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + packet = capture[0] + try: + self.assertEqual(packet[IPv6].src, self.pg0.remote_ip6) + self.assertEqual(packet[IPv6].dst, self.pg1.remote_ip6) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def clear_nat66(self): + """ + Clear NAT66 configuration. + """ + interfaces = self.vapi.nat66_interface_dump() + for intf in interfaces: + self.vapi.nat66_add_del_interface(intf.sw_if_index, + intf.is_inside, + is_add=0) + + static_mappings = self.vapi.nat66_static_mapping_dump() + for sm in static_mappings: + self.vapi.nat66_add_del_static_mapping(sm.local_ip_address, + sm.external_ip_address, + sm.vrf_id, + is_add=0) + + def tearDown(self): + super(TestNAT66, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.cli("show nat66 interfaces")) + self.logger.info(self.vapi.cli("show nat66 static mappings")) + self.clear_nat66() + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)