Revert "ipsec: Use the new tunnel API types to add flow label and TTL copy"
[vpp.git] / test / test_ipsec_tun_if_esp.py
index d8527a3..5bcd9dd 100644 (file)
@@ -7,29 +7,37 @@ from scapy.layers.l2 import Ether, GRE, Dot1Q
 from scapy.packet import Raw
 from scapy.layers.inet import IP, UDP
 from scapy.layers.inet6 import IPv6
 from scapy.packet import Raw
 from scapy.layers.inet import IP, UDP
 from scapy.layers.inet6 import IPv6
+from scapy.contrib.mpls import MPLS
 from framework import VppTestRunner
 from template_ipsec import TemplateIpsec, IpsecTun4Tests, IpsecTun6Tests, \
     IpsecTun4, IpsecTun6,  IpsecTcpTests, mk_scapy_crypt_key, \
     IpsecTun6HandoffTests, IpsecTun4HandoffTests, config_tun_params
 from framework import VppTestRunner
 from template_ipsec import TemplateIpsec, IpsecTun4Tests, IpsecTun6Tests, \
     IpsecTun4, IpsecTun6,  IpsecTcpTests, mk_scapy_crypt_key, \
     IpsecTun6HandoffTests, IpsecTun4HandoffTests, config_tun_params
-from vpp_ipsec_tun_interface import VppIpsecTunInterface
 from vpp_gre_interface import VppGreInterface
 from vpp_ipip_tun_interface import VppIpIpTunInterface
 from vpp_gre_interface import VppGreInterface
 from vpp_ipip_tun_interface import VppIpIpTunInterface
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
-from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect
+from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppMplsLabel, \
+    VppMplsTable, VppMplsRoute, FibPathProto
+from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect, VppIpsecInterface
 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
 from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
 from vpp_teib import VppTeib
 from util import ppp
 from vpp_papi import VppEnum
 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
 from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
 from vpp_teib import VppTeib
 from util import ppp
 from vpp_papi import VppEnum
+from vpp_papi_provider import CliFailedCommandError
+from vpp_acl import AclRule, VppAcl, VppAclInterface
 
 
 
 
-def config_tun_params(p, encryption_type, tun_if):
+def config_tun_params(p, encryption_type, tun_if, src=None, dst=None):
     ip_class_by_addr_type = {socket.AF_INET: IP, socket.AF_INET6: IPv6}
     esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t.
                              IPSEC_API_SAD_FLAG_USE_ESN))
     crypt_key = mk_scapy_crypt_key(p)
     ip_class_by_addr_type = {socket.AF_INET: IP, socket.AF_INET6: IPv6}
     esn_en = bool(p.flags & (VppEnum.vl_api_ipsec_sad_flags_t.
                              IPSEC_API_SAD_FLAG_USE_ESN))
     crypt_key = mk_scapy_crypt_key(p)
-    p.tun_dst = tun_if.remote_ip
-    p.tun_src = tun_if.local_ip
+    if tun_if:
+        p.tun_dst = tun_if.remote_ip
+        p.tun_src = tun_if.local_ip
+    else:
+        p.tun_dst = dst
+        p.tun_src = src
+
     p.scapy_tun_sa = SecurityAssociation(
         encryption_type, spi=p.vpp_tun_spi,
         crypt_algo=p.crypt_algo,
     p.scapy_tun_sa = SecurityAssociation(
         encryption_type, spi=p.vpp_tun_spi,
         crypt_algo=p.crypt_algo,
@@ -64,16 +72,107 @@ def config_tra_params(p, encryption_type, tun_if):
         crypt_algo=p.crypt_algo,
         crypt_key=crypt_key,
         auth_algo=p.auth_algo, auth_key=p.auth_key,
         crypt_algo=p.crypt_algo,
         crypt_key=crypt_key,
         auth_algo=p.auth_algo, auth_key=p.auth_key,
-        esn_en=esn_en)
+        esn_en=esn_en,
+        nat_t_header=p.nat_header)
     p.vpp_tun_sa = SecurityAssociation(
         encryption_type, spi=p.scapy_tun_spi,
         crypt_algo=p.crypt_algo,
         crypt_key=crypt_key,
         auth_algo=p.auth_algo, auth_key=p.auth_key,
     p.vpp_tun_sa = SecurityAssociation(
         encryption_type, spi=p.scapy_tun_spi,
         crypt_algo=p.crypt_algo,
         crypt_key=crypt_key,
         auth_algo=p.auth_algo, auth_key=p.auth_key,
-        esn_en=esn_en)
+        esn_en=esn_en,
+        nat_t_header=p.nat_header)
+
+
+class TemplateIpsec4TunProtect(object):
+    """ IPsec IPv4 Tunnel protect """
+
+    encryption_type = ESP
+    tun4_encrypt_node_name = "esp4-encrypt-tun"
+    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_input_node = "ipsec4-tun-input"
+
+    def config_sa_tra(self, p):
+        config_tun_params(p, self.encryption_type, p.tun_if)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  flags=p.flags)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 flags=p.flags)
+        p.tun_sa_in.add_vpp_config()
+
+    def config_sa_tun(self, p):
+        config_tun_params(p, self.encryption_type, p.tun_if)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  self.tun_if.local_addr[p.addr_type],
+                                  self.tun_if.remote_addr[p.addr_type],
+                                  flags=p.flags)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 self.tun_if.remote_addr[p.addr_type],
+                                 self.tun_if.local_addr[p.addr_type],
+                                 flags=p.flags)
+        p.tun_sa_in.add_vpp_config()
+
+    def config_protect(self, p):
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+        p.tun_protect.add_vpp_config()
+
+    def config_network(self, p):
+        if hasattr(p, 'tun_dst'):
+            tun_dst = p.tun_dst
+        else:
+            tun_dst = self.pg0.remote_ip4
+        p.tun_if = VppIpIpTunInterface(self, self.pg0,
+                                       self.pg0.local_ip4,
+                                       tun_dst)
+        p.tun_if.add_vpp_config()
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        p.tun_if.config_ip6()
+
+        p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
+                             [VppRoutePath(p.tun_if.remote_ip4,
+                                           0xffffffff)])
+        p.route.add_vpp_config()
+        r = VppIpRoute(self, p.remote_tun_if_host6, 128,
+                       [VppRoutePath(p.tun_if.remote_ip6,
+                                     0xffffffff,
+                                     proto=DpoProto.DPO_PROTO_IP6)])
+        r.add_vpp_config()
+
+    def unconfig_network(self, p):
+        p.route.remove_vpp_config()
+        p.tun_if.remove_vpp_config()
+
+    def unconfig_protect(self, p):
+        p.tun_protect.remove_vpp_config()
+
+    def unconfig_sa(self, p):
+        p.tun_sa_out.remove_vpp_config()
+        p.tun_sa_in.remove_vpp_config()
 
 
 
 
-class TemplateIpsec4TunIfEsp(TemplateIpsec):
+class TemplateIpsec4TunIfEsp(TemplateIpsec4TunProtect,
+                             TemplateIpsec):
     """ IPsec tunnel interface tests """
 
     encryption_type = ESP
     """ IPsec tunnel interface tests """
 
     encryption_type = ESP
@@ -93,32 +192,16 @@ class TemplateIpsec4TunIfEsp(TemplateIpsec):
 
         p = self.ipv4_params
 
 
         p = self.ipv4_params
 
-        p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                        p.scapy_tun_spi, p.crypt_algo_vpp_id,
-                                        p.crypt_key, p.crypt_key,
-                                        p.auth_algo_vpp_id, p.auth_key,
-                                        p.auth_key)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip4()
-        p.tun_if.config_ip6()
-        config_tun_params(p, self.encryption_type, p.tun_if)
-
-        r = VppIpRoute(self, p.remote_tun_if_host, 32,
-                       [VppRoutePath(p.tun_if.remote_ip4,
-                                     0xffffffff)])
-        r.add_vpp_config()
-        r = VppIpRoute(self, p.remote_tun_if_host6, 128,
-                       [VppRoutePath(p.tun_if.remote_ip6,
-                                     0xffffffff,
-                                     proto=DpoProto.DPO_PROTO_IP6)])
-        r.add_vpp_config()
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
     def tearDown(self):
         super(TemplateIpsec4TunIfEsp, self).tearDown()
 
 
 
     def tearDown(self):
         super(TemplateIpsec4TunIfEsp, self).tearDown()
 
 
-class TemplateIpsec4TunIfEspUdp(TemplateIpsec):
+class TemplateIpsec4TunIfEspUdp(TemplateIpsec4TunProtect,
+                                TemplateIpsec):
     """ IPsec UDP tunnel interface tests """
 
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     """ IPsec UDP tunnel interface tests """
 
     tun4_encrypt_node_name = "esp4-encrypt-tun"
@@ -139,7 +222,7 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec):
                 # ensure the UDP ports are correct before we decrypt
                 # which strips them
                 self.assertTrue(rx.haslayer(UDP))
                 # ensure the UDP ports are correct before we decrypt
                 # which strips them
                 self.assertTrue(rx.haslayer(UDP))
-                self.assert_equal(rx[UDP].sport, 4500)
+                self.assert_equal(rx[UDP].sport, p.nat_header.sport)
                 self.assert_equal(rx[UDP].dport, 4500)
 
                 pkt = sa.decrypt(rx[IP])
                 self.assert_equal(rx[UDP].dport, 4500)
 
                 pkt = sa.decrypt(rx[IP])
@@ -157,6 +240,27 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec):
                     pass
                 raise
 
                     pass
                 raise
 
+    def config_sa_tra(self, p):
+        config_tun_params(p, self.encryption_type, p.tun_if)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  flags=p.flags,
+                                  udp_src=p.nat_header.sport,
+                                  udp_dst=p.nat_header.dport)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 flags=p.flags,
+                                 udp_src=p.nat_header.sport,
+                                 udp_dst=p.nat_header.dport)
+        p.tun_sa_in.add_vpp_config()
+
     def setUp(self):
         super(TemplateIpsec4TunIfEspUdp, self).setUp()
 
     def setUp(self):
         super(TemplateIpsec4TunIfEspUdp, self).setUp()
 
@@ -165,30 +269,11 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec):
                    IPSEC_API_SAD_FLAG_UDP_ENCAP)
         p.nat_header = UDP(sport=5454, dport=4500)
 
                    IPSEC_API_SAD_FLAG_UDP_ENCAP)
         p.nat_header = UDP(sport=5454, dport=4500)
 
-    def config_network(self):
-
         self.tun_if = self.pg0
         self.tun_if = self.pg0
-        p = self.ipv4_params
-        p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                        p.scapy_tun_spi, p.crypt_algo_vpp_id,
-                                        p.crypt_key, p.crypt_key,
-                                        p.auth_algo_vpp_id, p.auth_key,
-                                        p.auth_key, udp_encap=True)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip4()
-        p.tun_if.config_ip6()
-        config_tun_params(p, self.encryption_type, p.tun_if)
 
 
-        r = VppIpRoute(self, p.remote_tun_if_host, 32,
-                       [VppRoutePath(p.tun_if.remote_ip4,
-                                     0xffffffff)])
-        r.add_vpp_config()
-        r = VppIpRoute(self, p.remote_tun_if_host6, 128,
-                       [VppRoutePath(p.tun_if.remote_ip6,
-                                     0xffffffff,
-                                     proto=DpoProto.DPO_PROTO_IP6)])
-        r.add_vpp_config()
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
     def tearDown(self):
         super(TemplateIpsec4TunIfEspUdp, self).tearDown()
 
     def tearDown(self):
         super(TemplateIpsec4TunIfEspUdp, self).tearDown()
@@ -231,8 +316,7 @@ class TestIpsec4TunIfEspUdp(TemplateIpsec4TunIfEspUdp, IpsecTun4Tests):
     tun4_input_node = "ipsec4-tun-input"
 
     def setUp(self):
     tun4_input_node = "ipsec4-tun-input"
 
     def setUp(self):
-        super(TemplateIpsec4TunIfEspUdp, self).setUp()
-        self.config_network()
+        super(TestIpsec4TunIfEspUdp, self).setUp()
 
     def test_keepalive(self):
         """ IPSEC NAT Keepalive """
 
     def test_keepalive(self):
         """ IPSEC NAT Keepalive """
@@ -245,7 +329,7 @@ class TestIpsec4TunIfEspUdpGCM(TemplateIpsec4TunIfEspUdp, IpsecTun4Tests):
     tun4_input_node = "ipsec4-tun-input"
 
     def setUp(self):
     tun4_input_node = "ipsec4-tun-input"
 
     def setUp(self):
-        super(TemplateIpsec4TunIfEspUdp, self).setUp()
+        super(TestIpsec4TunIfEspUdpGCM, self).setUp()
         p = self.ipv4_params
         p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                               IPSEC_API_INTEG_ALG_NONE)
         p = self.ipv4_params
         p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                               IPSEC_API_INTEG_ALG_NONE)
@@ -255,7 +339,6 @@ class TestIpsec4TunIfEspUdpGCM(TemplateIpsec4TunIfEspUdp, IpsecTun4Tests):
         p.auth_algo = "NULL"
         p.crypt_key = b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h"
         p.salt = 0
         p.auth_algo = "NULL"
         p.crypt_key = b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h"
         p.salt = 0
-        self.config_network()
 
 
 class TestIpsec4TunIfEsp2(TemplateIpsec4TunIfEsp, IpsecTcpTests):
 
 
 class TestIpsec4TunIfEsp2(TemplateIpsec4TunIfEsp, IpsecTcpTests):
@@ -263,38 +346,101 @@ class TestIpsec4TunIfEsp2(TemplateIpsec4TunIfEsp, IpsecTcpTests):
     pass
 
 
     pass
 
 
-class TemplateIpsec6TunIfEsp(TemplateIpsec):
-    """ IPsec tunnel interface tests """
+class TemplateIpsec6TunProtect(object):
+    """ IPsec IPv6 Tunnel protect """
 
 
-    encryption_type = ESP
+    def config_sa_tra(self, p):
+        config_tun_params(p, self.encryption_type, p.tun_if)
 
 
-    def setUp(self):
-        super(TemplateIpsec6TunIfEsp, self).setUp()
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol)
+        p.tun_sa_out.add_vpp_config()
 
 
-        self.tun_if = self.pg0
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol)
+        p.tun_sa_in.add_vpp_config()
 
 
-        p = self.ipv6_params
-        p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                        p.scapy_tun_spi, p.crypt_algo_vpp_id,
-                                        p.crypt_key, p.crypt_key,
-                                        p.auth_algo_vpp_id, p.auth_key,
-                                        p.auth_key, is_ip6=True)
+    def config_sa_tun(self, p):
+        config_tun_params(p, self.encryption_type, p.tun_if)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  self.tun_if.local_addr[p.addr_type],
+                                  self.tun_if.remote_addr[p.addr_type])
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 self.tun_if.remote_addr[p.addr_type],
+                                 self.tun_if.local_addr[p.addr_type])
+        p.tun_sa_in.add_vpp_config()
+
+    def config_protect(self, p):
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+        p.tun_protect.add_vpp_config()
+
+    def config_network(self, p):
+        if hasattr(p, 'tun_dst'):
+            tun_dst = p.tun_dst
+        else:
+            tun_dst = self.pg0.remote_ip6
+        p.tun_if = VppIpIpTunInterface(self, self.pg0,
+                                       self.pg0.local_ip6,
+                                       tun_dst)
         p.tun_if.add_vpp_config()
         p.tun_if.admin_up()
         p.tun_if.config_ip6()
         p.tun_if.config_ip4()
         p.tun_if.add_vpp_config()
         p.tun_if.admin_up()
         p.tun_if.config_ip6()
         p.tun_if.config_ip4()
-        config_tun_params(p, self.encryption_type, p.tun_if)
 
 
-        r = VppIpRoute(self, p.remote_tun_if_host, 128,
-                       [VppRoutePath(p.tun_if.remote_ip6,
-                                     0xffffffff,
-                                     proto=DpoProto.DPO_PROTO_IP6)])
-        r.add_vpp_config()
+        p.route = VppIpRoute(self, p.remote_tun_if_host, 128,
+                             [VppRoutePath(p.tun_if.remote_ip6,
+                                           0xffffffff,
+                                           proto=DpoProto.DPO_PROTO_IP6)])
+        p.route.add_vpp_config()
         r = VppIpRoute(self, p.remote_tun_if_host4, 32,
                        [VppRoutePath(p.tun_if.remote_ip4,
                                      0xffffffff)])
         r.add_vpp_config()
 
         r = VppIpRoute(self, p.remote_tun_if_host4, 32,
                        [VppRoutePath(p.tun_if.remote_ip4,
                                      0xffffffff)])
         r.add_vpp_config()
 
+    def unconfig_network(self, p):
+        p.route.remove_vpp_config()
+        p.tun_if.remove_vpp_config()
+
+    def unconfig_protect(self, p):
+        p.tun_protect.remove_vpp_config()
+
+    def unconfig_sa(self, p):
+        p.tun_sa_out.remove_vpp_config()
+        p.tun_sa_in.remove_vpp_config()
+
+
+class TemplateIpsec6TunIfEsp(TemplateIpsec6TunProtect,
+                             TemplateIpsec):
+    """ IPsec tunnel interface tests """
+
+    encryption_type = ESP
+
+    def setUp(self):
+        super(TemplateIpsec6TunIfEsp, self).setUp()
+
+        self.tun_if = self.pg0
+
+        p = self.ipv6_params
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
+
     def tearDown(self):
         super(TemplateIpsec6TunIfEsp, self).tearDown()
 
     def tearDown(self):
         super(TemplateIpsec6TunIfEsp, self).tearDown()
 
@@ -330,7 +476,9 @@ class TestIpsec4TunIfEspHandoff(TemplateIpsec4TunIfEsp,
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
 
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
 
-class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4):
+class TestIpsec4MultiTunIfEsp(TemplateIpsec4TunProtect,
+                              TemplateIpsec,
+                              IpsecTun4):
     """ IPsec IPv4 Multi Tunnel interface """
 
     encryption_type = ESP
     """ IPsec IPv4 Multi Tunnel interface """
 
     encryption_type = ESP
@@ -361,22 +509,10 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4):
             p.vpp_tra_spi = p.vpp_tra_spi + ii
             p.tun_dst = self.pg0.remote_hosts[ii].ip4
 
             p.vpp_tra_spi = p.vpp_tra_spi + ii
             p.tun_dst = self.pg0.remote_hosts[ii].ip4
 
-            p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                            p.scapy_tun_spi,
-                                            p.crypt_algo_vpp_id,
-                                            p.crypt_key, p.crypt_key,
-                                            p.auth_algo_vpp_id, p.auth_key,
-                                            p.auth_key,
-                                            dst=p.tun_dst)
-            p.tun_if.add_vpp_config()
-            p.tun_if.admin_up()
-            p.tun_if.config_ip4()
-            config_tun_params(p, self.encryption_type, p.tun_if)
             self.multi_params.append(p)
             self.multi_params.append(p)
-
-            VppIpRoute(self, p.remote_tun_if_host, 32,
-                       [VppRoutePath(p.tun_if.remote_ip4,
-                                     0xffffffff)]).add_vpp_config()
+            self.config_network(p)
+            self.config_sa_tra(p)
+            self.config_protect(p)
 
     def tearDown(self):
         super(TestIpsec4MultiTunIfEsp, self).tearDown()
 
     def tearDown(self):
         super(TestIpsec4MultiTunIfEsp, self).tearDown()
@@ -412,51 +548,38 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4):
             self.verify_encrypted(p, p.vpp_tun_sa, [rx])
 
 
             self.verify_encrypted(p, p.vpp_tun_sa, [rx])
 
 
-class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
+class TestIpsec4TunIfEspAll(TemplateIpsec4TunProtect,
+                            TemplateIpsec,
+                            IpsecTun4):
     """ IPsec IPv4 Tunnel interface all Algos """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
     """ IPsec IPv4 Tunnel interface all Algos """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
-    def config_network(self, p):
-
-        p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                        p.scapy_tun_spi,
-                                        p.crypt_algo_vpp_id,
-                                        p.crypt_key, p.crypt_key,
-                                        p.auth_algo_vpp_id, p.auth_key,
-                                        p.auth_key,
-                                        salt=p.salt)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip4()
-        config_tun_params(p, self.encryption_type, p.tun_if)
-        self.logger.info(self.vapi.cli("sh ipsec sa 0"))
-        self.logger.info(self.vapi.cli("sh ipsec sa 1"))
-
-        p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
-                             [VppRoutePath(p.tun_if.remote_ip4,
-                                           0xffffffff)])
-        p.route.add_vpp_config()
-
-    def unconfig_network(self, p):
-        p.tun_if.unconfig_ip4()
-        p.tun_if.remove_vpp_config()
-        p.route.remove_vpp_config()
-
     def setUp(self):
         super(TestIpsec4TunIfEspAll, self).setUp()
 
         self.tun_if = self.pg0
     def setUp(self):
         super(TestIpsec4TunIfEspAll, self).setUp()
 
         self.tun_if = self.pg0
+        p = self.ipv4_params
 
 
-    def tearDown(self):
-        super(TestIpsec4TunIfEspAll, self).tearDown()
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
 
-    def rekey(self, p):
+    def tearDown(self):
+        p = self.ipv4_params
+        self.unconfig_protect(p)
+        self.unconfig_network(p)
+        self.unconfig_sa(p)
+
+        super(TestIpsec4TunIfEspAll, self).tearDown()
+
+    def rekey(self, p):
         #
         # change the key and the SPI
         #
         #
         # change the key and the SPI
         #
+        np = copy.copy(p)
         p.crypt_key = b'X' + p.crypt_key[1:]
         p.scapy_tun_spi += 1
         p.scapy_tun_sa_id += 1
         p.crypt_key = b'X' + p.crypt_key[1:]
         p.scapy_tun_spi += 1
         p.scapy_tun_sa_id += 1
@@ -467,19 +590,9 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
 
         config_tun_params(p, self.encryption_type, p.tun_if)
 
 
         config_tun_params(p, self.encryption_type, p.tun_if)
 
-        p.tun_sa_in = VppIpsecSA(self,
-                                 p.scapy_tun_sa_id,
-                                 p.scapy_tun_spi,
-                                 p.auth_algo_vpp_id,
-                                 p.auth_key,
-                                 p.crypt_algo_vpp_id,
-                                 p.crypt_key,
-                                 self.vpp_esp_protocol,
-                                 flags=p.flags,
-                                 salt=p.salt)
         p.tun_sa_out = VppIpsecSA(self,
         p.tun_sa_out = VppIpsecSA(self,
-                                  p.vpp_tun_sa_id,
-                                  p.vpp_tun_spi,
+                                  p.scapy_tun_sa_id,
+                                  p.scapy_tun_spi,
                                   p.auth_algo_vpp_id,
                                   p.auth_key,
                                   p.crypt_algo_vpp_id,
                                   p.auth_algo_vpp_id,
                                   p.auth_key,
                                   p.crypt_algo_vpp_id,
@@ -487,15 +600,22 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
                                   self.vpp_esp_protocol,
                                   flags=p.flags,
                                   salt=p.salt)
                                   self.vpp_esp_protocol,
                                   flags=p.flags,
                                   salt=p.salt)
+        p.tun_sa_in = VppIpsecSA(self,
+                                 p.vpp_tun_sa_id,
+                                 p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id,
+                                 p.auth_key,
+                                 p.crypt_algo_vpp_id,
+                                 p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 flags=p.flags,
+                                 salt=p.salt)
         p.tun_sa_in.add_vpp_config()
         p.tun_sa_out.add_vpp_config()
 
         p.tun_sa_in.add_vpp_config()
         p.tun_sa_out.add_vpp_config()
 
-        self.vapi.ipsec_tunnel_if_set_sa(sw_if_index=p.tun_if.sw_if_index,
-                                         sa_id=p.tun_sa_in.id,
-                                         is_outbound=1)
-        self.vapi.ipsec_tunnel_if_set_sa(sw_if_index=p.tun_if.sw_if_index,
-                                         sa_id=p.tun_sa_out.id,
-                                         is_outbound=0)
+        self.config_protect(p)
+        np.tun_sa_out.remove_vpp_config()
+        np.tun_sa_in.remove_vpp_config()
         self.logger.info(self.vapi.cli("sh ipsec sa"))
 
     def test_tun_44(self):
         self.logger.info(self.vapi.cli("sh ipsec sa"))
 
     def test_tun_44(self):
@@ -540,17 +660,17 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
                                  IPSEC_API_CRYPTO_ALG_AES_CBC_192),
                   'vpp-integ': (VppEnum.vl_api_ipsec_integ_alg_t.
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
                                  IPSEC_API_CRYPTO_ALG_AES_CBC_192),
                   'vpp-integ': (VppEnum.vl_api_ipsec_integ_alg_t.
-                                IPSEC_API_INTEG_ALG_SHA1_96),
+                                IPSEC_API_INTEG_ALG_SHA_512_256),
                   'scapy-crypto': "AES-CBC",
                   'scapy-crypto': "AES-CBC",
-                  'scapy-integ': "HMAC-SHA1-96",
+                  'scapy-integ': "SHA2-512-256",
                   'salt': 0,
                   'key': b"JPjyOWBeVEQiMe7hJPjyOWBe"},
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
                                  IPSEC_API_CRYPTO_ALG_AES_CBC_256),
                   'vpp-integ': (VppEnum.vl_api_ipsec_integ_alg_t.
                   'salt': 0,
                   'key': b"JPjyOWBeVEQiMe7hJPjyOWBe"},
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
                                  IPSEC_API_CRYPTO_ALG_AES_CBC_256),
                   'vpp-integ': (VppEnum.vl_api_ipsec_integ_alg_t.
-                                IPSEC_API_INTEG_ALG_SHA1_96),
+                                IPSEC_API_INTEG_ALG_SHA_256_128),
                   'scapy-crypto': "AES-CBC",
                   'scapy-crypto': "AES-CBC",
-                  'scapy-integ': "HMAC-SHA1-96",
+                  'scapy-integ': "SHA2-256-128",
                   'salt': 0,
                   'key': b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h"},
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
                   'salt': 0,
                   'key': b"JPjyOWBeVEQiMe7hJPjyOWBeVEQiMe7h"},
                  {'vpp-crypto': (VppEnum.vl_api_ipsec_crypto_alg_t.
@@ -571,7 +691,7 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
             for algo in algos:
                 # with self.subTest(algo=algo['scapy']):
 
             for algo in algos:
                 # with self.subTest(algo=algo['scapy']):
 
-                p = copy.copy(self.ipv4_params)
+                p = self.ipv4_params
                 p.auth_algo_vpp_id = algo['vpp-integ']
                 p.crypt_algo_vpp_id = algo['vpp-crypto']
                 p.crypt_algo = algo['scapy-crypto']
                 p.auth_algo_vpp_id = algo['vpp-integ']
                 p.crypt_algo_vpp_id = algo['vpp-crypto']
                 p.crypt_algo = algo['scapy-crypto']
@@ -579,34 +699,27 @@ class TestIpsec4TunIfEspAll(TemplateIpsec, IpsecTun4):
                 p.crypt_key = algo['key']
                 p.salt = algo['salt']
 
                 p.crypt_key = algo['key']
                 p.salt = algo['salt']
 
-                self.config_network(p)
-
-                self.verify_tun_44(p, count=127)
-                c = p.tun_if.get_rx_stats()
-                self.assertEqual(c['packets'], 127)
-                c = p.tun_if.get_tx_stats()
-                self.assertEqual(c['packets'], 127)
-
                 #
                 # rekey the tunnel
                 #
                 self.rekey(p)
                 self.verify_tun_44(p, count=127)
 
                 #
                 # rekey the tunnel
                 #
                 self.rekey(p)
                 self.verify_tun_44(p, count=127)
 
-                self.unconfig_network(p)
-                p.tun_sa_out.remove_vpp_config()
-                p.tun_sa_in.remove_vpp_config()
 
 
-
-class TestIpsec4TunIfEspNoAlgo(TemplateIpsec, IpsecTun4):
+class TestIpsec4TunIfEspNoAlgo(TemplateIpsec4TunProtect,
+                               TemplateIpsec,
+                               IpsecTun4):
     """ IPsec IPv4 Tunnel interface no Algos """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
     """ IPsec IPv4 Tunnel interface no Algos """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     tun4_decrypt_node_name = "esp4-decrypt-tun"
 
-    def config_network(self, p):
+    def setUp(self):
+        super(TestIpsec4TunIfEspNoAlgo, self).setUp()
 
 
+        self.tun_if = self.pg0
+        p = self.ipv4_params
         p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                               IPSEC_API_INTEG_ALG_NONE)
         p.auth_algo = 'NULL'
         p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                               IPSEC_API_INTEG_ALG_NONE)
         p.auth_algo = 'NULL'
@@ -617,35 +730,6 @@ class TestIpsec4TunIfEspNoAlgo(TemplateIpsec, IpsecTun4):
         p.crypt_algo = 'NULL'
         p.crypt_key = []
 
         p.crypt_algo = 'NULL'
         p.crypt_key = []
 
-        p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                        p.scapy_tun_spi,
-                                        p.crypt_algo_vpp_id,
-                                        p.crypt_key, p.crypt_key,
-                                        p.auth_algo_vpp_id, p.auth_key,
-                                        p.auth_key,
-                                        salt=p.salt)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip4()
-        config_tun_params(p, self.encryption_type, p.tun_if)
-        self.logger.info(self.vapi.cli("sh ipsec sa 0"))
-        self.logger.info(self.vapi.cli("sh ipsec sa 1"))
-
-        p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
-                             [VppRoutePath(p.tun_if.remote_ip4,
-                                           0xffffffff)])
-        p.route.add_vpp_config()
-
-    def unconfig_network(self, p):
-        p.tun_if.unconfig_ip4()
-        p.tun_if.remove_vpp_config()
-        p.route.remove_vpp_config()
-
-    def setUp(self):
-        super(TestIpsec4TunIfEspNoAlgo, self).setUp()
-
-        self.tun_if = self.pg0
-
     def tearDown(self):
         super(TestIpsec4TunIfEspNoAlgo, self).tearDown()
 
     def tearDown(self):
         super(TestIpsec4TunIfEspNoAlgo, self).tearDown()
 
@@ -654,15 +738,21 @@ class TestIpsec4TunIfEspNoAlgo(TemplateIpsec, IpsecTun4):
         p = self.ipv4_params
 
         self.config_network(p)
         p = self.ipv4_params
 
         self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
         tx = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4,
                            dst=p.remote_tun_if_host)
         self.send_and_assert_no_replies(self.pg1, tx)
 
 
         tx = self.gen_pkts(self.pg1, src=self.pg1.remote_ip4,
                            dst=p.remote_tun_if_host)
         self.send_and_assert_no_replies(self.pg1, tx)
 
+        self.unconfig_protect(p)
+        self.unconfig_sa(p)
         self.unconfig_network(p)
 
 
         self.unconfig_network(p)
 
 
-class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6):
+class TestIpsec6MultiTunIfEsp(TemplateIpsec6TunProtect,
+                              TemplateIpsec,
+                              IpsecTun6):
     """ IPsec IPv6 Multi Tunnel interface """
 
     encryption_type = ESP
     """ IPsec IPv6 Multi Tunnel interface """
 
     encryption_type = ESP
@@ -691,25 +781,12 @@ class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6):
             p.scapy_tra_spi = p.scapy_tra_spi + ii
             p.vpp_tra_sa_id = p.vpp_tra_sa_id + ii
             p.vpp_tra_spi = p.vpp_tra_spi + ii
             p.scapy_tra_spi = p.scapy_tra_spi + ii
             p.vpp_tra_sa_id = p.vpp_tra_sa_id + ii
             p.vpp_tra_spi = p.vpp_tra_spi + ii
+            p.tun_dst = self.pg0.remote_hosts[ii].ip6
 
 
-            p.tun_if = VppIpsecTunInterface(self, self.pg0, p.vpp_tun_spi,
-                                            p.scapy_tun_spi,
-                                            p.crypt_algo_vpp_id,
-                                            p.crypt_key, p.crypt_key,
-                                            p.auth_algo_vpp_id, p.auth_key,
-                                            p.auth_key, is_ip6=True,
-                                            dst=self.pg0.remote_hosts[ii].ip6)
-            p.tun_if.add_vpp_config()
-            p.tun_if.admin_up()
-            p.tun_if.config_ip6()
-            config_tun_params(p, self.encryption_type, p.tun_if)
             self.multi_params.append(p)
             self.multi_params.append(p)
-
-            r = VppIpRoute(self, p.remote_tun_if_host, 128,
-                           [VppRoutePath(p.tun_if.remote_ip6,
-                                         0xffffffff,
-                                         proto=DpoProto.DPO_PROTO_IP6)])
-            r.add_vpp_config()
+            self.config_network(p)
+            self.config_sa_tra(p)
+            self.config_protect(p)
 
     def tearDown(self):
         super(TestIpsec6MultiTunIfEsp, self).tearDown()
 
     def tearDown(self):
         super(TestIpsec6MultiTunIfEsp, self).tearDown()
@@ -1060,6 +1137,127 @@ class TestIpsecGreTebIfEspTra(TemplateIpsec,
         super(TestIpsecGreTebIfEspTra, self).tearDown()
 
 
         super(TestIpsecGreTebIfEspTra, self).tearDown()
 
 
+class TestIpsecGreTebUdpIfEspTra(TemplateIpsec,
+                                 IpsecTun4Tests):
+    """ Ipsec GRE TEB UDP ESP - Tra tests """
+    tun4_encrypt_node_name = "esp4-encrypt-tun"
+    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    encryption_type = ESP
+    omac = "00:11:22:33:44:55"
+
+    def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
+                         payload_size=100):
+        return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
+                sa.encrypt(IP(src=self.pg0.remote_ip4,
+                              dst=self.pg0.local_ip4) /
+                           GRE() /
+                           Ether(dst=self.omac) /
+                           IP(src="1.1.1.1", dst="1.1.1.2") /
+                           UDP(sport=1144, dport=2233) /
+                           Raw(b'X' * payload_size))
+                for i in range(count)]
+
+    def gen_pkts(self, sw_intf, src, dst, count=1,
+                 payload_size=100):
+        return [Ether(dst=self.omac) /
+                IP(src="1.1.1.1", dst="1.1.1.2") /
+                UDP(sport=1144, dport=2233) /
+                Raw(b'X' * payload_size)
+                for i in range(count)]
+
+    def verify_decrypted(self, p, rxs):
+        for rx in rxs:
+            self.assert_equal(rx[Ether].dst, self.omac)
+            self.assert_equal(rx[IP].dst, "1.1.1.2")
+
+    def verify_encrypted(self, p, sa, rxs):
+        for rx in rxs:
+            self.assertTrue(rx.haslayer(UDP))
+            self.assertEqual(rx[UDP].dport, 4545)
+            self.assertEqual(rx[UDP].sport, 5454)
+            try:
+                pkt = sa.decrypt(rx[IP])
+                if not pkt.haslayer(IP):
+                    pkt = IP(pkt[Raw].load)
+                self.assert_packet_checksums_valid(pkt)
+                self.assert_equal(pkt[IP].dst, self.pg0.remote_ip4)
+                self.assert_equal(pkt[IP].src, self.pg0.local_ip4)
+                self.assertTrue(pkt.haslayer(GRE))
+                e = pkt[Ether]
+                self.assertEqual(e[Ether].dst, self.omac)
+                self.assertEqual(e[IP].dst, "1.1.1.2")
+            except (IndexError, AssertionError):
+                self.logger.debug(ppp("Unexpected packet:", rx))
+                try:
+                    self.logger.debug(ppp("Decrypted packet:", pkt))
+                except:
+                    pass
+                raise
+
+    def setUp(self):
+        super(TestIpsecGreTebUdpIfEspTra, self).setUp()
+
+        self.tun_if = self.pg0
+
+        p = self.ipv4_params
+        p = self.ipv4_params
+        p.flags = (VppEnum.vl_api_ipsec_sad_flags_t.
+                   IPSEC_API_SAD_FLAG_UDP_ENCAP)
+        p.nat_header = UDP(sport=5454, dport=4545)
+
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  flags=p.flags,
+                                  udp_src=5454,
+                                  udp_dst=4545)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 flags=(p.flags |
+                                        VppEnum.vl_api_ipsec_sad_flags_t.
+                                        IPSEC_API_SAD_FLAG_IS_INBOUND),
+                                 udp_src=5454,
+                                 udp_dst=4545)
+        p.tun_sa_in.add_vpp_config()
+
+        p.tun_if = VppGreInterface(self,
+                                   self.pg0.local_ip4,
+                                   self.pg0.remote_ip4,
+                                   type=(VppEnum.vl_api_gre_tunnel_type_t.
+                                         GRE_API_TUNNEL_TYPE_TEB))
+        p.tun_if.add_vpp_config()
+
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+
+        p.tun_protect.add_vpp_config()
+
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        config_tra_params(p, self.encryption_type, p.tun_if)
+
+        VppBridgeDomainPort(self, bd1, p.tun_if).add_vpp_config()
+        VppBridgeDomainPort(self, bd1, self.pg1).add_vpp_config()
+
+        self.vapi.cli("clear ipsec sa")
+        self.logger.info(self.vapi.cli("sh ipsec sa 0"))
+
+    def tearDown(self):
+        p = self.ipv4_params
+        p.tun_if.unconfig_ip4()
+        super(TestIpsecGreTebUdpIfEspTra, self).tearDown()
+
+
 class TestIpsecGreIfEsp(TemplateIpsec,
                         IpsecTun4Tests):
     """ Ipsec GRE ESP - TUN tests """
 class TestIpsecGreIfEsp(TemplateIpsec,
                         IpsecTun4Tests):
     """ Ipsec GRE ESP - TUN tests """
@@ -1446,7 +1644,7 @@ class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
         # setup some SAs for several next-hops on the interface
         self.multi_params = []
 
         # setup some SAs for several next-hops on the interface
         self.multi_params = []
 
-        for ii in range(1):
+        for ii in range(N_NHS):
             p = copy.copy(self.ipv4_params)
 
             p.remote_tun_if_host = "1.1.1.%d" % (ii + 1)
             p = copy.copy(self.ipv4_params)
 
             p.remote_tun_if_host = "1.1.1.%d" % (ii + 1)
@@ -1641,124 +1839,40 @@ class TestIpsecMGreIfEspTra6(TemplateIpsec, IpsecTun6):
             self.verify_tun_66(p, count=63)
 
 
             self.verify_tun_66(p, count=63)
 
 
-class TemplateIpsec4TunProtect(object):
-    """ IPsec IPv4 Tunnel protect """
-
-    encryption_type = ESP
-    tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
-    tun4_input_node = "ipsec4-tun-input"
+class TestIpsec4TunProtect(TemplateIpsec,
+                           TemplateIpsec4TunProtect,
+                           IpsecTun4):
+    """ IPsec IPv4 Tunnel protect - transport mode"""
 
 
-    def config_sa_tra(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
+    def setUp(self):
+        super(TestIpsec4TunProtect, self).setUp()
 
 
-        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
-                                  p.auth_algo_vpp_id, p.auth_key,
-                                  p.crypt_algo_vpp_id, p.crypt_key,
-                                  self.vpp_esp_protocol,
-                                  flags=p.flags)
-        p.tun_sa_out.add_vpp_config()
+        self.tun_if = self.pg0
 
 
-        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
-                                 p.auth_algo_vpp_id, p.auth_key,
-                                 p.crypt_algo_vpp_id, p.crypt_key,
-                                 self.vpp_esp_protocol,
-                                 flags=p.flags)
-        p.tun_sa_in.add_vpp_config()
+    def tearDown(self):
+        super(TestIpsec4TunProtect, self).tearDown()
 
 
-    def config_sa_tun(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
+    def test_tun_44(self):
+        """IPSEC tunnel protect"""
 
 
-        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
-                                  p.auth_algo_vpp_id, p.auth_key,
-                                  p.crypt_algo_vpp_id, p.crypt_key,
-                                  self.vpp_esp_protocol,
-                                  self.tun_if.local_addr[p.addr_type],
-                                  self.tun_if.remote_addr[p.addr_type],
-                                  flags=p.flags)
-        p.tun_sa_out.add_vpp_config()
+        p = self.ipv4_params
 
 
-        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
-                                 p.auth_algo_vpp_id, p.auth_key,
-                                 p.crypt_algo_vpp_id, p.crypt_key,
-                                 self.vpp_esp_protocol,
-                                 self.tun_if.remote_addr[p.addr_type],
-                                 self.tun_if.local_addr[p.addr_type],
-                                 flags=p.flags)
-        p.tun_sa_in.add_vpp_config()
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
 
-    def config_protect(self, p):
-        p.tun_protect = VppIpsecTunProtect(self,
-                                           p.tun_if,
-                                           p.tun_sa_out,
-                                           [p.tun_sa_in])
-        p.tun_protect.add_vpp_config()
+        self.verify_tun_44(p, count=127)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 127)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 127)
 
 
-    def config_network(self, p):
-        p.tun_if = VppIpIpTunInterface(self, self.pg0,
-                                       self.pg0.local_ip4,
-                                       self.pg0.remote_ip4)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip4()
-        p.tun_if.config_ip6()
-
-        p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
-                             [VppRoutePath(p.tun_if.remote_ip4,
-                                           0xffffffff)])
-        p.route.add_vpp_config()
-        r = VppIpRoute(self, p.remote_tun_if_host6, 128,
-                       [VppRoutePath(p.tun_if.remote_ip6,
-                                     0xffffffff,
-                                     proto=DpoProto.DPO_PROTO_IP6)])
-        r.add_vpp_config()
-
-    def unconfig_network(self, p):
-        p.route.remove_vpp_config()
-        p.tun_if.remove_vpp_config()
-
-    def unconfig_protect(self, p):
-        p.tun_protect.remove_vpp_config()
-
-    def unconfig_sa(self, p):
-        p.tun_sa_out.remove_vpp_config()
-        p.tun_sa_in.remove_vpp_config()
-
-
-class TestIpsec4TunProtect(TemplateIpsec,
-                           TemplateIpsec4TunProtect,
-                           IpsecTun4):
-    """ IPsec IPv4 Tunnel protect - transport mode"""
-
-    def setUp(self):
-        super(TestIpsec4TunProtect, self).setUp()
-
-        self.tun_if = self.pg0
-
-    def tearDown(self):
-        super(TestIpsec4TunProtect, self).tearDown()
-
-    def test_tun_44(self):
-        """IPSEC tunnel protect"""
-
-        p = self.ipv4_params
-
-        self.config_network(p)
-        self.config_sa_tra(p)
-        self.config_protect(p)
-
-        self.verify_tun_44(p, count=127)
-        c = p.tun_if.get_rx_stats()
-        self.assertEqual(c['packets'], 127)
-        c = p.tun_if.get_tx_stats()
-        self.assertEqual(c['packets'], 127)
-
-        self.vapi.cli("clear ipsec sa")
-        self.verify_tun_64(p, count=127)
-        c = p.tun_if.get_rx_stats()
-        self.assertEqual(c['packets'], 254)
-        c = p.tun_if.get_tx_stats()
-        self.assertEqual(c['packets'], 254)
+        self.vapi.cli("clear ipsec sa")
+        self.verify_tun_64(p, count=127)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 254)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 254)
 
         # rekey - create new SAs and update the tunnel protection
         np = copy.copy(p)
 
         # rekey - create new SAs and update the tunnel protection
         np = copy.copy(p)
@@ -1799,7 +1913,7 @@ class TestIpsec4TunProtectUdp(TemplateIpsec,
         p = self.ipv4_params
         p.flags = (VppEnum.vl_api_ipsec_sad_flags_t.
                    IPSEC_API_SAD_FLAG_UDP_ENCAP)
         p = self.ipv4_params
         p.flags = (VppEnum.vl_api_ipsec_sad_flags_t.
                    IPSEC_API_SAD_FLAG_UDP_ENCAP)
-        p.nat_header = UDP(sport=5454, dport=4500)
+        p.nat_header = UDP(sport=4500, dport=4500)
         self.config_network(p)
         self.config_sa_tra(p)
         self.config_protect(p)
         self.config_network(p)
         self.config_sa_tra(p)
         self.config_protect(p)
@@ -1811,6 +1925,13 @@ class TestIpsec4TunProtectUdp(TemplateIpsec,
         self.unconfig_network(p)
         super(TestIpsec4TunProtectUdp, self).tearDown()
 
         self.unconfig_network(p)
         super(TestIpsec4TunProtectUdp, self).tearDown()
 
+    def verify_encrypted(self, p, sa, rxs):
+        # ensure encrypted packets are recieved with the default UDP ports
+        for rx in rxs:
+            self.assertEqual(rx[UDP].sport, 4500)
+            self.assertEqual(rx[UDP].dport, 4500)
+        super(TestIpsec4TunProtectUdp, self).verify_encrypted(p, sa, rxs)
+
     def test_tun_44(self):
         """IPSEC UDP tunnel protect"""
 
     def test_tun_44(self):
         """IPSEC UDP tunnel protect"""
 
@@ -1897,6 +2018,17 @@ class TestIpsec4TunProtectTun(TemplateIpsec,
         self.config_sa_tun(p)
         self.config_protect(p)
 
         self.config_sa_tun(p)
         self.config_protect(p)
 
+        # also add an output features on the tunnel and physical interface
+        # so we test they still work
+        r_all = AclRule(True,
+                        src_prefix="0.0.0.0/0",
+                        dst_prefix="0.0.0.0/0",
+                        proto=0)
+        a = VppAcl(self, [r_all]).add_vpp_config()
+
+        VppAclInterface(self, self.pg0.sw_if_index, [a]).add_vpp_config()
+        VppAclInterface(self, p.tun_if.sw_if_index, [a]).add_vpp_config()
+
         self.verify_tun_44(p, count=127)
 
         c = p.tun_if.get_rx_stats()
         self.verify_tun_44(p, count=127)
 
         c = p.tun_if.get_rx_stats()
@@ -1978,81 +2110,6 @@ class TestIpsec4TunProtectTunDrop(TemplateIpsec,
         self.unconfig_network(p)
 
 
         self.unconfig_network(p)
 
 
-class TemplateIpsec6TunProtect(object):
-    """ IPsec IPv6 Tunnel protect """
-
-    def config_sa_tra(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
-
-        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
-                                  p.auth_algo_vpp_id, p.auth_key,
-                                  p.crypt_algo_vpp_id, p.crypt_key,
-                                  self.vpp_esp_protocol)
-        p.tun_sa_out.add_vpp_config()
-
-        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
-                                 p.auth_algo_vpp_id, p.auth_key,
-                                 p.crypt_algo_vpp_id, p.crypt_key,
-                                 self.vpp_esp_protocol)
-        p.tun_sa_in.add_vpp_config()
-
-    def config_sa_tun(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
-
-        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
-                                  p.auth_algo_vpp_id, p.auth_key,
-                                  p.crypt_algo_vpp_id, p.crypt_key,
-                                  self.vpp_esp_protocol,
-                                  self.tun_if.local_addr[p.addr_type],
-                                  self.tun_if.remote_addr[p.addr_type])
-        p.tun_sa_out.add_vpp_config()
-
-        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
-                                 p.auth_algo_vpp_id, p.auth_key,
-                                 p.crypt_algo_vpp_id, p.crypt_key,
-                                 self.vpp_esp_protocol,
-                                 self.tun_if.remote_addr[p.addr_type],
-                                 self.tun_if.local_addr[p.addr_type])
-        p.tun_sa_in.add_vpp_config()
-
-    def config_protect(self, p):
-        p.tun_protect = VppIpsecTunProtect(self,
-                                           p.tun_if,
-                                           p.tun_sa_out,
-                                           [p.tun_sa_in])
-        p.tun_protect.add_vpp_config()
-
-    def config_network(self, p):
-        p.tun_if = VppIpIpTunInterface(self, self.pg0,
-                                       self.pg0.local_ip6,
-                                       self.pg0.remote_ip6)
-        p.tun_if.add_vpp_config()
-        p.tun_if.admin_up()
-        p.tun_if.config_ip6()
-        p.tun_if.config_ip4()
-
-        p.route = VppIpRoute(self, p.remote_tun_if_host, 128,
-                             [VppRoutePath(p.tun_if.remote_ip6,
-                                           0xffffffff,
-                                           proto=DpoProto.DPO_PROTO_IP6)])
-        p.route.add_vpp_config()
-        r = VppIpRoute(self, p.remote_tun_if_host4, 32,
-                       [VppRoutePath(p.tun_if.remote_ip4,
-                                     0xffffffff)])
-        r.add_vpp_config()
-
-    def unconfig_network(self, p):
-        p.route.remove_vpp_config()
-        p.tun_if.remove_vpp_config()
-
-    def unconfig_protect(self, p):
-        p.tun_protect.remove_vpp_config()
-
-    def unconfig_sa(self, p):
-        p.tun_sa_out.remove_vpp_config()
-        p.tun_sa_in.remove_vpp_config()
-
-
 class TestIpsec6TunProtect(TemplateIpsec,
                            TemplateIpsec6TunProtect,
                            IpsecTun6):
 class TestIpsec6TunProtect(TemplateIpsec,
                            TemplateIpsec6TunProtect,
                            IpsecTun6):
@@ -2331,5 +2388,620 @@ class TestIpsec6TunProtectTunDrop(TemplateIpsec,
         self.unconfig_network(p)
 
 
         self.unconfig_network(p)
 
 
+class TemplateIpsecItf4(object):
+    """ IPsec Interface IPv4 """
+
+    encryption_type = ESP
+    tun4_encrypt_node_name = "esp4-encrypt-tun"
+    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_input_node = "ipsec4-tun-input"
+
+    def config_sa_tun(self, p, src, dst):
+        config_tun_params(p, self.encryption_type, None, src, dst)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  src, dst,
+                                  flags=p.flags)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 dst, src,
+                                 flags=p.flags)
+        p.tun_sa_in.add_vpp_config()
+
+    def config_protect(self, p):
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+        p.tun_protect.add_vpp_config()
+
+    def config_network(self, p, instance=0xffffffff):
+        p.tun_if = VppIpsecInterface(self, instance=instance)
+
+        p.tun_if.add_vpp_config()
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        p.tun_if.config_ip6()
+
+        p.route = VppIpRoute(self, p.remote_tun_if_host, 32,
+                             [VppRoutePath(p.tun_if.remote_ip4,
+                                           0xffffffff)])
+        p.route.add_vpp_config()
+        r = VppIpRoute(self, p.remote_tun_if_host6, 128,
+                       [VppRoutePath(p.tun_if.remote_ip6,
+                                     0xffffffff,
+                                     proto=DpoProto.DPO_PROTO_IP6)])
+        r.add_vpp_config()
+
+    def unconfig_network(self, p):
+        p.route.remove_vpp_config()
+        p.tun_if.remove_vpp_config()
+
+    def unconfig_protect(self, p):
+        p.tun_protect.remove_vpp_config()
+
+    def unconfig_sa(self, p):
+        p.tun_sa_out.remove_vpp_config()
+        p.tun_sa_in.remove_vpp_config()
+
+
+class TestIpsecItf4(TemplateIpsec,
+                    TemplateIpsecItf4,
+                    IpsecTun4):
+    """ IPsec Interface IPv4 """
+
+    def setUp(self):
+        super(TestIpsecItf4, self).setUp()
+
+        self.tun_if = self.pg0
+
+    def tearDown(self):
+        super(TestIpsecItf4, self).tearDown()
+
+    def test_tun_instance_44(self):
+        p = self.ipv4_params
+        self.config_network(p, instance=3)
+
+        with self.assertRaises(CliFailedCommandError):
+            self.vapi.cli("show interface ipsec0")
+
+        output = self.vapi.cli("show interface ipsec3")
+        self.assertTrue("unknown" not in output)
+
+        self.unconfig_network(p)
+
+    def test_tun_44(self):
+        """IPSEC interface IPv4"""
+
+        n_pkts = 127
+        p = self.ipv4_params
+
+        self.config_network(p)
+        self.config_sa_tun(p,
+                           self.pg0.local_ip4,
+                           self.pg0.remote_ip4)
+        self.config_protect(p)
+
+        self.verify_tun_44(p, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+
+        p.tun_if.admin_down()
+        self.verify_tun_dropped_44(p, count=n_pkts)
+        p.tun_if.admin_up()
+        self.verify_tun_44(p, count=n_pkts)
+
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 3*n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 2*n_pkts)
+
+        # it's a v6 packet when its encrypted
+        self.tun4_encrypt_node_name = "esp6-encrypt-tun"
+
+        self.verify_tun_64(p, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 4*n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 3*n_pkts)
+
+        self.tun4_encrypt_node_name = "esp4-encrypt-tun"
+
+        self.vapi.cli("clear interfaces")
+
+        # rekey - create new SAs and update the tunnel protection
+        np = copy.copy(p)
+        np.crypt_key = b'X' + p.crypt_key[1:]
+        np.scapy_tun_spi += 100
+        np.scapy_tun_sa_id += 1
+        np.vpp_tun_spi += 100
+        np.vpp_tun_sa_id += 1
+        np.tun_if.local_spi = p.vpp_tun_spi
+        np.tun_if.remote_spi = p.scapy_tun_spi
+
+        self.config_sa_tun(np,
+                           self.pg0.local_ip4,
+                           self.pg0.remote_ip4)
+        self.config_protect(np)
+        self.unconfig_sa(p)
+
+        self.verify_tun_44(np, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+
+        # teardown
+        self.unconfig_protect(np)
+        self.unconfig_sa(np)
+        self.unconfig_network(p)
+
+    def test_tun_44_null(self):
+        """IPSEC interface IPv4 NULL auth/crypto"""
+
+        n_pkts = 127
+        p = copy.copy(self.ipv4_params)
+
+        p.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
+                              IPSEC_API_INTEG_ALG_NONE)
+        p.crypt_algo_vpp_id = (VppEnum.vl_api_ipsec_crypto_alg_t.
+                               IPSEC_API_CRYPTO_ALG_NONE)
+        p.crypt_algo = "NULL"
+        p.auth_algo = "NULL"
+
+        self.config_network(p)
+        self.config_sa_tun(p,
+                           self.pg0.local_ip4,
+                           self.pg0.remote_ip4)
+        self.config_protect(p)
+
+        self.verify_tun_44(p, count=n_pkts)
+
+        # teardown
+        self.unconfig_protect(p)
+        self.unconfig_sa(p)
+        self.unconfig_network(p)
+
+
+class TestIpsecItf4MPLS(TemplateIpsec,
+                        TemplateIpsecItf4,
+                        IpsecTun4):
+    """ IPsec Interface MPLSoIPv4 """
+
+    tun4_encrypt_node_name = "esp-mpls-encrypt-tun"
+
+    def setUp(self):
+        super(TestIpsecItf4MPLS, self).setUp()
+
+        self.tun_if = self.pg0
+
+    def tearDown(self):
+        super(TestIpsecItf4MPLS, self).tearDown()
+
+    def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
+                         payload_size=100):
+        return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
+                sa.encrypt(MPLS(label=44, ttl=3) /
+                           IP(src=src, dst=dst) /
+                           UDP(sport=1166, dport=2233) /
+                           Raw(b'X' * payload_size))
+                for i in range(count)]
+
+    def verify_encrypted(self, p, sa, rxs):
+        for rx in rxs:
+            try:
+                pkt = sa.decrypt(rx[IP])
+                if not pkt.haslayer(IP):
+                    pkt = IP(pkt[Raw].load)
+                self.assert_packet_checksums_valid(pkt)
+                self.assert_equal(pkt[MPLS].label, 44)
+                self.assert_equal(pkt[IP].dst, p.remote_tun_if_host)
+            except (IndexError, AssertionError):
+                self.logger.debug(ppp("Unexpected packet:", rx))
+                try:
+                    self.logger.debug(ppp("Decrypted packet:", pkt))
+                except:
+                    pass
+                raise
+
+    def test_tun_mpls_o_ip4(self):
+        """IPSEC interface MPLS over IPv4"""
+
+        n_pkts = 127
+        p = self.ipv4_params
+        f = FibPathProto
+
+        tbl = VppMplsTable(self, 0)
+        tbl.add_vpp_config()
+
+        self.config_network(p)
+        # deag MPLS routes from the tunnel
+        r4 = VppMplsRoute(self, 44, 1,
+                          [VppRoutePath(
+                              self.pg1.remote_ip4,
+                              self.pg1.sw_if_index)]).add_vpp_config()
+        p.route.modify([VppRoutePath(p.tun_if.remote_ip4,
+                                     p.tun_if.sw_if_index,
+                                     labels=[VppMplsLabel(44)])])
+        p.tun_if.enable_mpls()
+
+        self.config_sa_tun(p,
+                           self.pg0.local_ip4,
+                           self.pg0.remote_ip4)
+        self.config_protect(p)
+
+        self.verify_tun_44(p, count=n_pkts)
+
+        # cleanup
+        p.tun_if.disable_mpls()
+        self.unconfig_protect(p)
+        self.unconfig_sa(p)
+        self.unconfig_network(p)
+
+
+class TemplateIpsecItf6(object):
+    """ IPsec Interface IPv6 """
+
+    encryption_type = ESP
+    tun6_encrypt_node_name = "esp6-encrypt-tun"
+    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_input_node = "ipsec6-tun-input"
+
+    def config_sa_tun(self, p, src, dst):
+        config_tun_params(p, self.encryption_type, None, src, dst)
+
+        p.tun_sa_out = VppIpsecSA(self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                                  p.auth_algo_vpp_id, p.auth_key,
+                                  p.crypt_algo_vpp_id, p.crypt_key,
+                                  self.vpp_esp_protocol,
+                                  src, dst,
+                                  flags=p.flags)
+        p.tun_sa_out.add_vpp_config()
+
+        p.tun_sa_in = VppIpsecSA(self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                                 p.auth_algo_vpp_id, p.auth_key,
+                                 p.crypt_algo_vpp_id, p.crypt_key,
+                                 self.vpp_esp_protocol,
+                                 dst, src,
+                                 flags=p.flags)
+        p.tun_sa_in.add_vpp_config()
+
+    def config_protect(self, p):
+        p.tun_protect = VppIpsecTunProtect(self,
+                                           p.tun_if,
+                                           p.tun_sa_out,
+                                           [p.tun_sa_in])
+        p.tun_protect.add_vpp_config()
+
+    def config_network(self, p):
+        p.tun_if = VppIpsecInterface(self)
+
+        p.tun_if.add_vpp_config()
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        p.tun_if.config_ip6()
+
+        r = VppIpRoute(self, p.remote_tun_if_host4, 32,
+                       [VppRoutePath(p.tun_if.remote_ip4,
+                                     0xffffffff)])
+        r.add_vpp_config()
+
+        p.route = VppIpRoute(self, p.remote_tun_if_host, 128,
+                             [VppRoutePath(p.tun_if.remote_ip6,
+                                           0xffffffff,
+                                           proto=DpoProto.DPO_PROTO_IP6)])
+        p.route.add_vpp_config()
+
+    def unconfig_network(self, p):
+        p.route.remove_vpp_config()
+        p.tun_if.remove_vpp_config()
+
+    def unconfig_protect(self, p):
+        p.tun_protect.remove_vpp_config()
+
+    def unconfig_sa(self, p):
+        p.tun_sa_out.remove_vpp_config()
+        p.tun_sa_in.remove_vpp_config()
+
+
+class TestIpsecItf6(TemplateIpsec,
+                    TemplateIpsecItf6,
+                    IpsecTun6):
+    """ IPsec Interface IPv6 """
+
+    def setUp(self):
+        super(TestIpsecItf6, self).setUp()
+
+        self.tun_if = self.pg0
+
+    def tearDown(self):
+        super(TestIpsecItf6, self).tearDown()
+
+    def test_tun_44(self):
+        """IPSEC interface IPv6"""
+
+        n_pkts = 127
+        p = self.ipv6_params
+
+        self.config_network(p)
+        self.config_sa_tun(p,
+                           self.pg0.local_ip6,
+                           self.pg0.remote_ip6)
+        self.config_protect(p)
+
+        self.verify_tun_66(p, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+
+        p.tun_if.admin_down()
+        self.verify_drop_tun_66(p, count=n_pkts)
+        p.tun_if.admin_up()
+        self.verify_tun_66(p, count=n_pkts)
+
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 3*n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 2*n_pkts)
+
+        # it's a v4 packet when its encrypted
+        self.tun6_encrypt_node_name = "esp4-encrypt-tun"
+
+        self.verify_tun_46(p, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], 4*n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], 3*n_pkts)
+
+        self.tun6_encrypt_node_name = "esp6-encrypt-tun"
+
+        self.vapi.cli("clear interfaces")
+
+        # rekey - create new SAs and update the tunnel protection
+        np = copy.copy(p)
+        np.crypt_key = b'X' + p.crypt_key[1:]
+        np.scapy_tun_spi += 100
+        np.scapy_tun_sa_id += 1
+        np.vpp_tun_spi += 100
+        np.vpp_tun_sa_id += 1
+        np.tun_if.local_spi = p.vpp_tun_spi
+        np.tun_if.remote_spi = p.scapy_tun_spi
+
+        self.config_sa_tun(np,
+                           self.pg0.local_ip6,
+                           self.pg0.remote_ip6)
+        self.config_protect(np)
+        self.unconfig_sa(p)
+
+        self.verify_tun_66(np, count=n_pkts)
+        c = p.tun_if.get_rx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+        c = p.tun_if.get_tx_stats()
+        self.assertEqual(c['packets'], n_pkts)
+
+        # teardown
+        self.unconfig_protect(np)
+        self.unconfig_sa(np)
+        self.unconfig_network(p)
+
+
+class TestIpsecMIfEsp4(TemplateIpsec, IpsecTun4):
+    """ Ipsec P2MP ESP v4 tests """
+    tun4_encrypt_node_name = "esp4-encrypt-tun"
+    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    encryption_type = ESP
+
+    def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
+                         payload_size=100):
+        return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
+                sa.encrypt(IP(src=self.pg1.local_ip4,
+                              dst=self.pg1.remote_ip4) /
+                           UDP(sport=1144, dport=2233) /
+                           Raw(b'X' * payload_size))
+                for i in range(count)]
+
+    def gen_pkts(self, sw_intf, src, dst, count=1,
+                 payload_size=100):
+        return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
+                IP(src="1.1.1.1", dst=dst) /
+                UDP(sport=1144, dport=2233) /
+                Raw(b'X' * payload_size)
+                for i in range(count)]
+
+    def verify_decrypted(self, p, rxs):
+        for rx in rxs:
+            self.assert_equal(rx[Ether].dst, self.pg1.remote_mac)
+            self.assert_equal(rx[IP].dst, self.pg1.remote_ip4)
+
+    def verify_encrypted(self, p, sa, rxs):
+        for rx in rxs:
+            try:
+                self.assertEqual(rx[IP].tos,
+                                 VppEnum.vl_api_ip_dscp_t.IP_API_DSCP_EF << 2)
+                pkt = sa.decrypt(rx[IP])
+                if not pkt.haslayer(IP):
+                    pkt = IP(pkt[Raw].load)
+                self.assert_packet_checksums_valid(pkt)
+                e = pkt[IP]
+                self.assertEqual(e[IP].dst, p.remote_tun_if_host)
+            except (IndexError, AssertionError):
+                self.logger.debug(ppp("Unexpected packet:", rx))
+                try:
+                    self.logger.debug(ppp("Decrypted packet:", pkt))
+                except:
+                    pass
+                raise
+
+    def setUp(self):
+        super(TestIpsecMIfEsp4, self).setUp()
+
+        N_NHS = 16
+        self.tun_if = self.pg0
+        p = self.ipv4_params
+        p.tun_if = VppIpsecInterface(self,
+                                     mode=(VppEnum.vl_api_tunnel_mode_t.
+                                           TUNNEL_API_MODE_MP))
+        p.tun_if.add_vpp_config()
+        p.tun_if.admin_up()
+        p.tun_if.config_ip4()
+        p.tun_if.unconfig_ip4()
+        p.tun_if.config_ip4()
+        p.tun_if.generate_remote_hosts(N_NHS)
+        self.pg0.generate_remote_hosts(N_NHS)
+        self.pg0.configure_ipv4_neighbors()
+
+        # setup some SAs for several next-hops on the interface
+        self.multi_params = []
+
+        for ii in range(N_NHS):
+            p = copy.copy(self.ipv4_params)
+
+            p.remote_tun_if_host = "1.1.1.%d" % (ii + 1)
+            p.scapy_tun_sa_id = p.scapy_tun_sa_id + ii
+            p.scapy_tun_spi = p.scapy_tun_spi + ii
+            p.vpp_tun_sa_id = p.vpp_tun_sa_id + ii
+            p.vpp_tun_spi = p.vpp_tun_spi + ii
+
+            p.scapy_tra_sa_id = p.scapy_tra_sa_id + ii
+            p.scapy_tra_spi = p.scapy_tra_spi + ii
+            p.vpp_tra_sa_id = p.vpp_tra_sa_id + ii
+            p.vpp_tra_spi = p.vpp_tra_spi + ii
+            p.tun_sa_out = VppIpsecSA(
+                self, p.scapy_tun_sa_id, p.scapy_tun_spi,
+                p.auth_algo_vpp_id, p.auth_key,
+                p.crypt_algo_vpp_id, p.crypt_key,
+                self.vpp_esp_protocol,
+                self.pg0.local_ip4,
+                self.pg0.remote_hosts[ii].ip4,
+                dscp=VppEnum.vl_api_ip_dscp_t.IP_API_DSCP_EF)
+            p.tun_sa_out.add_vpp_config()
+
+            p.tun_sa_in = VppIpsecSA(
+                self, p.vpp_tun_sa_id, p.vpp_tun_spi,
+                p.auth_algo_vpp_id, p.auth_key,
+                p.crypt_algo_vpp_id, p.crypt_key,
+                self.vpp_esp_protocol,
+                self.pg0.remote_hosts[ii].ip4,
+                self.pg0.local_ip4,
+                dscp=VppEnum.vl_api_ip_dscp_t.IP_API_DSCP_EF)
+            p.tun_sa_in.add_vpp_config()
+
+            p.tun_protect = VppIpsecTunProtect(
+                self,
+                p.tun_if,
+                p.tun_sa_out,
+                [p.tun_sa_in],
+                nh=p.tun_if.remote_hosts[ii].ip4)
+            p.tun_protect.add_vpp_config()
+            config_tun_params(p, self.encryption_type, None,
+                              self.pg0.local_ip4,
+                              self.pg0.remote_hosts[ii].ip4)
+            self.multi_params.append(p)
+
+            VppIpRoute(self, p.remote_tun_if_host, 32,
+                       [VppRoutePath(p.tun_if.remote_hosts[ii].ip4,
+                                     p.tun_if.sw_if_index)]).add_vpp_config()
+
+            p.tun_dst = self.pg0.remote_hosts[ii].ip4
+
+    def tearDown(self):
+        p = self.ipv4_params
+        p.tun_if.unconfig_ip4()
+        super(TestIpsecMIfEsp4, self).tearDown()
+
+    def test_tun_44(self):
+        """P2MP IPSEC 44"""
+        N_PKTS = 63
+        for p in self.multi_params:
+            self.verify_tun_44(p, count=N_PKTS)
+
+
+class TestIpsecItf6MPLS(TemplateIpsec,
+                        TemplateIpsecItf6,
+                        IpsecTun6):
+    """ IPsec Interface MPLSoIPv6 """
+
+    tun6_encrypt_node_name = "esp-mpls-encrypt-tun"
+
+    def setUp(self):
+        super(TestIpsecItf6MPLS, self).setUp()
+
+        self.tun_if = self.pg0
+
+    def tearDown(self):
+        super(TestIpsecItf6MPLS, self).tearDown()
+
+    def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
+                          payload_size=100):
+        return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
+                sa.encrypt(MPLS(label=66, ttl=3) /
+                           IPv6(src=src, dst=dst) /
+                           UDP(sport=1166, dport=2233) /
+                           Raw(b'X' * payload_size))
+                for i in range(count)]
+
+    def verify_encrypted6(self, p, sa, rxs):
+        for rx in rxs:
+            try:
+                pkt = sa.decrypt(rx[IPv6])
+                if not pkt.haslayer(IPv6):
+                    pkt = IP(pkt[Raw].load)
+                self.assert_packet_checksums_valid(pkt)
+                self.assert_equal(pkt[MPLS].label, 66)
+                self.assert_equal(pkt[IPv6].dst, p.remote_tun_if_host)
+            except (IndexError, AssertionError):
+                self.logger.debug(ppp("Unexpected packet:", rx))
+                try:
+                    self.logger.debug(ppp("Decrypted packet:", pkt))
+                except:
+                    pass
+                raise
+
+    def test_tun_mpls_o_ip6(self):
+        """IPSEC interface MPLS over IPv6"""
+
+        n_pkts = 127
+        p = self.ipv6_params
+        f = FibPathProto
+
+        tbl = VppMplsTable(self, 0)
+        tbl.add_vpp_config()
+
+        self.config_network(p)
+        # deag MPLS routes from the tunnel
+        r6 = VppMplsRoute(self, 66, 1,
+                          [VppRoutePath(
+                              self.pg1.remote_ip6,
+                              self.pg1.sw_if_index)],
+                          eos_proto=f.FIB_PATH_NH_PROTO_IP6).add_vpp_config()
+        p.route.modify([VppRoutePath(p.tun_if.remote_ip6,
+                                     p.tun_if.sw_if_index,
+                                     labels=[VppMplsLabel(66)])])
+        p.tun_if.enable_mpls()
+
+        self.config_sa_tun(p,
+                           self.pg0.local_ip6,
+                           self.pg0.remote_ip6)
+        self.config_protect(p)
+
+        self.verify_tun_66(p, count=n_pkts)
+
+        # cleanup
+        p.tun_if.disable_mpls()
+        self.unconfig_protect(p)
+        self.unconfig_sa(p)
+        self.unconfig_network(p)
+
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)