text: #!/usr/bin/env python3
  
  import ipaddress
  import random
  import socket
  import struct
  import unittest
  from io import BytesIO
  from time import sleep
  
  import scapy.compat
  from framework import VppTestCase, VppTestRunner, running_extended_tests
  from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
  from scapy.all import bind_layers, Packet, ByteEnumField, ShortField, \
      IPField, IntField, LongField, XByteField, FlagsField, FieldLenField, \
      PacketListField
  from scapy.data import IP_PROTOS
  from scapy.layers.inet import IP, TCP, UDP, ICMP
  from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
  from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment
  from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply, \
      ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, fragment6
  from scapy.layers.l2 import Ether, ARP, GRE
  from scapy.packet import Raw
  from syslog_rfc5424_parser import SyslogMessage, ParseError
  from syslog_rfc5424_parser.constants import SyslogSeverity
  from util import ip4_range
  from util import ppc, ppp
  from vpp_acl import AclRule, VppAcl, VppAclInterface
  from vpp_ip_route import VppIpRoute, VppRoutePath
  from vpp_neighbor import VppNeighbor
  from vpp_papi import VppEnum
  
  
  # NAT HA protocol event data
  class Event(Packet):
      name = "Event"
      fields_desc = [ByteEnumField("event_type", None,
                                   {1: "add", 2: "del", 3: "refresh"}),
                     ByteEnumField("protocol", None,
                                   {0: "udp", 1: "tcp", 2: "icmp"}),
                     ShortField("flags", 0),
                     IPField("in_addr", None),
                     IPField("out_addr", None),
                     ShortField("in_port", None),
                     ShortField("out_port", None),
                     IPField("eh_addr", None),
                     IPField("ehn_addr", None),
                     ShortField("eh_port", None),
                     ShortField("ehn_port", None),
                     IntField("fib_index", None),
                     IntField("total_pkts", 0),
                     LongField("total_bytes", 0)]
  
      def extract_padding(self, s):
          return "", s
  
  
  # NAT HA protocol header
  class HANATStateSync(Packet):
      name = "HA NAT state sync"
      fields_desc = [XByteField("version", 1),
                     FlagsField("flags", 0, 8, ['ACK']),
                     FieldLenField("count", None, count_of="events"),
                     IntField("sequence_number", 1),
                     IntField("thread_index", 0),
                     PacketListField("events", [], Event,
                                     count_from=lambda pkt: pkt.count)]
  
  
  class MethodHolder(VppTestCase):
      """ NAT create capture and verify method holder """
  
      @property
      def config_flags(self):
          return VppEnum.vl_api_nat_config_flags_t
  
      @property
      def SYSLOG_SEVERITY(self):
          return VppEnum.vl_api_syslog_severity_t
  
      def clear_nat44(self):
          """
          Clear NAT44 configuration.
          """
          if hasattr(self, 'pg7') and hasattr(self, 'pg8'):
              if self.pg7.has_ip4_config:
                  self.pg7.unconfig_ip4()
  
          self.vapi.nat44_forwarding_enable_disable(enable=0)
  
          interfaces = self.vapi.nat44_interface_addr_dump()
          for intf in interfaces:
              self.vapi.nat44_add_del_interface_addr(
                  is_add=0,
                  sw_if_index=intf.sw_if_index,
                  flags=intf.flags)
  
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=0)
          self.ipfix_src_port = 4739
          self.ipfix_domain_id = 1
  
          self.vapi.syslog_set_filter(
              self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_EMERG)
  
          self.vapi.nat_ha_set_listener(ip_address='0.0.0.0', port=0,
                                        path_mtu=512)
          self.vapi.nat_ha_set_failover(ip_address='0.0.0.0', port=0,
                                        session_refresh_interval=10)
  
          interfaces = self.vapi.nat44_interface_dump()
          for intf in interfaces:
              if intf.flags & self.config_flags.NAT_IS_INSIDE and \
                      intf.flags & self.config_flags.NAT_IS_OUTSIDE:
                  self.vapi.nat44_interface_add_del_feature(
                      sw_if_index=intf.sw_if_index)
              self.vapi.nat44_interface_add_del_feature(
                  sw_if_index=intf.sw_if_index,
                  flags=intf.flags)
  
          interfaces = self.vapi.nat44_interface_output_feature_dump()
          for intf in interfaces:
              self.vapi.nat44_interface_add_del_output_feature(
                  is_add=0,
                  flags=intf.flags,
                  sw_if_index=intf.sw_if_index)
          static_mappings = self.vapi.nat44_static_mapping_dump()
          for sm in static_mappings:
              self.vapi.nat44_add_del_static_mapping(
                  is_add=0,
                  local_ip_address=sm.local_ip_address,
                  external_ip_address=sm.external_ip_address,
                  external_sw_if_index=sm.external_sw_if_index,
                  local_port=sm.local_port,
                  external_port=sm.external_port,
                  vrf_id=sm.vrf_id,
                  protocol=sm.protocol,
                  flags=sm.flags, tag=sm.tag)
  
          lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump()
          for lb_sm in lb_static_mappings:
              self.vapi.nat44_add_del_lb_static_mapping(
                  is_add=0,
                  flags=lb_sm.flags,
                  external_addr=lb_sm.external_addr,
                  external_port=lb_sm.external_port,
                  protocol=lb_sm.protocol,
                  local_num=0, locals=[],
                  tag=lb_sm.tag)
  
          identity_mappings = self.vapi.nat44_identity_mapping_dump()
          for id_m in identity_mappings:
              self.vapi.nat44_add_del_identity_mapping(
                  ip_address=id_m.ip_address,
                  sw_if_index=id_m.sw_if_index,
                  port=id_m.port,
                  flags=id_m.flags,
                  vrf_id=id_m.vrf_id,
                  protocol=id_m.protocol)
  
          addresses = self.vapi.nat44_address_dump()
          for addr in addresses:
              self.vapi.nat44_add_del_address_range(
                  first_ip_address=addr.ip_address,
                  last_ip_address=addr.ip_address,
                  vrf_id=0xFFFFFFFF, flags=addr.flags)
  
          self.verify_no_nat44_user()
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=240, icmp=60)
          self.vapi.nat_set_addr_and_port_alloc_alg()
          self.vapi.nat_set_mss_clamping(enable=0, mss_value=1500)
  
      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, tag="", flags=0):
          """
          Add/delete NAT44 static mapping
  
          :param local_ip: Local IP address
          :param external_ip: External IP address
          :param local_port: Local port number (Optional)
          :param external_port: External port number (Optional)
          :param vrf_id: VRF ID (Default 0)
          :param is_add: 1 if add, 0 if delete (Default add)
          :param external_sw_if_index: External interface instead of IP address
          :param proto: IP protocol (Mandatory if port specified)
          :param tag: Opaque string tag
          :param flags: NAT configuration flags
          """
  
          if not (local_port and external_port):
              flags |= self.config_flags.NAT_IS_ADDR_ONLY
  
          self.vapi.nat44_add_del_static_mapping(
              is_add=is_add,
              local_ip_address=local_ip,
              external_ip_address=external_ip,
              external_sw_if_index=external_sw_if_index,
              local_port=local_port,
              external_port=external_port,
              vrf_id=vrf_id, protocol=proto,
              flags=flags,
              tag=tag)
  
      def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0):
          """
          Add/delete NAT44 address
  
          :param ip: IP address
          :param is_add: 1 if add, 0 if delete (Default add)
          :param twice_nat: twice NAT address for external hosts
          """
          flags = self.config_flags.NAT_IS_TWICE_NAT if twice_nat else 0
          self.vapi.nat44_add_del_address_range(first_ip_address=ip,
                                                last_ip_address=ip,
                                                vrf_id=vrf_id,
                                                is_add=is_add,
                                                flags=flags)
  
      def create_stream_in(self, in_if, out_if, dst_ip=None, ttl=64):
          """
          Create packet stream for inside network
  
          :param in_if: Inside interface
          :param out_if: Outside interface
          :param dst_ip: Destination address
          :param ttl: TTL of generated packets
          """
          if dst_ip is None:
              dst_ip = out_if.remote_ip4
  
          pkts = []
          # TCP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               TCP(sport=self.tcp_port_in, dport=20))
          pkts.extend([p, p])
  
          # UDP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               UDP(sport=self.udp_port_in, dport=20))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               ICMP(id=self.icmp_id_in, type='echo-request'))
          pkts.append(p)
  
          return pkts
  
      def compose_ip6(self, ip4, pref, plen):
          """
          Compose IPv4-embedded IPv6 addresses
  
          :param ip4: IPv4 address
          :param pref: IPv6 prefix
          :param plen: IPv6 prefix length
          :returns: IPv4-embedded IPv6 addresses
          """
          pref_n = list(socket.inet_pton(socket.AF_INET6, pref))
          ip4_n = list(socket.inet_pton(socket.AF_INET, ip4))
          if plen == 32:
              pref_n[4] = ip4_n[0]
              pref_n[5] = ip4_n[1]
              pref_n[6] = ip4_n[2]
              pref_n[7] = ip4_n[3]
          elif plen == 40:
              pref_n[5] = ip4_n[0]
              pref_n[6] = ip4_n[1]
              pref_n[7] = ip4_n[2]
              pref_n[9] = ip4_n[3]
          elif plen == 48:
              pref_n[6] = ip4_n[0]
              pref_n[7] = ip4_n[1]
              pref_n[9] = ip4_n[2]
              pref_n[10] = ip4_n[3]
          elif plen == 56:
              pref_n[7] = ip4_n[0]
              pref_n[9] = ip4_n[1]
              pref_n[10] = ip4_n[2]
              pref_n[11] = ip4_n[3]
          elif plen == 64:
              pref_n[9] = ip4_n[0]
              pref_n[10] = ip4_n[1]
              pref_n[11] = ip4_n[2]
              pref_n[12] = ip4_n[3]
          elif plen == 96:
              pref_n[12] = ip4_n[0]
              pref_n[13] = ip4_n[1]
              pref_n[14] = ip4_n[2]
              pref_n[15] = ip4_n[3]
          packed_pref_n = b''.join([scapy.compat.chb(x) for x in pref_n])
          return socket.inet_ntop(socket.AF_INET6, packed_pref_n)
  
      def extract_ip4(self, ip6, plen):
          """
          Extract IPv4 address embedded in IPv6 addresses
  
          :param ip6: IPv6 address
          :param plen: IPv6 prefix length
          :returns: extracted IPv4 address
          """
          ip6_n = list(socket.inet_pton(socket.AF_INET6, ip6))
          ip4_n = [None] * 4
          if plen == 32:
              ip4_n[0] = ip6_n[4]
              ip4_n[1] = ip6_n[5]
              ip4_n[2] = ip6_n[6]
              ip4_n[3] = ip6_n[7]
          elif plen == 40:
              ip4_n[0] = ip6_n[5]
              ip4_n[1] = ip6_n[6]
              ip4_n[2] = ip6_n[7]
              ip4_n[3] = ip6_n[9]
          elif plen == 48:
              ip4_n[0] = ip6_n[6]
              ip4_n[1] = ip6_n[7]
              ip4_n[2] = ip6_n[9]
              ip4_n[3] = ip6_n[10]
          elif plen == 56:
              ip4_n[0] = ip6_n[7]
              ip4_n[1] = ip6_n[9]
              ip4_n[2] = ip6_n[10]
              ip4_n[3] = ip6_n[11]
          elif plen == 64:
              ip4_n[0] = ip6_n[9]
              ip4_n[1] = ip6_n[10]
              ip4_n[2] = ip6_n[11]
              ip4_n[3] = ip6_n[12]
          elif plen == 96:
              ip4_n[0] = ip6_n[12]
              ip4_n[1] = ip6_n[13]
              ip4_n[2] = ip6_n[14]
              ip4_n[3] = ip6_n[15]
          return socket.inet_ntop(socket.AF_INET, ''.join(ip4_n))
  
      def create_stream_in_ip6(self, in_if, out_if, hlim=64, pref=None, plen=0):
          """
          Create IPv6 packet stream for inside network
  
          :param in_if: Inside interface
          :param out_if: Outside interface
          :param ttl: Hop Limit of generated packets
          :param pref: NAT64 prefix
          :param plen: NAT64 prefix length
          """
          pkts = []
          if pref is None:
              dst = ''.join(['64:ff9b::', out_if.remote_ip4])
          else:
              dst = self.compose_ip6(out_if.remote_ip4, pref, plen)
  
          # TCP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
               TCP(sport=self.tcp_port_in, dport=20))
          pkts.append(p)
  
          # UDP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
               UDP(sport=self.udp_port_in, dport=20))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
               ICMPv6EchoRequest(id=self.icmp_id_in))
          pkts.append(p)
  
          return pkts
  
      def create_stream_out(self, out_if, dst_ip=None, ttl=64,
                            use_inside_ports=False):
          """
          Create packet stream for outside network
  
          :param out_if: Outside interface
          :param dst_ip: Destination IP address (Default use global NAT address)
          :param ttl: TTL of generated packets
          :param use_inside_ports: Use inside NAT ports as destination ports
                 instead of outside ports
          """
          if dst_ip is None:
              dst_ip = self.nat_addr
          if not use_inside_ports:
              tcp_port = self.tcp_port_out
              udp_port = self.udp_port_out
              icmp_id = self.icmp_id_out
          else:
              tcp_port = self.tcp_port_in
              udp_port = self.udp_port_in
              icmp_id = self.icmp_id_in
          pkts = []
          # TCP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               TCP(dport=tcp_port, sport=20))
          pkts.extend([p, p])
  
          # UDP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               UDP(dport=udp_port, sport=20))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               ICMP(id=icmp_id, type='echo-reply'))
          pkts.append(p)
  
          return pkts
  
      def create_stream_out_ip6(self, out_if, src_ip, dst_ip, hl=64):
          """
          Create packet stream for outside network
  
          :param out_if: Outside interface
          :param dst_ip: Destination IP address (Default use global NAT address)
          :param hl: HL of generated packets
          """
          pkts = []
          # TCP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IPv6(src=src_ip, dst=dst_ip, hlim=hl) /
               TCP(dport=self.tcp_port_out, sport=20))
          pkts.append(p)
  
          # UDP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IPv6(src=src_ip, dst=dst_ip, hlim=hl) /
               UDP(dport=self.udp_port_out, sport=20))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IPv6(src=src_ip, dst=dst_ip, hlim=hl) /
               ICMPv6EchoReply(id=self.icmp_id_out))
          pkts.append(p)
  
          return pkts
  
      def verify_capture_out(self, capture, nat_ip=None, same_port=False,
                             dst_ip=None, is_ip6=False):
          """
          Verify captured packets on outside network
  
          :param capture: Captured packets
          :param nat_ip: Translated IP address (Default use global NAT address)
          :param same_port: Source port number is not translated (Default False)
          :param dst_ip: Destination IP address (Default do not verify)
          :param is_ip6: If L3 protocol is IPv6 (Default False)
          """
          if is_ip6:
              IP46 = IPv6
              ICMP46 = ICMPv6EchoRequest
          else:
              IP46 = IP
              ICMP46 = ICMP
          if nat_ip is None:
              nat_ip = self.nat_addr
          for packet in capture:
              try:
                  if not is_ip6:
                      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)
                  if packet.haslayer(TCP):
                      if same_port:
                          self.assertEqual(packet[TCP].sport, self.tcp_port_in)
                      else:
                          self.assertNotEqual(
                              packet[TCP].sport, self.tcp_port_in)
                      self.tcp_port_out = packet[TCP].sport
                      self.assert_packet_checksums_valid(packet)
                  elif packet.haslayer(UDP):
                      if same_port:
                          self.assertEqual(packet[UDP].sport, self.udp_port_in)
                      else:
                          self.assertNotEqual(
                              packet[UDP].sport, self.udp_port_in)
                      self.udp_port_out = packet[UDP].sport
                  else:
                      if same_port:
                          self.assertEqual(packet[ICMP46].id, self.icmp_id_in)
                      else:
                          self.assertNotEqual(packet[ICMP46].id, self.icmp_id_in)
                      self.icmp_id_out = packet[ICMP46].id
                      self.assert_packet_checksums_valid(packet)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(outside network):", packet))
                  raise
  
      def verify_capture_out_ip6(self, capture, nat_ip, same_port=False,
                                 dst_ip=None):
          """
          Verify captured packets on outside network
  
          :param capture: Captured packets
          :param nat_ip: Translated IP address
          :param same_port: Source port number is not translated (Default False)
          :param dst_ip: Destination IP address (Default do not verify)
          """
          return self.verify_capture_out(capture, nat_ip, same_port, dst_ip,
                                         True)
  
      def verify_capture_in(self, capture, in_if):
          """
          Verify captured packets on inside network
  
          :param capture: Captured packets
          :param in_if: Inside interface
          """
          for packet in capture:
              try:
                  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)
                  elif packet.haslayer(UDP):
                      self.assertEqual(packet[UDP].dport, self.udp_port_in)
                  else:
                      self.assertEqual(packet[ICMP].id, self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(inside network):", packet))
                  raise
  
      def verify_capture_in_ip6(self, capture, src_ip, dst_ip):
          """
          Verify captured IPv6 packets on inside network
  
          :param capture: Captured packets
          :param src_ip: Source IP
          :param dst_ip: Destination IP address
          """
          for packet in capture:
              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)
                  elif packet.haslayer(UDP):
                      self.assertEqual(packet[UDP].dport, self.udp_port_in)
                  else:
                      self.assertEqual(packet[ICMPv6EchoReply].id,
                                       self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(inside network):", packet))
                  raise
  
      def verify_capture_no_translation(self, capture, ingress_if, egress_if):
          """
          Verify captured packet that don't have to be translated
  
          :param capture: Captured packets
          :param ingress_if: Ingress interface
          :param egress_if: Egress interface
          """
          for packet in capture:
              try:
                  self.assertEqual(packet[IP].src, ingress_if.remote_ip4)
                  self.assertEqual(packet[IP].dst, egress_if.remote_ip4)
                  if packet.haslayer(TCP):
                      self.assertEqual(packet[TCP].sport, self.tcp_port_in)
                  elif packet.haslayer(UDP):
                      self.assertEqual(packet[UDP].sport, self.udp_port_in)
                  else:
                      self.assertEqual(packet[ICMP].id, self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(inside network):", packet))
                  raise
  
      def verify_capture_out_with_icmp_errors(self, capture, src_ip=None,
                                              icmp_type=11):
          """
          Verify captured packets with ICMP errors on outside network
  
          :param capture: Captured packets
          :param src_ip: Translated IP address or IP address of VPP
                         (Default use global NAT address)
          :param icmp_type: Type of error ICMP packet
                            we are expecting (Default 11)
          """
          if src_ip is None:
              src_ip = self.nat_addr
          for packet in capture:
              try:
                  self.assertEqual(packet[IP].src, src_ip)
                  self.assertEqual(packet.haslayer(ICMP), 1)
                  icmp = packet[ICMP]
                  self.assertEqual(icmp.type, icmp_type)
                  self.assertTrue(icmp.haslayer(IPerror))
                  inner_ip = icmp[IPerror]
                  if inner_ip.haslayer(TCPerror):
                      self.assertEqual(inner_ip[TCPerror].dport,
                                       self.tcp_port_out)
                  elif inner_ip.haslayer(UDPerror):
                      self.assertEqual(inner_ip[UDPerror].dport,
                                       self.udp_port_out)
                  else:
                      self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_out)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(outside network):", packet))
                  raise
  
      def verify_capture_in_with_icmp_errors(self, capture, in_if, icmp_type=11):
          """
          Verify captured packets with ICMP errors on inside network
  
          :param capture: Captured packets
          :param in_if: Inside interface
          :param icmp_type: Type of error ICMP packet
                            we are expecting (Default 11)
          """
          for packet in capture:
              try:
                  self.assertEqual(packet[IP].dst, in_if.remote_ip4)
                  self.assertEqual(packet.haslayer(ICMP), 1)
                  icmp = packet[ICMP]
                  self.assertEqual(icmp.type, icmp_type)
                  self.assertTrue(icmp.haslayer(IPerror))
                  inner_ip = icmp[IPerror]
                  if inner_ip.haslayer(TCPerror):
                      self.assertEqual(inner_ip[TCPerror].sport,
                                       self.tcp_port_in)
                  elif inner_ip.haslayer(UDPerror):
                      self.assertEqual(inner_ip[UDPerror].sport,
                                       self.udp_port_in)
                  else:
                      self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(inside network):", packet))
                  raise
  
      def create_stream_frag(self, src_if, dst, sport, dport, data,
                             proto=IP_PROTOS.tcp, echo_reply=False):
          """
          Create fragmented packet stream
  
          :param src_if: Source interface
          :param dst: Destination IPv4 address
          :param sport: Source port
          :param dport: Destination port
          :param data: Payload data
          :param proto: protocol (TCP, UDP, ICMP)
          :param echo_reply: use echo_reply if protocol is ICMP
          :returns: Fragments
          """
          if proto == IP_PROTOS.tcp:
              p = (IP(src=src_if.remote_ip4, dst=dst) /
                   TCP(sport=sport, dport=dport) /
                   Raw(data))
              p = p.__class__(scapy.compat.raw(p))
              chksum = p[TCP].chksum
              proto_header = TCP(sport=sport, dport=dport, chksum=chksum)
          elif proto == IP_PROTOS.udp:
              proto_header = UDP(sport=sport, dport=dport)
          elif proto == IP_PROTOS.icmp:
              if not echo_reply:
                  proto_header = ICMP(id=sport, type='echo-request')
              else:
                  proto_header = ICMP(id=sport, type='echo-reply')
          else:
              raise Exception("Unsupported protocol")
          id = random.randint(0, 65535)
          pkts = []
          if proto == IP_PROTOS.tcp:
              raw = Raw(data[0:4])
          else:
              raw = Raw(data[0:16])
          p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) /
               IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=0, id=id) /
               proto_header /
               raw)
          pkts.append(p)
          if proto == IP_PROTOS.tcp:
              raw = Raw(data[4:20])
          else:
              raw = Raw(data[16:32])
          p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) /
               IP(src=src_if.remote_ip4, dst=dst, flags="MF", frag=3, id=id,
                  proto=proto) /
               raw)
          pkts.append(p)
          if proto == IP_PROTOS.tcp:
              raw = Raw(data[20:])
          else:
              raw = Raw(data[32:])
          p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) /
               IP(src=src_if.remote_ip4, dst=dst, frag=5, proto=proto,
                  id=id) /
               raw)
          pkts.append(p)
          return pkts
  
      def create_stream_frag_ip6(self, src_if, dst, sport, dport, data,
                                 pref=None, plen=0, frag_size=128):
          """
          Create fragmented packet stream
  
          :param src_if: Source interface
          :param dst: Destination IPv4 address
          :param sport: Source TCP port
          :param dport: Destination TCP port
          :param data: Payload data
          :param pref: NAT64 prefix
          :param plen: NAT64 prefix length
          :param fragsize: size of fragments
          :returns: Fragments
          """
          if pref is None:
              dst_ip6 = ''.join(['64:ff9b::', dst])
          else:
              dst_ip6 = self.compose_ip6(dst, pref, plen)
  
          p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
               IPv6(src=src_if.remote_ip6, dst=dst_ip6) /
               IPv6ExtHdrFragment(id=random.randint(0, 65535)) /
               TCP(sport=sport, dport=dport) /
               Raw(data))
  
          return fragment6(p, frag_size)
  
      def reass_frags_and_verify(self, frags, src, dst):
          """
          Reassemble and verify fragmented packet
  
          :param frags: Captured fragments
          :param src: Source IPv4 address to verify
          :param dst: Destination IPv4 address to verify
  
          :returns: Reassembled IPv4 packet
          """
          buffer = BytesIO()
          for p in frags:
              self.assertEqual(p[IP].src, src)
              self.assertEqual(p[IP].dst, dst)
              self.assert_ip_checksum_valid(p)
              buffer.seek(p[IP].frag * 8)
              buffer.write(bytes(p[IP].payload))
          ip = IP(src=frags[0][IP].src, dst=frags[0][IP].dst,
                  proto=frags[0][IP].proto)
          if ip.proto == IP_PROTOS.tcp:
              p = (ip / TCP(buffer.getvalue()))
              self.logger.debug(ppp("Reassembled:", p))
              self.assert_tcp_checksum_valid(p)
          elif ip.proto == IP_PROTOS.udp:
              p = (ip / UDP(buffer.getvalue()[:8]) /
                   Raw(buffer.getvalue()[8:]))
          elif ip.proto == IP_PROTOS.icmp:
              p = (ip / ICMP(buffer.getvalue()))
          return p
  
      def reass_frags_and_verify_ip6(self, frags, src, dst):
          """
          Reassemble and verify fragmented packet
  
          :param frags: Captured fragments
          :param src: Source IPv6 address to verify
          :param dst: Destination IPv6 address to verify
  
          :returns: Reassembled IPv6 packet
          """
          buffer = BytesIO()
          for p in frags:
              self.assertEqual(p[IPv6].src, src)
              self.assertEqual(p[IPv6].dst, dst)
              buffer.seek(p[IPv6ExtHdrFragment].offset * 8)
              buffer.write(bytes(p[IPv6ExtHdrFragment].payload))
          ip = IPv6(src=frags[0][IPv6].src, dst=frags[0][IPv6].dst,
                    nh=frags[0][IPv6ExtHdrFragment].nh)
          if ip.nh == IP_PROTOS.tcp:
              p = (ip / TCP(buffer.getvalue()))
          elif ip.nh == IP_PROTOS.udp:
              p = (ip / UDP(buffer.getvalue()))
          self.logger.debug(ppp("Reassembled:", p))
          self.assert_packet_checksums_valid(p)
          return p
  
      def initiate_tcp_session(self, in_if, out_if):
          """
          Initiates TCP session
  
          :param in_if: Inside interface
          :param out_if: Outside interface
          """
          try:
              # SYN packet in->out
              p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
                   IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
                   TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                       flags="S"))
              in_if.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = out_if.get_capture(1)
              p = capture[0]
              self.tcp_port_out = p[TCP].sport
  
              # SYN + ACK packet out->in
              p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) /
                   IP(src=out_if.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                       flags="SA"))
              out_if.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              in_if.get_capture(1)
  
              # ACK packet in->out
              p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
                   IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
                   TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                       flags="A"))
              in_if.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              out_if.get_capture(1)
  
          except:
              self.logger.error("TCP 3 way handshake failed")
              raise
  
      def verify_ipfix_nat44_ses(self, data):
          """
          Verify IPFIX NAT44 session create/delete event
  
          :param data: Decoded IPFIX data records
          """
          nat44_ses_create_num = 0
          nat44_ses_delete_num = 0
          self.assertEqual(6, len(data))
          for record in data:
              # natEvent
              self.assertIn(scapy.compat.orb(record[230]), [4, 5])
              if scapy.compat.orb(record[230]) == 4:
                  nat44_ses_create_num += 1
              else:
                  nat44_ses_delete_num += 1
              # sourceIPv4Address
              self.assertEqual(self.pg0.remote_ip4,
                               str(ipaddress.IPv4Address(record[8])))
              # postNATSourceIPv4Address
              self.assertEqual(socket.inet_pton(socket.AF_INET, self.nat_addr),
                               record[225])
              # ingressVRFID
              self.assertEqual(struct.pack("!I", 0), record[234])
              # protocolIdentifier/sourceTransportPort
              # /postNAPTSourceTransportPort
              if IP_PROTOS.icmp == scapy.compat.orb(record[4]):
                  self.assertEqual(struct.pack("!H", self.icmp_id_in), record[7])
                  self.assertEqual(struct.pack("!H", self.icmp_id_out),
                                   record[227])
              elif IP_PROTOS.tcp == scapy.compat.orb(record[4]):
                  self.assertEqual(struct.pack("!H", self.tcp_port_in),
                                   record[7])
                  self.assertEqual(struct.pack("!H", self.tcp_port_out),
                                   record[227])
              elif IP_PROTOS.udp == scapy.compat.orb(record[4]):
                  self.assertEqual(struct.pack("!H", self.udp_port_in),
                                   record[7])
                  self.assertEqual(struct.pack("!H", self.udp_port_out),
                                   record[227])
              else:
                  self.fail("Invalid protocol")
          self.assertEqual(3, nat44_ses_create_num)
          self.assertEqual(3, nat44_ses_delete_num)
  
      def verify_ipfix_addr_exhausted(self, data):
          """
          Verify IPFIX NAT addresses event
  
          :param data: Decoded IPFIX data records
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          self.assertEqual(scapy.compat.orb(record[230]), 3)
          # natPoolID
          self.assertEqual(struct.pack("!I", 0), record[283])
  
      def verify_ipfix_max_sessions(self, data, limit):
          """
          Verify IPFIX maximum session entries exceeded event
  
          :param data: Decoded IPFIX data records
          :param limit: Number of maximum session entries that can be created.
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          self.assertEqual(scapy.compat.orb(record[230]), 13)
          # natQuotaExceededEvent
          self.assertEqual(struct.pack("I", 1), record[466])
          # maxSessionEntries
          self.assertEqual(struct.pack("I", limit), record[471])
  
      def verify_ipfix_max_bibs(self, data, limit):
          """
          Verify IPFIX maximum BIB entries exceeded event
  
          :param data: Decoded IPFIX data records
          :param limit: Number of maximum BIB entries that can be created.
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          self.assertEqual(scapy.compat.orb(record[230]), 13)
          # natQuotaExceededEvent
          self.assertEqual(struct.pack("I", 2), record[466])
          # maxBIBEntries
          self.assertEqual(struct.pack("I", limit), record[472])
  
      def verify_ipfix_bib(self, data, is_create, src_addr):
          """
          Verify IPFIX NAT64 BIB create and delete events
  
          :param data: Decoded IPFIX data records
          :param is_create: Create event if nonzero value otherwise delete event
          :param src_addr: IPv6 source address
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          if is_create:
              self.assertEqual(scapy.compat.orb(record[230]), 10)
          else:
              self.assertEqual(scapy.compat.orb(record[230]), 11)
          # sourceIPv6Address
          self.assertEqual(src_addr, str(ipaddress.IPv6Address(record[27])))
          # postNATSourceIPv4Address
          self.assertEqual(self.nat_addr_n, record[225])
          # protocolIdentifier
          self.assertEqual(IP_PROTOS.tcp, scapy.compat.orb(record[4]))
          # ingressVRFID
          self.assertEqual(struct.pack("!I", 0), record[234])
          # sourceTransportPort
          self.assertEqual(struct.pack("!H", self.tcp_port_in), record[7])
          # postNAPTSourceTransportPort
          self.assertEqual(struct.pack("!H", self.tcp_port_out), record[227])
  
      def verify_ipfix_nat64_ses(self, data, is_create, src_addr, dst_addr,
                                 dst_port):
          """
          Verify IPFIX NAT64 session create and delete events
  
          :param data: Decoded IPFIX data records
          :param is_create: Create event if nonzero value otherwise delete event
          :param src_addr: IPv6 source address
          :param dst_addr: IPv4 destination address
          :param dst_port: destination TCP port
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          if is_create:
              self.assertEqual(scapy.compat.orb(record[230]), 6)
          else:
              self.assertEqual(scapy.compat.orb(record[230]), 7)
          # sourceIPv6Address
          self.assertEqual(src_addr, str(ipaddress.IPv6Address(record[27])))
          # destinationIPv6Address
          self.assertEqual(socket.inet_pton(socket.AF_INET6,
                                            self.compose_ip6(dst_addr,
                                                             '64:ff9b::',
                                                             96)),
                           record[28])
          # postNATSourceIPv4Address
          self.assertEqual(self.nat_addr_n, record[225])
          # postNATDestinationIPv4Address
          self.assertEqual(socket.inet_pton(socket.AF_INET, dst_addr),
                           record[226])
          # protocolIdentifier
          self.assertEqual(IP_PROTOS.tcp, scapy.compat.orb(record[4]))
          # ingressVRFID
          self.assertEqual(struct.pack("!I", 0), record[234])
          # sourceTransportPort
          self.assertEqual(struct.pack("!H", self.tcp_port_in), record[7])
          # postNAPTSourceTransportPort
          self.assertEqual(struct.pack("!H", self.tcp_port_out), record[227])
          # destinationTransportPort
          self.assertEqual(struct.pack("!H", dst_port), record[11])
          # postNAPTDestinationTransportPort
          self.assertEqual(struct.pack("!H", dst_port), record[228])
  
      def verify_no_nat44_user(self):
          """ Verify that there is no NAT44 user """
          users = self.vapi.nat44_user_dump()
          self.assertEqual(len(users), 0)
          users = self.statistics.get_counter('/nat44/total-users')
          self.assertEqual(users[0][0], 0)
          sessions = self.statistics.get_counter('/nat44/total-sessions')
          self.assertEqual(sessions[0][0], 0)
  
      def verify_ipfix_max_entries_per_user(self, data, limit, src_addr):
          """
          Verify IPFIX maximum entries per user exceeded event
  
          :param data: Decoded IPFIX data records
          :param limit: Number of maximum entries per user
          :param src_addr: IPv4 source address
          """
          self.assertEqual(1, len(data))
          record = data[0]
          # natEvent
          self.assertEqual(scapy.compat.orb(record[230]), 13)
          # natQuotaExceededEvent
          self.assertEqual(struct.pack("I", 3), record[466])
          # maxEntriesPerUser
          self.assertEqual(struct.pack("I", limit), record[473])
          # sourceIPv4Address
          self.assertEqual(socket.inet_pton(socket.AF_INET, src_addr), record[8])
  
      def verify_syslog_apmap(self, data, is_add=True):
          message = data.decode('utf-8')
          try:
              message = SyslogMessage.parse(message)
          except ParseError as e:
              self.logger.error(e)
              raise
          else:
              self.assertEqual(message.severity, SyslogSeverity.info)
              self.assertEqual(message.appname, 'NAT')
              self.assertEqual(message.msgid, 'APMADD' if is_add else 'APMDEL')
              sd_params = message.sd.get('napmap')
              self.assertTrue(sd_params is not None)
              self.assertEqual(sd_params.get('IATYP'), 'IPv4')
              self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4)
              self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in)
              self.assertEqual(sd_params.get('XATYP'), 'IPv4')
              self.assertEqual(sd_params.get('XSADDR'), self.nat_addr)
              self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out)
              self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp)
              self.assertTrue(sd_params.get('SSUBIX') is not None)
              self.assertEqual(sd_params.get('SVLAN'), '0')
  
      def verify_syslog_sess(self, data, is_add=True, is_ip6=False):
          message = data.decode('utf-8')
          try:
              message = SyslogMessage.parse(message)
          except ParseError as e:
              self.logger.error(e)
              raise
          else:
              self.assertEqual(message.severity, SyslogSeverity.info)
              self.assertEqual(message.appname, 'NAT')
              self.assertEqual(message.msgid, 'SADD' if is_add else 'SDEL')
              sd_params = message.sd.get('nsess')
              self.assertTrue(sd_params is not None)
              if is_ip6:
                  self.assertEqual(sd_params.get('IATYP'), 'IPv6')
                  self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip6)
              else:
                  self.assertEqual(sd_params.get('IATYP'), 'IPv4')
                  self.assertEqual(sd_params.get('ISADDR'), self.pg0.remote_ip4)
                  self.assertTrue(sd_params.get('SSUBIX') is not None)
              self.assertEqual(sd_params.get('ISPORT'), "%d" % self.tcp_port_in)
              self.assertEqual(sd_params.get('XATYP'), 'IPv4')
              self.assertEqual(sd_params.get('XSADDR'), self.nat_addr)
              self.assertEqual(sd_params.get('XSPORT'), "%d" % self.tcp_port_out)
              self.assertEqual(sd_params.get('PROTO'), "%d" % IP_PROTOS.tcp)
              self.assertEqual(sd_params.get('SVLAN'), '0')
              self.assertEqual(sd_params.get('XDADDR'), self.pg1.remote_ip4)
              self.assertEqual(sd_params.get('XDPORT'),
                               "%d" % self.tcp_external_port)
  
      def verify_mss_value(self, pkt, mss):
          """
          Verify TCP MSS value
  
          :param pkt:
          :param mss:
          """
          if not pkt.haslayer(IP) or not pkt.haslayer(TCP):
              raise TypeError("Not a TCP/IP packet")
  
          for option in pkt[TCP].options:
              if option[0] == 'MSS':
                  self.assertEqual(option[1], mss)
                  self.assert_tcp_checksum_valid(pkt)
  
      @staticmethod
      def proto2layer(proto):
          if proto == IP_PROTOS.tcp:
              return TCP
          elif proto == IP_PROTOS.udp:
              return UDP
          elif proto == IP_PROTOS.icmp:
              return ICMP
          else:
              raise Exception("Unsupported protocol")
  
      def frag_in_order(self, proto=IP_PROTOS.tcp, dont_translate=False):
          layer = self.proto2layer(proto)
  
          if proto == IP_PROTOS.tcp:
              data = b"A" * 4 + b"B" * 16 + b"C" * 3
          else:
              data = b"A" * 16 + b"B" * 16 + b"C" * 3
          self.port_in = random.randint(1025, 65535)
  
          # in2out
          pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4,
                                         self.port_in, 20, data, proto)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg1.get_capture(len(pkts))
          if not dont_translate:
              p = self.reass_frags_and_verify(frags,
                                              self.nat_addr,
                                              self.pg1.remote_ip4)
          else:
              p = self.reass_frags_and_verify(frags,
                                              self.pg0.remote_ip4,
                                              self.pg1.remote_ip4)
          if proto != IP_PROTOS.icmp:
              if not dont_translate:
                  self.assertEqual(p[layer].dport, 20)
                  self.assertNotEqual(p[layer].sport, self.port_in)
              else:
                  self.assertEqual(p[layer].sport, self.port_in)
          else:
              if not dont_translate:
                  self.assertNotEqual(p[layer].id, self.port_in)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
          self.assertEqual(data, p[Raw].load)
  
          # out2in
          if not dont_translate:
              dst_addr = self.nat_addr
          else:
              dst_addr = self.pg0.remote_ip4
          if proto != IP_PROTOS.icmp:
              sport = 20
              dport = p[layer].sport
          else:
              sport = p[layer].id
              dport = 0
          pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, data,
                                         proto, echo_reply=True)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          p = self.reass_frags_and_verify(frags,
                                          self.pg1.remote_ip4,
                                          self.pg0.remote_ip4)
          if proto != IP_PROTOS.icmp:
              self.assertEqual(p[layer].sport, 20)
              self.assertEqual(p[layer].dport, self.port_in)
          else:
              self.assertEqual(p[layer].id, self.port_in)
          self.assertEqual(data, p[Raw].load)
  
      def frag_in_order_in_plus_out(self, proto=IP_PROTOS.tcp):
          layer = self.proto2layer(proto)
  
          if proto == IP_PROTOS.tcp:
              data = b"A" * 4 + b"B" * 16 + b"C" * 3
          else:
              data = b"A" * 16 + b"B" * 16 + b"C" * 3
          self.port_in = random.randint(1025, 65535)
  
          for i in range(2):
              # out2in
              pkts = self.create_stream_frag(self.pg0, self.server_out_addr,
                                             self.port_in, self.server_out_port,
                                             data, proto)
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg1.get_capture(len(pkts))
              p = self.reass_frags_and_verify(frags,
                                              self.pg0.remote_ip4,
                                              self.server_in_addr)
              if proto != IP_PROTOS.icmp:
                  self.assertEqual(p[layer].sport, self.port_in)
                  self.assertEqual(p[layer].dport, self.server_in_port)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
              # in2out
              if proto != IP_PROTOS.icmp:
                  pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                 self.server_in_port,
                                                 p[layer].sport, data, proto)
              else:
                  pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                 p[layer].id, 0, data, proto,
                                                 echo_reply=True)
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg0.get_capture(len(pkts))
              p = self.reass_frags_and_verify(frags,
                                              self.server_out_addr,
                                              self.pg0.remote_ip4)
              if proto != IP_PROTOS.icmp:
                  self.assertEqual(p[layer].sport, self.server_out_port)
                  self.assertEqual(p[layer].dport, self.port_in)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
      def reass_hairpinning(self, proto=IP_PROTOS.tcp):
          layer = self.proto2layer(proto)
  
          if proto == IP_PROTOS.tcp:
              data = b"A" * 4 + b"B" * 16 + b"C" * 3
          else:
              data = b"A" * 16 + b"B" * 16 + b"C" * 3
  
          # send packet from host to server
          pkts = self.create_stream_frag(self.pg0,
                                         self.nat_addr,
                                         self.host_in_port,
                                         self.server_out_port,
                                         data,
                                         proto)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          p = self.reass_frags_and_verify(frags,
                                          self.nat_addr,
                                          self.server.ip4)
          if proto != IP_PROTOS.icmp:
              self.assertNotEqual(p[layer].sport, self.host_in_port)
              self.assertEqual(p[layer].dport, self.server_in_port)
          else:
              self.assertNotEqual(p[layer].id, self.host_in_port)
          self.assertEqual(data, p[Raw].load)
  
      def frag_out_of_order(self, proto=IP_PROTOS.tcp, dont_translate=False):
          layer = self.proto2layer(proto)
  
          if proto == IP_PROTOS.tcp:
              data = b"A" * 4 + b"B" * 16 + b"C" * 3
          else:
              data = b"A" * 16 + b"B" * 16 + b"C" * 3
          self.port_in = random.randint(1025, 65535)
  
          for i in range(2):
              # in2out
              pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4,
                                             self.port_in, 20, data, proto)
              pkts.reverse()
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg1.get_capture(len(pkts))
              if not dont_translate:
                  p = self.reass_frags_and_verify(frags,
                                                  self.nat_addr,
                                                  self.pg1.remote_ip4)
              else:
                  p = self.reass_frags_and_verify(frags,
                                                  self.pg0.remote_ip4,
                                                  self.pg1.remote_ip4)
              if proto != IP_PROTOS.icmp:
                  if not dont_translate:
                      self.assertEqual(p[layer].dport, 20)
                      self.assertNotEqual(p[layer].sport, self.port_in)
                  else:
                      self.assertEqual(p[layer].sport, self.port_in)
              else:
                  if not dont_translate:
                      self.assertNotEqual(p[layer].id, self.port_in)
                  else:
                      self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
              # out2in
              if not dont_translate:
                  dst_addr = self.nat_addr
              else:
                  dst_addr = self.pg0.remote_ip4
              if proto != IP_PROTOS.icmp:
                  sport = 20
                  dport = p[layer].sport
              else:
                  sport = p[layer].id
                  dport = 0
              pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport,
                                             data, proto, echo_reply=True)
              pkts.reverse()
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg0.get_capture(len(pkts))
              p = self.reass_frags_and_verify(frags,
                                              self.pg1.remote_ip4,
                                              self.pg0.remote_ip4)
              if proto != IP_PROTOS.icmp:
                  self.assertEqual(p[layer].sport, 20)
                  self.assertEqual(p[layer].dport, self.port_in)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
      def frag_out_of_order_in_plus_out(self, proto=IP_PROTOS.tcp):
          layer = self.proto2layer(proto)
  
          if proto == IP_PROTOS.tcp:
              data = b"A" * 4 + b"B" * 16 + b"C" * 3
          else:
              data = b"A" * 16 + b"B" * 16 + b"C" * 3
          self.port_in = random.randint(1025, 65535)
  
          for i in range(2):
              # out2in
              pkts = self.create_stream_frag(self.pg0, self.server_out_addr,
                                             self.port_in, self.server_out_port,
                                             data, proto)
              pkts.reverse()
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg1.get_capture(len(pkts))
              p = self.reass_frags_and_verify(frags,
                                              self.pg0.remote_ip4,
                                              self.server_in_addr)
              if proto != IP_PROTOS.icmp:
                  self.assertEqual(p[layer].dport, self.server_in_port)
                  self.assertEqual(p[layer].sport, self.port_in)
                  self.assertEqual(p[layer].dport, self.server_in_port)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
              # in2out
              if proto != IP_PROTOS.icmp:
                  pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                 self.server_in_port,
                                                 p[layer].sport, data, proto)
              else:
                  pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                 p[layer].id, 0, data, proto,
                                                 echo_reply=True)
              pkts.reverse()
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              frags = self.pg0.get_capture(len(pkts))
              p = self.reass_frags_and_verify(frags,
                                              self.server_out_addr,
                                              self.pg0.remote_ip4)
              if proto != IP_PROTOS.icmp:
                  self.assertEqual(p[layer].sport, self.server_out_port)
                  self.assertEqual(p[layer].dport, self.port_in)
              else:
                  self.assertEqual(p[layer].id, self.port_in)
              self.assertEqual(data, p[Raw].load)
  
  
  class TestNAT44(MethodHolder):
      """ NAT44 Test Cases """
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT44, cls).setUpClass()
          cls.vapi.cli("set log class nat level debug")
  
          cls.tcp_port_in = 6303
          cls.tcp_port_out = 6303
          cls.udp_port_in = 6304
          cls.udp_port_out = 6304
          cls.icmp_id_in = 6305
          cls.icmp_id_out = 6305
          cls.nat_addr = '10.0.0.3'
          cls.ipfix_src_port = 4739
          cls.ipfix_domain_id = 1
          cls.tcp_external_port = 80
          cls.udp_external_port = 69
  
          cls.create_pg_interfaces(range(10))
          cls.interfaces = list(cls.pg_interfaces[0:4])
  
          for i in cls.interfaces:
              i.admin_up()
              i.config_ip4()
              i.resolve_arp()
  
          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(is_add=1, table={'table_id': 10})
          cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 20})
  
          cls.pg4._local_ip4 = "172.16.255.1"
          cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2"
          cls.pg4.set_table_ip4(10)
          cls.pg5._local_ip4 = "172.17.255.3"
          cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4"
          cls.pg5.set_table_ip4(10)
          cls.pg6._local_ip4 = "172.16.255.1"
          cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2"
          cls.pg6.set_table_ip4(20)
          for i in cls.overlapping_interfaces:
              i.config_ip4()
              i.admin_up()
              i.resolve_arp()
  
          cls.pg7.admin_up()
          cls.pg8.admin_up()
  
          cls.pg9.generate_remote_hosts(2)
          cls.pg9.config_ip4()
          cls.vapi.sw_interface_add_del_address(
              sw_if_index=cls.pg9.sw_if_index,
              prefix="10.0.0.1/24")
  
          cls.pg9.admin_up()
          cls.pg9.resolve_arp()
          cls.pg9._remote_hosts[1]._ip4 = cls.pg9._remote_hosts[0]._ip4
          cls.pg4._remote_ip4 = cls.pg9._remote_hosts[0]._ip4 = "10.0.0.2"
          cls.pg9.resolve_arp()
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT44, cls).tearDownClass()
  
      def test_dynamic(self):
          """ NAT44 dynamic translation test """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # in2out
          tcpn = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/TCP packets')
          udpn = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/good in2out packets processed')
  
          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)
  
          err = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/TCP packets')
          self.assertEqual(err - tcpn, 2)
          err = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-in2out-slowpath/good in2out packets processed')
          self.assertEqual(err - totaln, 4)
  
          # out2in
          tcpn = self.statistics.get_err_counter('/err/nat44-out2in/TCP packets')
          udpn = self.statistics.get_err_counter('/err/nat44-out2in/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat44-out2in/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat44-out2in/good out2in packets processed')
  
          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)
  
          err = self.statistics.get_err_counter('/err/nat44-out2in/TCP packets')
          self.assertEqual(err - tcpn, 2)
          err = self.statistics.get_err_counter('/err/nat44-out2in/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter('/err/nat44-out2in/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-out2in/good out2in packets processed')
          self.assertEqual(err - totaln, 4)
  
          users = self.statistics.get_counter('/nat44/total-users')
          self.assertEqual(users[0][0], 1)
          sessions = self.statistics.get_counter('/nat44/total-sessions')
          self.assertEqual(sessions[0][0], 3)
  
      def test_dynamic_icmp_errors_in2out_ttl_1(self):
          """ NAT44 handling of client packets with TTL=1 """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # Client side - generate traffic
          pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Client side - verify ICMP type 11 packets
          capture = self.pg0.get_capture(len(pkts))
          self.verify_capture_in_with_icmp_errors(capture, self.pg0)
  
      def test_dynamic_icmp_errors_out2in_ttl_1(self):
          """ NAT44 handling of server packets with TTL=1 """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # Client side - create sessions
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Server side - generate traffic
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out(capture)
          pkts = self.create_stream_out(self.pg1, ttl=1)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Server side - verify ICMP type 11 packets
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out_with_icmp_errors(capture,
                                                   src_ip=self.pg1.local_ip4)
  
      def test_dynamic_icmp_errors_in2out_ttl_2(self):
          """ NAT44 handling of error responses to client packets with TTL=2 """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # Client side - generate traffic
          pkts = self.create_stream_in(self.pg0, self.pg1, ttl=2)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Server side - simulate ICMP type 11 response
          capture = self.pg1.get_capture(len(pkts))
          pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
                  IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                  ICMP(type=11) / packet[IP] for packet in capture]
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Client side - verify ICMP type 11 packets
          capture = self.pg0.get_capture(len(pkts))
          self.verify_capture_in_with_icmp_errors(capture, self.pg0)
  
      def test_dynamic_icmp_errors_out2in_ttl_2(self):
          """ NAT44 handling of error responses to server packets with TTL=2 """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # Client side - create sessions
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Server side - generate traffic
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out(capture)
          pkts = self.create_stream_out(self.pg1, ttl=2)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Client side - simulate ICMP type 11 response
          capture = self.pg0.get_capture(len(pkts))
          pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                  IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
                  ICMP(type=11) / packet[IP] for packet in capture]
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Server side - verify ICMP type 11 packets
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out_with_icmp_errors(capture)
  
      def test_ping_out_interface_from_outside(self):
          """ Ping NAT44 out interface from outside network """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) /
               ICMP(id=self.icmp_id_out, type='echo-request'))
          pkts = [p]
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(len(pkts))
          packet = capture[0]
          try:
              self.assertEqual(packet[IP].src, self.pg1.local_ip4)
              self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
              self.assertEqual(packet[ICMP].id, self.icmp_id_in)
              self.assertEqual(packet[ICMP].type, 0)  # echo reply
          except:
              self.logger.error(ppp("Unexpected or invalid packet "
                                    "(outside network):", packet))
              raise
  
      def test_ping_internal_host_from_outside(self):
          """ Ping internal host from outside network """
  
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # out2in
          pkt = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
                 IP(src=self.pg1.remote_ip4, dst=self.nat_addr, ttl=64) /
                 ICMP(id=self.icmp_id_out, type='echo-request'))
          self.pg1.add_stream(pkt)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          self.verify_capture_in(capture, self.pg0)
          self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp)
  
          # in2out
          pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) /
                 ICMP(id=self.icmp_id_in, type='echo-reply'))
          self.pg0.add_stream(pkt)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          self.verify_capture_out(capture, same_port=True)
          self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp)
  
      def test_forwarding(self):
          """ NAT44 forwarding test """
  
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_forwarding_enable_disable(enable=1)
  
          real_ip = self.pg0.remote_ip4
          alias_ip = self.nat_addr
          flags = self.config_flags.NAT_IS_ADDR_ONLY
          self.vapi.nat44_add_del_static_mapping(is_add=1,
                                                 local_ip_address=real_ip,
                                                 external_ip_address=alias_ip,
                                                 external_sw_if_index=0xFFFFFFFF,
                                                 flags=flags)
  
          try:
              # static mapping match
  
              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)
  
              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, same_port=True)
  
              # no static mapping match
  
              host0 = self.pg0.remote_hosts[0]
              self.pg0.remote_hosts[0] = self.pg0.remote_hosts[1]
              try:
                  pkts = self.create_stream_out(self.pg1,
                                                dst_ip=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)
              finally:
                  self.pg0.remote_hosts[0] = host0
  
          finally:
              self.vapi.nat44_forwarding_enable_disable(enable=0)
              flags = self.config_flags.NAT_IS_ADDR_ONLY
              self.vapi.nat44_add_del_static_mapping(
                  is_add=0,
                  local_ip_address=real_ip,
                  external_ip_address=alias_ip,
                  external_sw_if_index=0xFFFFFFFF,
                  flags=flags)
  
      def test_static_in(self):
          """ 1:1 NAT initialized from inside network """
  
          nat_ip = "10.0.0.10"
          self.tcp_port_out = 6303
          self.udp_port_out = 6304
          self.icmp_id_out = 6305
  
          self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          sm = self.vapi.nat44_static_mapping_dump()
          self.assertEqual(len(sm), 1)
          self.assertEqual(sm[0].tag, '')
          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)
          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)
  
          # out2in
          pkts = self.create_stream_out(self.pg1, nat_ip)
          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_static_out(self):
          """ 1:1 NAT initialized from outside network """
  
          nat_ip = "10.0.0.20"
          self.tcp_port_out = 6303
          self.udp_port_out = 6304
          self.icmp_id_out = 6305
          tag = "testTAG"
  
          self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip, tag=tag)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          sm = self.vapi.nat44_static_mapping_dump()
          self.assertEqual(len(sm), 1)
          self.assertEqual(sm[0].tag, tag)
  
          # out2in
          pkts = self.create_stream_out(self.pg1, nat_ip)
          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)
  
          # in2out
          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_static_with_port_in(self):
          """ 1:1 NAPT initialized from inside network """
  
          self.tcp_port_out = 3606
          self.udp_port_out = 3607
          self.icmp_id_out = 3608
  
          self.nat44_add_address(self.nat_addr)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.tcp_port_in, self.tcp_port_out,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.udp_port_in, self.udp_port_out,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.icmp_id_in, self.icmp_id_out,
                                        proto=IP_PROTOS.icmp)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # in2out
          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)
  
          # out2in
          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_static_with_port_out(self):
          """ 1:1 NAPT initialized from outside network """
  
          self.tcp_port_out = 30606
          self.udp_port_out = 30607
          self.icmp_id_out = 30608
  
          self.nat44_add_address(self.nat_addr)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.tcp_port_in, self.tcp_port_out,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.udp_port_in, self.udp_port_out,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        self.icmp_id_in, self.icmp_id_out,
                                        proto=IP_PROTOS.icmp)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # out2in
          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)
  
          # in2out
          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)
  
      def test_static_vrf_aware(self):
          """ 1:1 NAT VRF awareness """
  
          nat_ip1 = "10.0.0.30"
          nat_ip2 = "10.0.0.40"
          self.tcp_port_out = 6303
          self.udp_port_out = 6304
          self.icmp_id_out = 6305
  
          self.nat44_add_static_mapping(self.pg4.remote_ip4, nat_ip1,
                                        vrf_id=10)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip2,
                                        vrf_id=10)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg3.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg4.sw_if_index,
              flags=flags, is_add=1)
  
          # inside interface VRF match NAT44 static mapping VRF
          pkts = self.create_stream_in(self.pg4, self.pg3)
          self.pg4.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture, nat_ip1, True)
  
          # inside interface VRF don't match NAT44 static mapping VRF (packets
          # are dropped)
          pkts = self.create_stream_in(self.pg0, self.pg3)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          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)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # 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_ip4, 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 """
          flags = self.config_flags.NAT_IS_ADDR_ONLY
          self.vapi.nat44_add_del_identity_mapping(
              ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF,
              flags=flags, is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          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=12345, dport=56789))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(ip.src, self.pg1.remote_ip4)
              self.assertEqual(tcp.dport, 56789)
              self.assertEqual(tcp.sport, 12345)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions), 0)
          flags = self.config_flags.NAT_IS_ADDR_ONLY
          self.vapi.nat44_add_del_identity_mapping(
              ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF,
              flags=flags, vrf_id=1, is_add=1)
          identity_mappings = self.vapi.nat44_identity_mapping_dump()
          self.assertEqual(len(identity_mappings), 2)
  
      def test_multiple_inside_interfaces(self):
          """ NAT44 multiple non-overlapping address space inside interfaces """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg3.sw_if_index,
              is_add=1)
  
          # between two NAT44 inside interfaces (no translation)
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_no_translation(capture, self.pg0, self.pg1)
  
          # from NAT44 inside to interface without NAT44 feature (no translation)
          pkts = self.create_stream_in(self.pg0, self.pg2)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg2.get_capture(len(pkts))
          self.verify_capture_no_translation(capture, self.pg0, self.pg2)
  
          # in2out 1st interface
          pkts = self.create_stream_in(self.pg0, self.pg3)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in 1st interface
          pkts = self.create_stream_out(self.pg3)
          self.pg3.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)
  
          # in2out 2nd interface
          pkts = self.create_stream_in(self.pg1, self.pg3)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in 2nd interface
          pkts = self.create_stream_out(self.pg3)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg1)
  
      def test_inside_overlapping_interfaces(self):
          """ NAT44 multiple inside interfaces with overlapping address space """
  
          static_nat_ip = "10.0.0.10"
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg3.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg4.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg5.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg6.sw_if_index,
              flags=flags, is_add=1)
          self.nat44_add_static_mapping(self.pg6.remote_ip4, static_nat_ip,
                                        vrf_id=20)
  
          # between NAT44 inside interfaces with same VRF (no translation)
          pkts = self.create_stream_in(self.pg4, self.pg5)
          self.pg4.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(len(pkts))
          self.verify_capture_no_translation(capture, self.pg4, self.pg5)
  
          # between NAT44 inside interfaces with different VRF (hairpinning)
          p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) /
               IP(src=self.pg4.remote_ip4, dst=static_nat_ip) /
               TCP(sport=1234, dport=5678))
          self.pg4.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(ip.dst, self.pg6.remote_ip4)
              self.assertNotEqual(tcp.sport, 1234)
              self.assertEqual(tcp.dport, 5678)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # in2out 1st interface
          pkts = self.create_stream_in(self.pg4, self.pg3)
          self.pg4.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in 1st interface
          pkts = self.create_stream_out(self.pg3)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg4.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg4)
  
          # in2out 2nd interface
          pkts = self.create_stream_in(self.pg5, self.pg3)
          self.pg5.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in 2nd interface
          pkts = self.create_stream_out(self.pg3)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg5)
  
          # pg5 session dump
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(len(addresses), 1)
          sessions = self.vapi.nat44_user_session_dump(self.pg5.remote_ip4, 10)
          self.assertEqual(len(sessions), 3)
          for session in sessions:
              self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC)
              self.assertEqual(str(session.inside_ip_address),
                               self.pg5.remote_ip4)
              self.assertEqual(session.outside_ip_address,
                               addresses[0].ip_address)
          self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp)
          self.assertEqual(sessions[1].protocol, IP_PROTOS.udp)
          self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp)
          self.assertEqual(sessions[0].inside_port, self.tcp_port_in)
          self.assertEqual(sessions[1].inside_port, self.udp_port_in)
          self.assertEqual(sessions[2].inside_port, self.icmp_id_in)
          self.assertEqual(sessions[0].outside_port, self.tcp_port_out)
          self.assertEqual(sessions[1].outside_port, self.udp_port_out)
          self.assertEqual(sessions[2].outside_port, self.icmp_id_out)
  
          # in2out 3rd interface
          pkts = self.create_stream_in(self.pg6, self.pg3)
          self.pg6.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture, static_nat_ip, True)
  
          # out2in 3rd interface
          pkts = self.create_stream_out(self.pg3, static_nat_ip)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg6)
  
          # general user and session dump verifications
          users = self.vapi.nat44_user_dump()
          self.assertGreaterEqual(len(users), 3)
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(len(addresses), 1)
          for user in users:
              sessions = self.vapi.nat44_user_session_dump(user.ip_address,
                                                           user.vrf_id)
              for session in sessions:
                  self.assertEqual(user.ip_address, session.inside_ip_address)
                  self.assertTrue(session.total_bytes > session.total_pkts > 0)
                  self.assertTrue(session.protocol in
                                  [IP_PROTOS.tcp, IP_PROTOS.udp,
                                   IP_PROTOS.icmp])
                  self.assertFalse(session.flags &
                                   self.config_flags.NAT_IS_EXT_HOST_VALID)
  
          # pg4 session dump
          sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4, 10)
          self.assertGreaterEqual(len(sessions), 4)
          for session in sessions:
              self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC)
              self.assertEqual(str(session.inside_ip_address),
                               self.pg4.remote_ip4)
              self.assertEqual(session.outside_ip_address,
                               addresses[0].ip_address)
  
          # pg6 session dump
          sessions = self.vapi.nat44_user_session_dump(self.pg6.remote_ip4, 20)
          self.assertGreaterEqual(len(sessions), 3)
          for session in sessions:
              self.assertTrue(session.flags & self.config_flags.NAT_IS_STATIC)
              self.assertEqual(str(session.inside_ip_address),
                               self.pg6.remote_ip4)
              self.assertEqual(str(session.outside_ip_address),
                               static_nat_ip)
              self.assertTrue(session.inside_port in
                              [self.tcp_port_in, self.udp_port_in,
                               self.icmp_id_in])
  
      def test_hairpinning(self):
          """ NAT44 hairpinning - 1:1 NAPT """
  
          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
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # add static mapping for server
          self.nat44_add_static_mapping(server.ip4, self.nat_addr,
                                        server_in_port, server_out_port,
                                        proto=IP_PROTOS.tcp)
  
          # send packet from host to server
          p = (Ether(src=host.mac, dst=self.pg0.local_mac) /
               IP(src=host.ip4, dst=self.nat_addr) /
               TCP(sport=host_in_port, dport=server_out_port))
          self.pg0.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, self.nat_addr)
              self.assertEqual(ip.dst, server.ip4)
              self.assertNotEqual(tcp.sport, host_in_port)
              self.assertEqual(tcp.dport, server_in_port)
              self.assert_packet_checksums_valid(p)
              host_out_port = tcp.sport
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # send reply from server to host
          p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
               IP(src=server.ip4, dst=self.nat_addr) /
               TCP(sport=server_in_port, dport=host_out_port))
          self.pg0.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, self.nat_addr)
              self.assertEqual(ip.dst, host.ip4)
              self.assertEqual(tcp.sport, server_out_port)
              self.assertEqual(tcp.dport, host_in_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
      def test_hairpinning2(self):
          """ NAT44 hairpinning - 1:1 NAT"""
  
          server1_nat_ip = "10.0.0.10"
          server2_nat_ip = "10.0.0.11"
          host = self.pg0.remote_hosts[0]
          server1 = self.pg0.remote_hosts[1]
          server2 = self.pg0.remote_hosts[2]
          server_tcp_port = 22
          server_udp_port = 20
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # add static mapping for servers
          self.nat44_add_static_mapping(server1.ip4, server1_nat_ip)
          self.nat44_add_static_mapping(server2.ip4, server2_nat_ip)
  
          # host to server1
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=host.ip4, dst=server1_nat_ip) /
               TCP(sport=self.tcp_port_in, dport=server_tcp_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=host.ip4, dst=server1_nat_ip) /
               UDP(sport=self.udp_port_in, dport=server_udp_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=host.ip4, dst=server1_nat_ip) /
               ICMP(id=self.icmp_id_in, type='echo-request'))
          pkts.append(p)
          self.pg0.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[IP].src, self.nat_addr)
                  self.assertEqual(packet[IP].dst, server1.ip4)
                  if packet.haslayer(TCP):
                      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.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)
                      self.udp_port_out = packet[UDP].sport
                  else:
                      self.assertNotEqual(packet[ICMP].id, self.icmp_id_in)
                      self.icmp_id_out = packet[ICMP].id
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # server1 to host
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=self.nat_addr) /
               TCP(sport=server_tcp_port, dport=self.tcp_port_out))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=self.nat_addr) /
               UDP(sport=server_udp_port, dport=self.udp_port_out))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=self.nat_addr) /
               ICMP(id=self.icmp_id_out, type='echo-reply'))
          pkts.append(p)
          self.pg0.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[IP].src, server1_nat_ip)
                  self.assertEqual(packet[IP].dst, host.ip4)
                  if packet.haslayer(TCP):
                      self.assertEqual(packet[TCP].dport, self.tcp_port_in)
                      self.assertEqual(packet[TCP].sport, server_tcp_port)
                      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)
                  else:
                      self.assertEqual(packet[ICMP].id, self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # server2 to server1
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server2.ip4, dst=server1_nat_ip) /
               TCP(sport=self.tcp_port_in, dport=server_tcp_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server2.ip4, dst=server1_nat_ip) /
               UDP(sport=self.udp_port_in, dport=server_udp_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server2.ip4, dst=server1_nat_ip) /
               ICMP(id=self.icmp_id_in, type='echo-request'))
          pkts.append(p)
          self.pg0.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[IP].src, server2_nat_ip)
                  self.assertEqual(packet[IP].dst, server1.ip4)
                  if packet.haslayer(TCP):
                      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.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)
                      self.udp_port_out = packet[UDP].sport
                  else:
                      self.assertEqual(packet[ICMP].id, self.icmp_id_in)
                      self.icmp_id_out = packet[ICMP].id
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # server1 to server2
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=server2_nat_ip) /
               TCP(sport=server_tcp_port, dport=self.tcp_port_out))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=server2_nat_ip) /
               UDP(sport=server_udp_port, dport=self.udp_port_out))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=server1.ip4, dst=server2_nat_ip) /
               ICMP(id=self.icmp_id_out, type='echo-reply'))
          pkts.append(p)
          self.pg0.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[IP].src, server1_nat_ip)
                  self.assertEqual(packet[IP].dst, server2.ip4)
                  if packet.haslayer(TCP):
                      self.assertEqual(packet[TCP].dport, self.tcp_port_in)
                      self.assertEqual(packet[TCP].sport, server_tcp_port)
                      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)
                  else:
                      self.assertEqual(packet[ICMP].id, self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
      def test_interface_addr(self):
          """ Acquire NAT44 addresses from interface """
          self.vapi.nat44_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg7.sw_if_index)
  
          # no address in NAT pool
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(0, len(addresses))
  
          # configure interface address and check NAT address pool
          self.pg7.config_ip4()
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(1, len(addresses))
          self.assertEqual(str(addresses[0].ip_address), self.pg7.local_ip4)
  
          # remove interface address and check NAT address pool
          self.pg7.unconfig_ip4()
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(0, len(addresses))
  
      def test_interface_addr_static_mapping(self):
          """ Static mapping with addresses from interface """
          tag = "testTAG"
  
          self.vapi.nat44_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg7.sw_if_index)
          self.nat44_add_static_mapping(
              '1.2.3.4',
              external_sw_if_index=self.pg7.sw_if_index,
              tag=tag)
  
          # static mappings with external interface
          static_mappings = self.vapi.nat44_static_mapping_dump()
          self.assertEqual(1, len(static_mappings))
          self.assertEqual(self.pg7.sw_if_index,
                           static_mappings[0].external_sw_if_index)
          self.assertEqual(static_mappings[0].tag, tag)
  
          # configure interface address 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(str(sm.external_ip_address),
                                   self.pg7.local_ip4)
                  self.assertEqual(sm.tag, tag)
                  resolved = True
          self.assertTrue(resolved)
  
          # remove interface address and check static mappings
          self.pg7.unconfig_ip4()
          static_mappings = self.vapi.nat44_static_mapping_dump()
          self.assertEqual(1, len(static_mappings))
          self.assertEqual(self.pg7.sw_if_index,
                           static_mappings[0].external_sw_if_index)
          self.assertEqual(static_mappings[0].tag, 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(str(sm.external_ip_address),
                                   self.pg7.local_ip4)
                  self.assertEqual(sm.tag, 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):
          """ Identity NAT with addresses from interface """
  
          port = 53053
          self.vapi.nat44_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg7.sw_if_index)
          self.vapi.nat44_add_del_identity_mapping(
              ip_address=b'0',
              sw_if_index=self.pg7.sw_if_index,
              port=port,
              protocol=IP_PROTOS.tcp,
              is_add=1)
  
          # identity mappings with external interface
          identity_mappings = self.vapi.nat44_identity_mapping_dump()
          self.assertEqual(1, len(identity_mappings))
          self.assertEqual(self.pg7.sw_if_index,
                           identity_mappings[0].sw_if_index)
  
          # configure interface address and check identity mappings
          self.pg7.config_ip4()
          identity_mappings = self.vapi.nat44_identity_mapping_dump()
          resolved = False
          self.assertEqual(2, len(identity_mappings))
          for sm in identity_mappings:
              if sm.sw_if_index == 0xFFFFFFFF:
                  self.assertEqual(str(identity_mappings[0].ip_address),
                                   self.pg7.local_ip4)
                  self.assertEqual(port, identity_mappings[0].port)
                  self.assertEqual(IP_PROTOS.tcp, identity_mappings[0].protocol)
                  resolved = True
          self.assertTrue(resolved)
  
          # remove interface address and check identity mappings
          self.pg7.unconfig_ip4()
          identity_mappings = self.vapi.nat44_identity_mapping_dump()
          self.assertEqual(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/deleted """
          self.ipfix_domain_id = 10
          self.ipfix_src_port = 20202
          collector_port = 30303
          bind_layers(UDP, IPFIX, dport=30303)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4,
                                       src_address=self.pg3.local_ip4,
                                       path_mtu=512,
                                       template_interval=10,
                                       collector_port=collector_port)
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=1)
  
          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)
          self.nat44_add_address(self.nat_addr, is_add=0)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(7)
          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, collector_port)
              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_nat44_ses(data)
  
      def test_ipfix_addr_exhausted(self):
          """ IPFIX logging NAT addresses exhausted """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4,
                                       src_address=self.pg3.local_ip4,
                                       path_mtu=512,
                                       template_interval=10)
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=1)
  
          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=3025))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
          sleep(1)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(7)
          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_addr_exhausted(data)
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_ipfix_max_sessions(self):
          """ IPFIX logging maximum session entries exceeded """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          nat44_config = self.vapi.nat_show_config()
          max_sessions = 10 * nat44_config.translation_buckets
  
          pkts = []
          for i in range(0, max_sessions):
              src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
              p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                   IP(src=src, dst=self.pg1.remote_ip4) /
                   TCP(sport=1025))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          self.pg1.get_capture(max_sessions)
          self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4,
                                       src_address=self.pg3.local_ip4,
                                       path_mtu=512,
                                       template_interval=10)
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=1)
  
          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=1025))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
          sleep(1)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(7)
          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_sessions(data, max_sessions)
  
      def test_syslog_apmap(self):
          """ Test syslog address and port mapping creation and deletion """
          self.vapi.syslog_set_filter(
              self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO)
          self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=20))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          self.tcp_port_out = capture[0][TCP].sport
          capture = self.pg3.get_capture(1)
          self.verify_syslog_apmap(capture[0][Raw].load)
  
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.nat44_add_address(self.nat_addr, is_add=0)
          capture = self.pg3.get_capture(1)
          self.verify_syslog_apmap(capture[0][Raw].load, False)
  
      def test_pool_addr_fib(self):
          """ NAT44 add pool addresses to FIB """
          static_addr = '10.0.0.10'
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr)
  
          # NAT44 address
          p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
               ARP(op=ARP.who_has, pdst=self.nat_addr,
                   psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          self.assertTrue(capture[0].haslayer(ARP))
          self.assertTrue(capture[0][ARP].op, ARP.is_at)
  
          # 1:1 NAT address
          p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
               ARP(op=ARP.who_has, pdst=static_addr,
                   psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          self.assertTrue(capture[0].haslayer(ARP))
          self.assertTrue(capture[0][ARP].op, ARP.is_at)
  
          # send ARP to non-NAT44 interface
          p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
               ARP(op=ARP.who_has, pdst=self.nat_addr,
                   psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac))
          self.pg2.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
  
          # remove addresses and verify
          self.nat44_add_address(self.nat_addr, is_add=0)
          self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr,
                                        is_add=0)
  
          p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
               ARP(op=ARP.who_has, pdst=self.nat_addr,
                   psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
  
          p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
               ARP(op=ARP.who_has, pdst=static_addr,
                   psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
  
      def test_vrf_mode(self):
          """ NAT44 tenant VRF aware address pool mode """
  
          vrf_id1 = 1
          vrf_id2 = 2
          nat_ip1 = "10.0.0.10"
          nat_ip2 = "10.0.0.11"
  
          self.pg0.unconfig_ip4()
          self.pg1.unconfig_ip4()
          self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1})
          self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2})
          self.pg0.set_table_ip4(vrf_id1)
          self.pg1.set_table_ip4(vrf_id2)
          self.pg0.config_ip4()
          self.pg1.config_ip4()
          self.pg0.resolve_arp()
          self.pg1.resolve_arp()
  
          self.nat44_add_address(nat_ip1, vrf_id=vrf_id1)
          self.nat44_add_address(nat_ip2, vrf_id=vrf_id2)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg2.sw_if_index,
              is_add=1)
  
          try:
              # first VRF
              pkts = self.create_stream_in(self.pg0, self.pg2)
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg2.get_capture(len(pkts))
              self.verify_capture_out(capture, nat_ip1)
  
              # second VRF
              pkts = self.create_stream_in(self.pg1, self.pg2)
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg2.get_capture(len(pkts))
              self.verify_capture_out(capture, nat_ip2)
  
          finally:
              self.pg0.unconfig_ip4()
              self.pg1.unconfig_ip4()
              self.pg0.set_table_ip4(0)
              self.pg1.set_table_ip4(0)
              self.pg0.config_ip4()
              self.pg1.config_ip4()
              self.pg0.resolve_arp()
              self.pg1.resolve_arp()
              self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id1})
              self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id2})
  
      def test_vrf_feature_independent(self):
          """ NAT44 tenant VRF independent address pool mode """
  
          nat_ip1 = "10.0.0.10"
          nat_ip2 = "10.0.0.11"
  
          self.nat44_add_address(nat_ip1)
          self.nat44_add_address(nat_ip2, vrf_id=99)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg2.sw_if_index,
              is_add=1)
  
          # first VRF
          pkts = self.create_stream_in(self.pg0, self.pg2)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg2.get_capture(len(pkts))
          self.verify_capture_out(capture, nat_ip1)
  
          # second VRF
          pkts = self.create_stream_in(self.pg1, self.pg2)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg2.get_capture(len(pkts))
          self.verify_capture_out(capture, nat_ip1)
  
      def create_routes_and_neigbors(self):
          r1 = VppIpRoute(self, self.pg7.remote_ip4, 32,
                          [VppRoutePath(self.pg7.remote_ip4,
                                        self.pg7.sw_if_index)])
          r2 = VppIpRoute(self, self.pg8.remote_ip4, 32,
                          [VppRoutePath(self.pg8.remote_ip4,
                                        self.pg8.sw_if_index)])
          r1.add_vpp_config()
          r2.add_vpp_config()
  
          n1 = VppNeighbor(self,
                           self.pg7.sw_if_index,
                           self.pg7.remote_mac,
                           self.pg7.remote_ip4,
                           is_static=1)
          n2 = VppNeighbor(self,
                           self.pg8.sw_if_index,
                           self.pg8.remote_mac,
                           self.pg8.remote_ip4,
                           is_static=1)
          n1.add_vpp_config()
          n2.add_vpp_config()
  
      def test_dynamic_ipless_interfaces(self):
          """ NAT44 interfaces without configured IP address """
          self.create_routes_and_neigbors()
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg7.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg8.sw_if_index,
              is_add=1)
  
          # in2out
          pkts = self.create_stream_in(self.pg7, self.pg8)
          self.pg7.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg8.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in
          pkts = self.create_stream_out(self.pg8, self.nat_addr)
          self.pg8.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg7.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg7)
  
      def test_static_ipless_interfaces(self):
          """ NAT44 interfaces without configured IP address - 1:1 NAT """
  
          self.create_routes_and_neigbors()
          self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg7.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg8.sw_if_index,
              is_add=1)
  
          # out2in
          pkts = self.create_stream_out(self.pg8)
          self.pg8.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg7.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg7)
  
          # in2out
          pkts = self.create_stream_in(self.pg7, self.pg8)
          self.pg7.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg8.get_capture(len(pkts))
          self.verify_capture_out(capture, self.nat_addr, True)
  
      def test_static_with_port_ipless_interfaces(self):
          """ NAT44 interfaces without configured IP address - 1:1 NAPT """
  
          self.tcp_port_out = 30606
          self.udp_port_out = 30607
          self.icmp_id_out = 30608
  
          self.create_routes_and_neigbors()
          self.nat44_add_address(self.nat_addr)
          self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr,
                                        self.tcp_port_in, self.tcp_port_out,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr,
                                        self.udp_port_in, self.udp_port_out,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr,
                                        self.icmp_id_in, self.icmp_id_out,
                                        proto=IP_PROTOS.icmp)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg7.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg8.sw_if_index,
              is_add=1)
  
          # out2in
          pkts = self.create_stream_out(self.pg8)
          self.pg8.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg7.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg7)
  
          # in2out
          pkts = self.create_stream_in(self.pg7, self.pg8)
          self.pg7.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg8.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
      def test_static_unknown_proto(self):
          """ 1:1 NAT translate packet with unknown protocol """
          nat_ip = "10.0.0.10"
          self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # in2out
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, nat_ip)
              self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # out2in
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=nat_ip) /
               GRE() /
               IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, self.pg1.remote_ip4)
              self.assertEqual(packet[IP].dst, self.pg0.remote_ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_hairpinning_static_unknown_proto(self):
          """ 1:1 NAT translate packet with unknown protocol - hairpinning """
  
          host = self.pg0.remote_hosts[0]
          server = self.pg0.remote_hosts[1]
  
          host_nat_ip = "10.0.0.10"
          server_nat_ip = "10.0.0.11"
  
          self.nat44_add_static_mapping(host.ip4, host_nat_ip)
          self.nat44_add_static_mapping(server.ip4, server_nat_ip)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # host to server
          p = (Ether(dst=self.pg0.local_mac, src=host.mac) /
               IP(src=host.ip4, dst=server_nat_ip) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, host_nat_ip)
              self.assertEqual(packet[IP].dst, server.ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # server to host
          p = (Ether(dst=self.pg0.local_mac, src=server.mac) /
               IP(src=server.ip4, dst=host_nat_ip) /
               GRE() /
               IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, server_nat_ip)
              self.assertEqual(packet[IP].dst, host.ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_output_feature(self):
          """ NAT44 interface output feature (in2out postrouting) """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1, flags=flags,
              sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1, flags=flags,
              sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg3.sw_if_index)
  
          # in2out
          pkts = self.create_stream_in(self.pg0, self.pg3)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # out2in
          pkts = self.create_stream_out(self.pg3)
          self.pg3.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)
  
          # from non-NAT interface to NAT inside interface
          pkts = self.create_stream_in(self.pg2, self.pg0)
          self.pg2.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          self.verify_capture_no_translation(capture, self.pg2, self.pg0)
  
      def test_output_feature_vrf_aware(self):
          """ NAT44 interface output feature VRF aware (in2out postrouting) """
          nat_ip_vrf10 = "10.0.0.10"
          nat_ip_vrf20 = "10.0.0.20"
  
          r1 = VppIpRoute(self, self.pg3.remote_ip4, 32,
                          [VppRoutePath(self.pg3.remote_ip4,
                                        self.pg3.sw_if_index)],
                          table_id=10)
          r2 = VppIpRoute(self, self.pg3.remote_ip4, 32,
                          [VppRoutePath(self.pg3.remote_ip4,
                                        self.pg3.sw_if_index)],
                          table_id=20)
          r1.add_vpp_config()
          r2.add_vpp_config()
  
          self.nat44_add_address(nat_ip_vrf10, vrf_id=10)
          self.nat44_add_address(nat_ip_vrf20, vrf_id=20)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1, flags=flags,
              sw_if_index=self.pg4.sw_if_index)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1, flags=flags,
              sw_if_index=self.pg6.sw_if_index)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg3.sw_if_index)
  
          # in2out VRF 10
          pkts = self.create_stream_in(self.pg4, self.pg3)
          self.pg4.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture, nat_ip=nat_ip_vrf10)
  
          # out2in VRF 10
          pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf10)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg4.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg4)
  
          # in2out VRF 20
          pkts = self.create_stream_in(self.pg6, self.pg3)
          self.pg6.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(len(pkts))
          self.verify_capture_out(capture, nat_ip=nat_ip_vrf20)
  
          # out2in VRF 20
          pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf20)
          self.pg3.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg6)
  
      def test_output_feature_hairpinning(self):
          """ NAT44 interface output feature hairpinning (in2out postrouting) """
          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
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1, flags=flags,
              sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
  
          # add static mapping for server
          self.nat44_add_static_mapping(server.ip4, self.nat_addr,
                                        server_in_port, server_out_port,
                                        proto=IP_PROTOS.tcp)
  
          # send packet from host to server
          p = (Ether(src=host.mac, dst=self.pg0.local_mac) /
               IP(src=host.ip4, dst=self.nat_addr) /
               TCP(sport=host_in_port, dport=server_out_port))
          self.pg0.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, self.nat_addr)
              self.assertEqual(ip.dst, server.ip4)
              self.assertNotEqual(tcp.sport, host_in_port)
              self.assertEqual(tcp.dport, server_in_port)
              self.assert_packet_checksums_valid(p)
              host_out_port = tcp.sport
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # send reply from server to host
          p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
               IP(src=server.ip4, dst=self.nat_addr) /
               TCP(sport=server_in_port, dport=host_out_port))
          self.pg0.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, self.nat_addr)
              self.assertEqual(ip.dst, host.ip4)
              self.assertEqual(tcp.sport, server_out_port)
              self.assertEqual(tcp.dport, host_in_port)
              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)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg9.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg9.sw_if_index,
              flags=flags, is_add=1)
  
          # 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
  
          err = self.statistics.get_err_counter(
              '/err/nat44-classify/next in2out')
          self.assertEqual(err, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-classify/next out2in')
          self.assertEqual(err, 1)
  
      def test_del_session(self):
          """ Delete NAT44 session """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(len(pkts))
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          nsessions = len(sessions)
  
          self.vapi.nat44_del_session(address=sessions[0].inside_ip_address,
                                      port=sessions[0].inside_port,
                                      protocol=sessions[0].protocol,
                                      flags=self.config_flags.NAT_IS_INSIDE)
          self.vapi.nat44_del_session(address=sessions[1].outside_ip_address,
                                      port=sessions[1].outside_port,
                                      protocol=sessions[1].protocol)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(nsessions - len(sessions), 2)
  
          self.vapi.nat44_del_session(address=sessions[0].inside_ip_address,
                                      port=sessions[0].inside_port,
                                      protocol=sessions[0].protocol,
                                      flags=self.config_flags.NAT_IS_INSIDE)
  
          self.verify_no_nat44_user()
  
      def test_frag_in_order(self):
          """ NAT44 translate fragments arriving in order """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          self.frag_in_order(proto=IP_PROTOS.tcp)
          self.frag_in_order(proto=IP_PROTOS.udp)
          self.frag_in_order(proto=IP_PROTOS.icmp)
  
      def test_frag_forwarding(self):
          """ NAT44 forwarding fragment test """
          self.vapi.nat44_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_forwarding_enable_disable(enable=1)
  
          data = b"A" * 16 + b"B" * 16 + b"C" * 3
          pkts = self.create_stream_frag(self.pg1,
                                         self.pg0.remote_ip4,
                                         4789,
                                         4789,
                                         data,
                                         proto=IP_PROTOS.udp)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          p = self.reass_frags_and_verify(frags,
                                          self.pg1.remote_ip4,
                                          self.pg0.remote_ip4)
          self.assertEqual(p[UDP].sport, 4789)
          self.assertEqual(p[UDP].dport, 4789)
          self.assertEqual(data, p[Raw].load)
  
      def test_reass_hairpinning(self):
          """ NAT44 fragments hairpinning """
  
          self.server = self.pg0.remote_hosts[1]
          self.host_in_port = random.randint(1025, 65535)
          self.server_in_port = random.randint(1025, 65535)
          self.server_out_port = random.randint(1025, 65535)
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          # add static mapping for server
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr)
  
          self.reass_hairpinning(proto=IP_PROTOS.tcp)
          self.reass_hairpinning(proto=IP_PROTOS.udp)
          self.reass_hairpinning(proto=IP_PROTOS.icmp)
  
      def test_frag_out_of_order(self):
          """ NAT44 translate fragments arriving out of order """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          self.frag_out_of_order(proto=IP_PROTOS.tcp)
          self.frag_out_of_order(proto=IP_PROTOS.udp)
          self.frag_out_of_order(proto=IP_PROTOS.icmp)
  
      def test_port_restricted(self):
          """ Port restricted NAT44 (MAP-E CE) """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_set_addr_and_port_alloc_alg(alg=1,
                                                    psid_offset=6,
                                                    psid_length=6,
                                                    psid=10)
  
          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=4567, dport=22))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg1.remote_ip4)
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(tcp.dport, 22)
              self.assertNotEqual(tcp.sport, 4567)
              self.assertEqual((tcp.sport >> 6) & 63, 10)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
      def test_port_range(self):
          """ External address port range """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_set_addr_and_port_alloc_alg(alg=2,
                                                    start_port=1025,
                                                    end_port=1027)
  
          pkts = []
          for port in range(0, 5):
              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=1125 + port))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(3)
          for p in capture:
              tcp = p[TCP]
              self.assertGreaterEqual(tcp.sport, 1025)
              self.assertLessEqual(tcp.sport, 1027)
  
      def test_multiple_outside_vrf(self):
          """ Multiple outside VRF """
          vrf_id1 = 1
          vrf_id2 = 2
  
          self.pg1.unconfig_ip4()
          self.pg2.unconfig_ip4()
          self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id1})
          self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id2})
          self.pg1.set_table_ip4(vrf_id1)
          self.pg2.set_table_ip4(vrf_id2)
          self.pg1.config_ip4()
          self.pg2.config_ip4()
          self.pg1.resolve_arp()
          self.pg2.resolve_arp()
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg2.sw_if_index,
              is_add=1)
  
          try:
              # first VRF
              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, self.nat_addr)
  
              pkts = self.create_stream_out(self.pg1, self.nat_addr)
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg0.get_capture(len(pkts))
              self.verify_capture_in(capture, self.pg0)
  
              self.tcp_port_in = 60303
              self.udp_port_in = 60304
              self.icmp_id_in = 60305
  
              # second VRF
              pkts = self.create_stream_in(self.pg0, self.pg2)
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg2.get_capture(len(pkts))
              self.verify_capture_out(capture, self.nat_addr)
  
              pkts = self.create_stream_out(self.pg2, self.nat_addr)
              self.pg2.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)
  
          finally:
              self.nat44_add_address(self.nat_addr, is_add=0)
              self.pg1.unconfig_ip4()
              self.pg2.unconfig_ip4()
              self.pg1.set_table_ip4(0)
              self.pg2.set_table_ip4(0)
              self.pg1.config_ip4()
              self.pg2.config_ip4()
              self.pg1.resolve_arp()
              self.pg2.resolve_arp()
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_timeout(self):
          """ NAT44 session timeouts """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_set_timeouts(udp=5, tcp_established=7440,
                                     tcp_transitory=240, icmp=60)
  
          max_sessions = 1000
          pkts = []
          for i in range(0, max_sessions):
              src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
              p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                   IP(src=src, dst=self.pg1.remote_ip4) /
                   UDP(sport=1025, dport=53))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(max_sessions)
  
          sleep(6)
  
          pkts = []
          for i in range(0, max_sessions):
              src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
              p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                   IP(src=src, dst=self.pg1.remote_ip4) /
                   UDP(sport=1026, dport=53))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(max_sessions)
  
          nsessions = 0
          users = self.vapi.nat44_user_dump()
          for user in users:
              nsessions = nsessions + user.nsessions
          self.assertLess(nsessions, 2 * max_sessions)
  
      def test_mss_clamping(self):
          """ TCP MSS clamping """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="S", options=[('MSS', 1400)]))
  
          self.vapi.nat_set_mss_clamping(enable=1, mss_value=1000)
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          # Negotiated MSS value greater than configured - changed
          self.verify_mss_value(capture[0], 1000)
  
          self.vapi.nat_set_mss_clamping(enable=0, mss_value=1500)
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          # MSS clamping disabled - negotiated MSS unchanged
          self.verify_mss_value(capture[0], 1400)
  
          self.vapi.nat_set_mss_clamping(enable=1, mss_value=1500)
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          # Negotiated MSS value smaller than configured - unchanged
          self.verify_mss_value(capture[0], 1400)
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_ha_send(self):
          """ Send HA session synchronization events (active) """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4,
                                        port=12345,
                                        path_mtu=512)
          self.vapi.nat_ha_set_failover(ip_address=self.pg3.remote_ip4,
                                        port=12346, session_refresh_interval=10)
          bind_layers(UDP, HANATStateSync, sport=12345)
  
          # create sessions
          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)
          # active send HA events
          self.vapi.nat_ha_flush()
          stats = self.statistics.get_counter('/nat44/ha/add-event-send')
          self.assertEqual(stats[0][0], 3)
          capture = self.pg3.get_capture(1)
          p = capture[0]
          self.assert_packet_checksums_valid(p)
          try:
              ip = p[IP]
              udp = p[UDP]
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(ip.src, self.pg3.local_ip4)
              self.assertEqual(ip.dst, self.pg3.remote_ip4)
              self.assertEqual(udp.sport, 12345)
              self.assertEqual(udp.dport, 12346)
              self.assertEqual(hanat.version, 1)
              self.assertEqual(hanat.thread_index, 0)
              self.assertEqual(hanat.count, 3)
              seq = hanat.sequence_number
              for event in hanat.events:
                  self.assertEqual(event.event_type, 1)
                  self.assertEqual(event.in_addr, self.pg0.remote_ip4)
                  self.assertEqual(event.out_addr, self.nat_addr)
                  self.assertEqual(event.fib_index, 0)
  
          # ACK received events
          ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
                 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
                 UDP(sport=12346, dport=12345) /
                 HANATStateSync(sequence_number=seq, flags='ACK'))
          self.pg3.add_stream(ack)
          self.pg_start()
          stats = self.statistics.get_counter('/nat44/ha/ack-recv')
          self.assertEqual(stats[0][0], 1)
  
          # delete one session
          self.pg_enable_capture(self.pg_interfaces)
          self.vapi.nat44_del_session(address=self.pg0.remote_ip4,
                                      port=self.tcp_port_in,
                                      protocol=IP_PROTOS.tcp,
                                      flags=self.config_flags.NAT_IS_INSIDE)
          self.vapi.nat_ha_flush()
          stats = self.statistics.get_counter('/nat44/ha/del-event-send')
          self.assertEqual(stats[0][0], 1)
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertGreater(hanat.sequence_number, seq)
  
          # do not send ACK, active retry send HA event again
          self.pg_enable_capture(self.pg_interfaces)
          sleep(12)
          stats = self.statistics.get_counter('/nat44/ha/retry-count')
          self.assertEqual(stats[0][0], 3)
          stats = self.statistics.get_counter('/nat44/ha/missed-count')
          self.assertEqual(stats[0][0], 1)
          capture = self.pg3.get_capture(3)
          for packet in capture:
              self.assertEqual(packet, p)
  
          # session counters refresh
          pkts = self.create_stream_out(self.pg1)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(2)
          self.vapi.nat_ha_flush()
          stats = self.statistics.get_counter('/nat44/ha/refresh-event-send')
          self.assertEqual(stats[0][0], 2)
          capture = self.pg3.get_capture(1)
          p = capture[0]
          self.assert_packet_checksums_valid(p)
          try:
              ip = p[IP]
              udp = p[UDP]
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(ip.src, self.pg3.local_ip4)
              self.assertEqual(ip.dst, self.pg3.remote_ip4)
              self.assertEqual(udp.sport, 12345)
              self.assertEqual(udp.dport, 12346)
              self.assertEqual(hanat.version, 1)
              self.assertEqual(hanat.count, 2)
              seq = hanat.sequence_number
              for event in hanat.events:
                  self.assertEqual(event.event_type, 3)
                  self.assertEqual(event.out_addr, self.nat_addr)
                  self.assertEqual(event.fib_index, 0)
                  self.assertEqual(event.total_pkts, 2)
                  self.assertGreater(event.total_bytes, 0)
  
          ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
                 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
                 UDP(sport=12346, dport=12345) /
                 HANATStateSync(sequence_number=seq, flags='ACK'))
          self.pg3.add_stream(ack)
          self.pg_start()
          stats = self.statistics.get_counter('/nat44/ha/ack-recv')
          self.assertEqual(stats[0][0], 2)
  
      def test_ha_recv(self):
          """ Receive HA session synchronization events (passive) """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4,
                                        port=12345,
                                        path_mtu=512)
          bind_layers(UDP, HANATStateSync, sport=12345)
  
          self.tcp_port_out = random.randint(1025, 65535)
          self.udp_port_out = random.randint(1025, 65535)
  
          # send HA session add events to failover/passive
          p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
               IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
               UDP(sport=12346, dport=12345) /
               HANATStateSync(sequence_number=1, events=[
                   Event(event_type='add', protocol='tcp',
                         in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr,
                         in_port=self.tcp_port_in, out_port=self.tcp_port_out,
                         eh_addr=self.pg1.remote_ip4,
                         ehn_addr=self.pg1.remote_ip4,
                         eh_port=self.tcp_external_port,
                         ehn_port=self.tcp_external_port, fib_index=0),
                   Event(event_type='add', protocol='udp',
                         in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr,
                         in_port=self.udp_port_in, out_port=self.udp_port_out,
                         eh_addr=self.pg1.remote_ip4,
                         ehn_addr=self.pg1.remote_ip4,
                         eh_port=self.udp_external_port,
                         ehn_port=self.udp_external_port, fib_index=0)]))
  
          self.pg3.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          # receive ACK
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(hanat.sequence_number, 1)
              self.assertEqual(hanat.flags, 'ACK')
              self.assertEqual(hanat.version, 1)
              self.assertEqual(hanat.thread_index, 0)
          stats = self.statistics.get_counter('/nat44/ha/ack-send')
          self.assertEqual(stats[0][0], 1)
          stats = self.statistics.get_counter('/nat44/ha/add-event-recv')
          self.assertEqual(stats[0][0], 2)
          users = self.statistics.get_counter('/nat44/total-users')
          self.assertEqual(users[0][0], 1)
          sessions = self.statistics.get_counter('/nat44/total-sessions')
          self.assertEqual(sessions[0][0], 2)
          users = self.vapi.nat44_user_dump()
          self.assertEqual(len(users), 1)
          self.assertEqual(str(users[0].ip_address),
                           self.pg0.remote_ip4)
          # there should be 2 sessions created by HA
          sessions = self.vapi.nat44_user_session_dump(users[0].ip_address,
                                                       users[0].vrf_id)
          self.assertEqual(len(sessions), 2)
          for session in sessions:
              self.assertEqual(str(session.inside_ip_address),
                               self.pg0.remote_ip4)
              self.assertEqual(str(session.outside_ip_address),
                               self.nat_addr)
              self.assertIn(session.inside_port,
                            [self.tcp_port_in, self.udp_port_in])
              self.assertIn(session.outside_port,
                            [self.tcp_port_out, self.udp_port_out])
              self.assertIn(session.protocol, [IP_PROTOS.tcp, IP_PROTOS.udp])
  
          # send HA session delete event to failover/passive
          p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
               IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
               UDP(sport=12346, dport=12345) /
               HANATStateSync(sequence_number=2, events=[
                   Event(event_type='del', protocol='udp',
                         in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr,
                         in_port=self.udp_port_in, out_port=self.udp_port_out,
                         eh_addr=self.pg1.remote_ip4,
                         ehn_addr=self.pg1.remote_ip4,
                         eh_port=self.udp_external_port,
                         ehn_port=self.udp_external_port, fib_index=0)]))
  
          self.pg3.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          # receive ACK
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(hanat.sequence_number, 2)
              self.assertEqual(hanat.flags, 'ACK')
              self.assertEqual(hanat.version, 1)
          users = self.vapi.nat44_user_dump()
          self.assertEqual(len(users), 1)
          self.assertEqual(str(users[0].ip_address),
                           self.pg0.remote_ip4)
          # now we should have only 1 session, 1 deleted by HA
          sessions = self.vapi.nat44_user_session_dump(users[0].ip_address,
                                                       users[0].vrf_id)
          self.assertEqual(len(sessions), 1)
          stats = self.statistics.get_counter('/nat44/ha/del-event-recv')
          self.assertEqual(stats[0][0], 1)
  
          stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed')
          self.assertEqual(stats, 2)
  
          # send HA session refresh event to failover/passive
          p = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
               IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
               UDP(sport=12346, dport=12345) /
               HANATStateSync(sequence_number=3, events=[
                   Event(event_type='refresh', protocol='tcp',
                         in_addr=self.pg0.remote_ip4, out_addr=self.nat_addr,
                         in_port=self.tcp_port_in, out_port=self.tcp_port_out,
                         eh_addr=self.pg1.remote_ip4,
                         ehn_addr=self.pg1.remote_ip4,
                         eh_port=self.tcp_external_port,
                         ehn_port=self.tcp_external_port, fib_index=0,
                         total_bytes=1024, total_pkts=2)]))
          self.pg3.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          # receive ACK
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              hanat = p[HANATStateSync]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(hanat.sequence_number, 3)
              self.assertEqual(hanat.flags, 'ACK')
              self.assertEqual(hanat.version, 1)
          users = self.vapi.nat44_user_dump()
          self.assertEqual(len(users), 1)
          self.assertEqual(str(users[0].ip_address),
                           self.pg0.remote_ip4)
          sessions = self.vapi.nat44_user_session_dump(users[0].ip_address,
                                                       users[0].vrf_id)
          self.assertEqual(len(sessions), 1)
          session = sessions[0]
          self.assertEqual(session.total_bytes, 1024)
          self.assertEqual(session.total_pkts, 2)
          stats = self.statistics.get_counter('/nat44/ha/refresh-event-recv')
          self.assertEqual(stats[0][0], 1)
  
          stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed')
          self.assertEqual(stats, 3)
  
          # send packet to test session created by HA
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out))
          self.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]
          except IndexError:
              self.logger.error(ppp("Invalid packet:", p))
              raise
          else:
              self.assertEqual(ip.src, self.pg1.remote_ip4)
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(tcp.sport, self.tcp_external_port)
              self.assertEqual(tcp.dport, self.tcp_port_in)
  
      def tearDown(self):
          super(TestNAT44, self).tearDown()
          self.clear_nat44()
          self.vapi.cli("clear logging")
  
      def show_commands_at_teardown(self):
          self.logger.info(self.vapi.cli("show nat44 addresses"))
          self.logger.info(self.vapi.cli("show nat44 interfaces"))
          self.logger.info(self.vapi.cli("show nat44 static mappings"))
          self.logger.info(self.vapi.cli("show nat44 interface address"))
          self.logger.info(self.vapi.cli("show nat44 sessions detail"))
          self.logger.info(self.vapi.cli("show nat44 hash tables detail"))
          self.logger.info(self.vapi.cli("show nat timeouts"))
          self.logger.info(
              self.vapi.cli("show nat addr-port-assignment-alg"))
          self.logger.info(self.vapi.cli("show nat ha"))
  
  
  class TestNAT44EndpointDependent2(MethodHolder):
      """ Endpoint-Dependent mapping and filtering test cases """
  
      @classmethod
      def setUpConstants(cls):
          super(TestNAT44EndpointDependent2, cls).setUpConstants()
          cls.vpp_cmdline.extend(["nat", "{", "endpoint-dependent", "}"])
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT44EndpointDependent2, cls).tearDownClass()
  
      def tearDown(self):
          super(TestNAT44EndpointDependent2, self).tearDown()
  
      @classmethod
      def create_and_add_ip4_table(cls, i, table_id):
          cls.vapi.ip_table_add_del(is_add=1, table={'table_id': table_id})
          i.set_table_ip4(table_id)
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT44EndpointDependent2, cls).setUpClass()
  
          cls.create_pg_interfaces(range(3))
          cls.interfaces = list(cls.pg_interfaces)
  
          cls.create_and_add_ip4_table(cls.pg1, 10)
  
          for i in cls.interfaces:
              i.admin_up()
              i.config_ip4()
              i.resolve_arp()
  
              i.generate_remote_hosts(1)
              i.configure_ipv4_neighbors()
  
      def setUp(self):
          super(TestNAT44EndpointDependent2, self).setUp()
  
          nat_config = self.vapi.nat_show_config()
          self.assertEqual(1, nat_config.endpoint_dependent)
  
      def nat_add_inside_interface(self, i):
          self.vapi.nat44_interface_add_del_feature(
              flags=self.config_flags.NAT_IS_INSIDE,
              sw_if_index=i.sw_if_index, is_add=1)
  
      def nat_add_outside_interface(self, i):
          self.vapi.nat44_interface_add_del_feature(
              flags=self.config_flags.NAT_IS_OUTSIDE,
              sw_if_index=i.sw_if_index, is_add=1)
  
      def nat_add_interface_address(self, i):
          self.nat_addr = i.local_ip4
          self.vapi.nat44_add_del_interface_addr(
              sw_if_index=i.sw_if_index, is_add=1)
  
      def nat_add_address(self, address, vrf_id=0xFFFFFFFF):
          self.nat_addr = address
          self.nat44_add_address(address, vrf_id=vrf_id)
  
      def cli(self, command):
          result = self.vapi.cli(command)
          self.logger.info(result)
          # print(result)
  
      def show_configuration(self):
          self.cli("show interface")
          self.cli("show interface address")
          self.cli("show nat44 addresses")
          self.cli("show nat44 interfaces")
  
      def create_tcp_stream(self, in_if, out_if, count):
          """
          Create tcp packet stream
  
          :param in_if: Inside interface
          :param out_if: Outside interface
          :param count: count of packets to generate
          """
          pkts = []
          port = 6303
  
          for i in range(count):
              p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
                   IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=64) /
                   TCP(sport=port + i, dport=20))
              pkts.append(p)
  
          return pkts
  
      def test_session_limit_per_vrf(self):
  
          inside = self.pg0
          inside_vrf10 = self.pg1
          outside = self.pg2
  
          limit = 5
  
          # 2 interfaces pg0, pg1 (vrf10, limit 1 tcp session)
          # non existing vrf_id makes process core dump
          self.vapi.nat44_set_session_limit(session_limit=limit, vrf_id=10)
  
          self.nat_add_inside_interface(inside)
          self.nat_add_inside_interface(inside_vrf10)
          self.nat_add_outside_interface(outside)
  
          # vrf independent
          self.nat_add_interface_address(outside)
  
          # BUG: causing core dump - when bad vrf_id is specified
          # self.nat44_add_address(outside.local_ip4, vrf_id=20)
  
          self.show_configuration()
  
          stream = self.create_tcp_stream(inside_vrf10, outside, limit * 2)
          inside_vrf10.add_stream(stream)
  
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          capture = outside.get_capture(limit)
  
          stream = self.create_tcp_stream(inside, outside, limit * 2)
          inside.add_stream(stream)
  
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          capture = outside.get_capture(len(stream))
  
  
  class TestNAT44EndpointDependent(MethodHolder):
      """ Endpoint-Dependent mapping and filtering test cases """
  
      @classmethod
      def setUpConstants(cls):
          super(TestNAT44EndpointDependent, cls).setUpConstants()
          cls.vpp_cmdline.extend(["nat", "{", "endpoint-dependent", "}"])
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT44EndpointDependent, cls).setUpClass()
          cls.vapi.cli("set log class nat level debug")
  
          cls.tcp_port_in = 6303
          cls.tcp_port_out = 6303
          cls.udp_port_in = 6304
          cls.udp_port_out = 6304
          cls.icmp_id_in = 6305
          cls.icmp_id_out = 6305
          cls.nat_addr = '10.0.0.3'
          cls.ipfix_src_port = 4739
          cls.ipfix_domain_id = 1
          cls.tcp_external_port = 80
  
          cls.create_pg_interfaces(range(9))
          cls.interfaces = list(cls.pg_interfaces[0:3])
  
          for i in cls.interfaces:
              i.admin_up()
              i.config_ip4()
              i.resolve_arp()
  
          cls.pg0.generate_remote_hosts(3)
          cls.pg0.configure_ipv4_neighbors()
  
          cls.pg3.admin_up()
  
          cls.pg4.generate_remote_hosts(2)
          cls.pg4.config_ip4()
          cls.vapi.sw_interface_add_del_address(
              sw_if_index=cls.pg4.sw_if_index,
              prefix="10.0.0.1/24")
  
          cls.pg4.admin_up()
          cls.pg4.resolve_arp()
          cls.pg4._remote_hosts[1]._ip4 = cls.pg4._remote_hosts[0]._ip4
          cls.pg4.resolve_arp()
  
          zero_ip4 = socket.inet_pton(socket.AF_INET, "0.0.0.0")
          cls.vapi.ip_table_add_del(is_add=1, table={'table_id': 1})
  
          cls.pg5._local_ip4 = "10.1.1.1"
          cls.pg5._remote_hosts[0]._ip4 = "10.1.1.2"
          cls.pg5.set_table_ip4(1)
          cls.pg5.config_ip4()
          cls.pg5.admin_up()
          r1 = VppIpRoute(cls, cls.pg5.remote_ip4, 32,
                          [VppRoutePath("0.0.0.0",
                                        cls.pg5.sw_if_index)],
                          table_id=1,
                          register=False)
          r1.add_vpp_config()
  
          cls.pg6._local_ip4 = "10.1.2.1"
          cls.pg6._remote_hosts[0]._ip4 = "10.1.2.2"
          cls.pg6.set_table_ip4(1)
          cls.pg6.config_ip4()
          cls.pg6.admin_up()
  
          r2 = VppIpRoute(cls, cls.pg6.remote_ip4, 32,
                          [VppRoutePath("0.0.0.0",
                                        cls.pg6.sw_if_index)],
                          table_id=1,
                          register=False)
          r3 = VppIpRoute(cls, cls.pg6.remote_ip4, 16,
                          [VppRoutePath("0.0.0.0",
                                        0xffffffff,
                                        nh_table_id=1)],
                          table_id=0,
                          register=False)
          r4 = VppIpRoute(cls, "0.0.0.0", 0,
                          [VppRoutePath("0.0.0.0", 0xffffffff,
                                        nh_table_id=0)],
                          table_id=1,
                          register=False)
          r5 = VppIpRoute(cls, "0.0.0.0", 0,
                          [VppRoutePath(cls.pg1.local_ip4,
                                        cls.pg1.sw_if_index)],
                          register=False)
          r2.add_vpp_config()
          r3.add_vpp_config()
          r4.add_vpp_config()
          r5.add_vpp_config()
  
          cls.pg5.resolve_arp()
          cls.pg6.resolve_arp()
  
          cls.pg7.admin_up()
          cls.pg7.config_ip4()
          cls.pg7.resolve_arp()
          cls.pg7.generate_remote_hosts(3)
          cls.pg7.configure_ipv4_neighbors()
  
          cls.pg8.admin_up()
          cls.pg8.config_ip4()
          cls.pg8.resolve_arp()
  
      def setUp(self):
          super(TestNAT44EndpointDependent, self).setUp()
          self.vapi.nat_set_timeouts(
              udp=300, tcp_established=7440, tcp_transitory=240, icmp=60)
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT44EndpointDependent, cls).tearDownClass()
  
      def test_frag_in_order(self):
          """ NAT44 translate fragments arriving in order """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.frag_in_order(proto=IP_PROTOS.tcp)
          self.frag_in_order(proto=IP_PROTOS.udp)
          self.frag_in_order(proto=IP_PROTOS.icmp)
  
      def test_frag_in_order_dont_translate(self):
          """ NAT44 don't translate fragments arriving in order """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_forwarding_enable_disable(enable=True)
          self.frag_in_order(proto=IP_PROTOS.tcp, dont_translate=True)
  
      def test_frag_out_of_order(self):
          """ NAT44 translate fragments arriving out of order """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.frag_out_of_order(proto=IP_PROTOS.tcp)
          self.frag_out_of_order(proto=IP_PROTOS.udp)
          self.frag_out_of_order(proto=IP_PROTOS.icmp)
  
      def test_frag_out_of_order_dont_translate(self):
          """ NAT44 don't translate fragments arriving out of order """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_forwarding_enable_disable(enable=True)
          self.frag_out_of_order(proto=IP_PROTOS.tcp, dont_translate=True)
  
      def test_frag_in_order_in_plus_out(self):
          """ in+out interface fragments in order """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=flags, is_add=1)
  
          self.server = self.pg1.remote_hosts[0]
  
          self.server_in_addr = self.server.ip4
          self.server_out_addr = '11.11.11.11'
          self.server_in_port = random.randint(1025, 65535)
          self.server_out_port = random.randint(1025, 65535)
  
          self.nat44_add_address(self.server_out_addr)
  
          # add static mappings for server
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        proto=IP_PROTOS.icmp)
  
          self.frag_in_order_in_plus_out(proto=IP_PROTOS.tcp)
          self.frag_in_order_in_plus_out(proto=IP_PROTOS.udp)
          self.frag_in_order_in_plus_out(proto=IP_PROTOS.icmp)
  
      def test_frag_out_of_order_in_plus_out(self):
          """ in+out interface fragments out of order """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=flags, is_add=1)
  
          self.server = self.pg1.remote_hosts[0]
  
          self.server_in_addr = self.server.ip4
          self.server_out_addr = '11.11.11.11'
          self.server_in_port = random.randint(1025, 65535)
          self.server_out_port = random.randint(1025, 65535)
  
          self.nat44_add_address(self.server_out_addr)
  
          # add static mappings for server
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.server_in_addr,
                                        self.server_out_addr,
                                        proto=IP_PROTOS.icmp)
  
          self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.tcp)
          self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.udp)
          self.frag_out_of_order_in_plus_out(proto=IP_PROTOS.icmp)
  
      def test_reass_hairpinning(self):
          """ NAT44 fragments hairpinning """
          self.server = self.pg0.remote_hosts[1]
          self.host_in_port = random.randint(1025, 65535)
          self.server_in_port = random.randint(1025, 65535)
          self.server_out_port = random.randint(1025, 65535)
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          # add static mapping for server
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.tcp)
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr,
                                        self.server_in_port,
                                        self.server_out_port,
                                        proto=IP_PROTOS.udp)
          self.nat44_add_static_mapping(self.server.ip4, self.nat_addr)
  
          self.reass_hairpinning(proto=IP_PROTOS.tcp)
          self.reass_hairpinning(proto=IP_PROTOS.udp)
          self.reass_hairpinning(proto=IP_PROTOS.icmp)
  
      def test_dynamic(self):
          """ NAT44 dynamic translation test """
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          nat_config = self.vapi.nat_show_config()
          self.assertEqual(1, nat_config.endpoint_dependent)
  
          # in2out
          tcpn = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/TCP packets')
          udpn = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
  
          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)
  
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/TCP packets')
          self.assertEqual(err - tcpn, 2)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
          self.assertEqual(err - totaln, 4)
  
          # out2in
          tcpn = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/TCP packets')
          udpn = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in-slowpath/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/good out2in packets processed')
  
          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)
  
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/TCP packets')
          self.assertEqual(err - tcpn, 2)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in-slowpath/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat44-ed-out2in/good out2in packets processed')
          self.assertEqual(err - totaln, 3)
  
          sessions = self.statistics.get_counter('/nat44/total-sessions')
          self.assertEqual(sessions[0][0], 3)
  
      def test_dynamic_output_feature_vrf(self):
          """ NAT44 dynamic translation test: output-feature, VRF"""
  
          # other then default (0)
          new_vrf_id = 22
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_output_feature(
              sw_if_index=self.pg7.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              sw_if_index=self.pg8.sw_if_index,
              is_add=1)
  
          try:
              self.vapi.ip_table_add_del(is_add=1,
                                         table={'table_id': new_vrf_id})
  
              self.pg7.unconfig_ip4()
              self.pg7.set_table_ip4(new_vrf_id)
              self.pg7.config_ip4()
              self.pg7.resolve_arp()
  
              self.pg8.unconfig_ip4()
              self.pg8.set_table_ip4(new_vrf_id)
              self.pg8.config_ip4()
              self.pg8.resolve_arp()
  
              nat_config = self.vapi.nat_show_config()
              self.assertEqual(1, nat_config.endpoint_dependent)
  
              # in2out
              tcpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/TCP packets')
              udpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/UDP packets')
              icmpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/ICMP packets')
              totaln = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
  
              pkts = self.create_stream_in(self.pg7, self.pg8)
              self.pg7.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg8.get_capture(len(pkts))
              self.verify_capture_out(capture)
  
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/TCP packets')
              self.assertEqual(err - tcpn, 2)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/UDP packets')
              self.assertEqual(err - udpn, 1)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/ICMP packets')
              self.assertEqual(err - icmpn, 1)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-in2out-slowpath/good in2out packets processed')
              self.assertEqual(err - totaln, 4)
  
              # out2in
              tcpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/TCP packets')
              udpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/UDP packets')
              icmpn = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in-slowpath/ICMP packets')
              totaln = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/good out2in packets processed')
  
              pkts = self.create_stream_out(self.pg8)
              self.pg8.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              capture = self.pg7.get_capture(len(pkts))
              self.verify_capture_in(capture, self.pg7)
  
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/TCP packets')
              self.assertEqual(err - tcpn, 2)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/UDP packets')
              self.assertEqual(err - udpn, 1)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in-slowpath/ICMP packets')
              self.assertEqual(err - icmpn, 1)
              err = self.statistics.get_err_counter(
                  '/err/nat44-ed-out2in/good out2in packets processed')
              self.assertEqual(err - totaln, 3)
  
              sessions = self.statistics.get_counter('/nat44/total-sessions')
              self.assertEqual(sessions[0][0], 3)
  
          finally:
              self.pg7.unconfig_ip4()
              self.pg7.set_table_ip4(1)
              self.pg7.config_ip4()
              self.pg7.resolve_arp()
  
              self.pg8.unconfig_ip4()
              self.pg8.set_table_ip4(1)
              self.pg8.config_ip4()
              self.pg8.resolve_arp()
  
              self.vapi.ip_table_add_del(is_add=0,
                                         table={'table_id': new_vrf_id})
  
      def test_forwarding(self):
          """ NAT44 forwarding test """
  
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat44_forwarding_enable_disable(enable=1)
  
          real_ip = self.pg0.remote_ip4
          alias_ip = self.nat_addr
          flags = self.config_flags.NAT_IS_ADDR_ONLY
          self.vapi.nat44_add_del_static_mapping(is_add=1,
                                                 local_ip_address=real_ip,
                                                 external_ip_address=alias_ip,
                                                 external_sw_if_index=0xFFFFFFFF,
                                                 flags=flags)
  
          try:
              # in2out - static mapping match
  
              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)
  
              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, same_port=True)
  
              # in2out - no static mapping match
  
              host0 = self.pg0.remote_hosts[0]
              self.pg0.remote_hosts[0] = self.pg0.remote_hosts[1]
              try:
                  pkts = self.create_stream_out(self.pg1,
                                                dst_ip=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)
              finally:
                  self.pg0.remote_hosts[0] = host0
  
              user = self.pg0.remote_hosts[1]
              sessions = self.vapi.nat44_user_session_dump(user.ip4, 0)
              self.assertEqual(len(sessions), 3)
              self.assertTrue(sessions[0].flags &
                              self.config_flags.NAT_IS_EXT_HOST_VALID)
              self.vapi.nat44_del_session(
                  address=sessions[0].inside_ip_address,
                  port=sessions[0].inside_port,
                  protocol=sessions[0].protocol,
                  flags=(self.config_flags.NAT_IS_INSIDE |
                         self.config_flags.NAT_IS_EXT_HOST_VALID),
                  ext_host_address=sessions[0].ext_host_address,
                  ext_host_port=sessions[0].ext_host_port)
              sessions = self.vapi.nat44_user_session_dump(user.ip4, 0)
              self.assertEqual(len(sessions), 2)
  
          finally:
              self.vapi.nat44_forwarding_enable_disable(enable=0)
              flags = self.config_flags.NAT_IS_ADDR_ONLY
              self.vapi.nat44_add_del_static_mapping(
                  is_add=0,
                  local_ip_address=real_ip,
                  external_ip_address=alias_ip,
                  external_sw_if_index=0xFFFFFFFF,
                  flags=flags)
  
      def test_static_lb(self):
          """ NAT44 local service load balancing """
          external_addr_n = self.nat_addr
          external_port = 80
          local_port = 8080
          server1 = self.pg0.remote_hosts[0]
          server2 = self.pg0.remote_hosts[1]
  
          locals = [{'addr': server1.ip4,
                     'port': local_port,
                     'probability': 70,
                     'vrf_id': 0},
                    {'addr': server2.ip4,
                     'port': local_port,
                     'probability': 30,
                     'vrf_id': 0}]
  
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_add_del_lb_static_mapping(
              is_add=1,
              external_addr=external_addr_n,
              external_port=external_port,
              protocol=IP_PROTOS.tcp,
              local_num=len(locals),
              locals=locals)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # from client to service
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          server = None
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertIn(ip.dst, [server1.ip4, server2.ip4])
              if ip.dst == server1.ip4:
                  server = server1
              else:
                  server = server2
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client
          p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
               IP(src=server.ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12345))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          sessions = self.vapi.nat44_user_session_dump(server.ip4, 0)
          self.assertEqual(len(sessions), 1)
          self.assertTrue(sessions[0].flags &
                          self.config_flags.NAT_IS_EXT_HOST_VALID)
          self.vapi.nat44_del_session(
              address=sessions[0].inside_ip_address,
              port=sessions[0].inside_port,
              protocol=sessions[0].protocol,
              flags=(self.config_flags.NAT_IS_INSIDE |
                     self.config_flags.NAT_IS_EXT_HOST_VALID),
              ext_host_address=sessions[0].ext_host_address,
              ext_host_port=sessions[0].ext_host_port)
          sessions = self.vapi.nat44_user_session_dump(server.ip4, 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 = self.nat_addr
          external_port = 80
          local_port = 8080
          server1 = self.pg0.remote_hosts[0]
          server2 = self.pg0.remote_hosts[1]
          server3 = self.pg0.remote_hosts[2]
  
          locals = [{'addr': server1.ip4,
                     'port': local_port,
                     'probability': 90,
                     'vrf_id': 0},
                    {'addr': server2.ip4,
                     'port': local_port,
                     'probability': 10,
                     'vrf_id': 0}]
  
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_add_del_lb_static_mapping(is_add=1,
                                                    external_addr=external_addr,
                                                    external_port=external_port,
                                                    protocol=IP_PROTOS.tcp,
                                                    local_num=len(locals),
                                                    locals=locals)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          server1_n = 0
          server2_n = 0
          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) /
                   IP(src=client, dst=self.nat_addr) /
                   TCP(sport=12345, dport=external_port))
              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 p in capture:
              if p[IP].dst == server1.ip4:
                  server1_n += 1
              else:
                  server2_n += 1
          self.assertGreater(server1_n, server2_n)
  
          local = {
              'addr': server3.ip4,
              'port': local_port,
              'probability': 20,
              'vrf_id': 0
          }
  
          # add new back-end
          self.vapi.nat44_lb_static_mapping_add_del_local(
              is_add=1,
              external_addr=external_addr,
              external_port=external_port,
              local=local,
              protocol=IP_PROTOS.tcp)
          server1_n = 0
          server2_n = 0
          server3_n = 0
          clients = ip4_range(self.pg1.remote_ip4, 60, 110)
          pkts = []
          for client in clients:
              p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
                   IP(src=client, dst=self.nat_addr) /
                   TCP(sport=12346, dport=external_port))
              pkts.append(p)
          self.assertGreater(len(pkts), 0)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          for p in capture:
              if p[IP].dst == server1.ip4:
                  server1_n += 1
              elif p[IP].dst == server2.ip4:
                  server2_n += 1
              else:
                  server3_n += 1
          self.assertGreater(server1_n, 0)
          self.assertGreater(server2_n, 0)
          self.assertGreater(server3_n, 0)
  
          local = {
              'addr': server2.ip4,
              'port': local_port,
              'probability': 10,
              'vrf_id': 0
          }
  
          # remove one back-end
          self.vapi.nat44_lb_static_mapping_add_del_local(
              is_add=0,
              external_addr=external_addr,
              external_port=external_port,
              local=local,
              protocol=IP_PROTOS.tcp)
          server1_n = 0
          server2_n = 0
          server3_n = 0
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          for p in capture:
              if p[IP].dst == server1.ip4:
                  server1_n += 1
              elif p[IP].dst == server2.ip4:
                  server2_n += 1
              else:
                  server3_n += 1
          self.assertGreater(server1_n, 0)
          self.assertEqual(server2_n, 0)
          self.assertGreater(server3_n, 0)
  
      def test_static_lb_2(self):
          """ NAT44 local service load balancing (asymmetrical rule) """
          external_addr = self.nat_addr
          external_port = 80
          local_port = 8080
          server1 = self.pg0.remote_hosts[0]
          server2 = self.pg0.remote_hosts[1]
  
          locals = [{'addr': server1.ip4,
                     'port': local_port,
                     'probability': 70,
                     'vrf_id': 0},
                    {'addr': server2.ip4,
                     'port': local_port,
                     'probability': 30,
                     'vrf_id': 0}]
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          flags = self.config_flags.NAT_IS_OUT2IN_ONLY
          self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags,
                                                    external_addr=external_addr,
                                                    external_port=external_port,
                                                    protocol=IP_PROTOS.tcp,
                                                    local_num=len(locals),
                                                    locals=locals)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # from client to service
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          server = None
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertIn(ip.dst, [server1.ip4, server2.ip4])
              if ip.dst == server1.ip4:
                  server = server1
              else:
                  server = server2
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client
          p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
               IP(src=server.ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12345))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client to server (no translation)
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=server1.ip4) /
               TCP(sport=12346, dport=local_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          server = None
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, server1.ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client (no translation)
          p = (Ether(src=server1.mac, dst=self.pg0.local_mac) /
               IP(src=server1.ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12346))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, server1.ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
      def test_lb_affinity(self):
          """ NAT44 local service load balancing affinity """
          external_addr = self.nat_addr
          external_port = 80
          local_port = 8080
          server1 = self.pg0.remote_hosts[0]
          server2 = self.pg0.remote_hosts[1]
  
          locals = [{'addr': server1.ip4,
                     'port': local_port,
                     'probability': 50,
                     'vrf_id': 0},
                    {'addr': server2.ip4,
                     'port': local_port,
                     'probability': 50,
                     'vrf_id': 0}]
  
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_add_del_lb_static_mapping(is_add=1,
                                                    external_addr=external_addr,
                                                    external_port=external_port,
                                                    protocol=IP_PROTOS.tcp,
                                                    affinity=10800,
                                                    local_num=len(locals),
                                                    locals=locals)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=1025, dport=external_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          backend = capture[0][IP].dst
  
          sessions = self.vapi.nat44_user_session_dump(backend, 0)
          self.assertEqual(len(sessions), 1)
          self.assertTrue(sessions[0].flags &
                          self.config_flags.NAT_IS_EXT_HOST_VALID)
          self.vapi.nat44_del_session(
              address=sessions[0].inside_ip_address,
              port=sessions[0].inside_port,
              protocol=sessions[0].protocol,
              flags=(self.config_flags.NAT_IS_INSIDE |
                     self.config_flags.NAT_IS_EXT_HOST_VALID),
              ext_host_address=sessions[0].ext_host_address,
              ext_host_port=sessions[0].ext_host_port)
  
          pkts = []
          for port in range(1030, 1100):
              p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
                   IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=port, dport=external_port))
              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 p in capture:
              self.assertEqual(p[IP].dst, backend)
  
      def test_unknown_proto(self):
          """ NAT44 translate packet with unknown protocol """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # in2out
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=20))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
  
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, self.nat_addr)
              self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # out2in
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, self.pg1.remote_ip4)
              self.assertEqual(packet[IP].dst, self.pg0.remote_ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_hairpinning_unknown_proto(self):
          """ NAT44 translate packet with unknown protocol - hairpinning """
          host = self.pg0.remote_hosts[0]
          server = self.pg0.remote_hosts[1]
          host_in_port = 1234
          server_out_port = 8765
          server_nat_ip = "10.0.0.11"
  
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # add static mapping for server
          self.nat44_add_static_mapping(server.ip4, server_nat_ip)
  
          # host to server
          p = (Ether(src=host.mac, dst=self.pg0.local_mac) /
               IP(src=host.ip4, dst=server_nat_ip) /
               TCP(sport=host_in_port, dport=server_out_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          p = (Ether(dst=self.pg0.local_mac, src=host.mac) /
               IP(src=host.ip4, dst=server_nat_ip) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, self.nat_addr)
              self.assertEqual(packet[IP].dst, server.ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # server to host
          p = (Ether(dst=self.pg0.local_mac, src=server.mac) /
               IP(src=server.ip4, dst=self.nat_addr) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, server_nat_ip)
              self.assertEqual(packet[IP].dst, host.ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_output_feature_and_service(self):
          """ NAT44 interface output feature and services """
          external_addr = '1.2.3.4'
          external_port = 80
          local_port = 8080
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_ADDR_ONLY
          self.vapi.nat44_add_del_identity_mapping(
              ip_address=self.pg1.remote_ip4, sw_if_index=0xFFFFFFFF,
              flags=flags, is_add=1)
          flags = self.config_flags.NAT_IS_OUT2IN_ONLY
          self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr,
                                        local_port, external_port,
                                        proto=IP_PROTOS.tcp, flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
  
          # from client to service
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=external_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12345))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, external_addr)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from local network host to external network
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out(capture)
          pkts = self.create_stream_in(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(len(pkts))
          self.verify_capture_out(capture)
  
          # from external network back to local network host
          pkts = self.create_stream_out(self.pg1)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          self.verify_capture_in(capture, self.pg0)
  
      def test_output_feature_and_service2(self):
          """ NAT44 interface output feature and service host direct access """
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
  
          # session initiated 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 initiated from remote host - do not translate
          self.tcp_port_in = 60303
          self.udp_port_in = 60304
          self.icmp_id_in = 60305
          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(enable=1)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_OUT2IN_ONLY
          self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr,
                                        local_port, external_port,
                                        proto=IP_PROTOS.tcp, flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
  
          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_next_src_nat(self):
          """ On way back forward packet to nat44-in2out node. """
          twice_nat_addr = '10.0.1.3'
          external_port = 80
          local_port = 8080
          post_twice_nat_port = 0
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(twice_nat_addr, twice_nat=1)
          flags = (self.config_flags.NAT_IS_OUT2IN_ONLY |
                   self.config_flags.NAT_IS_SELF_TWICE_NAT)
          self.nat44_add_static_mapping(self.pg6.remote_ip4, self.pg1.remote_ip4,
                                        local_port, external_port,
                                        proto=IP_PROTOS.tcp, vrf_id=1,
                                        flags=flags)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg6.sw_if_index,
              is_add=1)
  
          p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) /
               IP(src=self.pg6.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=12345, dport=external_port))
          self.pg6.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, twice_nat_addr)
              self.assertNotEqual(tcp.sport, 12345)
              post_twice_nat_port = tcp.sport
              self.assertEqual(ip.dst, self.pg6.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.pg6.remote_mac, dst=self.pg6.local_mac) /
               IP(src=self.pg6.remote_ip4, dst=twice_nat_addr) /
               TCP(sport=local_port, dport=post_twice_nat_port))
          self.pg6.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg1.remote_ip4)
              self.assertEqual(tcp.sport, external_port)
              self.assertEqual(ip.dst, self.pg6.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 twice_nat_common(self, self_twice_nat=False, same_pg=False, lb=False,
                           client_id=None):
          twice_nat_addr = '10.0.1.3'
  
          port_in = 8080
          if lb:
              if not same_pg:
                  port_in1 = port_in
                  port_in2 = port_in
              else:
                  port_in1 = port_in + 1
                  port_in2 = port_in + 2
  
          port_out = 80
          eh_port_out = 4567
  
          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)
  
          flags = 0
          if self_twice_nat:
              flags |= self.config_flags.NAT_IS_SELF_TWICE_NAT
          else:
              flags |= self.config_flags.NAT_IS_TWICE_NAT
  
          if not lb:
              self.nat44_add_static_mapping(pg0.remote_ip4, self.nat_addr,
                                            port_in, port_out,
                                            proto=IP_PROTOS.tcp,
                                            flags=flags)
          else:
              locals = [{'addr': server1.ip4,
                         'port': port_in1,
                         'probability': 50,
                         'vrf_id': 0},
                        {'addr': server2.ip4,
                         'port': port_in2,
                         'probability': 50,
                         'vrf_id': 0}]
              out_addr = self.nat_addr
  
              self.vapi.nat44_add_del_lb_static_mapping(is_add=1, flags=flags,
                                                        external_addr=out_addr,
                                                        external_port=port_out,
                                                        protocol=IP_PROTOS.tcp,
                                                        local_num=len(locals),
                                                        locals=locals)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=pg1.sw_if_index,
              is_add=1)
  
          if same_pg:
              if not lb:
                  client = server
              else:
                  assert client_id is not None
                  if client_id == 1:
                      client = self.pg0.remote_hosts[0]
                  elif client_id == 2:
                      client = self.pg0.remote_hosts[1]
          else:
              client = pg1.remote_hosts[0]
          p = (Ether(src=pg1.remote_mac, dst=pg1.local_mac) /
               IP(src=client.ip4, dst=self.nat_addr) /
               TCP(sport=eh_port_out, dport=port_out))
          pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              if lb:
                  if ip.dst == server1.ip4:
                      server = server1
                      port_in = port_in1
                  else:
                      server = server2
                      port_in = port_in2
              self.assertEqual(ip.dst, server.ip4)
              if lb and same_pg:
                  self.assertIn(tcp.dport, [port_in1, port_in2])
              else:
                  self.assertEqual(tcp.dport, port_in)
              if eh_translate:
                  self.assertEqual(ip.src, twice_nat_addr)
                  self.assertNotEqual(tcp.sport, eh_port_out)
              else:
                  self.assertEqual(ip.src, client.ip4)
                  self.assertEqual(tcp.sport, eh_port_out)
              eh_addr_in = ip.src
              eh_port_in = tcp.sport
              saved_port_in = tcp.dport
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          p = (Ether(src=server.mac, dst=pg0.local_mac) /
               IP(src=server.ip4, dst=eh_addr_in) /
               TCP(sport=saved_port_in, dport=eh_port_in))
          pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, client.ip4)
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(tcp.dport, eh_port_out)
              self.assertEqual(tcp.sport, port_out)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          if eh_translate:
              sessions = self.vapi.nat44_user_session_dump(server.ip4, 0)
              self.assertEqual(len(sessions), 1)
              self.assertTrue(sessions[0].flags &
                              self.config_flags.NAT_IS_EXT_HOST_VALID)
              self.assertTrue(sessions[0].flags &
                              self.config_flags.NAT_IS_TWICE_NAT)
              self.logger.info(self.vapi.cli("show nat44 sessions detail"))
              self.vapi.nat44_del_session(
                  address=sessions[0].inside_ip_address,
                  port=sessions[0].inside_port,
                  protocol=sessions[0].protocol,
                  flags=(self.config_flags.NAT_IS_INSIDE |
                         self.config_flags.NAT_IS_EXT_HOST_VALID),
                  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.ip4, 0)
              self.assertEqual(len(sessions), 0)
  
      def test_twice_nat(self):
          """ Twice NAT44 """
          self.twice_nat_common()
  
      def test_self_twice_nat_positive(self):
          """ Self Twice NAT44 (positive test) """
          self.twice_nat_common(self_twice_nat=True, same_pg=True)
  
      def test_self_twice_nat_negative(self):
          """ Self Twice NAT44 (negative test) """
          self.twice_nat_common(self_twice_nat=True)
  
      def test_twice_nat_lb(self):
          """ Twice NAT44 local service load balancing """
          self.twice_nat_common(lb=True)
  
      def test_self_twice_nat_lb_positive(self):
          """ Self Twice NAT44 local service load balancing (positive test) """
          self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True,
                                client_id=1)
  
      def test_self_twice_nat_lb_negative(self):
          """ Self Twice NAT44 local service load balancing (negative test) """
          self.twice_nat_common(lb=True, self_twice_nat=True, same_pg=True,
                                client_id=2)
  
      def test_twice_nat_interface_addr(self):
          """ Acquire twice NAT44 addresses from interface """
          flags = self.config_flags.NAT_IS_TWICE_NAT
          self.vapi.nat44_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg3.sw_if_index,
              flags=flags)
  
          # 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.pg3.config_ip4()
          adresses = self.vapi.nat44_address_dump()
          self.assertEqual(1, len(adresses))
          self.assertEqual(str(adresses[0].ip_address),
                           self.pg3.local_ip4)
          self.assertEqual(adresses[0].flags, flags)
  
          # remove interface address and check NAT address pool
          self.pg3.unconfig_ip4()
          adresses = self.vapi.nat44_address_dump()
          self.assertEqual(0, len(adresses))
  
      def test_tcp_close(self):
          """ Close TCP session from inside network - output feature """
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.pg1.local_ip4)
          twice_nat_addr = '10.0.1.3'
          service_ip = '192.168.16.150'
          self.nat44_add_address(twice_nat_addr, twice_nat=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
          flags = (self.config_flags.NAT_IS_OUT2IN_ONLY |
                   self.config_flags.NAT_IS_TWICE_NAT)
          self.nat44_add_static_mapping(self.pg0.remote_ip4,
                                        service_ip,
                                        80,
                                        80,
                                        proto=IP_PROTOS.tcp,
                                        flags=flags)
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          start_sessnum = len(sessions)
  
          # SYN packet out->in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=service_ip) /
               TCP(sport=33898, dport=80, flags="S"))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          tcp_port = p[TCP].sport
  
          # SYN + ACK packet in->out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
               TCP(sport=80, dport=tcp_port, flags="SA"))
          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=service_ip) /
               TCP(sport=33898, dport=80, flags="A"))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          # FIN packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
               TCP(sport=80, dport=tcp_port, flags="FA", seq=100, ack=300))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          # FIN+ACK packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=service_ip) /
               TCP(sport=33898, dport=80, flags="FA", seq=300, ack=101))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          # ACK packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
               TCP(sport=80, dport=tcp_port, flags="A", seq=101, ack=301))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4,
                                                       0)
          self.assertEqual(len(sessions) - start_sessnum, 0)
  
      def test_tcp_session_close_in(self):
          """ Close TCP session from inside network """
          self.tcp_port_out = 10505
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_TWICE_NAT
          self.nat44_add_static_mapping(self.pg0.remote_ip4,
                                        self.nat_addr,
                                        self.tcp_port_in,
                                        self.tcp_port_out,
                                        proto=IP_PROTOS.tcp,
                                        flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          start_sessnum = len(sessions)
  
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=2, icmp=5)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
  
          # FIN packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="FA", seq=100, ack=300))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          pkts = []
  
          # ACK packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="A", seq=300, ack=101))
          pkts.append(p)
  
          # FIN packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="FA", seq=300, ack=101))
          pkts.append(p)
  
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(2)
  
          # ACK packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="A", seq=101, ack=301))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 1)
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          out2in_drops = stats[0]
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          in2out_drops = stats[0]
  
          # extra FIN packet out -> in - this should be dropped
          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))
  
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.assert_nothing_captured()
  
          # extra ACK packet in -> out - this should be dropped
          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.assert_nothing_captured()
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - out2in_drops, 1)
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - in2out_drops, 1)
  
          self.sleep(3)
          # extra ACK packet in -> out - this will cause session to be wiped
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=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.assert_nothing_captured()
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 0)
  
      def test_tcp_session_close_out(self):
          """ Close TCP session from outside network """
          self.tcp_port_out = 10505
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_TWICE_NAT
          self.nat44_add_static_mapping(self.pg0.remote_ip4,
                                        self.nat_addr,
                                        self.tcp_port_in,
                                        self.tcp_port_out,
                                        proto=IP_PROTOS.tcp,
                                        flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          start_sessnum = len(sessions)
  
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=2, icmp=5)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
  
          # FIN packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="FA", seq=100, ack=300))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          # FIN+ACK packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="FA", seq=300, ack=101))
  
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          # ACK packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="A", seq=101, ack=301))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 1)
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          out2in_drops = stats[0]
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          in2out_drops = stats[0]
  
          # extra FIN packet out -> in - this should be dropped
          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))
  
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.assert_nothing_captured()
  
          # extra ACK packet in -> out - this should be dropped
          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.assert_nothing_captured()
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - out2in_drops, 1)
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - in2out_drops, 1)
  
          self.sleep(3)
          # extra ACK packet in -> out - this will cause session to be wiped
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=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.assert_nothing_captured()
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 0)
  
      def test_tcp_session_close_simultaneous(self):
          """ Close TCP session from inside network """
          self.tcp_port_out = 10505
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_TWICE_NAT
          self.nat44_add_static_mapping(self.pg0.remote_ip4,
                                        self.nat_addr,
                                        self.tcp_port_in,
                                        self.tcp_port_out,
                                        proto=IP_PROTOS.tcp,
                                        flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          start_sessnum = len(sessions)
  
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=2, icmp=5)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
  
          # FIN packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="FA", seq=100, ack=300))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          # FIN packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="FA", seq=300, ack=100))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          # ACK packet in -> out
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="A", seq=101, ack=301))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          # ACK packet out -> in
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                   flags="A", seq=301, ack=101))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.get_capture(1)
  
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 1)
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          out2in_drops = stats[0]
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          in2out_drops = stats[0]
  
          # extra FIN packet out -> in - this should be dropped
          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))
  
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg0.assert_nothing_captured()
  
          # extra ACK packet in -> out - this should be dropped
          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.assert_nothing_captured()
  
          stats = self.statistics.get_counter(
              '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - out2in_drops, 1)
          stats = self.statistics.get_counter(
              '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
          self.assertEqual(stats[0] - in2out_drops, 1)
  
          self.sleep(3)
          # extra ACK packet in -> out - this will cause session to be wiped
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=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.assert_nothing_captured()
          sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
          self.assertEqual(len(sessions) - start_sessnum, 0)
  
      def test_one_armed_nat44_static(self):
          """ One armed NAT44 and 1:1 NAPT asymmetrical rule """
          remote_host = self.pg4.remote_hosts[0]
          local_host = self.pg4.remote_hosts[1]
          external_port = 80
          local_port = 8080
          eh_port_in = 0
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.nat_addr, twice_nat=1)
          flags = (self.config_flags.NAT_IS_OUT2IN_ONLY |
                   self.config_flags.NAT_IS_TWICE_NAT)
          self.nat44_add_static_mapping(local_host.ip4, self.nat_addr,
                                        local_port, external_port,
                                        proto=IP_PROTOS.tcp, flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg4.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg4.sw_if_index,
              flags=flags, is_add=1)
  
          # from client to service
          p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) /
               IP(src=remote_host.ip4, dst=self.nat_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg4.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg4.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.pg4.remote_mac, dst=self.pg4.local_mac) /
               IP(src=local_host.ip4, dst=self.nat_addr) /
               TCP(sport=local_port, dport=eh_port_in))
          self.pg4.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg4.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_static_with_port_out2(self):
          """ 1:1 NAPT asymmetrical rule """
  
          external_port = 80
          local_port = 8080
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          flags = self.config_flags.NAT_IS_OUT2IN_ONLY
          self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
                                        local_port, external_port,
                                        proto=IP_PROTOS.tcp, flags=flags)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # from client to service
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # ICMP error
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               ICMP(type=11) / capture[0][IP])
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              self.assertEqual(p[IP].src, self.nat_addr)
              inner = p[IPerror]
              self.assertEqual(inner.dst, self.nat_addr)
              self.assertEqual(inner[TCPerror].dport, external_port)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12345))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # ICMP error
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               ICMP(type=11) / capture[0][IP])
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              self.assertEqual(p[IP].dst, self.pg0.remote_ip4)
              inner = p[IPerror]
              self.assertEqual(inner.src, self.pg0.remote_ip4)
              self.assertEqual(inner[TCPerror].sport, local_port)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client to server (no translation)
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) /
               TCP(sport=12346, dport=local_port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client (no translation)
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=local_port, dport=12346))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg0.remote_ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
      def test_output_feature(self):
          """ NAT44 interface output feature (in2out postrouting) """
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
  
          # in2out
          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)
  
          # out2in
          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_stateful_acl(self):
          """ NAT44 endpoint-dependent output feature works with stateful ACL """
          self.nat44_add_address(self.nat_addr)
          self.vapi.nat44_interface_add_del_output_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=self.config_flags.NAT_IS_INSIDE,
              is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              sw_if_index=self.pg1.sw_if_index,
              flags=self.config_flags.NAT_IS_OUTSIDE,
              is_add=1)
  
          # First ensure that the NAT is working sans ACL
  
          # send packets out2in, no sessions yet so packets should drop
          pkts_out2in = self.create_stream_out(self.pg1)
          self.send_and_assert_no_replies(self.pg1, pkts_out2in)
  
          # send packets into inside intf, ensure received via outside intf
          pkts_in2out = self.create_stream_in(self.pg0, self.pg1)
          capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1,
                                         len(pkts_in2out))
          self.verify_capture_out(capture)
  
          # send out2in again, with sessions created it should work now
          pkts_out2in = self.create_stream_out(self.pg1)
          capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0,
                                         len(pkts_out2in))
          self.verify_capture_in(capture, self.pg0)
  
          # Create an ACL blocking everything
          out2in_deny_rule = AclRule(is_permit=0)
          out2in_acl = VppAcl(self, rules=[out2in_deny_rule])
          out2in_acl.add_vpp_config()
  
          # create an ACL to permit/reflect everything
          in2out_reflect_rule = AclRule(is_permit=2)
          in2out_acl = VppAcl(self, rules=[in2out_reflect_rule])
          in2out_acl.add_vpp_config()
  
          # apply as input acl on interface and confirm it blocks everything
          acl_if = VppAclInterface(self, sw_if_index=self.pg1.sw_if_index,
                                   n_input=1, acls=[out2in_acl])
          acl_if.add_vpp_config()
          self.send_and_assert_no_replies(self.pg1, pkts_out2in)
  
          # apply output acl
          acl_if.acls = [out2in_acl, in2out_acl]
          acl_if.add_vpp_config()
          # send in2out to generate ACL state (NAT state was created earlier)
          capture = self.send_and_expect(self.pg0, pkts_in2out, self.pg1,
                                         len(pkts_in2out))
          self.verify_capture_out(capture)
  
          # send out2in again. ACL state exists so it should work now.
          # TCP packets with the syn flag set also need the ack flag
          for p in pkts_out2in:
              if p.haslayer(TCP) and p[TCP].flags & 0x02:
                  p[TCP].flags |= 0x10
          capture = self.send_and_expect(self.pg1, pkts_out2in, self.pg0,
                                         len(pkts_out2in))
          self.verify_capture_in(capture, self.pg0)
          self.logger.info(self.vapi.cli("show trace"))
  
      def test_multiple_vrf(self):
          """ Multiple VRF setup """
          external_addr = '1.2.3.4'
          external_port = 80
          local_port = 8080
          port = 0
  
          self.vapi.nat44_forwarding_enable_disable(enable=1)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_output_feature(
              is_add=1,
              sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg5.sw_if_index,
              is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg5.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg6.sw_if_index,
              is_add=1)
          flags = self.config_flags.NAT_IS_OUT2IN_ONLY
          self.nat44_add_static_mapping(self.pg5.remote_ip4, external_addr,
                                        local_port, external_port, vrf_id=1,
                                        proto=IP_PROTOS.tcp, flags=flags)
          self.nat44_add_static_mapping(
              self.pg0.remote_ip4,
              external_sw_if_index=self.pg0.sw_if_index,
              local_port=local_port,
              vrf_id=0,
              external_port=external_port,
              proto=IP_PROTOS.tcp,
              flags=flags
          )
  
          # from client to service (both VRF1)
          p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) /
               IP(src=self.pg6.remote_ip4, dst=external_addr) /
               TCP(sport=12345, dport=external_port))
          self.pg6.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg5.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service back to client (both VRF1)
          p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) /
               IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) /
               TCP(sport=local_port, dport=12345))
          self.pg5.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, external_addr)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # dynamic NAT from VRF1 to VRF0 (output-feature)
          p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) /
               IP(src=self.pg5.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=2345, dport=22))
          self.pg5.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertNotEqual(tcp.sport, 2345)
              self.assert_packet_checksums_valid(p)
              port = tcp.sport
          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.nat_addr) /
               TCP(sport=22, dport=port))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg5.remote_ip4)
              self.assertEqual(tcp.dport, 2345)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client VRF1 to service VRF0
          p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) /
               IP(src=self.pg6.remote_ip4, dst=self.pg0.local_ip4) /
               TCP(sport=12346, dport=external_port))
          self.pg6.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg0.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service VRF0 back to client VRF1
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) /
               TCP(sport=local_port, dport=12346))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg0.local_ip4)
              self.assertEqual(tcp.sport, external_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client VRF0 to service VRF1
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=external_addr) /
               TCP(sport=12347, dport=external_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg5.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from service VRF1 back to client VRF0
          p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) /
               IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) /
               TCP(sport=local_port, dport=12347))
          self.pg5.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.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client to server (both VRF1, no translation)
          p = (Ether(src=self.pg6.remote_mac, dst=self.pg6.local_mac) /
               IP(src=self.pg6.remote_ip4, dst=self.pg5.remote_ip4) /
               TCP(sport=12348, dport=local_port))
          self.pg6.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg5.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from server back to client (both VRF1, no translation)
          p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) /
               IP(src=self.pg5.remote_ip4, dst=self.pg6.remote_ip4) /
               TCP(sport=local_port, dport=12348))
          self.pg5.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg5.remote_ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client VRF1 to server VRF0 (no translation)
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) /
               TCP(sport=local_port, dport=12349))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg0.remote_ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from server VRF0 back to client VRF1 (no translation)
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg6.remote_ip4) /
               TCP(sport=local_port, dport=12349))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg6.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.pg0.remote_ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from client VRF0 to server VRF1 (no translation)
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg5.remote_ip4) /
               TCP(sport=12344, dport=local_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg5.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.dst, self.pg5.remote_ip4)
              self.assertEqual(tcp.dport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # from server VRF1 back to client VRF0 (no translation)
          p = (Ether(src=self.pg5.remote_mac, dst=self.pg5.local_mac) /
               IP(src=self.pg5.remote_ip4, dst=self.pg0.remote_ip4) /
               TCP(sport=local_port, dport=12344))
          self.pg5.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, self.pg5.remote_ip4)
              self.assertEqual(tcp.sport, local_port)
              self.assert_packet_checksums_valid(p)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_timeout(self):
          """ NAT44 session timeouts """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=240, icmp=5)
  
          max_sessions = 1000
          pkts = []
          for i in range(0, max_sessions):
              src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
              p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                   IP(src=src, dst=self.pg1.remote_ip4) /
                   ICMP(id=1025, type='echo-request'))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(max_sessions)
  
          sleep(10)
  
          pkts = []
          for i in range(0, max_sessions):
              src = "10.11.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
              p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                   IP(src=src, dst=self.pg1.remote_ip4) /
                   ICMP(id=1026, type='echo-request'))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(max_sessions)
  
          nsessions = 0
          users = self.vapi.nat44_user_dump()
          for user in users:
              nsessions = nsessions + user.nsessions
          self.assertLess(nsessions, 2 * max_sessions)
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_rst_timeout(self):
          """ NAT44 session RST timeouts """
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=5, icmp=60)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                   flags="R"))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
          sleep(6)
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in + 1, dport=self.tcp_external_port + 1,
                   flags="S"))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(1)
  
      def test_syslog_sess(self):
          """ Test syslog session creation and deletion """
          self.vapi.syslog_set_filter(
              self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO)
          self.vapi.syslog_set_sender(self.pg2.local_ip4, self.pg2.remote_ip4)
          self.nat44_add_address(self.nat_addr)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.get_capture(1)
          self.tcp_port_out = capture[0][TCP].sport
          capture = self.pg2.get_capture(1)
          self.verify_syslog_sess(capture[0][Raw].load)
  
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.nat44_add_address(self.nat_addr, is_add=0)
          capture = self.pg2.get_capture(1)
          self.verify_syslog_sess(capture[0][Raw].load, False)
  
      def tearDown(self):
          super(TestNAT44EndpointDependent, self).tearDown()
          if not self.vpp_dead:
              self.clear_nat44()
              self.vapi.cli("clear logging")
  
      def show_commands_at_teardown(self):
          self.logger.info(self.vapi.cli("show nat44 addresses"))
          self.logger.info(self.vapi.cli("show nat44 interfaces"))
          self.logger.info(self.vapi.cli("show nat44 static mappings"))
          self.logger.info(self.vapi.cli("show nat44 interface address"))
          self.logger.info(self.vapi.cli("show nat44 sessions detail"))
          self.logger.info(self.vapi.cli("show nat44 hash tables detail"))
          self.logger.info(self.vapi.cli("show nat timeouts"))
  
  
  class TestNAT44Out2InDPO(MethodHolder):
      """ NAT44 Test Cases using out2in DPO """
  
      @classmethod
      def setUpConstants(cls):
          super(TestNAT44Out2InDPO, cls).setUpConstants()
          cls.vpp_cmdline.extend(["nat", "{", "out2in dpo", "}"])
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT44Out2InDPO, cls).setUpClass()
          cls.vapi.cli("set log class nat level debug")
  
          cls.tcp_port_in = 6303
          cls.tcp_port_out = 6303
          cls.udp_port_in = 6304
          cls.udp_port_out = 6304
          cls.icmp_id_in = 6305
          cls.icmp_id_out = 6305
          cls.nat_addr = '10.0.0.3'
          cls.dst_ip4 = '192.168.70.1'
  
          cls.create_pg_interfaces(range(2))
  
          cls.pg0.admin_up()
          cls.pg0.config_ip4()
          cls.pg0.resolve_arp()
  
          cls.pg1.admin_up()
          cls.pg1.config_ip6()
          cls.pg1.resolve_ndp()
  
          r1 = VppIpRoute(cls, "::", 0,
                          [VppRoutePath(cls.pg1.remote_ip6,
                                        cls.pg1.sw_if_index)],
                          register=False)
          r1.add_vpp_config()
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT44Out2InDPO, cls).tearDownClass()
  
      def configure_xlat(self):
          self.dst_ip6_pfx = '1:2:3::'
          self.dst_ip6_pfx_n = socket.inet_pton(socket.AF_INET6,
                                                self.dst_ip6_pfx)
          self.dst_ip6_pfx_len = 96
          self.src_ip6_pfx = '4:5:6::'
          self.src_ip6_pfx_n = socket.inet_pton(socket.AF_INET6,
                                                self.src_ip6_pfx)
          self.src_ip6_pfx_len = 96
          self.vapi.map_add_domain(self.dst_ip6_pfx_n, self.dst_ip6_pfx_len,
                                   self.src_ip6_pfx_n, self.src_ip6_pfx_len,
                                   '\x00\x00\x00\x00', 0)
  
      @unittest.skip('Temporary disabled')
      def test_464xlat_ce(self):
          """ Test 464XLAT CE with NAT44 """
  
          nat_config = self.vapi.nat_show_config()
          self.assertEqual(1, nat_config.out2in_dpo)
  
          self.configure_xlat()
  
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_add_del_address_range(first_ip_address=self.nat_addr_n,
                                                last_ip_address=self.nat_addr_n,
                                                vrf_id=0xFFFFFFFF, is_add=1)
  
          out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx,
                                         self.dst_ip6_pfx_len)
          out_dst_ip6 = self.compose_ip6(self.nat_addr, self.src_ip6_pfx,
                                         self.src_ip6_pfx_len)
  
          try:
              pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4)
              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_ip6(capture, nat_ip=out_dst_ip6,
                                          dst_ip=out_src_ip6)
  
              pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6,
                                                out_dst_ip6)
              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)
          finally:
              self.vapi.nat44_interface_add_del_feature(
                  sw_if_index=self.pg0.sw_if_index,
                  flags=flags)
              self.vapi.nat44_add_del_address_range(
                  first_ip_address=self.nat_addr_n,
                  last_ip_address=self.nat_addr_n,
                  vrf_id=0xFFFFFFFF)
  
      @unittest.skip('Temporary disabled')
      def test_464xlat_ce_no_nat(self):
          """ Test 464XLAT CE without NAT44 """
  
          self.configure_xlat()
  
          out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx,
                                         self.dst_ip6_pfx_len)
          out_dst_ip6 = self.compose_ip6(self.pg0.remote_ip4, self.src_ip6_pfx,
                                         self.src_ip6_pfx_len)
  
          pkts = self.create_stream_in(self.pg0, self.pg1, self.dst_ip4)
          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_ip6(capture, dst_ip=out_src_ip6,
                                      nat_ip=out_dst_ip6, same_port=True)
  
          pkts = self.create_stream_out_ip6(self.pg1, out_src_ip6, out_dst_ip6)
          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)
  
  
  class TestDeterministicNAT(MethodHolder):
      """ Deterministic NAT Test Cases """
  
      @classmethod
      def setUpConstants(cls):
          super(TestDeterministicNAT, cls).setUpConstants()
          cls.vpp_cmdline.extend(["nat", "{", "deterministic", "}"])
  
      @classmethod
      def setUpClass(cls):
          super(TestDeterministicNAT, cls).setUpClass()
          cls.vapi.cli("set log class nat level debug")
  
          cls.tcp_port_in = 6303
          cls.tcp_external_port = 6303
          cls.udp_port_in = 6304
          cls.udp_external_port = 6304
          cls.icmp_id_in = 6305
          cls.nat_addr = '10.0.0.3'
  
          cls.create_pg_interfaces(range(3))
          cls.interfaces = list(cls.pg_interfaces)
  
          for i in cls.interfaces:
              i.admin_up()
              i.config_ip4()
              i.resolve_arp()
  
          cls.pg0.generate_remote_hosts(2)
          cls.pg0.configure_ipv4_neighbors()
  
      @classmethod
      def tearDownClass(cls):
          super(TestDeterministicNAT, cls).tearDownClass()
  
      def create_stream_in(self, in_if, out_if, ttl=64):
          """
          Create packet stream for inside network
  
          :param in_if: Inside interface
          :param out_if: Outside interface
          :param ttl: TTL of generated packets
          """
          pkts = []
          # TCP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
          pkts.append(p)
  
          # UDP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
               UDP(sport=self.udp_port_in, dport=self.udp_external_port))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
               IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) /
               ICMP(id=self.icmp_id_in, type='echo-request'))
          pkts.append(p)
  
          return pkts
  
      def create_stream_out(self, out_if, dst_ip=None, ttl=64):
          """
          Create packet stream for outside network
  
          :param out_if: Outside interface
          :param dst_ip: Destination IP address (Default use global NAT address)
          :param ttl: TTL of generated packets
          """
          if dst_ip is None:
              dst_ip = self.nat_addr
          pkts = []
          # TCP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               TCP(dport=self.tcp_port_out, sport=self.tcp_external_port))
          pkts.append(p)
  
          # UDP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               UDP(dport=self.udp_port_out, sport=self.udp_external_port))
          pkts.append(p)
  
          # ICMP
          p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) /
               IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) /
               ICMP(id=self.icmp_external_id, type='echo-reply'))
          pkts.append(p)
  
          return pkts
  
      def verify_capture_out(self, capture, nat_ip=None):
          """
          Verify captured packets on outside network
  
          :param capture: Captured packets
          :param nat_ip: Translated IP address (Default use global NAT address)
          :param same_port: Source port number is not translated (Default False)
          """
          if nat_ip is None:
              nat_ip = self.nat_addr
          for packet in capture:
              try:
                  self.assertEqual(packet[IP].src, nat_ip)
                  if packet.haslayer(TCP):
                      self.tcp_port_out = packet[TCP].sport
                  elif packet.haslayer(UDP):
                      self.udp_port_out = packet[UDP].sport
                  else:
                      self.icmp_external_id = packet[ICMP].id
              except:
                  self.logger.error(ppp("Unexpected or invalid packet "
                                        "(outside network):", packet))
                  raise
  
      def test_deterministic_mode(self):
          """ NAT plugin run deterministic mode """
          in_addr = '172.16.255.0'
          out_addr = '172.17.255.50'
          in_addr_t = '172.16.255.20'
          in_plen = 24
          out_plen = 32
  
          nat_config = self.vapi.nat_show_config()
          self.assertEqual(1, nat_config.deterministic)
  
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=in_addr,
                                        in_plen=in_plen, out_addr=out_addr,
                                        out_plen=out_plen)
  
          rep1 = self.vapi.nat_det_forward(in_addr_t)
          self.assertEqual(str(rep1.out_addr), out_addr)
          rep2 = self.vapi.nat_det_reverse(rep1.out_port_hi, out_addr)
  
          self.assertEqual(str(rep2.in_addr), in_addr_t)
  
          deterministic_mappings = self.vapi.nat_det_map_dump()
          self.assertEqual(len(deterministic_mappings), 1)
          dsm = deterministic_mappings[0]
          self.assertEqual(in_addr, str(dsm.in_addr))
          self.assertEqual(in_plen, dsm.in_plen)
          self.assertEqual(out_addr, str(dsm.out_addr))
          self.assertEqual(out_plen, dsm.out_plen)
  
          self.clear_nat_det()
          deterministic_mappings = self.vapi.nat_det_map_dump()
          self.assertEqual(len(deterministic_mappings), 0)
  
      def test_set_timeouts(self):
          """ Set deterministic NAT timeouts """
          timeouts_before = self.vapi.nat_get_timeouts()
  
          self.vapi.nat_set_timeouts(
              udp=timeouts_before.udp + 10,
              tcp_established=timeouts_before.tcp_established + 10,
              tcp_transitory=timeouts_before.tcp_transitory + 10,
              icmp=timeouts_before.icmp + 10)
  
          timeouts_after = self.vapi.nat_get_timeouts()
  
          self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
          self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
          self.assertNotEqual(timeouts_before.tcp_established,
                              timeouts_after.tcp_established)
          self.assertNotEqual(timeouts_before.tcp_transitory,
                              timeouts_after.tcp_transitory)
  
      def test_det_in(self):
          """ Deterministic NAT translation test (TCP, UDP, ICMP) """
  
          nat_ip = "10.0.0.10"
  
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
                                        in_plen=32,
                                        out_addr=socket.inet_aton(nat_ip),
                                        out_plen=32)
  
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # in2out
          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)
  
          # out2in
          pkts = self.create_stream_out(self.pg1, nat_ip)
          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 dump test
          sessions = self.vapi.nat_det_session_dump(self.pg0.remote_ip4)
          self.assertEqual(len(sessions), 3)
  
          # TCP session
          s = sessions[0]
          self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
          self.assertEqual(s.in_port, self.tcp_port_in)
          self.assertEqual(s.out_port, self.tcp_port_out)
          self.assertEqual(s.ext_port, self.tcp_external_port)
  
          # UDP session
          s = sessions[1]
          self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
          self.assertEqual(s.in_port, self.udp_port_in)
          self.assertEqual(s.out_port, self.udp_port_out)
          self.assertEqual(s.ext_port, self.udp_external_port)
  
          # ICMP session
          s = sessions[2]
          self.assertEqual(str(s.ext_addr), self.pg1.remote_ip4)
          self.assertEqual(s.in_port, self.icmp_id_in)
          self.assertEqual(s.out_port, self.icmp_external_id)
  
      def test_multiple_users(self):
          """ Deterministic NAT multiple users """
  
          nat_ip = "10.0.0.10"
          port_in = 80
          external_port = 6303
  
          host0 = self.pg0.remote_hosts[0]
          host1 = self.pg0.remote_hosts[1]
  
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=host0.ip4, in_plen=24,
                                        out_addr=socket.inet_aton(nat_ip),
                                        out_plen=32)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          # host0 to out
          p = (Ether(src=host0.mac, dst=self.pg0.local_mac) /
               IP(src=host0.ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=port_in, 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, nat_ip)
              self.assertEqual(ip.dst, self.pg1.remote_ip4)
              self.assertEqual(tcp.dport, external_port)
              port_out0 = tcp.sport
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # host1 to out
          p = (Ether(src=host1.mac, dst=self.pg0.local_mac) /
               IP(src=host1.ip4, dst=self.pg1.remote_ip4) /
               TCP(sport=port_in, 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, nat_ip)
              self.assertEqual(ip.dst, self.pg1.remote_ip4)
              self.assertEqual(tcp.dport, external_port)
              port_out1 = tcp.sport
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          dms = self.vapi.nat_det_map_dump()
          self.assertEqual(1, len(dms))
          self.assertEqual(2, dms[0].ses_num)
  
          # out to host0
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=nat_ip) /
               TCP(sport=external_port, dport=port_out0))
          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, self.pg1.remote_ip4)
              self.assertEqual(ip.dst, host0.ip4)
              self.assertEqual(tcp.dport, port_in)
              self.assertEqual(tcp.sport, external_port)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", p))
              raise
  
          # out to host1
          p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
               IP(src=self.pg1.remote_ip4, dst=nat_ip) /
               TCP(sport=external_port, dport=port_out1))
          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, self.pg1.remote_ip4)
              self.assertEqual(ip.dst, host1.ip4)
              self.assertEqual(tcp.dport, port_in)
              self.assertEqual(tcp.sport, external_port)
          except:
              self.logger.error(ppp("Unexpected or invalid packet", p))
              raise
  
          # session close api test
          self.vapi.nat_det_close_session_out(socket.inet_aton(nat_ip),
                                              port_out1,
                                              self.pg1.remote_ip4,
                                              external_port)
          dms = self.vapi.nat_det_map_dump()
          self.assertEqual(dms[0].ses_num, 1)
  
          self.vapi.nat_det_close_session_in(host0.ip4,
                                             port_in,
                                             self.pg1.remote_ip4,
                                             external_port)
          dms = self.vapi.nat_det_map_dump()
          self.assertEqual(dms[0].ses_num, 0)
  
      def test_tcp_session_close_detection_in(self):
          """ Deterministic NAT TCP session close from inside network """
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
                                        in_plen=32,
                                        out_addr=socket.inet_aton(self.nat_addr),
                                        out_plen=32)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          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="F"))
              self.pg0.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg1.get_capture(1)
  
              pkts = []
  
              # ACK packet out -> in
              p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
                   IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                       flags="A"))
              pkts.append(p)
  
              # FIN packet out -> in
              p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
                   IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                       flags="F"))
              pkts.append(p)
  
              self.pg1.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg0.get_capture(2)
  
              # ACK packet in -> out
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
                   TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                       flags="A"))
              self.pg0.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg1.get_capture(1)
  
              # Check if deterministic NAT44 closed the session
              dms = self.vapi.nat_det_map_dump()
              self.assertEqual(0, dms[0].ses_num)
          except:
              self.logger.error("TCP session termination failed")
              raise
  
      def test_tcp_session_close_detection_out(self):
          """ Deterministic NAT TCP session close from outside network """
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
                                        in_plen=32,
                                        out_addr=socket.inet_aton(self.nat_addr),
                                        out_plen=32)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
  
          # close the session from outside
          try:
              # FIN packet out -> in
              p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
                   IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                       flags="F"))
              self.pg1.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg0.get_capture(1)
  
              pkts = []
  
              # ACK packet in -> out
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
                   TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                       flags="A"))
              pkts.append(p)
  
              # ACK packet in -> out
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
                   TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
                       flags="F"))
              pkts.append(p)
  
              self.pg0.add_stream(pkts)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg1.get_capture(2)
  
              # ACK packet out -> in
              p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
                   IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                   TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
                       flags="A"))
              self.pg1.add_stream(p)
              self.pg_enable_capture(self.pg_interfaces)
              self.pg_start()
              self.pg0.get_capture(1)
  
              # Check if deterministic NAT44 closed the session
              dms = self.vapi.nat_det_map_dump()
              self.assertEqual(0, dms[0].ses_num)
          except:
              self.logger.error("TCP session termination failed")
              raise
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_timeout(self):
          """ Deterministic NAT session timeouts """
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
                                        in_plen=32,
                                        out_addr=socket.inet_aton(self.nat_addr),
                                        out_plen=32)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
  
          self.initiate_tcp_session(self.pg0, self.pg1)
          self.vapi.nat_set_timeouts(udp=5, tcp_established=5, tcp_transitory=5,
                                     icmp=5)
          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))
          sleep(15)
  
          dms = self.vapi.nat_det_map_dump()
          self.assertEqual(0, dms[0].ses_num)
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_limit_per_user(self):
          """ Deterministic NAT maximum sessions per user limit """
          self.vapi.nat_det_add_del_map(is_add=1, in_addr=self.pg0.remote_ip4,
                                        in_plen=32,
                                        out_addr=socket.inet_aton(self.nat_addr),
                                        out_plen=32)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg0.sw_if_index,
              flags=flags, is_add=1)
          self.vapi.nat44_interface_add_del_feature(
              sw_if_index=self.pg1.sw_if_index,
              is_add=1)
          self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4,
                                       src_address=self.pg2.local_ip4,
                                       path_mtu=512,
                                       template_interval=10)
          self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739,
                                             enable=1)
  
          pkts = []
          for port in range(1025, 2025):
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
                   UDP(sport=port, dport=port))
              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))
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
               UDP(sport=3001, dport=3002))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg1.assert_nothing_captured()
  
          # verify ICMP error packet
          capture = self.pg0.get_capture(1)
          p = capture[0]
          self.assertTrue(p.haslayer(ICMP))
          icmp = p[ICMP]
          self.assertEqual(icmp.type, 3)
          self.assertEqual(icmp.code, 1)
          self.assertTrue(icmp.haslayer(IPerror))
          inner_ip = icmp[IPerror]
          self.assertEqual(inner_ip[UDPerror].sport, 3001)
          self.assertEqual(inner_ip[UDPerror].dport, 3002)
  
          dms = self.vapi.nat_det_map_dump()
  
          self.assertEqual(1000, dms[0].ses_num)
  
          # verify IPFIX logging
          self.vapi.ipfix_flush()
          sleep(1)
          capture = self.pg2.get_capture(2)
          ipfix = IPFIXDecoder()
          # first load template
          for p in capture:
              self.assertTrue(p.haslayer(IPFIX))
              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_entries_per_user(data,
                                                         1000,
                                                         self.pg0.remote_ip4)
  
      def clear_nat_det(self):
          """
          Clear deterministic NAT configuration.
          """
          self.vapi.nat_ipfix_enable_disable(domain_id=1, src_port=4739,
                                             enable=0)
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=240, icmp=60)
          deterministic_mappings = self.vapi.nat_det_map_dump()
          for dsm in deterministic_mappings:
              self.vapi.nat_det_add_del_map(is_add=0, in_addr=dsm.in_addr,
                                            in_plen=dsm.in_plen,
                                            out_addr=dsm.out_addr,
                                            out_plen=dsm.out_plen)
  
          interfaces = self.vapi.nat44_interface_dump()
          for intf in interfaces:
              self.vapi.nat44_interface_add_del_feature(
                  sw_if_index=intf.sw_if_index,
                  flags=intf.flags)
  
      def tearDown(self):
          super(TestDeterministicNAT, self).tearDown()
          if not self.vpp_dead:
              self.clear_nat_det()
  
      def show_commands_at_teardown(self):
          self.logger.info(self.vapi.cli("show nat44 interfaces"))
          self.logger.info(self.vapi.cli("show nat timeouts"))
          self.logger.info(
              self.vapi.cli("show nat44 deterministic mappings"))
          self.logger.info(
              self.vapi.cli("show nat44 deterministic sessions"))
  
  
  class TestNAT64(MethodHolder):
      """ NAT64 Test Cases """
  
      @classmethod
      def setUpConstants(cls):
          super(TestNAT64, cls).setUpConstants()
          cls.vpp_cmdline.extend(["nat", "{", "nat64 bib hash buckets 128",
                                  "nat64 st hash buckets 256", "}"])
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT64, cls).setUpClass()
  
          cls.tcp_port_in = 6303
          cls.tcp_port_out = 6303
          cls.udp_port_in = 6304
          cls.udp_port_out = 6304
          cls.icmp_id_in = 6305
          cls.icmp_id_out = 6305
          cls.tcp_external_port = 80
          cls.nat_addr = '10.0.0.3'
          cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr)
          cls.vrf1_id = 10
          cls.vrf1_nat_addr = '10.0.10.3'
          cls.ipfix_src_port = 4739
          cls.ipfix_domain_id = 1
  
          cls.create_pg_interfaces(range(6))
          cls.ip6_interfaces = list(cls.pg_interfaces[0:1])
          cls.ip6_interfaces.append(cls.pg_interfaces[2])
          cls.ip4_interfaces = list(cls.pg_interfaces[1:2])
  
          cls.vapi.ip_table_add_del(is_add=1,
                                    table={'table_id': cls.vrf1_id,
                                           'is_ip6': 1})
  
          cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id)
  
          cls.pg0.generate_remote_hosts(2)
  
          for i in cls.ip6_interfaces:
              i.admin_up()
              i.config_ip6()
              i.configure_ipv6_neighbors()
  
          for i in cls.ip4_interfaces:
              i.admin_up()
              i.config_ip4()
              i.resolve_arp()
  
          cls.pg3.admin_up()
          cls.pg3.config_ip4()
          cls.pg3.resolve_arp()
          cls.pg3.config_ip6()
          cls.pg3.configure_ipv6_neighbors()
  
          cls.pg5.admin_up()
          cls.pg5.config_ip6()
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT64, cls).tearDownClass()
  
      def test_nat64_inside_interface_handles_neighbor_advertisement(self):
          """ NAT64 inside interface handles Neighbor Advertisement """
  
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg5.sw_if_index)
  
          # Try to send ping
          ping = (Ether(dst=self.pg5.local_mac, src=self.pg5.remote_mac) /
                  IPv6(src=self.pg5.remote_ip6, dst=self.pg5.local_ip6) /
                  ICMPv6EchoRequest())
          pkts = [ping]
          self.pg5.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Wait for Neighbor Solicitation
          capture = self.pg5.get_capture(len(pkts))
          packet = capture[0]
          try:
              self.assertEqual(packet[IPv6].src, self.pg5.local_ip6)
              self.assertEqual(packet.haslayer(ICMPv6ND_NS), 1)
              tgt = packet[ICMPv6ND_NS].tgt
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # Send Neighbor Advertisement
          p = (Ether(dst=self.pg5.local_mac, src=self.pg5.remote_mac) /
               IPv6(src=self.pg5.remote_ip6, dst=self.pg5.local_ip6) /
               ICMPv6ND_NA(tgt=tgt) /
               ICMPv6NDOptDstLLAddr(lladdr=self.pg5.remote_mac))
          pkts = [p]
          self.pg5.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Try to send ping again
          pkts = [ping]
          self.pg5.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
  
          # Wait for ping reply
          capture = self.pg5.get_capture(len(pkts))
          packet = capture[0]
          try:
              self.assertEqual(packet[IPv6].src, self.pg5.local_ip6)
              self.assertEqual(packet[IPv6].dst, self.pg5.remote_ip6)
              self.assertEqual(packet.haslayer(ICMPv6EchoReply), 1)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_pool(self):
          """ Add/delete address to NAT64 pool """
          nat_addr = '1.2.3.4'
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=nat_addr,
                                                  end_addr=nat_addr,
                                                  vrf_id=0xFFFFFFFF, is_add=1)
  
          addresses = self.vapi.nat64_pool_addr_dump()
          self.assertEqual(len(addresses), 1)
          self.assertEqual(str(addresses[0].address), nat_addr)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=nat_addr,
                                                  end_addr=nat_addr,
                                                  vrf_id=0xFFFFFFFF, is_add=0)
  
          addresses = self.vapi.nat64_pool_addr_dump()
          self.assertEqual(len(addresses), 0)
  
      def test_interface(self):
          """ Enable/disable NAT64 feature on the interface """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          interfaces = self.vapi.nat64_interface_dump()
          self.assertEqual(len(interfaces), 2)
          pg0_found = False
          pg1_found = False
          for intf in interfaces:
              if intf.sw_if_index == self.pg0.sw_if_index:
                  self.assertEqual(intf.flags, self.config_flags.NAT_IS_INSIDE)
                  pg0_found = True
              elif intf.sw_if_index == self.pg1.sw_if_index:
                  self.assertEqual(intf.flags, self.config_flags.NAT_IS_OUTSIDE)
                  pg1_found = True
          self.assertTrue(pg0_found)
          self.assertTrue(pg1_found)
  
          features = self.vapi.cli("show interface features pg0")
          self.assertIn('nat64-in2out', features)
          features = self.vapi.cli("show interface features pg1")
          self.assertIn('nat64-out2in', features)
  
          self.vapi.nat64_add_del_interface(is_add=0, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=0, flags=flags,
                                            sw_if_index=self.pg1.sw_if_index)
  
          interfaces = self.vapi.nat64_interface_dump()
          self.assertEqual(len(interfaces), 0)
  
      def test_static_bib(self):
          """ Add/delete static BIB entry """
          in_addr = '2001:db8:85a3::8a2e:370:7334'
          out_addr = '10.1.1.3'
          in_port = 1234
          out_port = 5678
          proto = IP_PROTOS.tcp
  
          self.vapi.nat64_add_del_static_bib(i_addr=in_addr, o_addr=out_addr,
                                             i_port=in_port, o_port=out_port,
                                             proto=proto, vrf_id=0, is_add=1)
          bib = self.vapi.nat64_bib_dump(proto=IP_PROTOS.tcp)
          static_bib_num = 0
          for bibe in bib:
              if bibe.flags & self.config_flags.NAT_IS_STATIC:
                  static_bib_num += 1
                  self.assertEqual(str(bibe.i_addr), in_addr)
                  self.assertEqual(str(bibe.o_addr), out_addr)
                  self.assertEqual(bibe.i_port, in_port)
                  self.assertEqual(bibe.o_port, out_port)
          self.assertEqual(static_bib_num, 1)
          bibs = self.statistics.get_counter('/nat64/total-bibs')
          self.assertEqual(bibs[0][0], 1)
  
          self.vapi.nat64_add_del_static_bib(i_addr=in_addr, o_addr=out_addr,
                                             i_port=in_port, o_port=out_port,
                                             proto=proto, vrf_id=0, is_add=0)
          bib = self.vapi.nat64_bib_dump(proto=IP_PROTOS.tcp)
          static_bib_num = 0
          for bibe in bib:
              if bibe.flags & self.config_flags.NAT_IS_STATIC:
                  static_bib_num += 1
          self.assertEqual(static_bib_num, 0)
          bibs = self.statistics.get_counter('/nat64/total-bibs')
          self.assertEqual(bibs[0][0], 0)
  
      def test_set_timeouts(self):
          """ Set NAT64 timeouts """
          # verify default values
          timeouts = self.vapi.nat_get_timeouts()
          self.assertEqual(timeouts.udp, 300)
          self.assertEqual(timeouts.icmp, 60)
          self.assertEqual(timeouts.tcp_transitory, 240)
          self.assertEqual(timeouts.tcp_established, 7440)
  
          # set and verify custom values
          self.vapi.nat_set_timeouts(udp=200, tcp_established=7450,
                                     tcp_transitory=250, icmp=30)
          timeouts = self.vapi.nat_get_timeouts()
          self.assertEqual(timeouts.udp, 200)
          self.assertEqual(timeouts.icmp, 30)
          self.assertEqual(timeouts.tcp_transitory, 250)
          self.assertEqual(timeouts.tcp_established, 7450)
  
      def test_dynamic(self):
          """ NAT64 dynamic translation test """
          self.tcp_port_in = 6303
          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(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          # in2out
          tcpn = self.statistics.get_err_counter('/err/nat64-in2out/TCP packets')
          udpn = self.statistics.get_err_counter('/err/nat64-in2out/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat64-in2out/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat64-in2out/good in2out packets processed')
  
          pkts = self.create_stream_in_ip6(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.nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          err = self.statistics.get_err_counter('/err/nat64-in2out/TCP packets')
          self.assertEqual(err - tcpn, 1)
          err = self.statistics.get_err_counter('/err/nat64-in2out/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter('/err/nat64-in2out/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat64-in2out/good in2out packets processed')
          self.assertEqual(err - totaln, 3)
  
          # out2in
          tcpn = self.statistics.get_err_counter('/err/nat64-out2in/TCP packets')
          udpn = self.statistics.get_err_counter('/err/nat64-out2in/UDP packets')
          icmpn = self.statistics.get_err_counter(
              '/err/nat64-out2in/ICMP packets')
          totaln = self.statistics.get_err_counter(
              '/err/nat64-out2in/good out2in packets processed')
  
          pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
          self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
  
          err = self.statistics.get_err_counter('/err/nat64-out2in/TCP packets')
          self.assertEqual(err - tcpn, 2)
          err = self.statistics.get_err_counter('/err/nat64-out2in/UDP packets')
          self.assertEqual(err - udpn, 1)
          err = self.statistics.get_err_counter('/err/nat64-out2in/ICMP packets')
          self.assertEqual(err - icmpn, 1)
          err = self.statistics.get_err_counter(
              '/err/nat64-out2in/good out2in packets processed')
          self.assertEqual(err - totaln, 4)
  
          bibs = self.statistics.get_counter('/nat64/total-bibs')
          self.assertEqual(bibs[0][0], 3)
          sessions = self.statistics.get_counter('/nat64/total-sessions')
          self.assertEqual(sessions[0][0], 3)
  
          # in2out
          pkts = self.create_stream_in_ip6(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.nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          # out2in
          pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
          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_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
  
          ses_num_end = self.nat64_get_ses_num()
  
          self.assertEqual(ses_num_end - ses_num_start, 3)
  
          # tenant with specific VRF
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.vrf1_nat_addr,
                                                  end_addr=self.vrf1_nat_addr,
                                                  vrf_id=self.vrf1_id, is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg2.sw_if_index)
  
          pkts = self.create_stream_in_ip6(self.pg2, self.pg1)
          self.pg2.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.vrf1_nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg2.get_capture(len(pkts))
          self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg2.remote_ip6)
  
      def test_static(self):
          """ NAT64 static translation test """
          self.tcp_port_in = 60303
          self.udp_port_in = 60304
          self.icmp_id_in = 60305
          self.tcp_port_out = 60303
          self.udp_port_out = 60304
          self.icmp_id_out = 60305
  
          ses_num_start = self.nat64_get_ses_num()
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          self.vapi.nat64_add_del_static_bib(i_addr=self.pg0.remote_ip6,
                                             o_addr=self.nat_addr,
                                             i_port=self.tcp_port_in,
                                             o_port=self.tcp_port_out,
                                             proto=IP_PROTOS.tcp, vrf_id=0,
                                             is_add=1)
          self.vapi.nat64_add_del_static_bib(i_addr=self.pg0.remote_ip6,
                                             o_addr=self.nat_addr,
                                             i_port=self.udp_port_in,
                                             o_port=self.udp_port_out,
                                             proto=IP_PROTOS.udp, vrf_id=0,
                                             is_add=1)
          self.vapi.nat64_add_del_static_bib(i_addr=self.pg0.remote_ip6,
                                             o_addr=self.nat_addr,
                                             i_port=self.icmp_id_in,
                                             o_port=self.icmp_id_out,
                                             proto=IP_PROTOS.icmp, vrf_id=0,
                                             is_add=1)
  
          # in2out
          pkts = self.create_stream_in_ip6(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.nat_addr,
                                  dst_ip=self.pg1.remote_ip4, same_port=True)
  
          # out2in
          pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
          self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6)
  
          ses_num_end = self.nat64_get_ses_num()
  
          self.assertEqual(ses_num_end - ses_num_start, 3)
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_session_timeout(self):
          """ NAT64 session timeout """
          self.icmp_id_in = 1234
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat_set_timeouts(udp=300, tcp_established=5,
                                     tcp_transitory=5,
                                     icmp=5)
  
          pkts = self.create_stream_in_ip6(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))
  
          ses_num_before_timeout = self.nat64_get_ses_num()
  
          sleep(15)
  
          # ICMP and TCP session after timeout
          ses_num_after_timeout = self.nat64_get_ses_num()
          self.assertEqual(ses_num_before_timeout - ses_num_after_timeout, 2)
  
      def test_icmp_error(self):
          """ NAT64 ICMP Error message translation """
          self.tcp_port_in = 6303
          self.udp_port_in = 6304
          self.icmp_id_in = 6305
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          # send some packets to create sessions
          pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture_ip4 = self.pg1.get_capture(len(pkts))
          self.verify_capture_out(capture_ip4,
                                  nat_ip=self.nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture_ip6 = self.pg0.get_capture(len(pkts))
          ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
          self.verify_capture_in_ip6(capture_ip6, ip[IPv6].src,
                                     self.pg0.remote_ip6)
  
          # in2out
          pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                  IPv6(src=self.pg0.remote_ip6, dst=ip[IPv6].src) /
                  ICMPv6DestUnreach(code=1) /
                  packet[IPv6] for packet in capture_ip6]
          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[IP].src, self.nat_addr)
                  self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
                  self.assertEqual(packet[ICMP].type, 3)
                  self.assertEqual(packet[ICMP].code, 13)
                  inner = packet[IPerror]
                  self.assertEqual(inner.src, self.pg1.remote_ip4)
                  self.assertEqual(inner.dst, self.nat_addr)
                  self.assert_packet_checksums_valid(packet)
                  if inner.haslayer(TCPerror):
                      self.assertEqual(inner[TCPerror].dport, self.tcp_port_out)
                  elif inner.haslayer(UDPerror):
                      self.assertEqual(inner[UDPerror].dport, self.udp_port_out)
                  else:
                      self.assertEqual(inner[ICMPerror].id, self.icmp_id_out)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # out2in
          pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
                  IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
                  ICMP(type=3, code=13) /
                  packet[IP] for packet in capture_ip4]
          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, ip.src)
                  self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6)
                  icmp = packet[ICMPv6DestUnreach]
                  self.assertEqual(icmp.code, 1)
                  inner = icmp[IPerror6]
                  self.assertEqual(inner.src, self.pg0.remote_ip6)
                  self.assertEqual(inner.dst, ip.src)
                  self.assert_icmpv6_checksum_valid(packet)
                  if inner.haslayer(TCPerror):
                      self.assertEqual(inner[TCPerror].sport, self.tcp_port_in)
                  elif inner.haslayer(UDPerror):
                      self.assertEqual(inner[UDPerror].sport, self.udp_port_in)
                  else:
                      self.assertEqual(inner[ICMPv6EchoRequest].id,
                                       self.icmp_id_in)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
      def test_hairpinning(self):
          """ NAT64 hairpinning """
  
          client = self.pg0.remote_hosts[0]
          server = self.pg0.remote_hosts[1]
          server_tcp_in_port = 22
          server_tcp_out_port = 4022
          server_udp_in_port = 23
          server_udp_out_port = 4023
          client_tcp_in_port = 1234
          client_udp_in_port = 1235
          client_tcp_out_port = 0
          client_udp_out_port = 0
          ip = IPv6(src=''.join(['64:ff9b::', self.nat_addr]))
          nat_addr_ip6 = ip.src
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          self.vapi.nat64_add_del_static_bib(i_addr=server.ip6n,
                                             o_addr=self.nat_addr,
                                             i_port=server_tcp_in_port,
                                             o_port=server_tcp_out_port,
                                             proto=IP_PROTOS.tcp, vrf_id=0,
                                             is_add=1)
          self.vapi.nat64_add_del_static_bib(i_addr=server.ip6n,
                                             o_addr=self.nat_addr,
                                             i_port=server_udp_in_port,
                                             o_port=server_udp_out_port,
                                             proto=IP_PROTOS.udp, vrf_id=0,
                                             is_add=1)
  
          # client to server
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=client.ip6, dst=nat_addr_ip6) /
               TCP(sport=client_tcp_in_port, dport=server_tcp_out_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=client.ip6, dst=nat_addr_ip6) /
               UDP(sport=client_udp_in_port, dport=server_udp_out_port))
          pkts.append(p)
          self.pg0.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, 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)
                      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_udp_out_port = packet[UDP].sport
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # server to client
          pkts = []
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=server.ip6, dst=nat_addr_ip6) /
               TCP(sport=server_tcp_in_port, dport=client_tcp_out_port))
          pkts.append(p)
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=server.ip6, dst=nat_addr_ip6) /
               UDP(sport=server_udp_in_port, dport=client_udp_out_port))
          pkts.append(p)
          self.pg0.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, 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)
                  else:
                      self.assertEqual(packet[UDP].sport, server_udp_out_port)
                      self.assertEqual(packet[UDP].dport, client_udp_in_port)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
          # ICMP error
          pkts = []
          pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                  IPv6(src=client.ip6, dst=nat_addr_ip6) /
                  ICMPv6DestUnreach(code=1) /
                  packet[IPv6] for packet in capture]
          self.pg0.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, nat_addr_ip6)
                  self.assertEqual(packet[IPv6].dst, server.ip6)
                  icmp = packet[ICMPv6DestUnreach]
                  self.assertEqual(icmp.code, 1)
                  inner = icmp[IPerror6]
                  self.assertEqual(inner.src, server.ip6)
                  self.assertEqual(inner.dst, nat_addr_ip6)
                  self.assert_packet_checksums_valid(packet)
                  if inner.haslayer(TCPerror):
                      self.assertEqual(inner[TCPerror].sport, server_tcp_in_port)
                      self.assertEqual(inner[TCPerror].dport,
                                       client_tcp_out_port)
                  else:
                      self.assertEqual(inner[UDPerror].sport, server_udp_in_port)
                      self.assertEqual(inner[UDPerror].dport,
                                       client_udp_out_port)
              except:
                  self.logger.error(ppp("Unexpected or invalid packet:", packet))
                  raise
  
      def test_prefix(self):
          """ NAT64 Network-Specific Prefix """
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.vrf1_nat_addr,
                                                  end_addr=self.vrf1_nat_addr,
                                                  vrf_id=self.vrf1_id, is_add=1)
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg2.sw_if_index)
  
          # Add global prefix
          global_pref64 = "2001:db8::"
          global_pref64_len = 32
          global_pref64_str = "{}/{}".format(global_pref64, global_pref64_len)
          self.vapi.nat64_add_del_prefix(prefix=global_pref64_str, vrf_id=0,
                                         is_add=1)
  
          prefix = self.vapi.nat64_prefix_dump()
          self.assertEqual(len(prefix), 1)
          self.assertEqual(str(prefix[0].prefix), global_pref64_str)
          self.assertEqual(prefix[0].vrf_id, 0)
  
          # Add tenant specific prefix
          vrf1_pref64 = "2001:db8:122:300::"
          vrf1_pref64_len = 56
          vrf1_pref64_str = "{}/{}".format(vrf1_pref64, vrf1_pref64_len)
          self.vapi.nat64_add_del_prefix(prefix=vrf1_pref64_str,
                                         vrf_id=self.vrf1_id, is_add=1)
  
          prefix = self.vapi.nat64_prefix_dump()
          self.assertEqual(len(prefix), 2)
  
          # Global prefix
          pkts = self.create_stream_in_ip6(self.pg0,
                                           self.pg1,
                                           pref=global_pref64,
                                           plen=global_pref64_len)
          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.nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg0.get_capture(len(pkts))
          dst_ip = self.compose_ip6(self.pg1.remote_ip4,
                                    global_pref64,
                                    global_pref64_len)
          self.verify_capture_in_ip6(capture, dst_ip, self.pg0.remote_ip6)
  
          # Tenant specific prefix
          pkts = self.create_stream_in_ip6(self.pg2,
                                           self.pg1,
                                           pref=vrf1_pref64,
                                           plen=vrf1_pref64_len)
          self.pg2.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.vrf1_nat_addr,
                                  dst_ip=self.pg1.remote_ip4)
  
          pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg2.get_capture(len(pkts))
          dst_ip = self.compose_ip6(self.pg1.remote_ip4,
                                    vrf1_pref64,
                                    vrf1_pref64_len)
          self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6)
  
      def test_unknown_proto(self):
          """ NAT64 translate packet with unknown protocol """
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
          remote_ip6 = self.compose_ip6(self.pg1.remote_ip4, '64:ff9b::', 96)
  
          # in2out
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=self.pg0.remote_ip6, dst=remote_ip6) /
               TCP(sport=self.tcp_port_in, dport=20))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
  
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=self.pg0.remote_ip6, dst=remote_ip6, nh=47) /
               GRE() /
               IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IP].src, self.nat_addr)
              self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
              self.assertEqual(packet.haslayer(GRE), 1)
              self.assert_packet_checksums_valid(packet)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # out2in
          p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
               IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg1.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IPv6].src, remote_ip6)
              self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6)
              self.assertEqual(packet[IPv6].nh, 47)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_hairpinning_unknown_proto(self):
          """ NAT64 translate packet with unknown protocol - hairpinning """
  
          client = self.pg0.remote_hosts[0]
          server = self.pg0.remote_hosts[1]
          server_tcp_in_port = 22
          server_tcp_out_port = 4022
          client_tcp_in_port = 1234
          client_tcp_out_port = 1235
          server_nat_ip = "10.0.0.100"
          client_nat_ip = "10.0.0.110"
          server_nat_ip6 = self.compose_ip6(server_nat_ip, '64:ff9b::', 96)
          client_nat_ip6 = self.compose_ip6(client_nat_ip, '64:ff9b::', 96)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=server_nat_ip,
                                                  end_addr=client_nat_ip,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          self.vapi.nat64_add_del_static_bib(i_addr=server.ip6n,
                                             o_addr=server_nat_ip,
                                             i_port=server_tcp_in_port,
                                             o_port=server_tcp_out_port,
                                             proto=IP_PROTOS.tcp, vrf_id=0,
                                             is_add=1)
  
          self.vapi.nat64_add_del_static_bib(i_addr=server.ip6n,
                                             o_addr=server_nat_ip, i_port=0,
                                             o_port=0,
                                             proto=IP_PROTOS.gre, vrf_id=0,
                                             is_add=1)
  
          self.vapi.nat64_add_del_static_bib(i_addr=client.ip6n,
                                             o_addr=client_nat_ip,
                                             i_port=client_tcp_in_port,
                                             o_port=client_tcp_out_port,
                                             proto=IP_PROTOS.tcp, vrf_id=0,
                                             is_add=1)
  
          # client to server
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=client.ip6, dst=server_nat_ip6) /
               TCP(sport=client_tcp_in_port, dport=server_tcp_out_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
  
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=client.ip6, dst=server_nat_ip6, nh=IP_PROTOS.gre) /
               GRE() /
               IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IPv6].src, client_nat_ip6)
              self.assertEqual(packet[IPv6].dst, server.ip6)
              self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
          # server to client
          p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src=server.ip6, dst=client_nat_ip6, nh=IP_PROTOS.gre) /
               GRE() /
               IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) /
               TCP(sport=1234, dport=1234))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg0.get_capture(1)
          packet = p[0]
          try:
              self.assertEqual(packet[IPv6].src, server_nat_ip6)
              self.assertEqual(packet[IPv6].dst, client.ip6)
              self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre)
          except:
              self.logger.error(ppp("Unexpected or invalid packet:", packet))
              raise
  
      def test_one_armed_nat64(self):
          """ One armed NAT64 """
          external_port = 0
          remote_host_ip6 = self.compose_ip6(self.pg3.remote_ip4,
                                             '64:ff9b::',
                                             96)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg3.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg3.sw_if_index)
  
          # in2out
          p = (Ether(src=self.pg3.remote_mac, dst=self.pg3.local_mac) /
               IPv6(src=self.pg3.remote_ip6, dst=remote_host_ip6) /
               TCP(sport=12345, dport=80))
          self.pg3.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              ip = p[IP]
              tcp = p[TCP]
              self.assertEqual(ip.src, self.nat_addr)
              self.assertEqual(ip.dst, self.pg3.remote_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.pg3.remote_mac, dst=self.pg3.local_mac) /
               IP(src=self.pg3.remote_ip4, dst=self.nat_addr) /
               TCP(sport=80, dport=external_port))
          self.pg3.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          capture = self.pg3.get_capture(1)
          p = capture[0]
          try:
              ip = p[IPv6]
              tcp = p[TCP]
              self.assertEqual(ip.src, remote_host_ip6)
              self.assertEqual(ip.dst, self.pg3.remote_ip6)
              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_frag_in_order(self):
          """ NAT64 translate fragments arriving in order """
          self.tcp_port_in = random.randint(1025, 65535)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          # in2out
          data = b'a' * 200
          pkts = self.create_stream_frag_ip6(self.pg0, self.pg1.remote_ip4,
                                             self.tcp_port_in, 20, data)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg1.get_capture(len(pkts))
          p = self.reass_frags_and_verify(frags,
                                          self.nat_addr,
                                          self.pg1.remote_ip4)
          self.assertEqual(p[TCP].dport, 20)
          self.assertNotEqual(p[TCP].sport, self.tcp_port_in)
          self.tcp_port_out = p[TCP].sport
          self.assertEqual(data, p[Raw].load)
  
          # out2in
          data = b"A" * 4 + b"b" * 16 + b"C" * 3
          pkts = self.create_stream_frag(self.pg1,
                                         self.nat_addr,
                                         20,
                                         self.tcp_port_out,
                                         data)
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          self.logger.debug(ppc("Captured:", frags))
          src = self.compose_ip6(self.pg1.remote_ip4, '64:ff9b::', 96)
          p = self.reass_frags_and_verify_ip6(frags, src, self.pg0.remote_ip6)
          self.assertEqual(p[TCP].sport, 20)
          self.assertEqual(p[TCP].dport, self.tcp_port_in)
          self.assertEqual(data, p[Raw].load)
  
      def test_reass_hairpinning(self):
          """ NAT64 fragments hairpinning """
          data = b'a' * 200
          server = self.pg0.remote_hosts[1]
          server_in_port = random.randint(1025, 65535)
          server_out_port = random.randint(1025, 65535)
          client_in_port = random.randint(1025, 65535)
          ip = IPv6(src=''.join(['64:ff9b::', self.nat_addr]))
          nat_addr_ip6 = ip.src
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          # add static BIB entry for server
          self.vapi.nat64_add_del_static_bib(i_addr=server.ip6n,
                                             o_addr=self.nat_addr,
                                             i_port=server_in_port,
                                             o_port=server_out_port,
                                             proto=IP_PROTOS.tcp, vrf_id=0,
                                             is_add=1)
  
          # send packet from host to server
          pkts = self.create_stream_frag_ip6(self.pg0,
                                             self.nat_addr,
                                             client_in_port,
                                             server_out_port,
                                             data)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          self.logger.debug(ppc("Captured:", frags))
          p = self.reass_frags_and_verify_ip6(frags, nat_addr_ip6, server.ip6)
          self.assertNotEqual(p[TCP].sport, client_in_port)
          self.assertEqual(p[TCP].dport, server_in_port)
          self.assertEqual(data, p[Raw].load)
  
      def test_frag_out_of_order(self):
          """ NAT64 translate fragments arriving out of order """
          self.tcp_port_in = random.randint(1025, 65535)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          # in2out
          data = b'a' * 200
          pkts = self.create_stream_frag_ip6(self.pg0, self.pg1.remote_ip4,
                                             self.tcp_port_in, 20, data)
          pkts.reverse()
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg1.get_capture(len(pkts))
          p = self.reass_frags_and_verify(frags,
                                          self.nat_addr,
                                          self.pg1.remote_ip4)
          self.assertEqual(p[TCP].dport, 20)
          self.assertNotEqual(p[TCP].sport, self.tcp_port_in)
          self.tcp_port_out = p[TCP].sport
          self.assertEqual(data, p[Raw].load)
  
          # out2in
          data = b"A" * 4 + b"B" * 16 + b"C" * 3
          pkts = self.create_stream_frag(self.pg1,
                                         self.nat_addr,
                                         20,
                                         self.tcp_port_out,
                                         data)
          pkts.reverse()
          self.pg1.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          frags = self.pg0.get_capture(len(pkts))
          src = self.compose_ip6(self.pg1.remote_ip4, '64:ff9b::', 96)
          p = self.reass_frags_and_verify_ip6(frags, src, self.pg0.remote_ip6)
          self.assertEqual(p[TCP].sport, 20)
          self.assertEqual(p[TCP].dport, self.tcp_port_in)
          self.assertEqual(data, p[Raw].load)
  
      def test_interface_addr(self):
          """ Acquire NAT64 pool addresses from interface """
          self.vapi.nat64_add_del_interface_addr(
              is_add=1,
              sw_if_index=self.pg4.sw_if_index)
  
          # no address in NAT64 pool
          addresses = self.vapi.nat44_address_dump()
          self.assertEqual(0, len(addresses))
  
          # configure interface address and check NAT64 address pool
          self.pg4.config_ip4()
          addresses = self.vapi.nat64_pool_addr_dump()
          self.assertEqual(len(addresses), 1)
  
          self.assertEqual(str(addresses[0].address),
                           self.pg4.local_ip4)
  
          # remove interface address and check NAT64 address pool
          self.pg4.unconfig_ip4()
          addresses = self.vapi.nat64_pool_addr_dump()
          self.assertEqual(0, len(addresses))
  
      @unittest.skipUnless(running_extended_tests, "part of extended tests")
      def test_ipfix_max_bibs_sessions(self):
          """ IPFIX logging maximum session and BIB entries exceeded """
          max_bibs = 1280
          max_sessions = 2560
          remote_host_ip6 = self.compose_ip6(self.pg1.remote_ip4,
                                             '64:ff9b::',
                                             96)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
  
          pkts = []
          src = ""
          for i in range(0, max_bibs):
              src = "fd01:aa::%x" % (i)
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IPv6(src=src, dst=remote_host_ip6) /
                   TCP(sport=12345, dport=80))
              pkts.append(p)
              p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
                   IPv6(src=src, dst=remote_host_ip6) /
                   TCP(sport=12345, dport=22))
              pkts.append(p)
          self.pg0.add_stream(pkts)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.get_capture(max_sessions)
  
          self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4,
                                       src_address=self.pg3.local_ip4,
                                       path_mtu=512,
                                       template_interval=10)
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=1)
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IPv6(src=src, dst=remote_host_ip6) /
               TCP(sport=12345, dport=25))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
          sleep(1)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(7)
          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_sessions(data, max_sessions)
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IPv6(src=self.pg0.remote_ip6, dst=remote_host_ip6) /
               TCP(sport=12345, dport=80))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.pg1.assert_nothing_captured()
          sleep(1)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(1)
          # verify events in data set
          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(Data):
                  data = ipfix.decode_data_set(p.getlayer(Set))
                  self.verify_ipfix_max_bibs(data, max_bibs)
  
      def test_ipfix_bib_ses(self):
          """ IPFIX logging NAT64 BIB/session create and delete events """
          self.tcp_port_in = random.randint(1025, 65535)
          remote_host_ip6 = self.compose_ip6(self.pg1.remote_ip4,
                                             '64:ff9b::',
                                             96)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4,
                                       src_address=self.pg3.local_ip4,
                                       path_mtu=512,
                                       template_interval=10)
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=1)
  
          # Create
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IPv6(src=self.pg0.remote_ip6, dst=remote_host_ip6) /
               TCP(sport=self.tcp_port_in, dport=25))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
          self.tcp_port_out = p[0][TCP].sport
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(8)
          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))
                  if scapy.compat.orb(data[0][230]) == 10:
                      self.verify_ipfix_bib(data, 1, self.pg0.remote_ip6)
                  elif scapy.compat.orb(data[0][230]) == 6:
                      self.verify_ipfix_nat64_ses(data,
                                                  1,
                                                  self.pg0.remote_ip6,
                                                  self.pg1.remote_ip4,
                                                  25)
                  else:
                      self.logger.error(ppp("Unexpected or invalid packet: ", p))
  
          # Delete
          self.pg_enable_capture(self.pg_interfaces)
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=0)
          self.vapi.ipfix_flush()
          capture = self.pg3.get_capture(2)
          # verify events in data set
          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(Data):
                  data = ipfix.decode_data_set(p.getlayer(Set))
                  if scapy.compat.orb(data[0][230]) == 11:
                      self.verify_ipfix_bib(data, 0, self.pg0.remote_ip6)
                  elif scapy.compat.orb(data[0][230]) == 7:
                      self.verify_ipfix_nat64_ses(data,
                                                  0,
                                                  self.pg0.remote_ip6,
                                                  self.pg1.remote_ip4,
                                                  25)
                  else:
                      self.logger.error(ppp("Unexpected or invalid packet: ", p))
  
      def test_syslog_sess(self):
          """ Test syslog session creation and deletion """
          self.tcp_port_in = random.randint(1025, 65535)
          remote_host_ip6 = self.compose_ip6(self.pg1.remote_ip4,
                                             '64:ff9b::',
                                             96)
  
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=1)
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat64_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat64_add_del_interface(is_add=1, flags=0,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.syslog_set_filter(
              self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO)
          self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4)
  
          p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
               IPv6(src=self.pg0.remote_ip6, dst=remote_host_ip6) /
               TCP(sport=self.tcp_port_in, dport=self.tcp_external_port))
          self.pg0.add_stream(p)
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          p = self.pg1.get_capture(1)
          self.tcp_port_out = p[0][TCP].sport
          capture = self.pg3.get_capture(1)
          self.verify_syslog_sess(capture[0][Raw].load, is_ip6=True)
  
          self.pg_enable_capture(self.pg_interfaces)
          self.pg_start()
          self.vapi.nat64_add_del_pool_addr_range(start_addr=self.nat_addr,
                                                  end_addr=self.nat_addr,
                                                  vrf_id=0xFFFFFFFF,
                                                  is_add=0)
          capture = self.pg3.get_capture(1)
          self.verify_syslog_sess(capture[0][Raw].load, False, True)
  
      def nat64_get_ses_num(self):
          """
          Return number of active NAT64 sessions.
          """
          st = self.vapi.nat64_st_dump(proto=255)
          return len(st)
  
      def clear_nat64(self):
          """
          Clear NAT64 configuration.
          """
          self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id,
                                             src_port=self.ipfix_src_port,
                                             enable=0)
          self.ipfix_src_port = 4739
          self.ipfix_domain_id = 1
  
          self.vapi.syslog_set_filter(
              self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_EMERG)
  
          self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
                                     tcp_transitory=240, icmp=60)
  
          interfaces = self.vapi.nat64_interface_dump()
          for intf in interfaces:
              self.vapi.nat64_add_del_interface(is_add=0, flags=intf.flags,
                                                sw_if_index=intf.sw_if_index)
  
          bib = self.vapi.nat64_bib_dump(proto=255)
          for bibe in bib:
              if bibe.flags & self.config_flags.NAT_IS_STATIC:
                  self.vapi.nat64_add_del_static_bib(i_addr=bibe.i_addr,
                                                     o_addr=bibe.o_addr,
                                                     i_port=bibe.i_port,
                                                     o_port=bibe.o_port,
                                                     proto=bibe.proto,
                                                     vrf_id=bibe.vrf_id,
                                                     is_add=0)
  
          adresses = self.vapi.nat64_pool_addr_dump()
          for addr in adresses:
              self.vapi.nat64_add_del_pool_addr_range(start_addr=addr.address,
                                                      end_addr=addr.address,
                                                      vrf_id=addr.vrf_id,
                                                      is_add=0)
  
          prefixes = self.vapi.nat64_prefix_dump()
          for prefix in prefixes:
              self.vapi.nat64_add_del_prefix(prefix=str(prefix.prefix),
                                             vrf_id=prefix.vrf_id, is_add=0)
  
          bibs = self.statistics.get_counter('/nat64/total-bibs')
          self.assertEqual(bibs[0][0], 0)
          sessions = self.statistics.get_counter('/nat64/total-sessions')
          self.assertEqual(sessions[0][0], 0)
  
      def tearDown(self):
          super(TestNAT64, self).tearDown()
          if not self.vpp_dead:
              self.clear_nat64()
  
      def show_commands_at_teardown(self):
          self.logger.info(self.vapi.cli("show nat64 pool"))
          self.logger.info(self.vapi.cli("show nat64 interfaces"))
          self.logger.info(self.vapi.cli("show nat64 prefix"))
          self.logger.info(self.vapi.cli("show nat64 bib all"))
          self.logger.info(self.vapi.cli("show nat64 session table all"))
  
  
  class TestNAT66(MethodHolder):
      """ NAT66 Test Cases """
  
      @classmethod
      def setUpClass(cls):
          super(TestNAT66, cls).setUpClass()
  
          cls.nat_addr = 'fd01:ff::2'
  
          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()
  
      @classmethod
      def tearDownClass(cls):
          super(TestNAT66, cls).tearDownClass()
  
      def test_static(self):
          """ 1:1 NAT66 test """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat66_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat66_add_del_interface(is_add=1,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat66_add_del_static_mapping(
              local_ip_address=self.pg0.remote_ip6,
              external_ip_address=self.nat_addr,
              is_add=1)
  
          # 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 """
          flags = self.config_flags.NAT_IS_INSIDE
          self.vapi.nat66_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg0.sw_if_index)
          self.vapi.nat66_add_del_interface(is_add=1, flags=flags,
                                            sw_if_index=self.pg1.sw_if_index)
          self.vapi.nat66_add_del_static_mapping(
              local_ip_address=self.pg0.remote_ip6,
              external_ip_address=self.nat_addr,
              is_add=1)
  
          # 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(is_add=0, flags=intf.flags,
                                                sw_if_index=intf.sw_if_index)
  
          static_mappings = self.vapi.nat66_static_mapping_dump()
          for sm in static_mappings:
              self.vapi.nat66_add_del_static_mapping(
                  local_ip_address=sm.local_ip_address,
                  external_ip_address=sm.external_ip_address, vrf_id=sm.vrf_id,
                  is_add=0)
  
      def tearDown(self):
          super(TestNAT66, self).tearDown()
          self.clear_nat66()
  
      def show_commands_at_teardown(self):
          self.logger.info(self.vapi.cli("show nat66 interfaces"))
          self.logger.info(self.vapi.cli("show nat66 static mappings"))
  
  
  if __name__ == '__main__':
      unittest.main(testRunner=VppTestRunner)
  