ip-neighbor: add ip neighbor flush
[vpp.git] / test / test_srv6_ad.py
index 13979ba..88c0b1d 100644 (file)
@@ -1,35 +1,44 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import unittest
 import binascii
 from socket import AF_INET6
 
 from framework import VppTestCase, VppTestRunner
 
 import unittest
 import binascii
 from socket import AF_INET6
 
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable
-from vpp_srv6 import SRv6LocalSIDBehaviors, VppSRv6LocalSID, VppSRv6Policy, \
-    SRv6PolicyType, VppSRv6Steering, SRv6PolicySteeringTypes
-
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
+from vpp_srv6 import (
+    SRv6LocalSIDBehaviors,
+    VppSRv6LocalSID,
+    VppSRv6Policy,
+    SRv6PolicyType,
+    VppSRv6Steering,
+    SRv6PolicySteeringTypes,
+)
+
+import scapy.compat
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, Dot1Q
 from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
 from scapy.layers.inet import IP, UDP
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, Dot1Q
 from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
 from scapy.layers.inet import IP, UDP
 
-from scapy.utils import inet_pton, inet_ntop
-
 from util import ppp
 
 
 from util import ppp
 
 
-class TestSRv6(VppTestCase):
-    """ SRv6 Dynamic Proxy plugin Test Case """
+class TestSRv6Ad(VppTestCase):
+    """SRv6 Dynamic Proxy plugin Test Case"""
 
     @classmethod
     def setUpClass(self):
 
     @classmethod
     def setUpClass(self):
-        super(TestSRv6, self).setUpClass()
+        super(TestSRv6Ad, self).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestSRv6Ad, cls).tearDownClass()
 
     def setUp(self):
 
     def setUp(self):
-        """ Perform test setup before each test case.
-        """
-        super(TestSRv6, self).setUp()
+        """Perform test setup before each test case."""
+        super(TestSRv6Ad, self).setUp()
 
         # packet sizes, inclusive L2 overhead
         self.pg_packet_sizes = [64, 512, 1518, 9018]
 
         # packet sizes, inclusive L2 overhead
         self.pg_packet_sizes = [64, 512, 1518, 9018]
@@ -38,17 +47,15 @@ class TestSRv6(VppTestCase):
         self.reset_packet_infos()
 
     def tearDown(self):
         self.reset_packet_infos()
 
     def tearDown(self):
-        """ Clean up test setup after each test case.
-        """
+        """Clean up test setup after each test case."""
         self.teardown_interfaces()
 
         self.teardown_interfaces()
 
-        super(TestSRv6, self).tearDown()
+        super(TestSRv6Ad, self).tearDown()
 
 
-    def configure_interface(self,
-                            interface,
-                            ipv6=False, ipv4=False,
-                            ipv6_table_id=0, ipv4_table_id=0):
-        """ Configure interface.
+    def configure_interface(
+        self, interface, ipv6=False, ipv4=False, ipv6_table_id=0, ipv4_table_id=0
+    ):
+        """Configure interface.
         :param ipv6: configure IPv6 on interface
         :param ipv4: configure IPv4 on interface
         :param ipv6_table_id: FIB table_id for IPv6
         :param ipv6: configure IPv6 on interface
         :param ipv4: configure IPv4 on interface
         :param ipv6_table_id: FIB table_id for IPv6
@@ -67,9 +74,8 @@ class TestSRv6(VppTestCase):
             interface.resolve_arp()
         interface.admin_up()
 
             interface.resolve_arp()
         interface.admin_up()
 
-    def setup_interfaces(self, ipv6=[], ipv4=[],
-                         ipv6_table_id=[], ipv4_table_id=[]):
-        """ Create and configure interfaces.
+    def setup_interfaces(self, ipv6=[], ipv4=[], ipv6_table_id=[], ipv4_table_id=[]):
+        """Create and configure interfaces.
 
         :param ipv6: list of interface IPv6 capabilities
         :param ipv4: list of interface IPv4 capabilities
 
         :param ipv6: list of interface IPv6 capabilities
         :param ipv4: list of interface IPv4 capabilities
@@ -104,22 +110,21 @@ class TestSRv6(VppTestCase):
         # setup all interfaces
         for i in range(count):
             intf = self.pg_interfaces[i]
         # setup all interfaces
         for i in range(count):
             intf = self.pg_interfaces[i]
-            self.configure_interface(intf,
-                                     ipv6[i], ipv4[i],
-                                     ipv6_table_id[i], ipv4_table_id[i])
+            self.configure_interface(
+                intf, ipv6[i], ipv4[i], ipv6_table_id[i], ipv4_table_id[i]
+            )
 
         if any(ipv6):
             self.logger.debug(self.vapi.cli("show ip6 neighbors"))
         if any(ipv4):
 
         if any(ipv6):
             self.logger.debug(self.vapi.cli("show ip6 neighbors"))
         if any(ipv4):
-            self.logger.debug(self.vapi.cli("show ip arp"))
+            self.logger.debug(self.vapi.cli("show ip4 neighbors"))
         self.logger.debug(self.vapi.cli("show interface"))
         self.logger.debug(self.vapi.cli("show hardware"))
 
         return self.pg_interfaces
 
     def teardown_interfaces(self):
         self.logger.debug(self.vapi.cli("show interface"))
         self.logger.debug(self.vapi.cli("show hardware"))
 
         return self.pg_interfaces
 
     def teardown_interfaces(self):
-        """ Unconfigure and bring down interface.
-        """
+        """Unconfigure and bring down interface."""
         self.logger.debug("Tearing down interfaces")
         # tear down all interfaces
         # AFAIK they cannot be deleted
         self.logger.debug("Tearing down interfaces")
         # tear down all interfaces
         # AFAIK they cannot be deleted
@@ -131,10 +136,9 @@ class TestSRv6(VppTestCase):
             i.set_table_ip6(0)
 
     def test_SRv6_End_AD_IPv6(self):
             i.set_table_ip6(0)
 
     def test_SRv6_End_AD_IPv6(self):
-        """ Test SRv6 End.AD behavior with IPv6 traffic.
-        """
-        self.src_addr = 'a0::'
-        self.sid_list = ['a1::', 'a2::a6', 'a3::']
+        """Test SRv6 End.AD behavior with IPv6 traffic."""
+        self.src_addr = "a0::"
+        self.sid_list = ["a1::", "a2::a6", "a3::"]
         self.test_sid_index = 1
 
         # send traffic to one destination interface
         self.test_sid_index = 1
 
         # send traffic to one destination interface
@@ -142,20 +146,32 @@ class TestSRv6(VppTestCase):
         self.setup_interfaces(ipv6=[True, True])
 
         # configure route to next segment
         self.setup_interfaces(ipv6=[True, True])
 
         # configure route to next segment
-        route = VppIpRoute(self, self.sid_list[self.test_sid_index + 1], 128,
-                           [VppRoutePath(self.pg0.remote_ip6,
-                                         self.pg0.sw_if_index,
-                                         proto=DpoProto.DPO_PROTO_IP6)],
-                           is_ip6=1)
+        route = VppIpRoute(
+            self,
+            self.sid_list[self.test_sid_index + 1],
+            128,
+            [
+                VppRoutePath(
+                    self.pg0.remote_ip6,
+                    self.pg0.sw_if_index,
+                    proto=DpoProto.DPO_PROTO_IP6,
+                )
+            ],
+        )
         route.add_vpp_config()
 
         # configure SRv6 localSID behavior
         route.add_vpp_config()
 
         # configure SRv6 localSID behavior
-        cli_str = "sr localsid address " + \
-                  self.sid_list[self.test_sid_index] + \
-                  " behavior end.ad" + \
-                  " nh " + self.pg1.remote_ip6 + \
-                  " oif " + self.pg1.name + \
-                  " iif " + self.pg1.name
+        cli_str = (
+            "sr localsid address "
+            + self.sid_list[self.test_sid_index]
+            + " behavior end.ad"
+            + " nh "
+            + self.pg1.remote_ip6
+            + " oif "
+            + self.pg1.name
+            + " iif "
+            + self.pg1.name
+        )
         self.vapi.cli(cli_str)
 
         # log the localsids
         self.vapi.cli(cli_str)
 
         # log the localsids
@@ -168,15 +184,18 @@ class TestSRv6(VppTestCase):
         packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
             srcaddr=self.src_addr,
             sidlist=self.sid_list[::-1],
         packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
             srcaddr=self.src_addr,
             sidlist=self.sid_list[::-1],
-            segleft=len(self.sid_list) - self.test_sid_index - 1)
+            segleft=len(self.sid_list) - self.test_sid_index - 1,
+        )
 
         # generate packets (pg0->pg1)
 
         # generate packets (pg0->pg1)
-        pkts1 = self.create_stream(self.pg0, self.pg1, packet_header1,
-                                   self.pg_packet_sizes, count)
+        pkts1 = self.create_stream(
+            self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
+        )
 
         # send packets and verify received packets
 
         # send packets and verify received packets
-        self.send_and_verify_pkts(self.pg0, pkts1, self.pg1,
-                                  self.compare_rx_tx_packet_End_AD_IPv6_out)
+        self.send_and_verify_pkts(
+            self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv6_out
+        )
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
@@ -185,26 +204,27 @@ class TestSRv6(VppTestCase):
         packet_header2 = self.create_packet_header_IPv6()
 
         # generate returning packets (pg1->pg0)
         packet_header2 = self.create_packet_header_IPv6()
 
         # generate returning packets (pg1->pg0)
-        pkts2 = self.create_stream(self.pg1, self.pg0, packet_header2,
-                                   self.pg_packet_sizes, count)
+        pkts2 = self.create_stream(
+            self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
+        )
 
         # send packets and verify received packets
 
         # send packets and verify received packets
-        self.send_and_verify_pkts(self.pg1, pkts2, self.pg0,
-                                  self.compare_rx_tx_packet_End_AD_IPv6_in)
+        self.send_and_verify_pkts(
+            self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv6_in
+        )
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # remove SRv6 localSIDs
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # remove SRv6 localSIDs
-        cli_str = "sr localsid del address " + \
-                  self.sid_list[self.test_sid_index]
+        cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
         self.vapi.cli(cli_str)
 
         # cleanup interfaces
         self.teardown_interfaces()
 
     def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
         self.vapi.cli(cli_str)
 
         # cleanup interfaces
         self.teardown_interfaces()
 
     def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
-        """ Compare input and output packet after passing End.AD with IPv6
+        """Compare input and output packet after passing End.AD with IPv6
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
@@ -229,7 +249,7 @@ class TestSRv6(VppTestCase):
         self.logger.debug("packet verification: SUCCESS")
 
     def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
         self.logger.debug("packet verification: SUCCESS")
 
     def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
-        """ Compare input and output packet after passing End.AD
+        """Compare input and output packet after passing End.AD
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
@@ -250,8 +270,7 @@ class TestSRv6(VppTestCase):
         # rx'ed seglist should be equal to SID-list in reversed order
         self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
         # segleft should be equal to previous segleft value minus 1
         # rx'ed seglist should be equal to SID-list in reversed order
         self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
         # segleft should be equal to previous segleft value minus 1
-        self.assertEqual(rx_srh.segleft,
-                         len(self.sid_list) - self.test_sid_index - 2)
+        self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
         # lastentry should be equal to the SID-list length minus 1
         self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
 
         # lastentry should be equal to the SID-list length minus 1
         self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
 
@@ -266,10 +285,9 @@ class TestSRv6(VppTestCase):
         self.logger.debug("packet verification: SUCCESS")
 
     def test_SRv6_End_AD_IPv4(self):
         self.logger.debug("packet verification: SUCCESS")
 
     def test_SRv6_End_AD_IPv4(self):
-        """ Test SRv6 End.AD behavior with IPv4 traffic.
-        """
-        self.src_addr = 'a0::'
-        self.sid_list = ['a1::', 'a2::a4', 'a3::']
+        """Test SRv6 End.AD behavior with IPv4 traffic."""
+        self.src_addr = "a0::"
+        self.sid_list = ["a1::", "a2::a4", "a3::"]
         self.test_sid_index = 1
 
         # send traffic to one destination interface
         self.test_sid_index = 1
 
         # send traffic to one destination interface
@@ -277,20 +295,32 @@ class TestSRv6(VppTestCase):
         self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
 
         # configure route to next segment
         self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
 
         # configure route to next segment
-        route = VppIpRoute(self, self.sid_list[self.test_sid_index + 1], 128,
-                           [VppRoutePath(self.pg0.remote_ip6,
-                                         self.pg0.sw_if_index,
-                                         proto=DpoProto.DPO_PROTO_IP6)],
-                           is_ip6=1)
+        route = VppIpRoute(
+            self,
+            self.sid_list[self.test_sid_index + 1],
+            128,
+            [
+                VppRoutePath(
+                    self.pg0.remote_ip6,
+                    self.pg0.sw_if_index,
+                    proto=DpoProto.DPO_PROTO_IP6,
+                )
+            ],
+        )
         route.add_vpp_config()
 
         # configure SRv6 localSID behavior
         route.add_vpp_config()
 
         # configure SRv6 localSID behavior
-        cli_str = "sr localsid address " + \
-                  self.sid_list[self.test_sid_index] + \
-                  " behavior end.ad" + \
-                  " nh " + self.pg1.remote_ip4 + \
-                  " oif " + self.pg1.name + \
-                  " iif " + self.pg1.name
+        cli_str = (
+            "sr localsid address "
+            + self.sid_list[self.test_sid_index]
+            + " behavior end.ad"
+            + " nh "
+            + self.pg1.remote_ip4
+            + " oif "
+            + self.pg1.name
+            + " iif "
+            + self.pg1.name
+        )
         self.vapi.cli(cli_str)
 
         # log the localsids
         self.vapi.cli(cli_str)
 
         # log the localsids
@@ -303,15 +333,18 @@ class TestSRv6(VppTestCase):
         packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
             srcaddr=self.src_addr,
             sidlist=self.sid_list[::-1],
         packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
             srcaddr=self.src_addr,
             sidlist=self.sid_list[::-1],
-            segleft=len(self.sid_list) - self.test_sid_index - 1)
+            segleft=len(self.sid_list) - self.test_sid_index - 1,
+        )
 
         # generate packets (pg0->pg1)
 
         # generate packets (pg0->pg1)
-        pkts1 = self.create_stream(self.pg0, self.pg1, packet_header1,
-                                   self.pg_packet_sizes, count)
+        pkts1 = self.create_stream(
+            self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
+        )
 
         # send packets and verify received packets
 
         # send packets and verify received packets
-        self.send_and_verify_pkts(self.pg0, pkts1, self.pg1,
-                                  self.compare_rx_tx_packet_End_AD_IPv4_out)
+        self.send_and_verify_pkts(
+            self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv4_out
+        )
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
@@ -320,26 +353,27 @@ class TestSRv6(VppTestCase):
         packet_header2 = self.create_packet_header_IPv4()
 
         # generate returning packets (pg1->pg0)
         packet_header2 = self.create_packet_header_IPv4()
 
         # generate returning packets (pg1->pg0)
-        pkts2 = self.create_stream(self.pg1, self.pg0, packet_header2,
-                                   self.pg_packet_sizes, count)
+        pkts2 = self.create_stream(
+            self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
+        )
 
         # send packets and verify received packets
 
         # send packets and verify received packets
-        self.send_and_verify_pkts(self.pg1, pkts2, self.pg0,
-                                  self.compare_rx_tx_packet_End_AD_IPv4_in)
+        self.send_and_verify_pkts(
+            self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv4_in
+        )
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # remove SRv6 localSIDs
 
         # log the localsid counters
         self.logger.info(self.vapi.cli("show sr localsid"))
 
         # remove SRv6 localSIDs
-        cli_str = "sr localsid del address " + \
-                  self.sid_list[self.test_sid_index]
+        cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
         self.vapi.cli(cli_str)
 
         # cleanup interfaces
         self.teardown_interfaces()
 
     def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
         self.vapi.cli(cli_str)
 
         # cleanup interfaces
         self.teardown_interfaces()
 
     def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
-        """ Compare input and output packet after passing End.AD with IPv4
+        """Compare input and output packet after passing End.AD with IPv4
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
@@ -362,14 +396,14 @@ class TestSRv6(VppTestCase):
         tx_ip2.chksum = None
         # read back the pkt (with str()) to force computing these fields
         # probably other ways to accomplish this are possible
         tx_ip2.chksum = None
         # read back the pkt (with str()) to force computing these fields
         # probably other ways to accomplish this are possible
-        tx_ip2 = IP(str(tx_ip2))
+        tx_ip2 = IP(scapy.compat.raw(tx_ip2))
 
         self.assertEqual(rx_ip, tx_ip2)
 
         self.logger.debug("packet verification: SUCCESS")
 
     def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
 
         self.assertEqual(rx_ip, tx_ip2)
 
         self.logger.debug("packet verification: SUCCESS")
 
     def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
-        """ Compare input and output packet after passing End.AD
+        """Compare input and output packet after passing End.AD
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
 
         :param tx_pkt: transmitted packet
         :param rx_pkt: received packet
@@ -390,8 +424,7 @@ class TestSRv6(VppTestCase):
         # rx'ed seglist should be equal to SID-list in reversed order
         self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
         # segleft should be equal to previous segleft value minus 1
         # rx'ed seglist should be equal to SID-list in reversed order
         self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
         # segleft should be equal to previous segleft value minus 1
-        self.assertEqual(rx_srh.segleft,
-                         len(self.sid_list) - self.test_sid_index - 2)
+        self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
         # lastentry should be equal to the SID-list length minus 1
         self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
 
         # lastentry should be equal to the SID-list length minus 1
         self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
 
@@ -404,12 +437,154 @@ class TestSRv6(VppTestCase):
         tx_ip.chksum = None
         #   -> read back the pkt (with str()) to force computing these fields
         # probably other ways to accomplish this are possible
         tx_ip.chksum = None
         #   -> read back the pkt (with str()) to force computing these fields
         # probably other ways to accomplish this are possible
-        self.assertEqual(rx_srh.payload, IP(str(tx_ip)))
+        self.assertEqual(rx_srh.payload, IP(scapy.compat.raw(tx_ip)))
+
+        self.logger.debug("packet verification: SUCCESS")
+
+    def test_SRv6_End_AD_L2(self):
+        """Test SRv6 End.AD behavior with L2 traffic."""
+        self.src_addr = "a0::"
+        self.sid_list = ["a1::", "a2::a4", "a3::"]
+        self.test_sid_index = 1
+
+        # send traffic to one destination interface
+        # source and destination interfaces are IPv6 only
+        self.setup_interfaces(ipv6=[True, False])
+
+        # configure route to next segment
+        route = VppIpRoute(
+            self,
+            self.sid_list[self.test_sid_index + 1],
+            128,
+            [
+                VppRoutePath(
+                    self.pg0.remote_ip6,
+                    self.pg0.sw_if_index,
+                    proto=DpoProto.DPO_PROTO_IP6,
+                )
+            ],
+        )
+        route.add_vpp_config()
+
+        # configure SRv6 localSID behavior
+        cli_str = (
+            "sr localsid address "
+            + self.sid_list[self.test_sid_index]
+            + " behavior end.ad"
+            + " oif "
+            + self.pg1.name
+            + " iif "
+            + self.pg1.name
+        )
+        self.vapi.cli(cli_str)
+
+        # log the localsids
+        self.logger.debug(self.vapi.cli("show sr localsid"))
+
+        # send one packet per packet size
+        count = len(self.pg_packet_sizes)
+
+        # prepare L2 in SRv6 headers
+        packet_header1 = self.create_packet_header_IPv6_SRH_L2(
+            srcaddr=self.src_addr,
+            sidlist=self.sid_list[::-1],
+            segleft=len(self.sid_list) - self.test_sid_index - 1,
+            vlan=0,
+        )
+
+        # generate packets (pg0->pg1)
+        pkts1 = self.create_stream(
+            self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
+        )
+
+        # send packets and verify received packets
+        self.send_and_verify_pkts(
+            self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_L2_out
+        )
+
+        # log the localsid counters
+        self.logger.info(self.vapi.cli("show sr localsid"))
+
+        # prepare L2 header for returning packets
+        packet_header2 = self.create_packet_header_L2()
+
+        # generate returning packets (pg1->pg0)
+        pkts2 = self.create_stream(
+            self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
+        )
+
+        # send packets and verify received packets
+        self.send_and_verify_pkts(
+            self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_L2_in
+        )
+
+        # log the localsid counters
+        self.logger.info(self.vapi.cli("show sr localsid"))
+
+        # remove SRv6 localSIDs
+        cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
+        self.vapi.cli(cli_str)
+
+        # cleanup interfaces
+        self.teardown_interfaces()
+
+    def compare_rx_tx_packet_End_AD_L2_out(self, tx_pkt, rx_pkt):
+        """Compare input and output packet after passing End.AD with L2
+
+        :param tx_pkt: transmitted packet
+        :param rx_pkt: received packet
+        """
+
+        # get IPv4 header of rx'ed packet
+        rx_eth = rx_pkt.getlayer(Ether)
+
+        tx_ip = tx_pkt.getlayer(IPv6)
+        # we can't just get the 2nd Ether layer
+        # get the Raw content and dissect it as Ether
+        tx_eth1 = Ether(scapy.compat.raw(tx_pkt[Raw]))
+
+        # verify if rx'ed packet has no SRH
+        self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
+
+        # the whole rx_eth pkt should be equal to tx_eth1
+        self.assertEqual(rx_eth, tx_eth1)
+
+        self.logger.debug("packet verification: SUCCESS")
+
+    def compare_rx_tx_packet_End_AD_L2_in(self, tx_pkt, rx_pkt):
+        """Compare input and output packet after passing End.AD
+
+        :param tx_pkt: transmitted packet
+        :param rx_pkt: received packet
+        """
+
+        ####
+        # get first (outer) IPv6 header of rx'ed packet
+        rx_ip = rx_pkt.getlayer(IPv6)
+        # received ip.src should be equal to SR Policy source
+        self.assertEqual(rx_ip.src, self.src_addr)
+        # received ip.dst should be equal to expected sidlist next segment
+        self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
+
+        # rx'ed packet should have SRH
+        self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
+
+        # get SRH
+        rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
+        # rx'ed seglist should be equal to SID-list in reversed order
+        self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
+        # segleft should be equal to previous segleft value minus 1
+        self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
+        # lastentry should be equal to the SID-list length minus 1
+        self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
+
+        # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
+        tx_ether = tx_pkt.getlayer(Ether)
+        self.assertEqual(Ether(scapy.compat.raw(rx_srh.payload)), tx_ether)
 
         self.logger.debug("packet verification: SUCCESS")
 
 
         self.logger.debug("packet verification: SUCCESS")
 
-    def create_stream(self, src_if, dst_if, packet_header, packet_sizes,
-                      count):
+    def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
         """Create SRv6 input packet stream for defined interface.
 
         :param VppInterface src_if: Interface to create packet stream for
         """Create SRv6 input packet stream for defined interface.
 
         :param VppInterface src_if: Interface to create packet stream for
@@ -426,15 +601,17 @@ class TestSRv6(VppTestCase):
         pkts = []
         for i in range(0, count - 1):
             payload_info = self.create_packet_info(src_if, dst_if)
         pkts = []
         for i in range(0, count - 1):
             payload_info = self.create_packet_info(src_if, dst_if)
-            self.logger.debug(
-                "Creating packet with index %d" % (payload_info.index))
+            self.logger.debug("Creating packet with index %d" % (payload_info.index))
             payload = self.info_to_payload(payload_info)
             # add L2 header if not yet provided in packet_header
             payload = self.info_to_payload(payload_info)
             # add L2 header if not yet provided in packet_header
-            if packet_header.getlayer(0).name == 'Ethernet':
+            if packet_header.getlayer(0).name == "Ethernet":
                 p = packet_header / Raw(payload)
             else:
                 p = packet_header / Raw(payload)
             else:
-                p = Ether(dst=src_if.local_mac, src=src_if.remote_mac) / \
-                    packet_header / Raw(payload)
+                p = (
+                    Ether(dst=src_if.local_mac, src=src_if.remote_mac)
+                    / packet_header
+                    / Raw(payload)
+                )
             size = packet_sizes[i % len(packet_sizes)]
             self.logger.debug("Packet size %d" % (size))
             self.extend_packet(p, size)
             size = packet_sizes[i % len(packet_sizes)]
             self.logger.debug("Packet size %d" % (size))
             self.extend_packet(p, size)
@@ -442,7 +619,7 @@ class TestSRv6(VppTestCase):
             # read back the dumped packet (with str())
             # to force computing these fields
             # probably other ways are possible
             # read back the dumped packet (with str())
             # to force computing these fields
             # probably other ways are possible
-            p = Ether(str(p))
+            p = Ether(scapy.compat.raw(p))
             payload_info.data = p.copy()
             self.logger.debug(ppp("Created packet:", p))
             pkts.append(p)
             payload_info.data = p.copy()
             self.logger.debug(ppp("Created packet:", p))
             pkts.append(p)
@@ -487,7 +664,7 @@ class TestSRv6(VppTestCase):
         UDP source port and destination port are 1234
         """
 
         UDP source port and destination port are 1234
         """
 
-        p = IPv6(src='1234::1', dst='4321::1') / UDP(sport=1234, dport=1234)
+        p = IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234)
         return p
 
     def create_packet_header_IPv6_SRH_IPv6(self, srcaddr, sidlist, segleft):
         return p
 
     def create_packet_header_IPv6_SRH_IPv6(self, srcaddr, sidlist, segleft):
@@ -505,11 +682,12 @@ class TestSRv6(VppTestCase):
         UDP source port and destination port are 1234
         """
 
         UDP source port and destination port are 1234
         """
 
-        p = IPv6(src=srcaddr, dst=sidlist[segleft]) / \
-            IPv6ExtHdrSegmentRouting(addresses=sidlist,
-                                     segleft=segleft, nh=41) / \
-            IPv6(src='1234::1', dst='4321::1') / \
-            UDP(sport=1234, dport=1234)
+        p = (
+            IPv6(src=srcaddr, dst=sidlist[segleft])
+            / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
+            / IPv6(src="1234::1", dst="4321::1")
+            / UDP(sport=1234, dport=1234)
+        )
         return p
 
     def create_packet_header_IPv4(self):
         return p
 
     def create_packet_header_IPv4(self):
@@ -522,7 +700,7 @@ class TestSRv6(VppTestCase):
         UDP source port and destination port are 1234
         """
 
         UDP source port and destination port are 1234
         """
 
-        p = IP(src='123.1.1.1', dst='124.1.1.1') / UDP(sport=1234, dport=1234)
+        p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
         return p
 
     def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
         return p
 
     def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
@@ -540,28 +718,73 @@ class TestSRv6(VppTestCase):
         UDP source port and destination port are 1234
         """
 
         UDP source port and destination port are 1234
         """
 
-        p = IPv6(src=srcaddr, dst=sidlist[segleft]) / \
-            IPv6ExtHdrSegmentRouting(addresses=sidlist,
-                                     segleft=segleft, nh=4) / \
-            IP(src='123.1.1.1', dst='124.1.1.1') / \
-            UDP(sport=1234, dport=1234)
+        p = (
+            IPv6(src=srcaddr, dst=sidlist[segleft])
+            / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
+            / IP(src="123.1.1.1", dst="124.1.1.1")
+            / UDP(sport=1234, dport=1234)
+        )
         return p
 
         return p
 
-    def get_payload_info(self, packet):
-        """ Extract the payload_info from the packet
+    def create_packet_header_L2(self, vlan=0):
+        """Create packet header: L2 header
+
+        :param vlan: if vlan!=0 then add 802.1q header
         """
         """
+        # Note: the dst addr ('00:55:44:33:22:11') is used in
+        # the compare function compare_rx_tx_packet_T_Encaps_L2
+        # to detect presence of L2 in SRH payload
+        p = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
+        etype = 0x8137  # IPX
+        if vlan:
+            # add 802.1q layer
+            p /= Dot1Q(vlan=vlan, type=etype)
+        else:
+            p.type = etype
+        return p
+
+    def create_packet_header_IPv6_SRH_L2(self, srcaddr, sidlist, segleft, vlan=0):
+        """Create packet header: L2 encapsulated in SRv6:
+        IPv6 header with SRH, L2
+
+        :param int srcaddr: IPv6 source address
+        :param list sidlist: segment list of outer IPv6 SRH
+        :param int segleft: segments-left field of outer IPv6 SRH
+        :param vlan: L2 vlan; if vlan!=0 then add 802.1q header
+
+        IPv6 source address is set to srcaddr
+        IPv6 destination address is set to sidlist[segleft]
+        """
+        eth = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
+        etype = 0x8137  # IPX
+        if vlan:
+            # add 802.1q layer
+            eth /= Dot1Q(vlan=vlan, type=etype)
+        else:
+            eth.type = etype
+
+        p = (
+            IPv6(src=srcaddr, dst=sidlist[segleft])
+            / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=143)
+            / eth
+        )
+        return p
+
+    def get_payload_info(self, packet):
+        """Extract the payload_info from the packet"""
         # in most cases, payload_info is in packet[Raw]
         # but packet[Raw] gives the complete payload
         # (incl L2 header) for the T.Encaps L2 case
         try:
         # in most cases, payload_info is in packet[Raw]
         # but packet[Raw] gives the complete payload
         # (incl L2 header) for the T.Encaps L2 case
         try:
-            payload_info = self.payload_to_info(str(packet[Raw]))
+            payload_info = self.payload_to_info(packet[Raw])
 
         except:
             # remote L2 header from packet[Raw]:
             # take packet[Raw], convert it to an Ether layer
             # and then extract Raw from it
             payload_info = self.payload_to_info(
 
         except:
             # remote L2 header from packet[Raw]:
             # take packet[Raw], convert it to an Ether layer
             # and then extract Raw from it
             payload_info = self.payload_to_info(
-                str(Ether(str(packet[Raw]))[Raw]))
+                Ether(scapy.compat.raw(packet[Raw]))[Raw]
+            )
 
         return payload_info
 
 
         return payload_info
 
@@ -574,8 +797,10 @@ class TestSRv6(VppTestCase):
         :param capture: captured packets
         :param compare_func: function to compare in and out packet
         """
         :param capture: captured packets
         :param compare_func: function to compare in and out packet
         """
-        self.logger.info("Verifying capture on interface %s using function %s"
-                         % (dst_if.name, compare_func.func_name))
+        self.logger.info(
+            "Verifying capture on interface %s using function %s"
+            % (dst_if.name, compare_func.__name__)
+        )
 
         last_info = dict()
         for i in self.pg_interfaces:
 
         last_info = dict()
         for i in self.pg_interfaces:
@@ -588,19 +813,19 @@ class TestSRv6(VppTestCase):
                 payload_info = self.get_payload_info(packet)
                 packet_index = payload_info.index
 
                 payload_info = self.get_payload_info(packet)
                 packet_index = payload_info.index
 
-                self.logger.debug("Verifying packet with index %d"
-                                  % (packet_index))
+                self.logger.debug("Verifying packet with index %d" % (packet_index))
                 # packet should have arrived on the expected interface
                 self.assertEqual(payload_info.dst, dst_sw_if_index)
                 self.logger.debug(
                 # packet should have arrived on the expected interface
                 self.assertEqual(payload_info.dst, dst_sw_if_index)
                 self.logger.debug(
-                    "Got packet on interface %s: src=%u (idx=%u)" %
-                    (dst_if.name, payload_info.src, packet_index))
+                    "Got packet on interface %s: src=%u (idx=%u)"
+                    % (dst_if.name, payload_info.src, packet_index)
+                )
 
                 # search for payload_info with same src and dst if_index
                 # this will give us the transmitted packet
                 next_info = self.get_next_packet_info_for_interface2(
 
                 # search for payload_info with same src and dst if_index
                 # this will give us the transmitted packet
                 next_info = self.get_next_packet_info_for_interface2(
-                    payload_info.src, dst_sw_if_index,
-                    last_info[payload_info.src])
+                    payload_info.src, dst_sw_if_index, last_info[payload_info.src]
+                )
                 last_info[payload_info.src] = next_info
                 # next_info should not be None
                 self.assertTrue(next_info is not None)
                 last_info[payload_info.src] = next_info
                 # next_info should not be None
                 self.assertTrue(next_info is not None)
@@ -609,8 +834,9 @@ class TestSRv6(VppTestCase):
                 # data field of next_info contains the tx packet
                 txed_packet = next_info.data
 
                 # data field of next_info contains the tx packet
                 txed_packet = next_info.data
 
-                self.logger.debug(ppp("Transmitted packet:",
-                                      txed_packet))  # ppp=Pretty Print Packet
+                self.logger.debug(
+                    ppp("Transmitted packet:", txed_packet)
+                )  # ppp=Pretty Print Packet
 
                 self.logger.debug(ppp("Received packet:", packet))
 
 
                 self.logger.debug(ppp("Received packet:", packet))
 
@@ -618,18 +844,20 @@ class TestSRv6(VppTestCase):
                 compare_func(txed_packet, packet)
 
             except:
                 compare_func(txed_packet, packet)
 
             except:
-                print packet.command()
                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
                 raise
 
         # have all expected packets arrived?
         for i in self.pg_interfaces:
             remaining_packet = self.get_next_packet_info_for_interface2(
                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
                 raise
 
         # have all expected packets arrived?
         for i in self.pg_interfaces:
             remaining_packet = self.get_next_packet_info_for_interface2(
-                i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
-            self.assertTrue(remaining_packet is None,
-                            "Interface %s: Packet expected from interface %s "
-                            "didn't arrive" % (dst_if.name, i.name))
+                i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
+            )
+            self.assertTrue(
+                remaining_packet is None,
+                "Interface %s: Packet expected from interface %s "
+                "didn't arrive" % (dst_if.name, i.name),
+            )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main(testRunner=VppTestRunner)
     unittest.main(testRunner=VppTestRunner)