ikev2: do not accept rekey until old SA is deleted 22/36822/3
authorAtzm Watanabe <atzmism@gmail.com>
Tue, 9 Aug 2022 05:00:03 +0000 (14:00 +0900)
committerBeno�t Ganne <bganne@cisco.com>
Wed, 10 Aug 2022 07:44:55 +0000 (07:44 +0000)
Type: fix
Signed-off-by: Atzm Watanabe <atzmism@gmail.com>
Change-Id: I11b6107492004a45104857dc2dae01b9a5a01e3b

src/plugins/ikev2/ikev2.c
src/plugins/ikev2/ikev2_priv.h
test/test_ikev2.py

index 20be89e..f8b9c6f 100644 (file)
@@ -1463,6 +1463,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       rekey = sa->rekey;
       if (vec_len (rekey) == 0)
        goto cleanup_and_exit;
+      rekey->notify_type = 0;
       rekey->protocol_id = proposal->protocol_id;
       rekey->i_proposal =
        ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
@@ -1491,22 +1492,34 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
              goto cleanup_and_exit;
            }
          vec_add2 (sa->rekey, rekey, 1);
+         rekey->notify_type = 0;
          rekey->protocol_id = n->protocol_id;
          rekey->spi = n->spi;
-         rekey->i_proposal = proposal;
-         rekey->r_proposal =
-           ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
-         /* update Ni */
-         vec_reset_length (sa->i_nonce);
-         vec_add (sa->i_nonce, nonce, nonce_len);
-         /* generate new Nr */
-         vec_validate (sa->r_nonce, nonce_len - 1);
-         RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+         if (sa->old_remote_id_present)
+           {
+             rekey->notify_type = IKEV2_NOTIFY_MSG_TEMPORARY_FAILURE;
+             vec_free (proposal);
+             vec_free (tsr);
+             vec_free (tsi);
+           }
+         else
+           {
+             rekey->i_proposal = proposal;
+             rekey->r_proposal =
+               ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
+             /* update Ni */
+             vec_reset_length (sa->i_nonce);
+             vec_add (sa->i_nonce, nonce, nonce_len);
+             /* generate new Nr */
+             vec_validate (sa->r_nonce, nonce_len - 1);
+             RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+           }
        }
       else
        {
          /* create new child SA */
          vec_add2 (sa->new_child, rekey, 1);
+         rekey->notify_type = 0;
          rekey->i_proposal = proposal;
          rekey->r_proposal =
            ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
@@ -2555,10 +2568,17 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
        }
       else if (vec_len (sa->rekey) > 0)
        {
-         ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
-         ikev2_payload_add_nonce (chain, sa->r_nonce);
-         ikev2_payload_add_ts (chain, sa->rekey[0].tsi, IKEV2_PAYLOAD_TSI);
-         ikev2_payload_add_ts (chain, sa->rekey[0].tsr, IKEV2_PAYLOAD_TSR);
+         if (sa->rekey[0].notify_type)
+           ikev2_payload_add_notify (chain, sa->rekey[0].notify_type, 0);
+         else
+           {
+             ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
+             ikev2_payload_add_nonce (chain, sa->r_nonce);
+             ikev2_payload_add_ts (chain, sa->rekey[0].tsi,
+                                   IKEV2_PAYLOAD_TSI);
+             ikev2_payload_add_ts (chain, sa->rekey[0].tsr,
+                                   IKEV2_PAYLOAD_TSR);
+           }
          vec_del1 (sa->rekey, 0);
        }
       else if (vec_len (sa->new_child) > 0)
@@ -3320,7 +3340,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
 
              if (sa0->rekey)
                {
-                 if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE)
+                 if (!sa0->rekey[0].notify_type &&
+                     sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE)
                    {
                      if (sa0->childs)
                        ikev2_sa_free_all_child_sa (&sa0->childs);
index 0a60368..379b68d 100644 (file)
@@ -312,6 +312,7 @@ typedef struct
 
 typedef struct
 {
+  u16 notify_type;
   u8 protocol_id;
   u32 spi;
   u32 ispi;
index ac77a41..3c58871 100644 (file)
@@ -752,14 +752,15 @@ class IkePeer(VppTestCase):
         else:
             self.assertNotIn(e.IPSEC_API_SAD_FLAG_UDP_ENCAP, ipsec_sa.flags)
 
-    def verify_ipsec_sas(self, is_rekey=False):
+    def verify_ipsec_sas(self, is_rekey=False, sa_count=None):
         sas = self.vapi.ipsec_sa_dump()
-        if is_rekey:
-            # after rekey there is a short period of time in which old
-            # inbound SA is still present
-            sa_count = 3
-        else:
-            sa_count = 2
+        if sa_count is None:
+            if is_rekey:
+                # after rekey there is a short period of time in which old
+                # inbound SA is still present
+                sa_count = 3
+            else:
+                sa_count = 2
         self.assertEqual(len(sas), sa_count)
         if self.sa.is_initiator:
             if is_rekey:
@@ -2078,12 +2079,15 @@ class TestResponderDpd(TestResponderPsk):
 class TestResponderRekey(TestResponderPsk):
     """test ikev2 responder - rekey"""
 
-    def rekey_from_initiator(self):
+    def send_rekey_from_initiator(self):
         packet = self.create_rekey_request()
         self.pg0.add_stream(packet)
         self.pg0.enable_capture()
         self.pg_start()
         capture = self.pg0.get_capture(1)
+        return capture
+
+    def process_rekey_response(self, capture):
         ih = self.get_ike_header(capture[0])
         plain = self.sa.hmac_and_decrypt(ih)
         sa = ikev2.IKEv2_payload_SA(plain)
@@ -2094,7 +2098,7 @@ class TestResponderRekey(TestResponderPsk):
 
     def test_responder(self):
         super(TestResponderRekey, self).test_responder()
-        self.rekey_from_initiator()
+        self.process_rekey_response(self.send_rekey_from_initiator())
         self.sa.calc_child_keys()
         self.verify_ike_sas()
         self.verify_ipsec_sas(is_rekey=True)
@@ -2103,6 +2107,32 @@ class TestResponderRekey(TestResponderPsk):
         self.assertEqual(r[0].sa.stats.n_rekey_req, 1)
 
 
+@tag_fixme_vpp_workers
+class TestResponderRekeyRepeat(TestResponderRekey):
+    """test ikev2 responder - rekey repeat"""
+
+    def test_responder(self):
+        super(TestResponderRekeyRepeat, self).test_responder()
+        # rekey request is not accepted until old IPsec SA is expired
+        capture = self.send_rekey_from_initiator()
+        ih = self.get_ike_header(capture[0])
+        plain = self.sa.hmac_and_decrypt(ih)
+        notify = ikev2.IKEv2_payload_Notify(plain)
+        self.assertEqual(notify.type, 43)
+        self.assertEqual(len(self.vapi.ipsec_sa_dump()), 3)
+        # rekey request is accepted after old IPsec SA was expired
+        for _ in range(50):
+            if len(self.vapi.ipsec_sa_dump()) != 3:
+                break
+            time.sleep(0.2)
+        else:
+            self.fail("old IPsec SA not expired")
+        self.process_rekey_response(self.send_rekey_from_initiator())
+        self.sa.calc_child_keys()
+        self.verify_ike_sas()
+        self.verify_ipsec_sas(sa_count=3)
+
+
 class TestResponderVrf(TestResponderPsk, Ikev2Params):
     """test ikev2 responder - non-default table id"""