#!/usr/bin/env python3 import unittest import ipaddress from framework import VppTestCase from asfframework import VppTestRunner from config import config from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6DestUnreach from scapy.layers.l2 import Ether from scapy.packet import Raw @unittest.skipIf("npt66" in config.excluded_plugins, "Exclude NPTv6 plugin tests") class TestNPT66(VppTestCase): """NPTv6 Test Case""" extra_vpp_plugin_config = [ "plugin npt66_plugin.so {enable}", ] def setUp(self): super(TestNPT66, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(2)) for i in self.pg_interfaces: i.admin_up() i.config_ip6() i.resolve_ndp() def tearDown(self): for i in self.pg_interfaces: i.unconfig_ip6() i.admin_down() super(TestNPT66, self).tearDown() def send_and_verify(self, internal, reply_icmp_error=False): sendif = self.pg0 recvif = self.pg1 local_mac = self.pg0.local_mac remote_mac = self.pg0.remote_mac src = ipaddress.ip_interface(internal).ip + 1 dst = self.pg1.remote_ip6 p = ( Ether(dst=local_mac, src=remote_mac) / IPv6(src=src, dst=dst) / ICMPv6EchoRequest() / Raw(b"Request") ) # print('Sending packet') # p.show2() rxs = self.send_and_expect(sendif, p, recvif) for rx in rxs: # print('Received packet') # rx.show2() original_cksum = rx[ICMPv6EchoRequest].cksum del rx[ICMPv6EchoRequest].cksum rx = rx.__class__(bytes(rx)) self.assertEqual(original_cksum, rx[ICMPv6EchoRequest].cksum) # Generate a replies if reply_icmp_error: # print('Generating an ICMP error message') reply = ( Ether(dst=rx[Ether].src, src=local_mac) / IPv6(src=rx[IPv6].dst, dst=rx[IPv6].src) / ICMPv6DestUnreach() / rx[IPv6] ) # print('Sending ICMP error message reply') # reply.show2() replies = self.send_and_expect(recvif, reply, sendif) for r in replies: # print('Received ICMP error message reply on the other side') # r.show2() self.assertEqual(str(p[IPv6].src), r[IPv6].dst) original_cksum = r[ICMPv6EchoRequest].cksum del r[ICMPv6EchoRequest].cksum r = r.__class__(bytes(r)) self.assertEqual(original_cksum, r[ICMPv6EchoRequest].cksum) else: reply = ( Ether(dst=rx[Ether].src, src=local_mac) / IPv6(src=rx[IPv6].dst, dst=rx[IPv6].src) / ICMPv6EchoRequest() / Raw(b"Reply") ) replies = self.send_and_expect(recvif, reply, sendif) for r in replies: # r.show2() self.assertEqual(str(p[IPv6].src), r[IPv6].dst) original_cksum = r[ICMPv6EchoRequest].cksum del r[ICMPv6EchoRequest].cksum r = r.__class__(bytes(r)) self.assertEqual(original_cksum, r[ICMPv6EchoRequest].cksum) def do_test(self, internal, external, reply_icmp_error=False): """Add NPT66 binding and send packet""" self.vapi.npt66_binding_add_del( sw_if_index=self.pg1.sw_if_index, internal=internal, external=external, is_add=True, ) ## TODO use route api self.vapi.cli(f"ip route add {internal} via {self.pg0.remote_ip6}") self.send_and_verify(internal, reply_icmp_error=reply_icmp_error) self.vapi.npt66_binding_add_del( sw_if_index=self.pg1.sw_if_index, internal=internal, external=external, is_add=False, ) def test_npt66_simple(self): """Send and receive a packet through NPT66""" self.do_test("fd00:0000:0000::/48", "2001:4650:c3ed::/48") self.do_test("fc00:1::/48", "2001:db8:1::/48") self.do_test("fc00:1234::/32", "2001:db8:1::/32") self.do_test("fc00:1234::/63", "2001:db8:1::/56") def test_npt66_icmp6(self): """Send and receive a packet through NPT66""" # Test ICMP6 error packets self.do_test( "fd00:0000:0000::/48", "2001:4650:c3ed::/48", reply_icmp_error=True ) self.do_test("fc00:1::/48", "2001:db8:1::/48", reply_icmp_error=True) self.do_test("fc00:1234::/32", "2001:db8:1::/32", reply_icmp_error=True) self.do_test("fc00:1234::/63", "2001:db8:1::/56", reply_icmp_error=True) if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)