ikev2: support variable-length nonces
[vpp.git] / test / template_ipsec.py
index 5a700e8..d5f1332 100644 (file)
@@ -5,7 +5,7 @@ import struct
 from scapy.layers.inet import IP, ICMP, TCP, UDP
 from scapy.layers.ipsec import SecurityAssociation, ESP
 from scapy.layers.l2 import Ether
 from scapy.layers.inet import IP, ICMP, TCP, UDP
 from scapy.layers.ipsec import SecurityAssociation, ESP
 from scapy.layers.l2 import Ether
-from scapy.packet import Raw
+from scapy.packet import raw, Raw
 from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, IPv6ExtHdrHopByHop, \
     IPv6ExtHdrFragment, IPv6ExtHdrDestOpt
 
 from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, IPv6ExtHdrHopByHop, \
     IPv6ExtHdrFragment, IPv6ExtHdrDestOpt
 
@@ -15,7 +15,7 @@ from util import ppp, reassemble4, fragment_rfc791, fragment_rfc8200
 from vpp_papi import VppEnum
 
 
 from vpp_papi import VppEnum
 
 
-class IPsecIPv4Params(object):
+class IPsecIPv4Params:
 
     addr_type = socket.AF_INET
     addr_any = "0.0.0.0"
 
     addr_type = socket.AF_INET
     addr_any = "0.0.0.0"
@@ -28,14 +28,19 @@ class IPsecIPv4Params(object):
         self.remote_tun_if_host6 = '1111::1'
 
         self.scapy_tun_sa_id = 100
         self.remote_tun_if_host6 = '1111::1'
 
         self.scapy_tun_sa_id = 100
-        self.scapy_tun_spi = 1001
+        self.scapy_tun_spi = 1000
         self.vpp_tun_sa_id = 200
         self.vpp_tun_sa_id = 200
-        self.vpp_tun_spi = 1000
+        self.vpp_tun_spi = 2000
 
         self.scapy_tra_sa_id = 300
 
         self.scapy_tra_sa_id = 300
-        self.scapy_tra_spi = 2001
+        self.scapy_tra_spi = 3000
         self.vpp_tra_sa_id = 400
         self.vpp_tra_sa_id = 400
-        self.vpp_tra_spi = 2000
+        self.vpp_tra_spi = 4000
+
+        self.outer_hop_limit = 64
+        self.inner_hop_limit = 255
+        self.outer_flow_label = 0
+        self.inner_flow_label = 0x12345
 
         self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                                  IPSEC_API_INTEG_ALG_SHA1_96)
 
         self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                                  IPSEC_API_INTEG_ALG_SHA1_96)
@@ -49,9 +54,13 @@ class IPsecIPv4Params(object):
         self.salt = 0
         self.flags = 0
         self.nat_header = None
         self.salt = 0
         self.flags = 0
         self.nat_header = None
+        self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t.
+                          TUNNEL_API_ENCAP_DECAP_FLAG_NONE)
+        self.dscp = 0
+        self.async_mode = False
 
 
 
 
-class IPsecIPv6Params(object):
+class IPsecIPv6Params:
 
     addr_type = socket.AF_INET6
     addr_any = "0::0"
 
     addr_type = socket.AF_INET6
     addr_any = "0::0"
@@ -73,6 +82,11 @@ class IPsecIPv6Params(object):
         self.vpp_tra_sa_id = 800
         self.vpp_tra_spi = 4000
 
         self.vpp_tra_sa_id = 800
         self.vpp_tra_spi = 4000
 
+        self.outer_hop_limit = 64
+        self.inner_hop_limit = 255
+        self.outer_flow_label = 0
+        self.inner_flow_label = 0x12345
+
         self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                                  IPSEC_API_INTEG_ALG_SHA1_96)
         self.auth_algo = 'HMAC-SHA1-96'  # scapy name
         self.auth_algo_vpp_id = (VppEnum.vl_api_ipsec_integ_alg_t.
                                  IPSEC_API_INTEG_ALG_SHA1_96)
         self.auth_algo = 'HMAC-SHA1-96'  # scapy name
@@ -85,10 +99,14 @@ class IPsecIPv6Params(object):
         self.salt = 0
         self.flags = 0
         self.nat_header = None
         self.salt = 0
         self.flags = 0
         self.nat_header = None
+        self.tun_flags = (VppEnum.vl_api_tunnel_encap_decap_flags_t.
+                          TUNNEL_API_ENCAP_DECAP_FLAG_NONE)
+        self.dscp = 0
+        self.async_mode = False
 
 
 def mk_scapy_crypt_key(p):
 
 
 def mk_scapy_crypt_key(p):
-    if p.crypt_algo == "AES-GCM":
+    if p.crypt_algo in ("AES-GCM", "AES-CTR"):
         return p.crypt_key + struct.pack("!I", p.salt)
     else:
         return p.crypt_key
         return p.crypt_key + struct.pack("!I", p.salt)
     else:
         return p.crypt_key
@@ -149,21 +167,21 @@ def config_tra_params(p, encryption_type):
 
 class TemplateIpsec(VppTestCase):
     """
 
 class TemplateIpsec(VppTestCase):
     """
-    TRANSPORT MODE:
+    TRANSPORT MODE::
 
 
-     ------   encrypt   ---
-    |tra_if| <-------> |VPP|
-     ------   decrypt   ---
+         ------   encrypt   ---
+        |tra_if| <-------> |VPP|
+         ------   decrypt   ---
 
 
-    TUNNEL MODE:
+    TUNNEL MODE::
 
 
-     ------   encrypt   ---   plain   ---
-    |tun_if| <-------  |VPP| <------ |pg1|
-     ------             ---           ---
+         ------   encrypt   ---   plain   ---
+        |tun_if| <-------  |VPP| <------ |pg1|
+         ------             ---           ---
 
 
-     ------   decrypt   ---   plain   ---
-    |tun_if| ------->  |VPP| ------> |pg1|
-     ------             ---           ---
+         ------   decrypt   ---   plain   ---
+        |tun_if| ------->  |VPP| ------> |pg1|
+         ------             ---           ---
     """
     tun_spd_id = 1
     tra_spd_id = 2
     """
     tun_spd_id = 1
     tra_spd_id = 2
@@ -181,8 +199,10 @@ class TemplateIpsec(VppTestCase):
         super(TemplateIpsec, cls).tearDownClass()
 
     def setup_params(self):
         super(TemplateIpsec, cls).tearDownClass()
 
     def setup_params(self):
-        self.ipv4_params = IPsecIPv4Params()
-        self.ipv6_params = IPsecIPv6Params()
+        if not hasattr(self, 'ipv4_params'):
+            self.ipv4_params = IPsecIPv4Params()
+        if not hasattr(self, 'ipv6_params'):
+            self.ipv6_params = IPsecIPv6Params()
         self.params = {self.ipv4_params.addr_type: self.ipv4_params,
                        self.ipv6_params.addr_type: self.ipv6_params}
 
         self.params = {self.ipv4_params.addr_type: self.ipv4_params,
                        self.ipv6_params.addr_type: self.ipv6_params}
 
@@ -234,7 +254,9 @@ class TemplateIpsec(VppTestCase):
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
                           payload_size=54):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
     def gen_encrypt_pkts6(self, p, sa, sw_intf, src, dst, count=1,
                           payload_size=54):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
-                sa.encrypt(IPv6(src=src, dst=dst) /
+                sa.encrypt(IPv6(src=src, dst=dst,
+                                hlim=p.inner_hop_limit,
+                                fl=p.inner_flow_label) /
                            ICMPv6EchoRequest(id=0, seq=1,
                                              data='X' * payload_size))
                 for i in range(count)]
                            ICMPv6EchoRequest(id=0, seq=1,
                                              data='X' * payload_size))
                 for i in range(count)]
@@ -244,9 +266,10 @@ class TemplateIpsec(VppTestCase):
                 IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size)
                 for i in range(count)]
 
                 IP(src=src, dst=dst) / ICMP() / Raw(b'X' * payload_size)
                 for i in range(count)]
 
-    def gen_pkts6(self, sw_intf, src, dst, count=1, payload_size=54):
+    def gen_pkts6(self, p, sw_intf, src, dst, count=1, payload_size=54):
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
         return [Ether(src=sw_intf.remote_mac, dst=sw_intf.local_mac) /
-                IPv6(src=src, dst=dst) /
+                IPv6(src=src, dst=dst,
+                     hlim=p.inner_hop_limit, fl=p.inner_flow_label) /
                 ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size)
                 for i in range(count)]
 
                 ICMPv6EchoRequest(id=0, seq=1, data='X' * payload_size)
                 for i in range(count)]
 
@@ -274,28 +297,240 @@ class IpsecTcpTests(IpsecTcp):
 
 class IpsecTra4(object):
     """ verify methods for Transport v4 """
 
 class IpsecTra4(object):
     """ verify methods for Transport v4 """
-    def verify_tra_anti_replay(self):
-        p = self.params[socket.AF_INET]
-        esn_en = p.vpp_tra_sa.esn_en
-
-        seq_cycle_node_name = ('/err/%s/sequence number cycled' %
-                               self.tra4_encrypt_node_name)
+    def get_replay_counts(self, p):
         replay_node_name = ('/err/%s/SA replayed packet' %
         replay_node_name = ('/err/%s/SA replayed packet' %
-                            self.tra4_decrypt_node_name)
+                            self.tra4_decrypt_node_name[0])
+        count = self.statistics.get_err_counter(replay_node_name)
+
+        if p.async_mode:
+            replay_post_node_name = ('/err/%s/SA replayed packet' %
+                                     self.tra4_decrypt_node_name[p.async_mode])
+            count += self.statistics.get_err_counter(replay_post_node_name)
+
+        return count
+
+    def get_hash_failed_counts(self, p):
         if ESP == self.encryption_type and p.crypt_algo == "AES-GCM":
             hash_failed_node_name = ('/err/%s/ESP decryption failed' %
         if ESP == self.encryption_type and p.crypt_algo == "AES-GCM":
             hash_failed_node_name = ('/err/%s/ESP decryption failed' %
-                                     self.tra4_decrypt_node_name)
+                                     self.tra4_decrypt_node_name[p.async_mode])
         else:
             hash_failed_node_name = ('/err/%s/Integrity check failed' %
         else:
             hash_failed_node_name = ('/err/%s/Integrity check failed' %
-                                     self.tra4_decrypt_node_name)
-        replay_count = self.statistics.get_err_counter(replay_node_name)
-        hash_failed_count = self.statistics.get_err_counter(
-            hash_failed_node_name)
+                                     self.tra4_decrypt_node_name[p.async_mode])
+        count = self.statistics.get_err_counter(hash_failed_node_name)
+
+        if p.async_mode:
+            count += self.statistics.get_err_counter(
+                '/err/crypto-dispatch/bad-hmac')
+
+        return count
+
+    def verify_hi_seq_num(self):
+        p = self.params[socket.AF_INET]
+        saf = VppEnum.vl_api_ipsec_sad_flags_t
+        esn_on = p.vpp_tra_sa.esn_en
+        ar_on = p.flags & saf.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY
+
+        seq_cycle_node_name = \
+            ('/err/%s/sequence number cycled (packet dropped)' %
+             self.tra4_encrypt_node_name)
+        replay_count = self.get_replay_counts(p)
+        hash_failed_count = self.get_hash_failed_counts(p)
+        seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
+
+        # a few packets so we get the rx seq number above the window size and
+        # thus can simulate a wrap with an out of window packet
+        pkts = [(Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=seq))
+                for seq in range(63, 80)]
+        recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        # these 4 packets will all choose seq-num 0 to decrpyt since none
+        # are out of window when first checked. however, once #200 has
+        # decrypted it will move the window to 200 and has #81 is out of
+        # window. this packet should be dropped.
+        pkts = [(Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=200)),
+                (Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=81)),
+                (Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=201)),
+                (Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=202))]
+
+        # if anti-replay is off then we won't drop #81
+        n_rx = 3 if ar_on else 4
+        self.send_and_expect(self.tra_if, pkts, self.tra_if, n_rx=n_rx)
+        # this packet is one before the wrap
+        pkts = [(Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=203))]
+        recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        # move the window over half way to a wrap
+        pkts = [(Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=0x80000001))]
+        recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        # anti-replay will drop old packets, no anti-replay will not
+        pkts = [(Ether(src=self.tra_if.remote_mac,
+                       dst=self.tra_if.local_mac) /
+                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                           dst=self.tra_if.local_ip4) /
+                                        ICMP(),
+                                        seq_num=0x44000001))]
+
+        if ar_on:
+            self.send_and_assert_no_replies(self.tra_if, pkts)
+        else:
+            recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        if esn_on:
+            #
+            # validate wrapping the ESN
+            #
+
+            # wrap scapy's TX SA SN
+            p.scapy_tra_sa.seq_num = 0x100000005
+
+            # send a packet that wraps the window for both AR and no AR
+            pkts = [(Ether(src=self.tra_if.remote_mac,
+                           dst=self.tra_if.local_mac) /
+                     p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                               dst=self.tra_if.local_ip4) /
+                                            ICMP(),
+                                            seq_num=0x100000005))]
+
+            rxs = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+            for rx in rxs:
+                decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
+
+            # move the window forward to half way to the next wrap
+            pkts = [(Ether(src=self.tra_if.remote_mac,
+                           dst=self.tra_if.local_mac) /
+                     p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                               dst=self.tra_if.local_ip4) /
+                                            ICMP(),
+                                            seq_num=0x180000005))]
+
+            rxs = self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+            # a packet less than 2^30 from the current position is:
+            #  - AR: out of window and dropped
+            #  - non-AR: accepted
+            pkts = [(Ether(src=self.tra_if.remote_mac,
+                           dst=self.tra_if.local_mac) /
+                     p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                               dst=self.tra_if.local_ip4) /
+                                            ICMP(),
+                                            seq_num=0x170000005))]
+
+            if ar_on:
+                self.send_and_assert_no_replies(self.tra_if, pkts)
+            else:
+                self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+            # a packet more than 2^30 from the current position is:
+            #  - AR: out of window and dropped
+            #  - non-AR: considered a wrap, but since it's not a wrap
+            #    it won't decrpyt and so will be dropped
+            pkts = [(Ether(src=self.tra_if.remote_mac,
+                           dst=self.tra_if.local_mac) /
+                     p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                               dst=self.tra_if.local_ip4) /
+                                            ICMP(),
+                                            seq_num=0x130000005))]
+
+            self.send_and_assert_no_replies(self.tra_if, pkts)
+
+            # a packet less than 2^30 from the current position and is a
+            # wrap; (the seq is currently at 0x180000005).
+            #  - AR: out of window so considered a wrap, so accepted
+            #  - non-AR: not considered a wrap, so won't decrypt
+            p.scapy_tra_sa.seq_num = 0x260000005
+            pkts = [(Ether(src=self.tra_if.remote_mac,
+                           dst=self.tra_if.local_mac) /
+                     p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                               dst=self.tra_if.local_ip4) /
+                                            ICMP(),
+                                            seq_num=0x260000005))]
+            if ar_on:
+                self.send_and_expect(self.tra_if, pkts, self.tra_if)
+            else:
+                self.send_and_assert_no_replies(self.tra_if, pkts)
+
+            #
+            # window positions are different now for AR/non-AR
+            #  move non-AR forward
+            #
+            if not ar_on:
+                # a packet more than 2^30 from the current position and is a
+                # wrap; (the seq is currently at 0x180000005).
+                #  - AR: accepted
+                #  - non-AR: not considered a wrap, so won't decrypt
+
+                pkts = [(Ether(src=self.tra_if.remote_mac,
+                               dst=self.tra_if.local_mac) /
+                         p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                                   dst=self.tra_if.local_ip4) /
+                                                ICMP(),
+                                                seq_num=0x200000005)),
+                        (Ether(src=self.tra_if.remote_mac,
+                               dst=self.tra_if.local_mac) /
+                         p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                                   dst=self.tra_if.local_ip4) /
+                                                ICMP(),
+                                                seq_num=0x200000006))]
+                self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+                pkts = [(Ether(src=self.tra_if.remote_mac,
+                               dst=self.tra_if.local_mac) /
+                         p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                                   dst=self.tra_if.local_ip4) /
+                                                ICMP(),
+                                                seq_num=0x260000005))]
+                self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+    def verify_tra_anti_replay(self):
+        p = self.params[socket.AF_INET]
+        esn_en = p.vpp_tra_sa.esn_en
+
+        seq_cycle_node_name = \
+            ('/err/%s/sequence number cycled (packet dropped)' %
+             self.tra4_encrypt_node_name)
+        replay_count = self.get_replay_counts(p)
+        hash_failed_count = self.get_hash_failed_counts(p)
         seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
 
         if ESP == self.encryption_type:
             undersize_node_name = ('/err/%s/undersized packet' %
         seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
 
         if ESP == self.encryption_type:
             undersize_node_name = ('/err/%s/undersized packet' %
-                                   self.tra4_decrypt_node_name)
+                                   self.tra4_decrypt_node_name[0])
             undersize_count = self.statistics.get_err_counter(
                 undersize_node_name)
 
             undersize_count = self.statistics.get_err_counter(
                 undersize_node_name)
 
@@ -317,14 +552,16 @@ class IpsecTra4(object):
         recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
 
         # replayed packets are dropped
         recv_pkts = self.send_and_expect(self.tra_if, pkts, self.tra_if)
 
         # replayed packets are dropped
-        self.send_and_assert_no_replies(self.tra_if, pkts)
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
         replay_count += len(pkts)
         replay_count += len(pkts)
-        self.assert_error_counter_equal(replay_node_name, replay_count)
+        self.assertEqual(self.get_replay_counts(p), replay_count)
 
         #
         # now send a batch of packets all with the same sequence number
         # the first packet in the batch is legitimate, the rest bogus
         #
 
         #
         # now send a batch of packets all with the same sequence number
         # the first packet in the batch is legitimate, the rest bogus
         #
+        self.vapi.cli("clear error")
+        self.vapi.cli("clear node counters")
         pkts = (Ether(src=self.tra_if.remote_mac,
                       dst=self.tra_if.local_mac) /
                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
         pkts = (Ether(src=self.tra_if.remote_mac,
                       dst=self.tra_if.local_mac) /
                 p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
@@ -334,11 +571,12 @@ class IpsecTra4(object):
         recv_pkts = self.send_and_expect(self.tra_if, pkts * 8,
                                          self.tra_if, n_rx=1)
         replay_count += 7
         recv_pkts = self.send_and_expect(self.tra_if, pkts * 8,
                                          self.tra_if, n_rx=1)
         replay_count += 7
-        self.assert_error_counter_equal(replay_node_name, replay_count)
+        self.assertEqual(self.get_replay_counts(p), replay_count)
 
         #
         # now move the window over to 257 (more than one byte) and into Case A
         #
 
         #
         # now move the window over to 257 (more than one byte) and into Case A
         #
+        self.vapi.cli("clear error")
         pkt = (Ether(src=self.tra_if.remote_mac,
                      dst=self.tra_if.local_mac) /
                p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
         pkt = (Ether(src=self.tra_if.remote_mac,
                      dst=self.tra_if.local_mac) /
                p.scapy_tra_sa.encrypt(IP(src=self.tra_if.remote_ip4,
@@ -348,9 +586,9 @@ class IpsecTra4(object):
         recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
 
         # replayed packets are dropped
         recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
 
         # replayed packets are dropped
-        self.send_and_assert_no_replies(self.tra_if, pkt * 3)
+        self.send_and_assert_no_replies(self.tra_if, pkt * 3, timeout=0.2)
         replay_count += 3
         replay_count += 3
-        self.assert_error_counter_equal(replay_node_name, replay_count)
+        self.assertEqual(self.get_replay_counts(p), replay_count)
 
         # the window size is 64 packets
         # in window are still accepted
 
         # the window size is 64 packets
         # in window are still accepted
@@ -375,11 +613,10 @@ class IpsecTra4(object):
                                    dst=self.tra_if.local_ip4) /
                                 ICMP(),
                                 seq_num=350))
                                    dst=self.tra_if.local_ip4) /
                                 ICMP(),
                                 seq_num=350))
-        self.send_and_assert_no_replies(self.tra_if, pkt * 17)
+        self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2)
 
         hash_failed_count += 17
 
         hash_failed_count += 17
-        self.assert_error_counter_equal(hash_failed_node_name,
-                                        hash_failed_count)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
 
         # a malformed 'runt' packet
         #  created by a mis-constructed SA
 
         # a malformed 'runt' packet
         #  created by a mis-constructed SA
@@ -392,7 +629,7 @@ class IpsecTra4(object):
                                        dst=self.tra_if.local_ip4) /
                                     ICMP(),
                                     seq_num=350))
                                        dst=self.tra_if.local_ip4) /
                                     ICMP(),
                                     seq_num=350))
-            self.send_and_assert_no_replies(self.tra_if, pkt * 17)
+            self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2)
 
             undersize_count += 17
             self.assert_error_counter_equal(undersize_node_name,
 
             undersize_count += 17
             self.assert_error_counter_equal(undersize_node_name,
@@ -418,19 +655,17 @@ class IpsecTra4(object):
                                          dst=self.tra_if.local_ip4) /
                                       ICMP(),
                                       seq_num=17))
                                          dst=self.tra_if.local_ip4) /
                                       ICMP(),
                                       seq_num=17))
-        self.send_and_assert_no_replies(self.tra_if, pkt * 17)
+        self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2)
 
         if esn_en:
             # an out of window error with ESN looks like a high sequence
             # wrap. but since it isn't then the verify will fail.
             hash_failed_count += 17
 
         if esn_en:
             # an out of window error with ESN looks like a high sequence
             # wrap. but since it isn't then the verify will fail.
             hash_failed_count += 17
-            self.assert_error_counter_equal(hash_failed_node_name,
-                                            hash_failed_count)
+            self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
 
         else:
             replay_count += 17
 
         else:
             replay_count += 17
-            self.assert_error_counter_equal(replay_node_name,
-                                            replay_count)
+            self.assertEqual(self.get_replay_counts(p), replay_count)
 
         # valid packet moves the window over to 258
         pkt = (Ether(src=self.tra_if.remote_mac,
 
         # valid packet moves the window over to 258
         pkt = (Ether(src=self.tra_if.remote_mac,
@@ -484,6 +719,7 @@ class IpsecTra4(object):
                                           ICMP(),
                                           seq_num=0x100000005))
             rx = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
                                           ICMP(),
                                           seq_num=0x100000005))
             rx = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
+
             decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
 
             #
             decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
 
             #
@@ -502,7 +738,7 @@ class IpsecTra4(object):
 
             #
             # While in case A we cannot wrap the high sequence number again
 
             #
             # While in case A we cannot wrap the high sequence number again
-            # becuase VPP will consider this packet to be one that moves the
+            # because VPP will consider this packet to be one that moves the
             # window forward
             #
             pkt = (Ether(src=self.tra_if.remote_mac,
             # window forward
             #
             pkt = (Ether(src=self.tra_if.remote_mac,
@@ -511,14 +747,14 @@ class IpsecTra4(object):
                                              dst=self.tra_if.local_ip4) /
                                           ICMP(),
                                           seq_num=0x200000999))
                                              dst=self.tra_if.local_ip4) /
                                           ICMP(),
                                           seq_num=0x200000999))
-            self.send_and_assert_no_replies(self.tra_if, [pkt], self.tra_if)
+            self.send_and_assert_no_replies(self.tra_if, [pkt], self.tra_if,
+                                            timeout=0.2)
 
             hash_failed_count += 1
 
             hash_failed_count += 1
-            self.assert_error_counter_equal(hash_failed_node_name,
-                                            hash_failed_count)
+            self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
 
             #
 
             #
-            # but if we move the wondow forward to case B, then we can wrap
+            # but if we move the window forward to case B, then we can wrap
             # again
             #
             p.scapy_tra_sa.seq_num = 0x100000555
             # again
             #
             p.scapy_tra_sa.seq_num = 0x100000555
@@ -546,7 +782,7 @@ class IpsecTra4(object):
             # without ESN TX sequence numbers can't wrap and packets are
             # dropped from here on out.
             #
             # without ESN TX sequence numbers can't wrap and packets are
             # dropped from here on out.
             #
-            self.send_and_assert_no_replies(self.tra_if, pkts)
+            self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
             seq_cycle_count += len(pkts)
             self.assert_error_counter_equal(seq_cycle_node_name,
                                             seq_cycle_count)
             seq_cycle_count += len(pkts)
             self.assert_error_counter_equal(seq_cycle_node_name,
                                             seq_cycle_count)
@@ -592,7 +828,7 @@ class IpsecTra4(object):
                          (count, pkts))
 
         self.assert_packet_counter_equal(self.tra4_encrypt_node_name, count)
                          (count, pkts))
 
         self.assert_packet_counter_equal(self.tra4_encrypt_node_name, count)
-        self.assert_packet_counter_equal(self.tra4_decrypt_node_name, count)
+        self.assert_packet_counter_equal(self.tra4_decrypt_node_name[0], count)
 
 
 class IpsecTra4Tests(IpsecTra4):
 
 
 class IpsecTra4Tests(IpsecTra4):
@@ -646,7 +882,7 @@ class IpsecTra6(object):
                          "incorrect SA out counts: expected %d != %d" %
                          (count, pkts))
         self.assert_packet_counter_equal(self.tra6_encrypt_node_name, count)
                          "incorrect SA out counts: expected %d != %d" %
                          (count, pkts))
         self.assert_packet_counter_equal(self.tra6_encrypt_node_name, count)
-        self.assert_packet_counter_equal(self.tra6_decrypt_node_name, count)
+        self.assert_packet_counter_equal(self.tra6_decrypt_node_name[0], count)
 
     def gen_encrypt_pkts_ext_hdrs6(self, sa, sw_intf, src, dst, count=1,
                                    payload_size=54):
 
     def gen_encrypt_pkts_ext_hdrs6(self, sa, sw_intf, src, dst, count=1,
                                    payload_size=54):
@@ -790,12 +1026,12 @@ class IpsecTun4(object):
                              "incorrect SA in counts: expected %d != %d" %
                              (count, pkts))
             pkts = p.tun_sa_out.get_stats(worker)['packets']
                              "incorrect SA in counts: expected %d != %d" %
                              (count, pkts))
             pkts = p.tun_sa_out.get_stats(worker)['packets']
-            self.assertEqual(pkts, count,
+            self.assertEqual(pkts, n_frags,
                              "incorrect SA out counts: expected %d != %d" %
                              (count, pkts))
 
         self.assert_packet_counter_equal(self.tun4_encrypt_node_name, n_frags)
                              "incorrect SA out counts: expected %d != %d" %
                              (count, pkts))
 
         self.assert_packet_counter_equal(self.tun4_encrypt_node_name, n_frags)
-        self.assert_packet_counter_equal(self.tun4_decrypt_node_name, count)
+        self.assert_packet_counter_equal(self.tun4_decrypt_node_name[0], count)
 
     def verify_decrypted(self, p, rxs):
         for rx in rxs:
 
     def verify_decrypted(self, p, rxs):
         for rx in rxs:
@@ -803,6 +1039,15 @@ class IpsecTun4(object):
             self.assert_equal(rx[IP].dst, self.pg1.remote_ip4)
             self.assert_packet_checksums_valid(rx)
 
             self.assert_equal(rx[IP].dst, self.pg1.remote_ip4)
             self.assert_packet_checksums_valid(rx)
 
+    def verify_esp_padding(self, sa, esp_payload, decrypt_pkt):
+        align = sa.crypt_algo.block_size
+        if align < 4:
+            align = 4
+        exp_len = (len(decrypt_pkt) + 2 + (align - 1)) & ~(align - 1)
+        exp_len += sa.crypt_algo.iv_size
+        exp_len += sa.crypt_algo.icv_size or sa.auth_algo.icv_size
+        self.assertEqual(exp_len, len(esp_payload))
+
     def verify_encrypted(self, p, sa, rxs):
         decrypt_pkts = []
         for rx in rxs:
     def verify_encrypted(self, p, sa, rxs):
         decrypt_pkts = []
         for rx in rxs:
@@ -811,9 +1056,12 @@ class IpsecTun4(object):
             self.assert_packet_checksums_valid(rx)
             self.assertEqual(len(rx) - len(Ether()), rx[IP].len)
             try:
             self.assert_packet_checksums_valid(rx)
             self.assertEqual(len(rx) - len(Ether()), rx[IP].len)
             try:
-                decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IP])
+                rx_ip = rx[IP]
+                decrypt_pkt = p.vpp_tun_sa.decrypt(rx_ip)
                 if not decrypt_pkt.haslayer(IP):
                     decrypt_pkt = IP(decrypt_pkt[Raw].load)
                 if not decrypt_pkt.haslayer(IP):
                     decrypt_pkt = IP(decrypt_pkt[Raw].load)
+                if rx_ip.proto == socket.IPPROTO_ESP:
+                    self.verify_esp_padding(sa, rx_ip[ESP].data, decrypt_pkt)
                 decrypt_pkts.append(decrypt_pkt)
                 self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip4)
                 self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host)
                 decrypt_pkts.append(decrypt_pkt)
                 self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip4)
                 self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host)
@@ -914,6 +1162,7 @@ class IpsecTun4(object):
 
     def verify_tun_64(self, p, count=1):
         self.vapi.cli("clear errors")
 
     def verify_tun_64(self, p, count=1):
         self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
         try:
             send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if,
                                                src=p.remote_tun_if_host6,
         try:
             send_pkts = self.gen_encrypt_pkts6(p, p.scapy_tun_sa, self.tun_if,
                                                src=p.remote_tun_if_host6,
@@ -924,7 +1173,7 @@ class IpsecTun4(object):
                 self.assert_equal(recv_pkt[IPv6].src, p.remote_tun_if_host6)
                 self.assert_equal(recv_pkt[IPv6].dst, self.pg1.remote_ip6)
                 self.assert_packet_checksums_valid(recv_pkt)
                 self.assert_equal(recv_pkt[IPv6].src, p.remote_tun_if_host6)
                 self.assert_equal(recv_pkt[IPv6].dst, self.pg1.remote_ip6)
                 self.assert_packet_checksums_valid(recv_pkt)
-            send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6,
+            send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6,
                                        dst=p.remote_tun_if_host6, count=count)
             recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if)
             for recv_pkt in recv_pkts:
                                        dst=p.remote_tun_if_host6, count=count)
             recv_pkts = self.send_and_expect(self.pg1, send_pkts, self.tun_if)
             for recv_pkt in recv_pkts:
@@ -1000,7 +1249,7 @@ class IpsecTun6(object):
                              "incorrect SA out counts: expected %d != %d" %
                              (count, pkts))
         self.assert_packet_counter_equal(self.tun6_encrypt_node_name, count)
                              "incorrect SA out counts: expected %d != %d" %
                              (count, pkts))
         self.assert_packet_counter_equal(self.tun6_encrypt_node_name, count)
-        self.assert_packet_counter_equal(self.tun6_decrypt_node_name, count)
+        self.assert_packet_counter_equal(self.tun6_decrypt_node_name[0], count)
 
     def verify_decrypted6(self, p, rxs):
         for rx in rxs:
 
     def verify_decrypted6(self, p, rxs):
         for rx in rxs:
@@ -1013,6 +1262,9 @@ class IpsecTun6(object):
             self.assert_packet_checksums_valid(rx)
             self.assertEqual(len(rx) - len(Ether()) - len(IPv6()),
                              rx[IPv6].plen)
             self.assert_packet_checksums_valid(rx)
             self.assertEqual(len(rx) - len(Ether()) - len(IPv6()),
                              rx[IPv6].plen)
+            self.assert_equal(rx[IPv6].hlim, p.outer_hop_limit)
+            if p.outer_flow_label:
+                self.assert_equal(rx[IPv6].fl, p.outer_flow_label)
             try:
                 decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IPv6])
                 if not decrypt_pkt.haslayer(IPv6):
             try:
                 decrypt_pkt = p.vpp_tun_sa.decrypt(rx[IPv6])
                 if not decrypt_pkt.haslayer(IPv6):
@@ -1020,6 +1272,8 @@ class IpsecTun6(object):
                 self.assert_packet_checksums_valid(decrypt_pkt)
                 self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6)
                 self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host)
                 self.assert_packet_checksums_valid(decrypt_pkt)
                 self.assert_equal(decrypt_pkt.src, self.pg1.remote_ip6)
                 self.assert_equal(decrypt_pkt.dst, p.remote_tun_if_host)
+                self.assert_equal(decrypt_pkt.hlim, p.inner_hop_limit - 1)
+                self.assert_equal(decrypt_pkt.fl, p.inner_flow_label)
             except:
                 self.logger.debug(ppp("Unexpected packet:", rx))
                 try:
             except:
                 self.logger.debug(ppp("Unexpected packet:", rx))
                 try:
@@ -1055,7 +1309,7 @@ class IpsecTun6(object):
             recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1)
             self.verify_decrypted6(p_in, recv_pkts)
 
             recv_pkts = self.send_and_expect(self.tun_if, send_pkts, self.pg1)
             self.verify_decrypted6(p_in, recv_pkts)
 
-            send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6,
+            send_pkts = self.gen_pkts6(p_in, self.pg1, src=self.pg1.remote_ip6,
                                        dst=p_out.remote_tun_if_host,
                                        count=count,
                                        payload_size=payload_size)
                                        dst=p_out.remote_tun_if_host,
                                        count=count,
                                        payload_size=payload_size)
@@ -1087,7 +1341,7 @@ class IpsecTun6(object):
                                              self.pg1, n_rx=1)
             self.verify_decrypted6(p, recv_pkts)
 
                                              self.pg1, n_rx=1)
             self.verify_decrypted6(p, recv_pkts)
 
-            send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6,
+            send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6,
                                        dst=p.remote_tun_if_host,
                                        count=1,
                                        payload_size=64)
                                        dst=p.remote_tun_if_host,
                                        count=1,
                                        payload_size=64)
@@ -1104,6 +1358,7 @@ class IpsecTun6(object):
     def verify_tun_46(self, p, count=1):
         """ ipsec 4o6 tunnel basic test """
         self.vapi.cli("clear errors")
     def verify_tun_46(self, p, count=1):
         """ ipsec 4o6 tunnel basic test """
         self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
         try:
             send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if,
                                               src=p.remote_tun_if_host4,
         try:
             send_pkts = self.gen_encrypt_pkts(p, p.scapy_tun_sa, self.tun_if,
                                               src=p.remote_tun_if_host4,
@@ -1158,10 +1413,13 @@ class IpsecTun6Tests(IpsecTun6):
 
 class IpsecTun6HandoffTests(IpsecTun6):
     """ UT test methods for Tunnel v6 with multiple workers """
 
 class IpsecTun6HandoffTests(IpsecTun6):
     """ UT test methods for Tunnel v6 with multiple workers """
-    worker_config = "workers 2"
+    vpp_worker_count = 2
 
     def test_tun_handoff_66(self):
         """ ipsec 6o6 tunnel worker hand-off test """
 
     def test_tun_handoff_66(self):
         """ ipsec 6o6 tunnel worker hand-off test """
+        self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
+
         N_PKTS = 15
         p = self.params[socket.AF_INET6]
 
         N_PKTS = 15
         p = self.params[socket.AF_INET6]
 
@@ -1176,7 +1434,7 @@ class IpsecTun6HandoffTests(IpsecTun6):
                                              self.pg1, worker=worker)
             self.verify_decrypted6(p, recv_pkts)
 
                                              self.pg1, worker=worker)
             self.verify_decrypted6(p, recv_pkts)
 
-            send_pkts = self.gen_pkts6(self.pg1, src=self.pg1.remote_ip6,
+            send_pkts = self.gen_pkts6(p, self.pg1, src=self.pg1.remote_ip6,
                                        dst=p.remote_tun_if_host,
                                        count=N_PKTS)
             recv_pkts = self.send_and_expect(self.pg1, send_pkts,
                                        dst=p.remote_tun_if_host,
                                        count=N_PKTS)
             recv_pkts = self.send_and_expect(self.pg1, send_pkts,
@@ -1189,10 +1447,13 @@ class IpsecTun6HandoffTests(IpsecTun6):
 
 class IpsecTun4HandoffTests(IpsecTun4):
     """ UT test methods for Tunnel v4 with multiple workers """
 
 class IpsecTun4HandoffTests(IpsecTun4):
     """ UT test methods for Tunnel v4 with multiple workers """
-    worker_config = "workers 2"
+    vpp_worker_count = 2
 
     def test_tun_handooff_44(self):
         """ ipsec 4o4 tunnel worker hand-off test """
 
     def test_tun_handooff_44(self):
         """ ipsec 4o4 tunnel worker hand-off test """
+        self.vapi.cli("clear errors")
+        self.vapi.cli("clear ipsec sa")
+
         N_PKTS = 15
         p = self.params[socket.AF_INET]
 
         N_PKTS = 15
         p = self.params[socket.AF_INET]