X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_nat.py;h=ad2b9649b7de53c5c8d063d6584f935644575154;hb=refs%2Fchanges%2F02%2F12802%2F3;hp=5f6334ebb39e03d36a9cb77b2790a7c9e5132df3;hpb=c5c6a3342f3cbaaac647d52fd960b6f5b0fcd4e0;p=vpp.git diff --git a/test/test_nat.py b/test/test_nat.py index 5f6334ebb39..ad2b9649b7d 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -7,7 +7,6 @@ import StringIO import random from framework import VppTestCase, VppTestRunner, running_extended_tests -from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto from scapy.layers.inet import IP, TCP, UDP, ICMP from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply @@ -26,114 +25,6 @@ from util import mactobinary class MethodHolder(VppTestCase): """ NAT create capture and verify method holder """ - @classmethod - def setUpClass(cls): - super(MethodHolder, cls).setUpClass() - - def tearDown(self): - super(MethodHolder, self).tearDown() - - def check_ip_checksum(self, pkt): - """ - Check IP checksum of the packet - - :param pkt: Packet to check IP checksum - """ - new = pkt.__class__(str(pkt)) - del new['IP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['IP'].chksum, pkt['IP'].chksum) - - def check_tcp_checksum(self, pkt): - """ - Check TCP checksum in IP packet - - :param pkt: Packet to check TCP checksum - """ - new = pkt.__class__(str(pkt)) - del new['TCP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['TCP'].chksum, pkt['TCP'].chksum) - - def check_udp_checksum(self, pkt): - """ - Check UDP checksum in IP packet - - :param pkt: Packet to check UDP checksum - """ - new = pkt.__class__(str(pkt)) - del new['UDP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['UDP'].chksum, pkt['UDP'].chksum) - - def check_icmp_errror_embedded(self, pkt): - """ - Check ICMP error embeded packet checksum - - :param pkt: Packet to check ICMP error embeded packet checksum - """ - if pkt.haslayer(IPerror): - new = pkt.__class__(str(pkt)) - del new['IPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['IPerror'].chksum, pkt['IPerror'].chksum) - - if pkt.haslayer(TCPerror): - new = pkt.__class__(str(pkt)) - del new['TCPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['TCPerror'].chksum, pkt['TCPerror'].chksum) - - if pkt.haslayer(UDPerror): - if pkt['UDPerror'].chksum != 0: - new = pkt.__class__(str(pkt)) - del new['UDPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['UDPerror'].chksum, - pkt['UDPerror'].chksum) - - if pkt.haslayer(ICMPerror): - del new['ICMPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPerror'].chksum, pkt['ICMPerror'].chksum) - - def check_icmp_checksum(self, pkt): - """ - Check ICMP checksum in IPv4 packet - - :param pkt: Packet to check ICMP checksum - """ - new = pkt.__class__(str(pkt)) - del new['ICMP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMP'].chksum, pkt['ICMP'].chksum) - if pkt.haslayer(IPerror): - self.check_icmp_errror_embedded(pkt) - - def check_icmpv6_checksum(self, pkt): - """ - Check ICMPv6 checksum in IPv4 packet - - :param pkt: Packet to check ICMPv6 checksum - """ - new = pkt.__class__(str(pkt)) - if pkt.haslayer(ICMPv6DestUnreach): - del new['ICMPv6DestUnreach'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6DestUnreach'].cksum, - pkt['ICMPv6DestUnreach'].cksum) - self.check_icmp_errror_embedded(pkt) - if pkt.haslayer(ICMPv6EchoRequest): - del new['ICMPv6EchoRequest'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6EchoRequest'].cksum, - pkt['ICMPv6EchoRequest'].cksum) - if pkt.haslayer(ICMPv6EchoReply): - del new['ICMPv6EchoReply'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6EchoReply'].cksum, - pkt['ICMPv6EchoReply'].cksum) - def create_stream_in(self, in_if, out_if, dst_ip=None, ttl=64): """ Create packet stream for inside network @@ -383,7 +274,7 @@ class MethodHolder(VppTestCase): for packet in capture: try: if not is_ip6: - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) self.assertEqual(packet[IP46].src, nat_ip) if dst_ip is not None: self.assertEqual(packet[IP46].dst, dst_ip) @@ -394,7 +285,7 @@ class MethodHolder(VppTestCase): self.assertNotEqual( packet[TCP].sport, self.tcp_port_in) self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) + self.assert_packet_checksums_valid(packet) elif packet.haslayer(UDP): if same_port: self.assertEqual(packet[UDP].sport, self.udp_port_in) @@ -408,10 +299,7 @@ class MethodHolder(VppTestCase): else: self.assertNotEqual(packet[ICMP46].id, self.icmp_id_in) self.icmp_id_out = packet[ICMP46].id - if is_ip6: - self.check_icmpv6_checksum(packet) - else: - self.check_icmp_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(outside network):", packet)) @@ -442,16 +330,14 @@ class MethodHolder(VppTestCase): self.assertEqual(packet_num, len(capture)) for packet in capture: try: - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) self.assertEqual(packet[IP].dst, in_if.remote_ip4) if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.check_tcp_checksum(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) else: self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.check_icmp_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(inside network):", packet)) @@ -471,16 +357,14 @@ class MethodHolder(VppTestCase): try: self.assertEqual(packet[IPv6].src, src_ip) self.assertEqual(packet[IPv6].dst, dst_ip) + self.assert_packet_checksums_valid(packet) if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.check_tcp_checksum(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.check_udp_checksum(packet) else: self.assertEqual(packet[ICMPv6EchoReply].id, self.icmp_id_in) - self.check_icmpv6_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet " "(inside network):", packet)) @@ -655,7 +539,7 @@ class MethodHolder(VppTestCase): for p in frags: self.assertEqual(p[IP].src, src) self.assertEqual(p[IP].dst, dst) - self.check_ip_checksum(p) + self.assert_ip_checksum_valid(p) buffer.seek(p[IP].frag * 8) buffer.write(p[IP].payload) ip = frags[0].getlayer(IP) @@ -663,7 +547,7 @@ class MethodHolder(VppTestCase): proto=frags[0][IP].proto) if ip.proto == IP_PROTOS.tcp: p = (ip / TCP(buffer.getvalue())) - self.check_tcp_checksum(p) + self.assert_tcp_checksum_valid(p) elif ip.proto == IP_PROTOS.udp: p = (ip / UDP(buffer.getvalue())) return p @@ -688,11 +572,55 @@ class MethodHolder(VppTestCase): nh=frags[0][IPv6ExtHdrFragment].nh) if ip.nh == IP_PROTOS.tcp: p = (ip / TCP(buffer.getvalue())) - self.check_tcp_checksum(p) elif ip.nh == IP_PROTOS.udp: p = (ip / UDP(buffer.getvalue())) + self.assert_packet_checksums_valid(p) return p + def initiate_tcp_session(self, in_if, out_if): + """ + Initiates TCP session + + :param in_if: Inside interface + :param out_if: Outside interface + """ + try: + # SYN packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="S")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = out_if.get_capture(1) + p = capture[0] + self.tcp_port_out = p[TCP].sport + + # SYN + ACK packet out->in + p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / + IP(src=out_if.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="SA")) + out_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + in_if.get_capture(1) + + # ACK packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + out_if.get_capture(1) + + except: + self.logger.error("TCP 3 way handshake failed") + raise + def verify_ipfix_nat44_ses(self, data): """ Verify IPFIX NAT44 session create/delete event @@ -898,6 +826,7 @@ class TestNAT44(MethodHolder): @classmethod def setUpClass(cls): super(TestNAT44, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") try: cls.tcp_port_in = 6303 @@ -910,6 +839,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 +852,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) @@ -989,6 +922,8 @@ class TestNAT44(MethodHolder): if self.pg7.has_ip4_config: self.pg7.unconfig_ip4() + self.vapi.nat44_forwarding_enable_disable(0) + interfaces = self.vapi.nat44_interface_addr_dump() for intf in interfaces: self.vapi.nat44_add_interface_addr(intf.sw_if_index, @@ -1027,6 +962,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() @@ -1037,6 +976,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=[]) @@ -1065,7 +1007,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): + proto=0, twice_nat=0, self_twice_nat=0, + out2in_only=0, tag=""): """ Add/delete NAT44 static mapping @@ -1078,6 +1021,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: @@ -1094,6 +1042,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): @@ -1352,6 +1303,19 @@ class TestNAT44(MethodHolder): finally: self.pg0.remote_hosts[0] = host0 + user = self.pg0.remote_hosts[1] + sessions = self.vapi.nat44_user_session_dump(user.ip4n, 0) + self.assertEqual(len(sessions), 3) + self.assertTrue(sessions[0].ext_host_valid) + self.vapi.nat44_del_session( + sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol, + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(user.ip4n, 0) + self.assertEqual(len(sessions), 2) + finally: self.vapi.nat44_forwarding_enable_disable(0) self.vapi.nat44_add_del_static_mapping(local_ip=real_ip, @@ -1370,6 +1334,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) @@ -1394,11 +1364,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) @@ -1490,6 +1464,132 @@ class TestNAT44(MethodHolder): capture = self.pg1.get_capture(len(pkts)) self.verify_capture_out(capture) + def test_static_with_port_out2(self): + """ 1:1 NAPT symmetrical rule """ + + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_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.pg1.sw_if_index, + is_inside=0) + + # from client to service + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + ICMP(type=11) / capture[0][IP]) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + self.assertEqual(p[IP].src, self.nat_addr) + inner = p[IPerror] + self.assertEqual(inner.dst, self.nat_addr) + self.assertEqual(inner[TCPerror].dport, external_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=11) / capture[0][IP]) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + self.assertEqual(p[IP].dst, self.pg0.remote_ip4) + inner = p[IPerror] + self.assertEqual(inner.src, self.pg0.remote_ip4) + self.assertEqual(inner[TCPerror].sport, local_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client to server (no translation) + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=12346, dport=local_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12346)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg0.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + def test_static_vrf_aware(self): """ 1:1 NAT VRF awareness """ @@ -1524,6 +1624,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 """ @@ -1547,8 +1678,7 @@ class TestNAT44(MethodHolder): self.assertEqual(ip.src, self.pg1.remote_ip4) self.assertEqual(tcp.dport, 56789) self.assertEqual(tcp.sport, 12345) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -1597,8 +1727,7 @@ class TestNAT44(MethodHolder): else: server = server2 self.assertEqual(tcp.dport, local_port) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -1617,16 +1746,53 @@ class TestNAT44(MethodHolder): tcp = p[TCP] self.assertEqual(ip.src, self.nat_addr) self.assertEqual(tcp.sport, external_port) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - # multiple clients + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].ext_host_valid) + self.vapi.nat44_del_session( + sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol, + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 0) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_static_lb_multi_clients(self): + """ NAT44 local service load balancing - multiple clients""" + + external_addr_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) / @@ -1644,18 +1810,126 @@ class TestNAT44(MethodHolder): server2_n += 1 self.assertTrue(server1_n > server2_n) - def test_multiple_inside_interfaces(self): - """ NAT44 multiple non-overlapping address space inside interfaces """ + def test_static_lb_2(self): + """ NAT44 local service load balancing (asymmetrical rule) """ + 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] - self.nat44_add_address(self.nat_addr) + locals = [{'addr': server1.ip4n, + 'port': local_port, + 'probability': 70}, + {'addr': server2.ip4n, + 'port': local_port, + 'probability': 30}] + + self.vapi.nat44_forwarding_enable_disable(1) + self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, + external_port, + IP_PROTOS.tcp, + out2in_only=1, + 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) - self.vapi.nat44_interface_add_del_feature(self.pg3.sw_if_index, + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, is_inside=0) - # between two NAT44 inside interfaces (no translation) - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) + # from client to service + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + server = None + try: + ip = p[IP] + tcp = p[TCP] + self.assertIn(ip.dst, [server1.ip4, server2.ip4]) + if ip.dst == server1.ip4: + server = server1 + else: + server = server2 + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client to server (no translation) + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=server1.ip4) / + TCP(sport=12346, dport=local_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + server = None + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, server1.ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (no translation) + p = (Ether(src=server1.mac, dst=self.pg0.local_mac) / + IP(src=server1.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12346)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, server1.ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_multiple_inside_interfaces(self): + """ NAT44 multiple non-overlapping address space inside interfaces """ + + 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) + self.vapi.nat44_interface_add_del_feature(self.pg3.sw_if_index, + is_inside=0) + + # between two NAT44 inside interfaces (no translation) + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() capture = self.pg1.get_capture(len(pkts)) @@ -1825,6 +2099,7 @@ class TestNAT44(MethodHolder): self.assertTrue(session.protocol in [IP_PROTOS.tcp, IP_PROTOS.udp, IP_PROTOS.icmp]) + self.assertFalse(session.ext_host_valid) # pg4 session dump sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4n, 10) @@ -1884,7 +2159,7 @@ class TestNAT44(MethodHolder): self.assertEqual(ip.dst, server.ip4) self.assertNotEqual(tcp.sport, host_in_port) self.assertEqual(tcp.dport, server_in_port) - self.check_tcp_checksum(p) + self.assert_packet_checksums_valid(p) host_out_port = tcp.sport except: self.logger.error(ppp("Unexpected or invalid packet:", p)) @@ -1906,9 +2181,9 @@ class TestNAT44(MethodHolder): self.assertEqual(ip.dst, host.ip4) self.assertEqual(tcp.sport, server_out_port) self.assertEqual(tcp.dport, host_in_port) - self.check_tcp_checksum(p) + self.assert_packet_checksums_valid(p) except: - self.logger.error(ppp("Unexpected or invalid packet:"), p) + self.logger.error(ppp("Unexpected or invalid packet:", p)) raise def test_hairpinning2(self): @@ -1957,7 +2232,7 @@ class TestNAT44(MethodHolder): self.assertNotEqual(packet[TCP].sport, self.tcp_port_in) self.assertEqual(packet[TCP].dport, server_tcp_port) self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) + self.assert_packet_checksums_valid(packet) elif packet.haslayer(UDP): self.assertNotEqual(packet[UDP].sport, self.udp_port_in) self.assertEqual(packet[UDP].dport, server_udp_port) @@ -1994,7 +2269,7 @@ class TestNAT44(MethodHolder): if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) self.assertEqual(packet[TCP].sport, server_tcp_port) - self.check_tcp_checksum(packet) + self.assert_packet_checksums_valid(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) self.assertEqual(packet[UDP].sport, server_udp_port) @@ -2030,7 +2305,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[TCP].sport, self.tcp_port_in) self.assertEqual(packet[TCP].dport, server_tcp_port) self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) + self.assert_packet_checksums_valid(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].sport, self.udp_port_in) self.assertEqual(packet[UDP].dport, server_udp_port) @@ -2067,7 +2342,7 @@ class TestNAT44(MethodHolder): if packet.haslayer(TCP): self.assertEqual(packet[TCP].dport, self.tcp_port_in) self.assertEqual(packet[TCP].sport, server_tcp_port) - self.check_tcp_checksum(packet) + self.assert_packet_checksums_valid(packet) elif packet.haslayer(UDP): self.assertEqual(packet[UDP].dport, self.udp_port_in) self.assertEqual(packet[UDP].sport, server_udp_port) @@ -2103,6 +2378,31 @@ class TestNAT44(MethodHolder): # verify number of translated packet self.pg1.get_capture(pkts_num) + users = self.vapi.nat44_user_dump() + for user in users: + if user.ip_address == self.pg0.remote_ip4n: + self.assertEqual(user.nsessions, + nat44_config.max_translations_per_user) + self.assertEqual(user.nstaticsessions, 0) + + tcp_port = 22 + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + tcp_port, tcp_port, + proto=IP_PROTOS.tcp) + 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=tcp_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + users = self.vapi.nat44_user_dump() + for user in users: + if user.ip_address == self.pg0.remote_ip4n: + self.assertEqual(user.nsessions, + nat44_config.max_translations_per_user - 1) + self.assertEqual(user.nstaticsessions, 1) + def test_interface_addr(self): """ Acquire NAT44 addresses from interface """ self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index) @@ -2124,28 +2424,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): @@ -2168,17 +2502,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 """ @@ -2243,7 +2583,8 @@ class TestNAT44(MethodHolder): self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(9) ipfix = IPFIXDecoder() @@ -2300,7 +2641,8 @@ class TestNAT44(MethodHolder): self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(9) ipfix = IPFIXDecoder() @@ -2359,7 +2701,7 @@ class TestNAT44(MethodHolder): self.pg2.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() # remove addresses and verify self.nat44_add_address(self.nat_addr, is_add=0) @@ -2372,7 +2714,7 @@ class TestNAT44(MethodHolder): self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / ARP(op=ARP.who_has, pdst=static_addr, @@ -2380,7 +2722,7 @@ class TestNAT44(MethodHolder): self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() def test_vrf_mode(self): """ NAT44 tenant VRF aware address pool mode """ @@ -2620,7 +2962,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, nat_ip) self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2640,7 +2982,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, self.pg1.remote_ip4) self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2675,7 +3017,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, host_nat_ip) self.assertEqual(packet[IP].dst, server.ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2695,7 +3037,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, server_nat_ip) self.assertEqual(packet[IP].dst, host.ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2730,7 +3072,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, self.nat_addr) self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2750,7 +3092,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, self.pg1.remote_ip4) self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2760,8 +3102,6 @@ class TestNAT44(MethodHolder): host = self.pg0.remote_hosts[0] server = self.pg0.remote_hosts[1] host_in_port = 1234 - host_out_port = 0 - server_in_port = 5678 server_out_port = 8765 server_nat_ip = "10.0.0.11" @@ -2780,7 +3120,7 @@ class TestNAT44(MethodHolder): self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1) + self.pg0.get_capture(1) p = (Ether(dst=self.pg0.local_mac, src=host.mac) / IP(src=host.ip4, dst=server_nat_ip) / @@ -2796,7 +3136,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, self.nat_addr) self.assertEqual(packet[IP].dst, server.ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2816,7 +3156,7 @@ class TestNAT44(MethodHolder): self.assertEqual(packet[IP].src, server_nat_ip) self.assertEqual(packet[IP].dst, host.ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -2943,7 +3283,7 @@ class TestNAT44(MethodHolder): self.assertEqual(ip.dst, server.ip4) self.assertNotEqual(tcp.sport, host_in_port) self.assertEqual(tcp.dport, server_in_port) - self.check_tcp_checksum(p) + self.assert_packet_checksums_valid(p) host_out_port = tcp.sport except: self.logger.error(ppp("Unexpected or invalid packet:", p)) @@ -2965,9 +3305,184 @@ class TestNAT44(MethodHolder): self.assertEqual(ip.dst, host.ip4) self.assertEqual(tcp.sport, server_out_port) self.assertEqual(tcp.dport, host_in_port) - self.check_tcp_checksum(p) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_output_feature_and_service(self): + """ NAT44 interface output feature and services """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr) + 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) + + # from client to service + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=external_addr) / + TCP(sport=12345, dport=external_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from local network host to external network + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + 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_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) + + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # session 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) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.pg0.remote_ip4, + same_port=True) + + def test_output_feature_and_service3(self): + """ NAT44 interface output feature and DST NAT """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(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.assert_packet_checksums_valid(p) except: - self.logger.error(ppp("Unexpected or invalid packet:"), p) + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) raise def test_one_armed_nat44(self): @@ -2998,8 +3513,7 @@ class TestNAT44(MethodHolder): self.assertNotEqual(tcp.sport, 12345) external_port = tcp.sport self.assertEqual(tcp.dport, 80) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -3020,8 +3534,68 @@ class TestNAT44(MethodHolder): 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) + self.assert_packet_checksums_valid(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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, local_host.ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, local_port) + self.assertNotEqual(tcp.sport, 12345) + eh_port_in = tcp.sport + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.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.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -3037,7 +3611,7 @@ class TestNAT44(MethodHolder): self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(len(pkts)) + self.pg1.get_capture(len(pkts)) sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) nsessions = len(sessions) @@ -3125,10 +3699,8 @@ class TestNAT44(MethodHolder): def test_reass_hairpinning(self): """ NAT44 fragments hairpinning """ - host = self.pg0.remote_hosts[0] server = self.pg0.remote_hosts[1] host_in_port = random.randint(1025, 65535) - host_out_port = 0 server_in_port = random.randint(1025, 65535) server_out_port = random.randint(1025, 65535) data = "A" * 4 + "B" * 16 + "C" * 3 @@ -3231,150 +3803,180 @@ class TestNAT44(MethodHolder): self.assertEqual(tcp.dport, 22) self.assertNotEqual(tcp.sport, 4567) self.assertEqual((tcp.sport >> 6) & 63, 10) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: 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 - 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, - 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) / - TCP(sport=eh_port_out, dport=port_out)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - 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) - 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 - - 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) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, eh_port_out) - self.assertEqual(tcp.sport, port_out) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - 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] + 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 - locals = [{'addr': server1.ip4n, - 'port': local_port, - 'probability': 50}, - {'addr': server2.ip4n, - 'port': local_port, - 'probability': 50}] + 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.vapi.nat44_add_del_lb_static_mapping(external_addr_n, - external_port, - IP_PROTOS.tcp, - twice_nat=1, - 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, + 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) / - TCP(sport=eh_port_out, dport=external_port)) - self.pg1.add_stream(p) + if same_pg: + if not lb: + client = server + else: + assert client_id is not None + if client_id == 1: + client = self.pg0.remote_hosts[0] + elif client_id == 2: + client = self.pg0.remote_hosts[1] + else: + client = pg1.remote_hosts[0] + p = (Ether(src=pg1.remote_mac, dst=pg1.local_mac) / + IP(src=client.ip4, dst=self.nat_addr) / + TCP(sport=eh_port_out, dport=port_out)) + pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1) + capture = pg0.get_capture(1) p = capture[0] - server = None 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 + 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: - server = server2 - self.assertNotEqual(tcp.sport, eh_port_out) + 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 - self.assertEqual(tcp.dport, local_port) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + saved_port_in = tcp.dport + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - p = (Ether(src=server.mac, dst=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) + 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, client.ip4) 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) + self.assertEqual(tcp.sport, port_out) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise + if eh_translate: + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].ext_host_valid) + self.assertTrue(sessions[0].is_twicenat) + self.vapi.nat44_del_session( + sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol, + ext_host_address=sessions[0].ext_host_nat_address, + ext_host_port=sessions[0].ext_host_nat_port) + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 0) + + def test_twice_nat(self): + """ Twice NAT44 """ + self.twice_nat_common() + + def test_self_twice_nat_positive(self): + """ Self Twice NAT44 (positive test) """ + self.twice_nat_common(self_twice_nat=True, same_pg=True) + + def test_self_twice_nat_negative(self): + """ Self Twice NAT44 (negative test) """ + self.twice_nat_common(self_twice_nat=True) + + def test_twice_nat_lb(self): + """ Twice NAT44 local service load balancing """ + self.twice_nat_common(lb=True) + + def test_self_twice_nat_lb_positive(self): + """ Self Twice NAT44 local service load balancing (positive test) """ + self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, + client_id=1) + + def test_self_twice_nat_lb_negative(self): + """ Self Twice NAT44 local service load balancing (negative test) """ + self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True, + client_id=2) + def test_twice_nat_interface_addr(self): """ Acquire twice NAT44 addresses from interface """ self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index, twice_nat=1) @@ -3419,7 +4021,8 @@ class TestNAT44(MethodHolder): self.pg0.add_stream(pkts[-1]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - frags = self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(9) ipfix = IPFIXDecoder() @@ -3441,13 +4044,200 @@ 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.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_port_out, + proto=IP_PROTOS.tcp, + 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, + 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) + + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=100, ack=300)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + pkts = [] + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=300, ack=101)) + pkts.append(p) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=101)) + pkts.append(p) + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(2) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_tcp_session_close_out(self): + """ Close TCP session from outside network """ + self.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_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, + 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) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=100, ack=300)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # FIN+ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=300, ack=101)) + + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=101, ack=301)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + + def test_tcp_session_close_simultaneous(self): + """ Close TCP session from inside network """ + self.tcp_port_out = 10505 + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, + self.nat_addr, + self.tcp_port_in, + self.tcp_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, + 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) + + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="FA", seq=100, ack=300)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="FA", seq=300, ack=100)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A", seq=101, ack=301)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A", seq=301, ack=101)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, + 0) + self.assertEqual(len(sessions) - start_sessnum, 0) + 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.logger.info(self.vapi.cli("show nat44 hash tables detail")) self.vapi.cli("nat addr-port-assignment-alg default") self.clear_nat44() + self.vapi.cli("clear logging") class TestNAT44Out2InDPO(MethodHolder): @@ -3461,6 +4251,7 @@ class TestNAT44Out2InDPO(MethodHolder): @classmethod def setUpClass(cls): super(TestNAT44Out2InDPO, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") try: cls.tcp_port_in = 6303 @@ -3578,6 +4369,7 @@ class TestDeterministicNAT(MethodHolder): @classmethod def setUpClass(cls): super(TestDeterministicNAT, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") try: cls.tcp_port_in = 6303 @@ -3688,50 +4480,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 @@ -4208,7 +4956,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() @@ -4532,8 +5286,6 @@ class TestNAT64(MethodHolder): self.udp_port_in = 6304 self.icmp_id_in = 6305 - ses_num_start = self.nat64_get_ses_num() - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, self.nat_addr_n) self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) @@ -4576,7 +5328,7 @@ class TestNAT64(MethodHolder): inner = packet[IPerror] self.assertEqual(inner.src, self.pg1.remote_ip4) self.assertEqual(inner.dst, self.nat_addr) - self.check_icmp_checksum(packet) + self.assert_packet_checksums_valid(packet) if inner.haslayer(TCPerror): self.assertEqual(inner[TCPerror].dport, self.tcp_port_out) elif inner.haslayer(UDPerror): @@ -4605,7 +5357,7 @@ class TestNAT64(MethodHolder): inner = icmp[IPerror6] self.assertEqual(inner.src, self.pg0.remote_ip6) self.assertEqual(inner.dst, ip.src) - self.check_icmpv6_checksum(packet) + self.assert_icmpv6_checksum_valid(packet) if inner.haslayer(TCPerror): self.assertEqual(inner[TCPerror].sport, self.tcp_port_in) elif inner.haslayer(UDPerror): @@ -4667,15 +5419,14 @@ class TestNAT64(MethodHolder): try: self.assertEqual(packet[IPv6].src, nat_addr_ip6) self.assertEqual(packet[IPv6].dst, server.ip6) + self.assert_packet_checksums_valid(packet) if packet.haslayer(TCP): self.assertNotEqual(packet[TCP].sport, client_tcp_in_port) self.assertEqual(packet[TCP].dport, server_tcp_in_port) - self.check_tcp_checksum(packet) client_tcp_out_port = packet[TCP].sport else: self.assertNotEqual(packet[UDP].sport, client_udp_in_port) self.assertEqual(packet[UDP].dport, server_udp_in_port) - self.check_udp_checksum(packet) client_udp_out_port = packet[UDP].sport except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) @@ -4699,14 +5450,13 @@ class TestNAT64(MethodHolder): try: self.assertEqual(packet[IPv6].src, nat_addr_ip6) self.assertEqual(packet[IPv6].dst, client.ip6) + self.assert_packet_checksums_valid(packet) if packet.haslayer(TCP): self.assertEqual(packet[TCP].sport, server_tcp_out_port) self.assertEqual(packet[TCP].dport, client_tcp_in_port) - self.check_tcp_checksum(packet) else: self.assertEqual(packet[UDP].sport, server_udp_out_port) self.assertEqual(packet[UDP].dport, client_udp_in_port) - self.check_udp_checksum(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -4730,7 +5480,7 @@ class TestNAT64(MethodHolder): inner = icmp[IPerror6] self.assertEqual(inner.src, server.ip6) self.assertEqual(inner.dst, nat_addr_ip6) - self.check_icmpv6_checksum(packet) + self.assert_packet_checksums_valid(packet) if inner.haslayer(TCPerror): self.assertEqual(inner[TCPerror].sport, server_tcp_in_port) self.assertEqual(inner[TCPerror].dport, @@ -4853,7 +5603,7 @@ class TestNAT64(MethodHolder): self.assertEqual(packet[IP].src, self.nat_addr) self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) + self.assert_packet_checksums_valid(packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -4991,8 +5741,7 @@ class TestNAT64(MethodHolder): self.assertNotEqual(tcp.sport, 12345) external_port = tcp.sport self.assertEqual(tcp.dport, 80) - self.check_tcp_checksum(p) - self.check_ip_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -5013,7 +5762,7 @@ class TestNAT64(MethodHolder): self.assertEqual(ip.dst, self.pg3.remote_ip6) self.assertEqual(tcp.sport, 80) self.assertEqual(tcp.dport, 12345) - self.check_tcp_checksum(p) + self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise @@ -5071,7 +5820,6 @@ class TestNAT64(MethodHolder): def test_reass_hairpinning(self): """ NAT64 fragments hairpinning """ data = 'a' * 200 - client = self.pg0.remote_hosts[0] server = self.pg0.remote_hosts[1] server_in_port = random.randint(1025, 65535) server_out_port = random.randint(1025, 65535) @@ -5213,7 +5961,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(9) ipfix = IPFIXDecoder() @@ -5240,7 +5989,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(1) # verify events in data set @@ -5276,7 +6026,8 @@ class TestNAT64(MethodHolder): self.pg0.add_stream(pkts[-1]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg1.get_capture(0) + self.pg1.assert_nothing_captured() + sleep(1) self.vapi.cli("ipfix flush") # FIXME this should be an API call capture = self.pg3.get_capture(9) ipfix = IPFIXDecoder() @@ -5497,7 +6248,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].dst, self.pg0.remote_ip4) self.assertNotEqual(capture[UDP].sport, 20000) self.assertEqual(capture[UDP].dport, 10000) - self.check_ip_checksum(capture) + self.assert_packet_checksums_valid(capture) out_port = capture[UDP].sport p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / @@ -5514,7 +6265,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].dst, '192.168.1.1') self.assertEqual(capture[UDP].sport, 10000) self.assertEqual(capture[UDP].dport, 20000) - self.check_ip_checksum(capture) + self.assert_packet_checksums_valid(capture) # TCP p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / @@ -5531,8 +6282,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].dst, self.pg0.remote_ip4) self.assertNotEqual(capture[TCP].sport, 20001) self.assertEqual(capture[TCP].dport, 10001) - self.check_ip_checksum(capture) - self.check_tcp_checksum(capture) + self.assert_packet_checksums_valid(capture) out_port = capture[TCP].sport p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / @@ -5549,8 +6299,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].dst, '192.168.1.1') self.assertEqual(capture[TCP].sport, 10001) self.assertEqual(capture[TCP].dport, 20001) - self.check_ip_checksum(capture) - self.check_tcp_checksum(capture) + self.assert_packet_checksums_valid(capture) # ICMP p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / @@ -5566,8 +6315,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].src, self.nat_addr) self.assertEqual(capture[IP].dst, self.pg0.remote_ip4) self.assertNotEqual(capture[ICMP].id, 4000) - self.check_ip_checksum(capture) - self.check_icmp_checksum(capture) + self.assert_packet_checksums_valid(capture) out_id = capture[ICMP].id p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / @@ -5583,8 +6331,7 @@ class TestDSlite(MethodHolder): self.assertEqual(capture[IP].src, self.pg0.remote_ip4) self.assertEqual(capture[IP].dst, '192.168.1.1') self.assertEqual(capture[ICMP].id, 4000) - self.check_ip_checksum(capture) - self.check_icmp_checksum(capture) + self.assert_packet_checksums_valid(capture) # ping DS-Lite AFTR tunnel endpoint address p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / @@ -5671,7 +6418,7 @@ class TestDSliteCE(MethodHolder): self.assertEqual(capture[IP].dst, self.pg1.remote_ip4) self.assertEqual(capture[UDP].sport, 10000) self.assertEqual(capture[UDP].dport, 20000) - self.check_ip_checksum(capture) + self.assert_packet_checksums_valid(capture) # UDP decapsulation p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / @@ -5688,7 +6435,7 @@ class TestDSliteCE(MethodHolder): self.assertEqual(capture[IP].dst, self.pg0.remote_ip4) self.assertEqual(capture[UDP].sport, 20000) self.assertEqual(capture[UDP].dport, 10000) - self.check_ip_checksum(capture) + self.assert_packet_checksums_valid(capture) # ping DS-Lite B4 tunnel endpoint address p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / @@ -5712,5 +6459,150 @@ 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) + self.assert_packet_checksums_valid(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) + self.assert_packet_checksums_valid(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)