NAT44: nat44_del_session and nat44_user_session_details API update (VPP-1271)
[vpp.git] / test / test_nat.py
index b9785f0..e2f3465 100644 (file)
@@ -7,7 +7,6 @@ import StringIO
 import random
 
 from framework import VppTestCase, VppTestRunner, running_extended_tests
 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
 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 """
 
 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
     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:
         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)
                 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.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)
                 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
                     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))
             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.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.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)
                 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))
             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)
             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)
                 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)
                 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)
                 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))
             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)
         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)
             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()))
                 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
         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()))
                   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()))
         elif ip.nh == IP_PROTOS.udp:
             p = (ip / UDP(buffer.getvalue()))
+        self.assert_packet_checksums_valid(p)
         return 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
     def verify_ipfix_nat44_ses(self, data):
         """
         Verify IPFIX NAT44 session create/delete event
@@ -910,6 +838,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.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])
 
             cls.create_pg_interfaces(range(10))
             cls.interfaces = list(cls.pg_interfaces[0:4])
@@ -922,6 +851,9 @@ class TestNAT44(MethodHolder):
             cls.pg0.generate_remote_hosts(3)
             cls.pg0.configure_ipv4_neighbors()
 
             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)
             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,8 +961,10 @@ class TestNAT44(MethodHolder):
                 vrf_id=sm.vrf_id,
                 protocol=sm.protocol,
                 twice_nat=sm.twice_nat,
                 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,
                 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()
                 is_add=0)
 
         lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump()
@@ -1041,6 +975,7 @@ class TestNAT44(MethodHolder):
                 lb_sm.protocol,
                 vrf_id=lb_sm.vrf_id,
                 twice_nat=lb_sm.twice_nat,
                 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,
                 out2in_only=lb_sm.out2in_only,
                 tag=lb_sm.tag,
                 is_add=0,
@@ -1071,7 +1006,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,
     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, tag=""):
+                                 proto=0, twice_nat=0, self_twice_nat=0,
+                                 out2in_only=0, tag=""):
         """
         Add/delete NAT44 static mapping
 
         """
         Add/delete NAT44 static mapping
 
@@ -1084,6 +1020,9 @@ 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 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
         """
         :param out2in_only: if 1 rule is matching only out2in direction
         :param tag: Opaque string tag
         """
@@ -1102,6 +1041,7 @@ class TestNAT44(MethodHolder):
             vrf_id,
             proto,
             twice_nat,
             vrf_id,
             proto,
             twice_nat,
+            self_twice_nat,
             out2in_only,
             tag,
             is_add)
             out2in_only,
             tag,
             is_add)
@@ -1362,6 +1302,19 @@ class TestNAT44(MethodHolder):
             finally:
                 self.pg0.remote_hosts[0] = host0
 
             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,
         finally:
             self.vapi.nat44_forwarding_enable_disable(0)
             self.vapi.nat44_add_del_static_mapping(local_ip=real_ip,
@@ -1383,6 +1336,9 @@ class TestNAT44(MethodHolder):
         sm = self.vapi.nat44_static_mapping_dump()
         self.assertEqual(len(sm), 1)
         self.assertEqual((sm[0].tag).split('\0', 1)[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)
 
         # in2out
         pkts = self.create_stream_in(self.pg0, self.pg1)
@@ -1530,14 +1486,12 @@ class TestNAT44(MethodHolder):
         self.pg_start()
         capture = self.pg0.get_capture(1)
         p = capture[0]
         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, self.pg0.remote_ip4)
             self.assertEqual(tcp.dport, local_port)
         try:
             ip = p[IP]
             tcp = p[TCP]
             self.assertEqual(ip.dst, self.pg0.remote_ip4)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1574,8 +1528,7 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.src, self.nat_addr)
             self.assertEqual(tcp.sport, external_port)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1607,14 +1560,12 @@ class TestNAT44(MethodHolder):
         self.pg_start()
         capture = self.pg0.get_capture(1)
         p = capture[0]
         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, self.pg0.remote_ip4)
             self.assertEqual(tcp.dport, local_port)
         try:
             ip = p[IP]
             tcp = p[TCP]
             self.assertEqual(ip.dst, self.pg0.remote_ip4)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1633,8 +1584,7 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.src, self.pg0.remote_ip4)
             self.assertEqual(tcp.sport, local_port)
             tcp = p[TCP]
             self.assertEqual(ip.src, self.pg0.remote_ip4)
             self.assertEqual(tcp.sport, 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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1673,6 +1623,37 @@ class TestNAT44(MethodHolder):
         self.pg_start()
         self.pg3.assert_nothing_captured()
 
         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 """
 
     def test_identity_nat(self):
         """ Identity NAT """
 
@@ -1696,8 +1677,7 @@ class TestNAT44(MethodHolder):
             self.assertEqual(ip.src, self.pg1.remote_ip4)
             self.assertEqual(tcp.dport, 56789)
             self.assertEqual(tcp.sport, 12345)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1746,8 +1726,7 @@ class TestNAT44(MethodHolder):
             else:
                 server = server2
             self.assertEqual(tcp.dport, local_port)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1766,16 +1745,53 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.src, self.nat_addr)
             self.assertEqual(tcp.sport, external_port)
             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
 
         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
         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) /
         pkts = []
         for client in clients:
             p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
@@ -1838,8 +1854,7 @@ class TestNAT44(MethodHolder):
             else:
                 server = server2
             self.assertEqual(tcp.dport, local_port)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1858,8 +1873,7 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.src, self.nat_addr)
             self.assertEqual(tcp.sport, external_port)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1879,8 +1893,7 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.dst, server1.ip4)
             self.assertEqual(tcp.dport, local_port)
             tcp = p[TCP]
             self.assertEqual(ip.dst, server1.ip4)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -1899,8 +1912,7 @@ class TestNAT44(MethodHolder):
             tcp = p[TCP]
             self.assertEqual(ip.src, server1.ip4)
             self.assertEqual(tcp.sport, local_port)
             tcp = p[TCP]
             self.assertEqual(ip.src, server1.ip4)
             self.assertEqual(tcp.sport, 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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -2086,6 +2098,7 @@ class TestNAT44(MethodHolder):
                 self.assertTrue(session.protocol in
                                 [IP_PROTOS.tcp, IP_PROTOS.udp,
                                  IP_PROTOS.icmp])
                 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)
 
         # pg4 session dump
         sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4n, 10)
@@ -2145,7 +2158,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.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))
             host_out_port = tcp.sport
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
@@ -2167,7 +2180,7 @@ class TestNAT44(MethodHolder):
             self.assertEqual(ip.dst, host.ip4)
             self.assertEqual(tcp.sport, server_out_port)
             self.assertEqual(tcp.dport, host_in_port)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -2218,7 +2231,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.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)
                 elif packet.haslayer(UDP):
                     self.assertNotEqual(packet[UDP].sport, self.udp_port_in)
                     self.assertEqual(packet[UDP].dport, server_udp_port)
@@ -2255,7 +2268,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)
                 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)
                 elif packet.haslayer(UDP):
                     self.assertEqual(packet[UDP].dport, self.udp_port_in)
                     self.assertEqual(packet[UDP].sport, server_udp_port)
@@ -2291,7 +2304,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.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)
                 elif packet.haslayer(UDP):
                     self.assertEqual(packet[UDP].sport, self.udp_port_in)
                     self.assertEqual(packet[UDP].dport, server_udp_port)
@@ -2328,7 +2341,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)
                 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)
                 elif packet.haslayer(UDP):
                     self.assertEqual(packet[UDP].dport, self.udp_port_in)
                     self.assertEqual(packet[UDP].sport, server_udp_port)
@@ -2364,6 +2377,31 @@ class TestNAT44(MethodHolder):
         # verify number of translated packet
         self.pg1.get_capture(pkts_num)
 
         # 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)
     def test_interface_addr(self):
         """ Acquire NAT44 addresses from interface """
         self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index)
@@ -2403,15 +2441,44 @@ class TestNAT44(MethodHolder):
         # configure interface address and check static mappings
         self.pg7.config_ip4()
         static_mappings = self.vapi.nat44_static_mapping_dump()
         # 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((static_mappings[0].tag).split('\0', 1)[0], tag)
+        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()
 
         # 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):
         self.assertEqual(0, len(static_mappings))
 
     def test_interface_addr_identity_nat(self):
@@ -2434,17 +2501,23 @@ class TestNAT44(MethodHolder):
         # configure interface address and check identity mappings
         self.pg7.config_ip4()
         identity_mappings = self.vapi.nat44_identity_mapping_dump()
         # 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()
 
         # 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 """
 
     def test_ipfix_nat44_sess(self):
         """ IPFIX logging NAT44 session created/delted """
@@ -2886,7 +2959,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -2906,7 +2979,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -2941,7 +3014,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -2961,7 +3034,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -2996,7 +3069,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -3016,7 +3089,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -3026,8 +3099,6 @@ class TestNAT44(MethodHolder):
         host = self.pg0.remote_hosts[0]
         server = self.pg0.remote_hosts[1]
         host_in_port = 1234
         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"
 
         server_out_port = 8765
         server_nat_ip = "10.0.0.11"
 
@@ -3062,7 +3133,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -3082,7 +3153,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -3209,7 +3280,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.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))
             host_out_port = tcp.sport
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
@@ -3231,132 +3302,302 @@ class TestNAT44(MethodHolder):
             self.assertEqual(ip.dst, host.ip4)
             self.assertEqual(tcp.sport, server_out_port)
             self.assertEqual(tcp.dport, host_in_port)
             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)
-        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)
+            self.assert_packet_checksums_valid(p)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
         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]
+    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
         external_port = 80
         local_port = 8080
-        eh_port_in = 0
 
         self.vapi.nat44_forwarding_enable_disable(1)
 
         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,
+        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,
                                       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,
+                                      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)
                                                   is_inside=0)
+        self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index,
+                                                         is_inside=0)
 
         # from client to service
 
         # 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) /
+        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))
              TCP(sport=12345, dport=external_port))
-        self.pg9.add_stream(p)
+        self.pg1.add_stream(p)
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
         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]
         p = capture[0]
-        server = None
         try:
             ip = p[IP]
             tcp = p[TCP]
         try:
             ip = p[IP]
             tcp = p[TCP]
-            self.assertEqual(ip.dst, local_host.ip4)
-            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(ip.dst, self.pg0.remote_ip4)
             self.assertEqual(tcp.dport, local_port)
             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)
+            self.assert_packet_checksums_valid(p)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
         # from service back to client
         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)
+        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()
         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]
         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(ip.src, external_addr)
             self.assertEqual(tcp.sport, external_port)
             self.assertEqual(tcp.sport, external_port)
-            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
 
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
-    def test_del_session(self):
+        # 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))
+            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):
+        """ 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.assert_packet_checksums_valid(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        # out2in
+        p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) /
+             IP(src=remote_host.ip4, dst=self.nat_addr) /
+             TCP(sport=80, dport=external_port))
+        self.pg9.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg9.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, remote_host.ip4)
+            self.assertEqual(ip.dst, local_host.ip4)
+            self.assertEqual(tcp.sport, 80)
+            self.assertEqual(tcp.dport, 12345)
+            self.assert_packet_checksums_valid(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+    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
+
+    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)
         """ Delete NAT44 session """
         self.nat44_add_address(self.nat_addr)
         self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
@@ -3455,10 +3696,8 @@ class TestNAT44(MethodHolder):
 
     def test_reass_hairpinning(self):
         """ NAT44 fragments hairpinning """
 
     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)
         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
         server_in_port = random.randint(1025, 65535)
         server_out_port = random.randint(1025, 65535)
         data = "A" * 4 + "B" * 16 + "C" * 3
@@ -3561,215 +3800,423 @@ class TestNAT44(MethodHolder):
             self.assertEqual(tcp.dport, 22)
             self.assertNotEqual(tcp.sport, 4567)
             self.assertEqual((tcp.sport >> 6) & 63, 10)
             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
 
         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'
         twice_nat_addr = '10.0.1.3'
+
         port_in = 8080
         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
         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_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)
 
                                                   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))
              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()
         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]
         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
             eh_port_in = tcp.sport
-            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
 
         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()
         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]
         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)
             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)
+            self.assert_packet_checksums_valid(p)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
         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 """
     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.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()
         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:
         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", 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)
+
+            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, 1)
         except:
         except:
-            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            self.logger.error("TCP session termination failed")
             raise
 
             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:
         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", seq=100, ack=300))
+            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)
+            # 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))
 
 
-        # no address in NAT pool
-        adresses = self.vapi.nat44_address_dump()
-        self.assertEqual(0, len(adresses))
+            self.pg0.add_stream(p)
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+            self.pg1.get_capture(1)
 
 
-        # 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", seq=101, ack=301))
+            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, 1)
+        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.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", 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)
+
+            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, 1)
+        except:
+            self.logger.error("TCP session termination failed")
+            raise
 
     def tearDown(self):
         super(TestNAT44, self).tearDown()
 
     def tearDown(self):
         super(TestNAT44, self).tearDown()
@@ -4022,50 +4469,6 @@ class TestDeterministicNAT(MethodHolder):
                                       "(outside network):", packet))
                 raise
 
                                       "(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
     def verify_ipfix_max_entries_per_user(self, data):
         """
         Verify IPFIX maximum entries per user exceeded event
@@ -4872,8 +5275,6 @@ class TestNAT64(MethodHolder):
         self.udp_port_in = 6304
         self.icmp_id_in = 6305
 
         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)
         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)
@@ -4916,7 +5317,7 @@ class TestNAT64(MethodHolder):
                 inner = packet[IPerror]
                 self.assertEqual(inner.src, self.pg1.remote_ip4)
                 self.assertEqual(inner.dst, self.nat_addr)
                 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):
                 if inner.haslayer(TCPerror):
                     self.assertEqual(inner[TCPerror].dport, self.tcp_port_out)
                 elif inner.haslayer(UDPerror):
@@ -4945,7 +5346,7 @@ class TestNAT64(MethodHolder):
                 inner = icmp[IPerror6]
                 self.assertEqual(inner.src, self.pg0.remote_ip6)
                 self.assertEqual(inner.dst, ip.src)
                 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):
                 if inner.haslayer(TCPerror):
                     self.assertEqual(inner[TCPerror].sport, self.tcp_port_in)
                 elif inner.haslayer(UDPerror):
@@ -5007,15 +5408,14 @@ class TestNAT64(MethodHolder):
             try:
                 self.assertEqual(packet[IPv6].src, nat_addr_ip6)
                 self.assertEqual(packet[IPv6].dst, server.ip6)
             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)
                 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)
                     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))
                     client_udp_out_port = packet[UDP].sport
             except:
                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
@@ -5039,14 +5439,13 @@ class TestNAT64(MethodHolder):
             try:
                 self.assertEqual(packet[IPv6].src, nat_addr_ip6)
                 self.assertEqual(packet[IPv6].dst, client.ip6)
             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)
                 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)
                 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
             except:
                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
                 raise
@@ -5070,7 +5469,7 @@ class TestNAT64(MethodHolder):
                 inner = icmp[IPerror6]
                 self.assertEqual(inner.src, server.ip6)
                 self.assertEqual(inner.dst, nat_addr_ip6)
                 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,
                 if inner.haslayer(TCPerror):
                     self.assertEqual(inner[TCPerror].sport, server_tcp_in_port)
                     self.assertEqual(inner[TCPerror].dport,
@@ -5193,7 +5592,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.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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -5331,8 +5730,7 @@ class TestNAT64(MethodHolder):
             self.assertNotEqual(tcp.sport, 12345)
             external_port = tcp.sport
             self.assertEqual(tcp.dport, 80)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -5353,7 +5751,7 @@ class TestNAT64(MethodHolder):
             self.assertEqual(ip.dst, self.pg3.remote_ip6)
             self.assertEqual(tcp.sport, 80)
             self.assertEqual(tcp.dport, 12345)
             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
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
@@ -5411,7 +5809,6 @@ class TestNAT64(MethodHolder):
     def test_reass_hairpinning(self):
         """ NAT64 fragments hairpinning """
         data = 'a' * 200
     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)
         server = self.pg0.remote_hosts[1]
         server_in_port = random.randint(1025, 65535)
         server_out_port = random.randint(1025, 65535)
@@ -5837,7 +6234,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.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) /
         out_port = capture[UDP].sport
 
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
@@ -5854,7 +6251,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.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) /
 
         # TCP
         p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
@@ -5871,8 +6268,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.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) /
         out_port = capture[TCP].sport
 
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
@@ -5889,8 +6285,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.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) /
 
         # ICMP
         p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
@@ -5906,8 +6301,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.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) /
         out_id = capture[ICMP].id
 
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
@@ -5923,8 +6317,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.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) /
 
         # ping DS-Lite AFTR tunnel endpoint address
         p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
@@ -6011,7 +6404,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.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) /
 
         # UDP decapsulation
         p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
@@ -6028,7 +6421,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.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) /
 
         # ping DS-Lite B4 tunnel endpoint address
         p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
@@ -6052,5 +6445,150 @@ class TestDSliteCE(MethodHolder):
             self.logger.info(
                 self.vapi.cli("show dslite b4-tunnel-endpoint-address"))
 
             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)
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)