session: fix mq ext config cleanup
[vpp.git] / test / test_ipsec_tun_if_esp.py
index 3cd2521..6534dd6 100644 (file)
@@ -7,29 +7,39 @@ 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 tag_fixme_vpp_workers
 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
+from vpp_policer import PolicerAction, VppPolicer
 
 
 
 
-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 +74,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", "esp4-decrypt-tun-post"]
+    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,36 +194,20 @@ 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"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
 
     @classmethod
     encryption_type = ESP
 
     @classmethod
@@ -139,7 +224,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 +242,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 +271,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()
@@ -197,7 +284,7 @@ class TemplateIpsec4TunIfEspUdp(TemplateIpsec):
 class TestIpsec4TunIfEsp1(TemplateIpsec4TunIfEsp, IpsecTun4Tests):
     """ Ipsec ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 class TestIpsec4TunIfEsp1(TemplateIpsec4TunIfEsp, IpsecTun4Tests):
     """ Ipsec ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
     def test_tun_basic64(self):
         """ ipsec 6o4 tunnel basic test """
 
     def test_tun_basic64(self):
         """ ipsec 6o4 tunnel basic test """
@@ -231,8 +318,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 +331,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 +341,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 +348,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()
 
@@ -303,7 +451,7 @@ class TestIpsec6TunIfEsp1(TemplateIpsec6TunIfEsp,
                           IpsecTun6Tests):
     """ Ipsec ESP - TUN tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
                           IpsecTun6Tests):
     """ Ipsec ESP - TUN tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
 
     def test_tun_basic46(self):
         """ ipsec 4o6 tunnel basic test """
 
     def test_tun_basic46(self):
         """ ipsec 4o6 tunnel basic test """
@@ -320,22 +468,155 @@ class TestIpsec6TunIfEspHandoff(TemplateIpsec6TunIfEsp,
                                 IpsecTun6HandoffTests):
     """ Ipsec ESP 6 Handoff tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
                                 IpsecTun6HandoffTests):
     """ Ipsec ESP 6 Handoff tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
+
+    def test_tun_handoff_66_police(self):
+        """ ESP 6o6 tunnel with policer worker hand-off test """
+        self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
+
+        N_PKTS = 15
+        p = self.params[socket.AF_INET6]
+
+        action_tx = PolicerAction(
+            VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
+            0)
+        policer = VppPolicer(self, "pol1", 80, 0, 1000, 0,
+                             conform_action=action_tx,
+                             exceed_action=action_tx,
+                             violate_action=action_tx)
+        policer.add_vpp_config()
+
+        # Start policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+
+        for pol_bind in [1, 0]:
+            policer.bind_vpp_config(pol_bind, True)
+
+            # inject alternately on worker 0 and 1.
+            for worker in [0, 1, 0, 1]:
+                send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa,
+                                                   self.tun_if,
+                                                   src=p.remote_tun_if_host,
+                                                   dst=self.pg1.remote_ip6,
+                                                   count=N_PKTS)
+                recv_pkts = self.send_and_expect(self.tun_if, send_pkts,
+                                                 self.pg1, worker=worker)
+                self.verify_decrypted6(p, recv_pkts)
+                self.logger.debug(self.vapi.cli("show trace max 100"))
+
+            stats = policer.get_stats()
+            stats0 = policer.get_stats(worker=0)
+            stats1 = policer.get_stats(worker=1)
+
+            if pol_bind == 1:
+                # First pass: Worker 1, should have done all the policing
+                self.assertEqual(stats, stats1)
+
+                # Worker 0, should have handed everything off
+                self.assertEqual(stats0['conform_packets'], 0)
+                self.assertEqual(stats0['exceed_packets'], 0)
+                self.assertEqual(stats0['violate_packets'], 0)
+            else:
+                # Second pass: both workers should have policed equal amounts
+                self.assertGreater(stats1['conform_packets'], 0)
+                self.assertEqual(stats1['exceed_packets'], 0)
+                self.assertGreater(stats1['violate_packets'], 0)
+
+                self.assertGreater(stats0['conform_packets'], 0)
+                self.assertEqual(stats0['exceed_packets'], 0)
+                self.assertGreater(stats0['violate_packets'], 0)
+
+                self.assertEqual(stats0['conform_packets'] +
+                                 stats0['violate_packets'],
+                                 stats1['conform_packets'] +
+                                 stats1['violate_packets'])
+
+        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.remove_vpp_config()
 
 
 class TestIpsec4TunIfEspHandoff(TemplateIpsec4TunIfEsp,
                                 IpsecTun4HandoffTests):
     """ Ipsec ESP 4 Handoff tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 
 
 class TestIpsec4TunIfEspHandoff(TemplateIpsec4TunIfEsp,
                                 IpsecTun4HandoffTests):
     """ Ipsec ESP 4 Handoff tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
 
+    def test_tun_handoff_44_police(self):
+        """ ESP 4o4 tunnel with policer worker hand-off test """
+        self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
 
 
-class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4):
+        N_PKTS = 15
+        p = self.params[socket.AF_INET]
+
+        action_tx = PolicerAction(
+            VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
+            0)
+        policer = VppPolicer(self, "pol1", 80, 0, 1000, 0,
+                             conform_action=action_tx,
+                             exceed_action=action_tx,
+                             violate_action=action_tx)
+        policer.add_vpp_config()
+
+        # Start policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+
+        for pol_bind in [1, 0]:
+            policer.bind_vpp_config(pol_bind, True)
+
+            # inject alternately on worker 0 and 1.
+            for worker in [0, 1, 0, 1]:
+                send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa,
+                                                  self.tun_if,
+                                                  src=p.remote_tun_if_host,
+                                                  dst=self.pg1.remote_ip4,
+                                                  count=N_PKTS)
+                recv_pkts = self.send_and_expect(self.tun_if, send_pkts,
+                                                 self.pg1, worker=worker)
+                self.verify_decrypted(p, recv_pkts)
+                self.logger.debug(self.vapi.cli("show trace max 100"))
+
+            stats = policer.get_stats()
+            stats0 = policer.get_stats(worker=0)
+            stats1 = policer.get_stats(worker=1)
+
+            if pol_bind == 1:
+                # First pass: Worker 1, should have done all the policing
+                self.assertEqual(stats, stats1)
+
+                # Worker 0, should have handed everything off
+                self.assertEqual(stats0['conform_packets'], 0)
+                self.assertEqual(stats0['exceed_packets'], 0)
+                self.assertEqual(stats0['violate_packets'], 0)
+            else:
+                # Second pass: both workers should have policed equal amounts
+                self.assertGreater(stats1['conform_packets'], 0)
+                self.assertEqual(stats1['exceed_packets'], 0)
+                self.assertGreater(stats1['violate_packets'], 0)
+
+                self.assertGreater(stats0['conform_packets'], 0)
+                self.assertEqual(stats0['exceed_packets'], 0)
+                self.assertGreater(stats0['violate_packets'], 0)
+
+                self.assertEqual(stats0['conform_packets'] +
+                                 stats0['violate_packets'],
+                                 stats1['conform_packets'] +
+                                 stats1['violate_packets'])
+
+        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        policer.remove_vpp_config()
+
+
+@tag_fixme_vpp_workers
+class TestIpsec4MultiTunIfEsp(TemplateIpsec4TunProtect,
+                              TemplateIpsec,
+                              IpsecTun4):
     """ IPsec IPv4 Multi Tunnel interface """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
     """ IPsec IPv4 Multi Tunnel interface """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec4MultiTunIfEsp, self).setUp()
 
     def setUp(self):
         super(TestIpsec4MultiTunIfEsp, self).setUp()
@@ -361,22 +642,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()
@@ -385,10 +654,8 @@ class TestIpsec4MultiTunIfEsp(TemplateIpsec, IpsecTun4):
         """Multiple IPSEC tunnel interfaces """
         for p in self.multi_params:
             self.verify_tun_44(p, count=127)
         """Multiple IPSEC tunnel interfaces """
         for p in self.multi_params:
             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.assertEqual(p.tun_if.get_rx_stats(), 127)
+            self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
     def test_tun_rr_44(self):
         """ Round-robin packets acrros multiple interface """
 
     def test_tun_rr_44(self):
         """ Round-robin packets acrros multiple interface """
@@ -412,51 +679,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"
     """ 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()
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
     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
+
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(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 tearDown(self):
-        super(TestIpsec4TunIfEspAll, self).tearDown()
-
     def rekey(self, p):
         #
         # change the key and the SPI
         #
     def rekey(self, p):
         #
         # 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 +721,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 +731,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 +791,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 +822,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 +830,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):
-    """ IPsec IPv4 Tunnel interface all Algos """
+class TestIpsec4TunIfEspNoAlgo(TemplateIpsec4TunProtect,
+                               TemplateIpsec,
+                               IpsecTun4):
+    """ IPsec IPv4 Tunnel interface no Algos """
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
 
-    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,56 +861,35 @@ 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 test_tun_44(self):
     def tearDown(self):
         super(TestIpsec4TunIfEspNoAlgo, self).tearDown()
 
     def test_tun_44(self):
+        """ IPSec SA with NULL algos """
         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):
+@tag_fixme_vpp_workers
+class TestIpsec6MultiTunIfEsp(TemplateIpsec6TunProtect,
+                              TemplateIpsec,
+                              IpsecTun6):
     """ IPsec IPv6 Multi Tunnel interface """
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
     """ IPsec IPv6 Multi Tunnel interface """
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec6MultiTunIfEsp, self).setUp()
 
     def setUp(self):
         super(TestIpsec6MultiTunIfEsp, self).setUp()
@@ -690,25 +913,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()
@@ -717,17 +927,15 @@ class TestIpsec6MultiTunIfEsp(TemplateIpsec, IpsecTun6):
         """Multiple IPSEC tunnel interfaces """
         for p in self.multi_params:
             self.verify_tun_66(p, count=127)
         """Multiple IPSEC tunnel interfaces """
         for p in self.multi_params:
             self.verify_tun_66(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.assertEqual(p.tun_if.get_rx_stats(), 127)
+            self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
 
 class TestIpsecGreTebIfEsp(TemplateIpsec,
                            IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 
 
 class TestIpsecGreTebIfEsp(TemplateIpsec,
                            IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
@@ -838,7 +1046,7 @@ class TestIpsecGreTebVlanIfEsp(TemplateIpsec,
                                IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
                                IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
@@ -958,7 +1166,7 @@ class TestIpsecGreTebIfEspTra(TemplateIpsec,
                               IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - Tra tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
                               IpsecTun4Tests):
     """ Ipsec GRE TEB ESP - Tra tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
     encryption_type = ESP
     omac = "00:11:22:33:44:55"
 
@@ -1059,11 +1267,132 @@ 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", "esp4-decrypt-tun-post"]
+    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 """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 class TestIpsecGreIfEsp(TemplateIpsec,
                         IpsecTun4Tests):
     """ Ipsec GRE ESP - TUN tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
@@ -1166,7 +1495,7 @@ class TestIpsecGreIfEspTra(TemplateIpsec,
                            IpsecTun4Tests):
     """ Ipsec GRE ESP - TRA tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
                            IpsecTun4Tests):
     """ Ipsec GRE ESP - TRA tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
@@ -1272,7 +1601,7 @@ class TestIpsecGreIfEspTra(TemplateIpsec,
                                           dst=self.pg1.remote_ip6)
         self.send_and_assert_no_replies(self.tun_if, tx)
         node_name = ('/err/%s/unsupported payload' %
                                           dst=self.pg1.remote_ip6)
         self.send_and_assert_no_replies(self.tun_if, tx)
         node_name = ('/err/%s/unsupported payload' %
-                     self.tun4_decrypt_node_name)
+                     self.tun4_decrypt_node_name[0])
         self.assertEqual(1, self.statistics.get_err_counter(node_name))
 
 
         self.assertEqual(1, self.statistics.get_err_counter(node_name))
 
 
@@ -1280,7 +1609,7 @@ class TestIpsecGre6IfEspTra(TemplateIpsec,
                             IpsecTun6Tests):
     """ Ipsec GRE ESP - TRA tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
                             IpsecTun6Tests):
     """ Ipsec GRE ESP - TRA tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
     encryption_type = ESP
 
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
     encryption_type = ESP
 
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
@@ -1295,7 +1624,7 @@ class TestIpsecGre6IfEspTra(TemplateIpsec,
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
-    def gen_pkts6(self, sw_intf, src, dst, count=1,
+    def gen_pkts6(self, p, sw_intf, src, dst, count=1,
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src="1::1", dst="1::2") /
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src="1::1", dst="1::2") /
@@ -1378,7 +1707,7 @@ class TestIpsecGre6IfEspTra(TemplateIpsec,
 class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
     """ Ipsec mGRE ESP v4 TRA tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
     """ Ipsec mGRE ESP v4 TRA tests """
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
     encryption_type = ESP
 
     def gen_encrypt_pkts(self, p, sa, sw_intf, src, dst, count=1,
@@ -1510,7 +1839,7 @@ class TestIpsecMGreIfEspTra4(TemplateIpsec, IpsecTun4):
 class TestIpsecMGreIfEspTra6(TemplateIpsec, IpsecTun6):
     """ Ipsec mGRE ESP v6 TRA tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
 class TestIpsecMGreIfEspTra6(TemplateIpsec, IpsecTun6):
     """ Ipsec mGRE ESP v6 TRA tests """
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
     encryption_type = ESP
 
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
     encryption_type = ESP
 
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
@@ -1525,7 +1854,7 @@ class TestIpsecMGreIfEspTra6(TemplateIpsec, IpsecTun6):
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
-    def gen_pkts6(self, sw_intf, src, dst, count=1,
+    def gen_pkts6(self, p, sw_intf, src, dst, count=1,
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src="1::1", dst=dst) /
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src="1::1", dst=dst) /
@@ -1640,144 +1969,55 @@ 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 """
+@tag_fixme_vpp_workers
+class TestIpsec4TunProtect(TemplateIpsec,
+                           TemplateIpsec4TunProtect,
+                           IpsecTun4):
+    """ IPsec IPv4 Tunnel protect - transport mode"""
 
 
-    encryption_type = ESP
-    tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
-    tun4_input_node = "ipsec4-tun-input"
+    def setUp(self):
+        super(TestIpsec4TunProtect, self).setUp()
 
 
-    def config_sa_tra(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
+        self.tun_if = self.pg0
 
 
-        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()
+    def tearDown(self):
+        super(TestIpsec4TunProtect, self).tearDown()
 
 
-        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 test_tun_44(self):
+        """IPSEC tunnel protect"""
 
 
-    def config_sa_tun(self, p):
-        config_tun_params(p, self.encryption_type, p.tun_if)
+        p = self.ipv4_params
 
 
-        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()
+        self.config_network(p)
+        self.config_sa_tra(p)
+        self.config_protect(p)
 
 
-        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.verify_tun_44(p, count=127)
+        self.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
 
-    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.vapi.cli("clear ipsec sa")
+        self.verify_tun_64(p, count=127)
+        self.assertEqual(p.tun_if.get_rx_stats(), 254)
+        self.assertEqual(p.tun_if.get_tx_stats(), 254)
 
 
-    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)
-
-        # 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
+        # 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_tra(np)
         self.config_protect(np)
         self.unconfig_sa(p)
 
         self.verify_tun_44(np, count=127)
 
         self.config_sa_tra(np)
         self.config_protect(np)
         self.unconfig_sa(p)
 
         self.verify_tun_44(np, count=127)
-        c = p.tun_if.get_rx_stats()
-        self.assertEqual(c['packets'], 381)
-        c = p.tun_if.get_tx_stats()
-        self.assertEqual(c['packets'], 381)
+        self.assertEqual(p.tun_if.get_rx_stats(), 381)
+        self.assertEqual(p.tun_if.get_tx_stats(), 381)
 
         # teardown
         self.unconfig_protect(np)
 
         # teardown
         self.unconfig_protect(np)
@@ -1785,6 +2025,7 @@ class TestIpsec4TunProtect(TemplateIpsec,
         self.unconfig_network(p)
 
 
         self.unconfig_network(p)
 
 
+@tag_fixme_vpp_workers
 class TestIpsec4TunProtectUdp(TemplateIpsec,
                               TemplateIpsec4TunProtect,
                               IpsecTun4):
 class TestIpsec4TunProtectUdp(TemplateIpsec,
                               TemplateIpsec4TunProtect,
                               IpsecTun4):
@@ -1798,7 +2039,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)
@@ -1810,22 +2051,28 @@ 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"""
 
         p = self.ipv4_params
 
         self.verify_tun_44(p, count=127)
     def test_tun_44(self):
         """IPSEC UDP tunnel protect"""
 
         p = self.ipv4_params
 
         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.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
     def test_keepalive(self):
         """ IPSEC NAT Keepalive """
         self.verify_keepalive(self.ipv4_params)
 
 
 
     def test_keepalive(self):
         """ IPSEC NAT Keepalive """
         self.verify_keepalive(self.ipv4_params)
 
 
+@tag_fixme_vpp_workers
 class TestIpsec4TunProtectTun(TemplateIpsec,
                               TemplateIpsec4TunProtect,
                               IpsecTun4):
 class TestIpsec4TunProtectTun(TemplateIpsec,
                               TemplateIpsec4TunProtect,
                               IpsecTun4):
@@ -1833,7 +2080,7 @@ class TestIpsec4TunProtectTun(TemplateIpsec,
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec4TunProtectTun, self).setUp()
 
     def setUp(self):
         super(TestIpsec4TunProtectTun, self).setUp()
@@ -1896,12 +2143,21 @@ 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)
 
         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.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
         # 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)
@@ -1918,10 +2174,8 @@ class TestIpsec4TunProtectTun(TemplateIpsec,
         self.unconfig_sa(p)
 
         self.verify_tun_44(np, count=127)
         self.unconfig_sa(p)
 
         self.verify_tun_44(np, 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.assertEqual(p.tun_if.get_rx_stats(), 254)
+        self.assertEqual(p.tun_if.get_tx_stats(), 254)
 
         # teardown
         self.unconfig_protect(np)
 
         # teardown
         self.unconfig_protect(np)
@@ -1936,7 +2190,7 @@ class TestIpsec4TunProtectTunDrop(TemplateIpsec,
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
 
     encryption_type = ESP
     tun4_encrypt_node_name = "esp4-encrypt-tun"
-    tun4_decrypt_node_name = "esp4-decrypt-tun"
+    tun4_decrypt_node_name = ["esp4-decrypt-tun", "esp4-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec4TunProtectTunDrop, self).setUp()
 
     def setUp(self):
         super(TestIpsec4TunProtectTunDrop, self).setUp()
@@ -1977,81 +2231,7 @@ 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()
-
-
+@tag_fixme_vpp_workers
 class TestIpsec6TunProtect(TemplateIpsec,
                            TemplateIpsec6TunProtect,
                            IpsecTun6):
 class TestIpsec6TunProtect(TemplateIpsec,
                            TemplateIpsec6TunProtect,
                            IpsecTun6):
@@ -2059,7 +2239,7 @@ class TestIpsec6TunProtect(TemplateIpsec,
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec6TunProtect, self).setUp()
 
     def setUp(self):
         super(TestIpsec6TunProtect, self).setUp()
@@ -2079,10 +2259,8 @@ class TestIpsec6TunProtect(TemplateIpsec,
         self.config_protect(p)
 
         self.verify_tun_66(p, count=127)
         self.config_protect(p)
 
         self.verify_tun_66(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.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
         # 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)
@@ -2099,10 +2277,8 @@ class TestIpsec6TunProtect(TemplateIpsec,
         self.unconfig_sa(p)
 
         self.verify_tun_66(np, count=127)
         self.unconfig_sa(p)
 
         self.verify_tun_66(np, 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.assertEqual(p.tun_if.get_rx_stats(), 254)
+        self.assertEqual(p.tun_if.get_tx_stats(), 254)
 
         # bounce the interface state
         p.tun_if.admin_down()
 
         # bounce the interface state
         p.tun_if.admin_down()
@@ -2146,10 +2322,8 @@ class TestIpsec6TunProtect(TemplateIpsec,
         self.verify_tun_66(np3, np3, count=127)
         self.verify_drop_tun_66(np, count=127)
 
         self.verify_tun_66(np3, np3, count=127)
         self.verify_drop_tun_66(np, count=127)
 
-        c = p.tun_if.get_rx_stats()
-        self.assertEqual(c['packets'], 127*9)
-        c = p.tun_if.get_tx_stats()
-        self.assertEqual(c['packets'], 127*8)
+        self.assertEqual(p.tun_if.get_rx_stats(), 127*9)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127*8)
         self.unconfig_sa(np)
 
         # teardown
         self.unconfig_sa(np)
 
         # teardown
@@ -2167,10 +2341,8 @@ class TestIpsec6TunProtect(TemplateIpsec,
         self.config_protect(p)
 
         self.verify_tun_46(p, count=127)
         self.config_protect(p)
 
         self.verify_tun_46(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.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
         # teardown
         self.unconfig_protect(p)
 
         # teardown
         self.unconfig_protect(p)
@@ -2178,6 +2350,7 @@ class TestIpsec6TunProtect(TemplateIpsec,
         self.unconfig_network(p)
 
 
         self.unconfig_network(p)
 
 
+@tag_fixme_vpp_workers
 class TestIpsec6TunProtectTun(TemplateIpsec,
                               TemplateIpsec6TunProtect,
                               IpsecTun6):
 class TestIpsec6TunProtectTun(TemplateIpsec,
                               TemplateIpsec6TunProtect,
                               IpsecTun6):
@@ -2185,7 +2358,7 @@ class TestIpsec6TunProtectTun(TemplateIpsec,
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec6TunProtectTun, self).setUp()
 
     def setUp(self):
         super(TestIpsec6TunProtectTun, self).setUp()
@@ -2205,7 +2378,7 @@ class TestIpsec6TunProtectTun(TemplateIpsec,
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
                            Raw(b'X' * payload_size))
                 for i in range(count)]
 
-    def gen_pkts6(self, sw_intf, src, dst, count=1,
+    def gen_pkts6(self, p, sw_intf, src, dst, count=1,
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src=src, dst=dst) /
                   payload_size=100):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
                 IPv6(src=src, dst=dst) /
@@ -2250,10 +2423,8 @@ class TestIpsec6TunProtectTun(TemplateIpsec,
 
         self.verify_tun_66(p, count=127)
 
 
         self.verify_tun_66(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.assertEqual(p.tun_if.get_rx_stats(), 127)
+        self.assertEqual(p.tun_if.get_tx_stats(), 127)
 
         # 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)
@@ -2270,10 +2441,8 @@ class TestIpsec6TunProtectTun(TemplateIpsec,
         self.unconfig_sa(p)
 
         self.verify_tun_66(np, count=127)
         self.unconfig_sa(p)
 
         self.verify_tun_66(np, 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.assertEqual(p.tun_if.get_rx_stats(), 254)
+        self.assertEqual(p.tun_if.get_tx_stats(), 254)
 
         # teardown
         self.unconfig_protect(np)
 
         # teardown
         self.unconfig_protect(np)
@@ -2288,7 +2457,7 @@ class TestIpsec6TunProtectTunDrop(TemplateIpsec,
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
 
     encryption_type = ESP
     tun6_encrypt_node_name = "esp6-encrypt-tun"
-    tun6_decrypt_node_name = "esp6-decrypt-tun"
+    tun6_decrypt_node_name = ["esp6-decrypt-tun", "esp6-decrypt-tun-post"]
 
     def setUp(self):
         super(TestIpsec6TunProtectTunDrop, self).setUp()
 
     def setUp(self):
         super(TestIpsec6TunProtectTunDrop, self).setUp()
@@ -2330,5 +2499,729 @@ 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", "esp4-decrypt-tun-post"]
+    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()
+
+
+@tag_fixme_vpp_workers
+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)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+
+        self.assertEqual(p.tun_if.get_rx_stats(), 3*n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+        self.assertEqual(p.tun_if.get_rx_stats(), 4*n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+
+    def test_tun_44_police(self):
+        """IPSEC interface IPv4 with input policer"""
+        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)
+
+        action_tx = PolicerAction(
+            VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
+            0)
+        policer = VppPolicer(self, "pol1", 80, 0, 1000, 0,
+                             conform_action=action_tx,
+                             exceed_action=action_tx,
+                             violate_action=action_tx)
+        policer.add_vpp_config()
+
+        # Start policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+
+        self.verify_tun_44(p, count=n_pkts)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), n_pkts)
+
+        stats = policer.get_stats()
+
+        # Single rate, 2 colour policer - expect conform, violate but no exceed
+        self.assertGreater(stats['conform_packets'], 0)
+        self.assertEqual(stats['exceed_packets'], 0)
+        self.assertGreater(stats['violate_packets'], 0)
+
+        # Stop policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        self.verify_tun_44(p, count=n_pkts)
+
+        # No new policer stats
+        statsnew = policer.get_stats()
+        self.assertEqual(stats, statsnew)
+
+        # teardown
+        policer.remove_vpp_config()
+        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", "esp6-decrypt-tun-post"]
+    tun6_input_node = "ipsec6-tun-input"
+
+    def config_sa_tun(self, p, src, dst):
+        config_tun_params(p, self.encryption_type, None, src, dst)
+
+        if not hasattr(p, 'tun_flags'):
+            p.tun_flags = None
+        if not hasattr(p, 'hop_limit'):
+            p.hop_limit = 255
+
+        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,
+                                  tun_flags=p.tun_flags,
+                                  hop_limit=p.hop_limit)
+        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()
+
+
+@tag_fixme_vpp_workers
+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"""
+
+        tf = VppEnum.vl_api_tunnel_encap_decap_flags_t
+        n_pkts = 127
+        p = self.ipv6_params
+        p.inner_hop_limit = 24
+        p.outer_hop_limit = 23
+        p.outer_flow_label = 243224
+        p.tun_flags = tf.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_HOP_LIMIT
+
+        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)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+
+        self.assertEqual(p.tun_if.get_rx_stats(), 3*n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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)
+        self.assertEqual(p.tun_if.get_rx_stats(), 4*n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), 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
+        np.inner_hop_limit = 24
+        np.outer_hop_limit = 128
+        np.inner_flow_label = 0xabcde
+        np.outer_flow_label = 0xabcde
+        np.hop_limit = 128
+        np.tun_flags = tf.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_FLOW_LABEL
+
+        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)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), n_pkts)
+
+        # teardown
+        self.unconfig_protect(np)
+        self.unconfig_sa(np)
+        self.unconfig_network(p)
+
+    def test_tun_66_police(self):
+        """IPSEC interface IPv6 with input policer"""
+        tf = VppEnum.vl_api_tunnel_encap_decap_flags_t
+        n_pkts = 127
+        p = self.ipv6_params
+        p.inner_hop_limit = 24
+        p.outer_hop_limit = 23
+        p.outer_flow_label = 243224
+        p.tun_flags = tf.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_HOP_LIMIT
+
+        self.config_network(p)
+        self.config_sa_tun(p,
+                           self.pg0.local_ip6,
+                           self.pg0.remote_ip6)
+        self.config_protect(p)
+
+        action_tx = PolicerAction(
+            VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
+            0)
+        policer = VppPolicer(self, "pol1", 80, 0, 1000, 0,
+                             conform_action=action_tx,
+                             exceed_action=action_tx,
+                             violate_action=action_tx)
+        policer.add_vpp_config()
+
+        # Start policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, True)
+
+        self.verify_tun_66(p, count=n_pkts)
+        self.assertEqual(p.tun_if.get_rx_stats(), n_pkts)
+        self.assertEqual(p.tun_if.get_tx_stats(), n_pkts)
+
+        stats = policer.get_stats()
+
+        # Single rate, 2 colour policer - expect conform, violate but no exceed
+        self.assertGreater(stats['conform_packets'], 0)
+        self.assertEqual(stats['exceed_packets'], 0)
+        self.assertGreater(stats['violate_packets'], 0)
+
+        # Stop policing on tun
+        policer.apply_vpp_config(p.tun_if.sw_if_index, False)
+        self.verify_tun_66(p, count=n_pkts)
+
+        # No new policer stats
+        statsnew = policer.get_stats()
+        self.assertEqual(stats, statsnew)
+
+        # teardown
+        policer.remove_vpp_config()
+        self.unconfig_protect(p)
+        self.unconfig_sa(p)
+        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", "esp4-decrypt-tun-post"]
+    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)
+                self.assertEqual(rx[IP].ttl, p.hop_limit)
+                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.hop_limit = ii+10
+            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,
+                hop_limit=p.hop_limit)
+            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,
+                hop_limit=p.hop_limit)
+            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)