IPSEC-AH: anti-replay testing 96/16296/2
authorNeale Ranns <nranns@cisco.com>
Wed, 28 Nov 2018 09:38:34 +0000 (01:38 -0800)
committerFlorin Coras <florin.coras@gmail.com>
Sun, 2 Dec 2018 20:43:32 +0000 (20:43 +0000)
Change-Id: Ia5d45db73e4bdb32214ed4f365d5eec8e28115f3
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet/ipsec/ah_decrypt.c
src/vnet/ipsec/ipsec_cli.c
test/template_ipsec.py
test/test_ipsec_ah.py
test/test_ipsec_esp.py
test/vpp_papi_provider.py

index 9b0c16e..a2fc07f 100644 (file)
@@ -60,6 +60,7 @@ static char *ah_decrypt_error_strings[] = {
 typedef struct
 {
   ipsec_integ_alg_t integ_alg;
+  u32 seq_num;
 } ah_decrypt_trace_t;
 
 /* packet trace format function */
@@ -70,7 +71,8 @@ format_ah_decrypt_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ah_decrypt_trace_t *t = va_arg (*args, ah_decrypt_trace_t *);
 
-  s = format (s, "ah: integrity %U", format_ipsec_integ_alg, t->integ_alg);
+  s = format (s, "ah: integrity %U seq-num %d",
+             format_ipsec_integ_alg, t->integ_alg, t->seq_num);
   return s;
 }
 
@@ -143,8 +145,8 @@ ah_decrypt_inline (vlib_main_t * vm,
            }
 
          seq = clib_host_to_net_u32 (ah0->seq_no);
+
          /* anti-replay check */
-         //TODO UT remaining
          if (sa0->use_anti_replay)
            {
              int rv = 0;
@@ -223,7 +225,6 @@ ah_decrypt_inline (vlib_main_t * vm,
                  goto trace;
                }
 
-             //TODO UT remaining
              if (PREDICT_TRUE (sa0->use_anti_replay))
                {
                  if (PREDICT_TRUE (sa0->use_esn))
@@ -247,7 +248,6 @@ ah_decrypt_inline (vlib_main_t * vm,
                next0 = AH_DECRYPT_NEXT_IP6_INPUT;
              else
                {
-                 clib_warning ("next header: 0x%x", ah0->nexthdr);
                  if (is_ip6)
                    vlib_node_increment_counter (vm,
                                                 ah6_decrypt_node.index,
@@ -313,6 +313,7 @@ ah_decrypt_inline (vlib_main_t * vm,
              ah_decrypt_trace_t *tr =
                vlib_add_trace (vm, node, i_b0, sizeof (*tr));
              tr->integ_alg = sa0->integ_alg;
+             tr->seq_num = seq;
            }
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
                                           n_left_to_next, i_bi0, next0);
index 9c64822..f965514 100644 (file)
@@ -462,10 +462,12 @@ show_ipsec_command_fn (vlib_main_t * vm,
   /* *INDENT-OFF* */
   pool_foreach (sa, im->sad, ({
     if (sa->id) {
-      vlib_cli_output(vm, "sa %u spi %u mode %s protocol %s%s", sa->id, sa->spi,
+      vlib_cli_output(vm, "sa %u spi %u mode %s protocol %s%s%s%s", sa->id, sa->spi,
                       sa->is_tunnel ? "tunnel" : "transport",
                       sa->protocol ? "esp" : "ah",
-                     sa->udp_encap ? " udp-encap-enabled" : "");
+                     sa->udp_encap ? " udp-encap-enabled" : "",
+                      sa->use_anti_replay ? " anti-replay" : "",
+                      sa->use_esn ? " extended-sequence-number" : "");
       if (sa->protocol == IPSEC_PROTOCOL_ESP) {
         vlib_cli_output(vm, "  crypto alg %U%s%U integrity alg %U%s%U",
                         format_ipsec_crypto_alg, sa->crypto_alg,
index 71485c5..961f63a 100644 (file)
@@ -158,20 +158,20 @@ class TemplateIpsec(VppTestCase):
                 src=self.tun_if.local_addr[params.addr_type]))
         return vpp_tun_sa, scapy_tun_sa
 
-    def configure_sa_tra(self, params):
-        scapy_tra_sa = SecurityAssociation(self.encryption_type,
-                                           spi=params.vpp_tra_spi,
-                                           crypt_algo=params.crypt_algo,
-                                           crypt_key=params.crypt_key,
-                                           auth_algo=params.auth_algo,
-                                           auth_key=params.auth_key)
-        vpp_tra_sa = SecurityAssociation(self.encryption_type,
-                                         spi=params.scapy_tra_spi,
-                                         crypt_algo=params.crypt_algo,
-                                         crypt_key=params.crypt_key,
-                                         auth_algo=params.auth_algo,
-                                         auth_key=params.auth_key)
-        return vpp_tra_sa, scapy_tra_sa
+    @classmethod
+    def configure_sa_tra(cls, params):
+        params.scapy_tra_sa = SecurityAssociation(cls.encryption_type,
+                                                  spi=params.vpp_tra_spi,
+                                                  crypt_algo=params.crypt_algo,
+                                                  crypt_key=params.crypt_key,
+                                                  auth_algo=params.auth_algo,
+                                                  auth_key=params.auth_key)
+        params.vpp_tra_sa = SecurityAssociation(cls.encryption_type,
+                                                spi=params.scapy_tra_spi,
+                                                crypt_algo=params.crypt_algo,
+                                                crypt_key=params.crypt_key,
+                                                auth_algo=params.auth_algo,
+                                                auth_key=params.auth_key)
 
 
 class IpsecTcpTests(object):
@@ -192,24 +192,96 @@ class IpsecTcpTests(object):
 
 
 class IpsecTraTests(object):
+    def test_tra_anti_replay(self, count=1):
+        """ ipsec v4 transport anti-reply test """
+        p = self.params[socket.AF_INET]
+
+        # fire in a packet with seq number 1
+        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,
+                                         dst=self.tra_if.local_ip4) /
+                                      ICMP(),
+                                      seq_num=1))
+        recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
+
+        # now move the window over to 235
+        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,
+                                         dst=self.tra_if.local_ip4) /
+                                      ICMP(),
+                                      seq_num=235))
+        recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
+
+        # the window size is 64 packets
+        # in window are still accepted
+        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,
+                                         dst=self.tra_if.local_ip4) /
+                                      ICMP(),
+                                      seq_num=172))
+        recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
+
+        # out of window are dropped
+        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,
+                                         dst=self.tra_if.local_ip4) /
+                                      ICMP(),
+                                      seq_num=17))
+        self.send_and_assert_no_replies(self.tra_if, pkt * 17)
+
+        err = self.statistics.get_counter(
+            '/err/%s/SA replayed packet' % self.tra4_decrypt_node_name)
+        self.assertEqual(err, 17)
+
+        # a packet that does not decrypt does not move the window forward
+        bogus_sa = SecurityAssociation(self.encryption_type,
+                                       p.vpp_tra_spi)
+        pkt = (Ether(src=self.tra_if.remote_mac,
+                     dst=self.tra_if.local_mac) /
+               bogus_sa.encrypt(IP(src=self.tra_if.remote_ip4,
+                                   dst=self.tra_if.local_ip4) /
+                                ICMP(),
+                                seq_num=350))
+        self.send_and_assert_no_replies(self.tra_if, pkt * 17)
+
+        err = self.statistics.get_counter(
+            '/err/%s/Integrity check failed' % self.tra4_decrypt_node_name)
+        self.assertEqual(err, 17)
+
+        # which we can determine since this packet is still in the window
+        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,
+                                         dst=self.tra_if.local_ip4) /
+                                      ICMP(),
+                                      seq_num=234))
+        recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
+
+        # move the security-associations seq number on to the last we used
+        p.scapy_tra_sa.seq_num = 351
+        p.vpp_tra_sa.seq_num = 351
+
     def test_tra_basic(self, count=1):
         """ ipsec v4 transport basic test """
         self.vapi.cli("clear errors")
         try:
             p = self.params[socket.AF_INET]
-            vpp_tra_sa, scapy_tra_sa = self.configure_sa_tra(p)
-            send_pkts = self.gen_encrypt_pkts(scapy_tra_sa, self.tra_if,
+            send_pkts = self.gen_encrypt_pkts(p.scapy_tra_sa, self.tra_if,
                                               src=self.tra_if.remote_ip4,
                                               dst=self.tra_if.local_ip4,
                                               count=count)
             recv_pkts = self.send_and_expect(self.tra_if, send_pkts,
                                              self.tra_if)
-            for p in recv_pkts:
+            for rx in recv_pkts:
                 try:
-                    decrypted = vpp_tra_sa.decrypt(p[IP])
+                    decrypted = p.vpp_tra_sa.decrypt(rx[IP])
                     self.assert_packet_checksums_valid(decrypted)
                 except:
-                    self.logger.debug(ppp("Unexpected packet:", p))
+                    self.logger.debug(ppp("Unexpected packet:", rx))
                     raise
         finally:
             self.logger.info(self.vapi.ppcli("show error"))
@@ -227,19 +299,18 @@ class IpsecTraTests(object):
         self.vapi.cli("clear errors")
         try:
             p = self.params[socket.AF_INET6]
-            vpp_tra_sa, scapy_tra_sa = self.configure_sa_tra(p)
-            send_pkts = self.gen_encrypt_pkts6(scapy_tra_sa, self.tra_if,
+            send_pkts = self.gen_encrypt_pkts6(p.scapy_tra_sa, self.tra_if,
                                                src=self.tra_if.remote_ip6,
                                                dst=self.tra_if.local_ip6,
                                                count=count)
             recv_pkts = self.send_and_expect(self.tra_if, send_pkts,
                                              self.tra_if)
-            for p in recv_pkts:
+            for rx in recv_pkts:
                 try:
-                    decrypted = vpp_tra_sa.decrypt(p[IPv6])
+                    decrypted = p.vpp_tra_sa.decrypt(rx[IPv6])
                     self.assert_packet_checksums_valid(decrypted)
                 except:
-                    self.logger.debug(ppp("Unexpected packet:", p))
+                    self.logger.debug(ppp("Unexpected packet:", rx))
                     raise
         finally:
             self.logger.info(self.vapi.ppcli("show error"))
index e832bfa..928cd53 100644 (file)
@@ -45,6 +45,7 @@ class TemplateIpsecAh(TemplateIpsec):
                                              cls.tra_if.sw_if_index)
         for _, p in cls.params.items():
             cls.config_ah_tra(p)
+            cls.configure_sa_tra(p)
         cls.logger.info(cls.vapi.ppcli("show ipsec"))
         for _, p in cls.params.items():
             cls.config_ah_tun(p)
@@ -134,12 +135,14 @@ class TemplateIpsecAh(TemplateIpsec):
                                          auth_algo_vpp_id, auth_key,
                                          crypt_algo_vpp_id, crypt_key,
                                          cls.vpp_ah_protocol, is_tunnel=0,
-                                         is_tunnel_ipv6=0)
+                                         is_tunnel_ipv6=0,
+                                         use_anti_replay=1)
         cls.vapi.ipsec_sad_add_del_entry(vpp_tra_sa_id, vpp_tra_spi,
                                          auth_algo_vpp_id, auth_key,
                                          crypt_algo_vpp_id, crypt_key,
                                          cls.vpp_ah_protocol, is_tunnel=0,
-                                         is_tunnel_ipv6=0)
+                                         is_tunnel_ipv6=0,
+                                         use_anti_replay=1)
         l_startaddr = r_startaddr = socket.inet_pton(addr_type, addr_any)
         l_stopaddr = r_stopaddr = socket.inet_pton(addr_type, addr_bcast)
         cls.vapi.ipsec_spd_add_del_entry(cls.tra_spd_id, vpp_tra_sa_id,
index ed9d0d9..d22f965 100644 (file)
@@ -51,6 +51,7 @@ class TemplateIpsecEsp(TemplateIpsec):
                                              cls.tra_if.sw_if_index)
         for _, p in cls.params.items():
             cls.config_esp_tra(p)
+            cls.configure_sa_tra(p)
         cls.logger.info(cls.vapi.ppcli("show ipsec"))
         cls.vapi.ipsec_spd_add_del(cls.tun_spd_id)
         cls.vapi.ipsec_interface_add_del_spd(cls.tun_spd_id,
@@ -144,11 +145,13 @@ class TemplateIpsecEsp(TemplateIpsec):
         cls.vapi.ipsec_sad_add_del_entry(scapy_tra_sa_id, scapy_tra_spi,
                                          auth_algo_vpp_id, auth_key,
                                          crypt_algo_vpp_id, crypt_key,
-                                         cls.vpp_esp_protocol, is_tunnel=0)
+                                         cls.vpp_esp_protocol, is_tunnel=0,
+                                         use_anti_replay=1)
         cls.vapi.ipsec_sad_add_del_entry(vpp_tra_sa_id, vpp_tra_spi,
                                          auth_algo_vpp_id, auth_key,
                                          crypt_algo_vpp_id, crypt_key,
-                                         cls.vpp_esp_protocol, is_tunnel=0)
+                                         cls.vpp_esp_protocol, is_tunnel=0,
+                                         use_anti_replay=1)
         l_startaddr = r_startaddr = socket.inet_pton(addr_type, addr_any)
         l_stopaddr = r_stopaddr = socket.inet_pton(addr_type, addr_bcast)
         cls.vapi.ipsec_spd_add_del_entry(cls.tra_spd_id, vpp_tra_sa_id,
index 3707587..130f178 100644 (file)
@@ -3348,7 +3348,9 @@ class VppPapiProvider(object):
                                 is_tunnel=1,
                                 is_tunnel_ipv6=0,
                                 is_add=1,
-                                udp_encap=0):
+                                udp_encap=0,
+                                use_anti_replay=0,
+                                use_extended_sequence_number=0):
         """ IPSEC SA add/del
         :param sad_id: security association ID
         :param spi: security param index of the SA in decimal
@@ -3381,7 +3383,9 @@ class VppPapiProvider(object):
              'is_add': is_add,
              'is_tunnel': is_tunnel,
              'is_tunnel_ipv6': is_tunnel_ipv6,
-             'udp_encap': udp_encap})
+             'udp_encap': udp_encap,
+             'use_extended_sequence_number': use_extended_sequence_number,
+             'use_anti_replay': use_anti_replay})
 
     def ipsec_spd_add_del_entry(self,
                                 spd_id,