/* * Copyright (c) 2017 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define vl_typedefs /* define message structures */ #include #undef vl_typedefs #define vl_endianfun /* define message structures */ #include #undef vl_endianfun /* instantiate all the print functions we know about */ #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) #define vl_printfun #include #undef vl_printfun #include dns_main_t dns_main; static int dns_cache_clear (dns_main_t * dm) { dns_cache_entry_t *ep; if (dm->is_enabled == 0) return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED; dns_cache_lock (dm); /* *INDENT-OFF* */ pool_foreach (ep, dm->entries, ({ vec_free (ep->name); vec_free (ep->pending_requests); })); /* *INDENT-ON* */ pool_free (dm->entries); hash_free (dm->cache_entry_by_name); dm->cache_entry_by_name = hash_create_string (0, sizeof (uword)); vec_free (dm->unresolved_entries); dns_cache_unlock (dm); return 0; } static int dns_enable_disable (dns_main_t * dm, int is_enable) { vlib_thread_main_t *tm = &vlib_thread_main; u32 n_vlib_mains = tm->n_vlib_mains; vlib_main_t *vm = dm->vlib_main; /* Create the resolver process if not done already */ vnet_dns_create_resolver_process (dm); if (is_enable) { if (vec_len (dm->ip4_name_servers) == 0 && (vec_len (dm->ip6_name_servers) == 0)) return VNET_API_ERROR_NO_NAME_SERVERS; if (dm->udp_ports_registered == 0) { udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index, 1 /* is_ip4 */ ); udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index, 0 /* is_ip4 */ ); udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index, 1 /* is_ip4 */ ); udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index, 0 /* is_ip4 */ ); dm->udp_ports_registered = 1; } if (dm->cache_entry_by_name == 0) { if (n_vlib_mains > 1) dm->cache_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES); dm->cache_entry_by_name = hash_create_string (0, sizeof (uword)); } dm->is_enabled = 1; } else { dns_cache_clear (dm); dm->is_enabled = 0; } return 0; } static void vl_api_dns_enable_disable_t_handler (vl_api_dns_enable_disable_t * mp) { vl_api_dns_enable_disable_reply_t *rmp; dns_main_t *dm = &dns_main; int rv; rv = dns_enable_disable (dm, mp->enable); REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY); } static int dns6_name_server_add_del (dns_main_t * dm, u8 * server_address_as_u8, int is_add) { int i; ip6_address_t *ap; if (is_add) { /* Already there? done... */ for (i = 0; i < vec_len (dm->ip6_name_servers); i++) { if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8, sizeof (ip6_address_t))) return 0; } vec_add2 (dm->ip6_name_servers, ap, 1); clib_memcpy (ap, server_address_as_u8, sizeof (*ap)); } else { for (i = 0; i < vec_len (dm->ip6_name_servers); i++) { if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8, sizeof (ip6_address_t))) { vec_delete (dm->ip6_name_servers, 1, i); return 0; } } return VNET_API_ERROR_NAME_SERVER_NOT_FOUND; } return 0; } static int dns4_name_server_add_del (dns_main_t * dm, u8 * server_address_as_u8, int is_add) { int i; ip4_address_t *ap; if (is_add) { /* Already there? done... */ for (i = 0; i < vec_len (dm->ip4_name_servers); i++) { if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8, sizeof (ip4_address_t))) return 0; } vec_add2 (dm->ip4_name_servers, ap, 1); clib_memcpy (ap, server_address_as_u8, sizeof (*ap)); } else { for (i = 0; i < vec_len (dm->ip4_name_servers); i++) { if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8, sizeof (ip4_address_t))) { vec_delete (dm->ip4_name_servers, 1, i); return 0; } } return VNET_API_ERROR_NAME_SERVER_NOT_FOUND; } return 0; } static void vl_api_dns_name_server_add_del_t_handler (vl_api_dns_name_server_add_del_t * mp) { dns_main_t *dm = &dns_main; vl_api_dns_name_server_add_del_reply_t *rmp; int rv; if (mp->is_ip6) rv = dns6_name_server_add_del (dm, mp->server_address, mp->is_add); else rv = dns4_name_server_add_del (dm, mp->server_address, mp->is_add); REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY); } void vnet_dns_send_dns4_request (dns_main_t * dm, dns_cache_entry_t * ep, ip4_address_t * server) { vlib_main_t *vm = dm->vlib_main; f64 now = vlib_time_now (vm); u32 bi; vlib_buffer_t *b; ip4_header_t *ip; fib_prefix_t prefix; fib_node_index_t fei; u32 sw_if_index, fib_index; udp_header_t *udp; ip4_main_t *im4 = &ip4_main; ip_lookup_main_t *lm4 = &im4->lookup_main; ip_interface_address_t *ia = 0; ip4_address_t *src_address; u8 *dns_request; vlib_frame_t *f; u32 *to_next; ASSERT (ep->dns_request); /* Find a FIB path to the server */ clib_memcpy (&prefix.fp_addr.ip4, server, sizeof (*server)); prefix.fp_proto = FIB_PROTOCOL_IP4; prefix.fp_len = 32; fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ ); if (fib_index == (u32) ~ 0) { clib_warning ("no fib table"); return; } fei = fib_table_lookup (fib_index, &prefix); /* Couldn't find route to destination. Bail out. */ if (fei == FIB_NODE_INDEX_INVALID) { clib_warning ("no route to DNS server"); return; } sw_if_index = fib_entry_get_resolving_interface (fei); if (sw_if_index == ~0) { clib_warning ("route to %U exists, fei %d, get_resolving_interface returned" " ~0", format_ip4_address, &prefix.fp_addr, fei); return; } /* *INDENT-OFF* */ foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnumbered */, ({ src_address = ip_interface_address_get_address (lm4, ia); goto found_src_address; })); /* *INDENT-ON* */ clib_warning ("FIB BUG"); return; found_src_address: /* Go get a buffer */ if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1) return; b = vlib_get_buffer (vm, bi); b->current_length = sizeof (ip4_header_t) + sizeof (udp_header_t) + vec_len (ep->dns_request); b->total_length_not_including_first_buffer = 0; b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED; vnet_buffer (b)->sw_if_index[VLIB_RX] = 0; /* "local0" */ vnet_buffer (b)->sw_if_index[VLIB_TX] = 0; /* default VRF for now */ ip = vlib_buffer_get_current (b); clib_memset (ip, 0, sizeof (*ip)); udp = (udp_header_t *) (ip + 1); clib_memset (udp, 0, sizeof (*udp)); dns_request = (u8 *) (udp + 1); /* IP header */ ip->ip_version_and_header_length = 0x45; ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b)); ip->ttl = 255; ip->protocol = IP_PROTOCOL_UDP; ip->src_address.as_u32 = src_address->as_u32; ip->dst_address.as_u32 = server->as_u32; ip->checksum = ip4_header_checksum (ip); /* UDP header */ udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply); udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns); udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) + vec_len (ep->dns_request)); udp->checksum = 0; /* The actual DNS request */ clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request)); /* Ship it to ip4_lookup */ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); to_next = vlib_frame_vector_args (f); to_next[0] = bi; f->n_vectors = 1; vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); ep->retry_timer = now + 2.0; } void vnet_dns_send_dns6_request (dns_main_t * dm, dns_cache_entry_t * ep, ip6_address_t * server) { vlib_main_t *vm = dm->vlib_main; f64 now = vlib_time_now (vm); u32 bi; vlib_buffer_t *b; ip6_header_t *ip; fib_prefix_t prefix; fib_node_index_t fei; u32 sw_if_index, fib_index; udp_header_t *udp; ip6_main_t *im6 = &ip6_main; ip_lookup_main_t *lm6 = &im6->lookup_main; ip_interface_address_t *ia = 0; ip6_address_t *src_address; u8 *dns_request; vlib_frame_t *f; u32 *to_next; int junk __attribute__ ((unused)); ASSERT (ep->dns_request); /* Find a FIB path to the server */ clib_memcpy (&prefix.fp_addr, server, sizeof (*server)); prefix.fp_proto = FIB_PROTOCOL_IP6; prefix.fp_len = 32; fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ ); if (fib_index == (u32) ~ 0) { clib_warning ("no fib table"); return; } fei = fib_table_lookup (fib_index, &prefix); /* Couldn't find route to destination. Bail out. */ if (fei == FIB_NODE_INDEX_INVALID) { clib_warning ("no route to DNS server"); } sw_if_index = fib_entry_get_resolving_interface (fei); /* *INDENT-OFF* */ foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnumbered */, ({ src_address = ip_interface_address_get_address (lm6, ia); goto found_src_address; })); /* *INDENT-ON* */ clib_warning ("FIB BUG"); return; found_src_address: /* Go get a buffer */ if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1) return; b = vlib_get_buffer (vm, bi); b->current_length = sizeof (ip6_header_t) + sizeof (udp_header_t) + vec_len (ep->dns_request); b->total_length_not_including_first_buffer = 0; b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED; ip = vlib_buffer_get_current (b); clib_memset (ip, 0, sizeof (*ip)); udp = (udp_header_t *) (ip + 1); clib_memset (udp, 0, sizeof (*udp)); dns_request = (u8 *) (udp + 1); /* IP header */ ip->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (0x6 << 28); ip->payload_length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) - sizeof (ip6_header_t)); ip->hop_limit = 255; ip->protocol = IP_PROTOCOL_UDP; clib_memcpy (&ip->src_address, src_address, sizeof (ip6_address_t)); clib_memcpy (&ip->dst_address, server, sizeof (ip6_address_t)); /* UDP header */ udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply); udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns); udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) + vec_len (ep->dns_request)); udp->checksum = 0; udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &junk); /* The actual DNS request */ clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request)); /* Ship it to ip6_lookup */ f = vlib_get_frame_to_node (vm, ip6_lookup_node.index); to_next = vlib_frame_vector_args (f); to_next[0] = bi; f->n_vectors = 1; ep->retry_timer = now + 2.0; } /** * Translate "foo.com" into "0x3 f o o 0x3 c o m 0x0" * A historical / hysterical micro-TLV scheme. DGMS. */ u8 * name_to_labels (u8 * name) { int i; int last_label_index; u8 *rv; rv = vec_dup (name); /* punch in space for the first length */ vec_insert (rv, 1, 0); last_label_index = 0; i = 1; while (i < vec_len (rv)) { if (rv[i] == '.') { rv[last_label_index] = (i - last_label_index) - 1; if ((i - last_label_index) > 63) clib_warning ("stupid name, label length %d", i - last_label_index); last_label_index = i; rv[i] = 0; } i++; } /* Set the last real label length */ rv[last_label_index] = (i - last_label_index) - 1; /* * Add a [sic] NULL root label. Otherwise, the name parser can't figure out * where to stop. */ vec_add1 (rv, 0); return rv; } /** * arc-function for the above. * Translate "0x3 f o o 0x3 c o m 0x0" into "foo.com" * Produces a non-NULL-terminated u8 *vector. %v format is your friend. */ u8 * vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here) { u8 *reply = 0; u16 offset; u8 len; int i; *parse_from_here = 0; /* chase initial pointer? */ if ((label[0] & 0xC0) == 0xC0) { *parse_from_here = label + 2; offset = ((label[0] & 0x3f) << 8) + label[1]; label = full_text + offset; } len = *label++; while (len) { for (i = 0; i < len; i++) vec_add1 (reply, *label++); /* chase pointer? */ if ((label[0] & 0xC0) == 0xC0) { *parse_from_here = label + 2; offset = ((label[0] & 0x3f) << 8) + label[1]; label = full_text + offset; } len = *label++; if (len) vec_add1 (reply, '.'); } if (*parse_from_here == 0) *parse_from_here = label; return reply; } void vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep) { dns_header_t *h; dns_query_t *qp; u16 tmp; u8 *request, *name_copy; u32 qp_offset; /* This can easily happen if sitting in GDB, etc. */ if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID || ep->server_fails > 1) return; /* Construct the dns request, if we haven't been here already */ if (vec_len (ep->dns_request) == 0) { /* * Start with the variadic portion of the exercise. * Turn the name into a set of DNS "labels". Max length * per label is 63, enforce that. */ request = name_to_labels (ep->name); name_copy = vec_dup (request); qp_offset = vec_len (request); /* * At least when testing against "known good" DNS servers: * it turns out that sending 2x requests - one for an A-record * and another for a AAAA-record - seems to work better than * sending a DNS_TYPE_ALL request. */ /* Add space for the query header */ vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1); qp = (dns_query_t *) (request + qp_offset); qp->type = clib_host_to_net_u16 (DNS_TYPE_A); qp->class = clib_host_to_net_u16 (DNS_CLASS_IN); qp++; clib_memcpy (qp, name_copy, vec_len (name_copy)); qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy)); vec_free (name_copy); qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA); qp->class = clib_host_to_net_u16 (DNS_CLASS_IN); /* Punch in space for the dns_header_t */ vec_insert (request, sizeof (dns_header_t), 0); h = (dns_header_t *) request; /* Transaction ID = pool index */ h->id = clib_host_to_net_u16 (ep - dm->entries); /* Ask for a recursive lookup */ tmp = DNS_RD | DNS_OPCODE_QUERY; h->flags = clib_host_to_net_u16 (tmp); h->qdcount = clib_host_to_net_u16 (2); h->nscount = 0; h->arcount = 0; ep->dns_request = request; } /* Work out which server / address family we're going to use */ /* Retry using current server */ if (ep->retry_count++ < DNS_RETRIES_PER_SERVER) { if (ep->server_af == 1 /* ip6 */ ) { if (vec_len (dm->ip6_name_servers)) { vnet_dns_send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor); goto out; } else ep->server_af = 0; } if (vec_len (dm->ip4_name_servers)) { vnet_dns_send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor); goto out; } } else /* switch to a new server */ { ep->retry_count = 1; ep->server_rotor++; if (ep->server_af == 1 /* ip6 */ ) { if (ep->server_rotor >= vec_len (dm->ip6_name_servers)) { ep->server_rotor = 0; ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1; } } else { if (ep->server_rotor >= vec_len (dm->ip4_name_servers)) { ep->server_rotor = 0; ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0; } } } if (ep->server_af == 1 /* ip6 */ ) vnet_dns_send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor); else vnet_dns_send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor); out: vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index, DNS_RESOLVER_EVENT_PENDING, 0); } int vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index
#!/usr/bin/env python3

import socket
import unittest
import struct
import random

from framework import tag_fixme_vpp_workers
from framework import VppTestCase, VppTestRunner

import scapy.compat
from scapy.layers.inet import IP, TCP, UDP, ICMP
from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
from scapy.layers.inet6 import (
    IPv6,
    ICMPv6EchoRequest,
    ICMPv6EchoReply,
    ICMPv6ND_NS,
    ICMPv6ND_NA,
    ICMPv6NDOptDstLLAddr,
    fragment6,
)
from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment
from scapy.layers.l2 import Ether, ARP, GRE
from scapy.data import IP_PROTOS
from scapy.packet import bind_layers, Raw
from util import ppp
from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
from time import sleep
from util import ip4_range
from vpp_papi import mac_pton
from syslog_rfc5424_parser import SyslogMessage, ParseError
from syslog_rfc5424_parser.constants import SyslogFacility, SyslogSeverity
from io import BytesIO
from vpp_papi import VppEnum
from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathType
from vpp_neighbor import VppNeighbor
from scapy.all import (
    bind_layers,
    Packet,
    ByteEnumField,
    ShortField,
    IPField,
    IntField,
    LongField,
    XByteField,
    FlagsField,
    FieldLenField,
    PacketListField,
)
from ipaddress import IPv6Network


@tag_fixme_vpp_workers
class TestDSlite(VppTestCase):
    """DS-Lite Test Cases"""

    @classmethod
    def setUpClass(cls):
        super(TestDSlite, cls).setUpClass()

        try:
            cls.nat_addr = "10.0.0.3"

            cls.create_pg_interfaces(range(3))
            cls.pg0.admin_up()
            cls.pg0.config_ip4()
            cls.pg0.resolve_arp()
            cls.pg1.admin_up()
            cls.pg1.config_ip6()
            cls.pg1.generate_remote_hosts(2)
            cls.pg1.configure_ipv6_neighbors()
            cls.pg2.admin_up()
            cls.pg2.config_ip4()
            cls.pg2.resolve_arp()

        except Exception:
            super(TestDSlite, cls).tearDownClass()
            raise

    @classmethod
    def tearDownClass(cls):
        super(TestDSlite, cls).tearDownClass()

    def verify_syslog_apmadd(self, data, isaddr, isport, xsaddr, xsport, sv6enc, proto):
        message = data.decode("utf-8")
        try:
            message = SyslogMessage.parse(message)
        except ParseError as e:
            self.logger.error(e)
        else:
            self.assertEqual(message.severity, SyslogSeverity.info)
            self.assertEqual(message.appname, "NAT")
            self.assertEqual(message.msgid, "APMADD")
            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"), isaddr)
            self.assertEqual(sd_params.get("ISPORT"), "%d" % isport)
            self.assertEqual(sd_params.get("XATYP"), "IPv4")
            self.assertEqual(sd_params.get("XSADDR"), xsaddr)
            self.assertEqual(sd_params.get("XSPORT"), "%d" % xsport)
            self.assertEqual(sd_params.get("PROTO"), "%d" % proto)
            self.assertTrue(sd_params.get("SSUBIX") is not None)
            self.assertEqual(sd_params.get("SV6ENC"), sv6enc)

    def test_dslite(self):
        """Test DS-Lite"""
        self.vapi.dslite_add_del_pool_addr_range(
            start_addr=self.nat_addr, end_addr=self.nat_addr, is_add=1
        )
        aftr_ip4 = "192.0.0.1"
        aftr_ip6 = "2001:db8:85a3::8a2e:370:1"
        self.vapi.dslite_set_aftr_addr(ip4_addr=aftr_ip4, ip6_addr=aftr_ip6)
        self.vapi.syslog_set_sender(self.pg2.local_ip4, self.pg2.remote_ip4)

        # UDP
        p = (
            Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
            / IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[0].ip6)
            / IP(dst=self.pg0.remote_ip4, src="192.168.1.1")
            / UDP(sport=20000, dport=10000)
        )
        self.pg1.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg0.get_capture(1)
        capture = capture[0]
        self.assertFalse(capture.haslayer(IPv6))
        self.assertEqual(capture[IP].src, self.nat_addr)
        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
        self.assertNotEqual(capture[UDP].sport, 20000)
        self.assertEqual(capture[UDP].dport, 10000)
        self.assert_packet_checksums_valid(capture)
        out_port = capture[UDP].sport
        capture = self.pg2.get_capture(1)
        self.verify_syslog_apmadd(
            capture[0][Raw].load,
            "192.168.1.1",
            20000,
            self.nat_addr,
            out_port,
            self.pg1.remote_hosts[0].ip6,
            IP_PROTOS.udp,
        )

        p = (
            Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
            / IP(dst=self.nat_addr, src=self.pg0.remote_ip4)
            / UDP(sport=10000, dport=out_port)
        )
        self.pg0.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg1.get_capture(1)
        capture = capture[0]
        self.assertEqual(capture[IPv6].src, aftr_ip6)
        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[0].ip6)
        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
        self.assertEqual(capture[IP].dst, "192.168.1.1")
        self.assertEqual(capture[UDP].sport, 10000)
        self.assertEqual(capture[UDP].dport, 20000)
        self.assert_packet_checksums_valid(capture)

        # TCP
        p = (
            Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
            / IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6)
            / IP(dst=self.pg0.remote_ip4, src="192.168.1.1")
            / TCP(sport=20001, dport=10001)
        )
        self.pg1.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg0.get_capture(1)
        capture = capture[0]
        self.assertFalse(capture.haslayer(IPv6))
        self.assertEqual(capture[IP].src, self.nat_addr)
        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
        self.assertNotEqual(capture[TCP].sport, 20001)
        self.assertEqual(capture[TCP].dport, 10001)
        self.assert_packet_checksums_valid(capture)
        out_port = capture[TCP].sport

        p = (
            Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
            / IP(dst=self.nat_addr, src=self.pg0.remote_ip4)
            / TCP(sport=10001, dport=out_port)
        )
        self.pg0.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg1.get_capture(1)
        capture = capture[0]
        self.assertEqual(capture[IPv6].src, aftr_ip6)
        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
        self.assertEqual(capture[IP].dst, "192.168.1.1")
        self.assertEqual(capture[TCP].sport, 10001)
        self.assertEqual(capture[TCP].dport, 20001)
        self.assert_packet_checksums_valid(capture)

        # ICMP
        p = (
            Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
            / IPv6(dst=aftr_ip6, src=self.pg1.remote_hosts[1].ip6)
            / IP(dst=self.pg0.remote_ip4, src="192.168.1.1")
            / ICMP(id=4000, type="echo-request")
        )
        self.pg1.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg0.get_capture(1)
        capture = capture[0]
        self.assertFalse(capture.haslayer(IPv6))
        self.assertEqual(capture[IP].src, self.nat_addr)
        self.assertEqual(capture[IP].dst, self.pg0.remote_ip4)
        self.assertNotEqual(capture[ICMP].id, 4000)
        self.assert_packet_checksums_valid(capture)
        out_id = capture[ICMP].id

        p = (
            Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
            / IP(dst=self.nat_addr, src=self.pg0.remote_ip4)
            / ICMP(id=out_id, type="echo-reply")
        )
        self.pg0.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg1.get_capture(1)
        capture = capture[0]
        self.assertEqual(capture[IPv6].src, aftr_ip6)
        self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[1].ip6)
        self.assertEqual(capture[IP].src, self.pg0.remote_ip4)
        self.assertEqual(capture[IP].dst, "192.168.1.1")
        self.assertEqual(capture[ICMP].id, 4000)
        self.assert_packet_checksums_valid(capture)

        # ping DS-Lite AFTR tunnel endpoint address
        p = (
            Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac)
            / IPv6(src=self.pg1.remote_hosts[1].ip6, dst=aftr_ip6)
            / ICMPv6EchoRequest()
        )
        self.pg1.add_stream(p)
        self.pg_enable_capture(self.pg_interfaces)
        self