ikev2: support sending requests from responder 47/29547/3
authorFilip Tehlar <ftehlar@cisco.com>
Sat, 10 Oct 2020 04:39:11 +0000 (04:39 +0000)
committerBeno�t Ganne <bganne@cisco.com>
Wed, 21 Oct 2020 16:11:28 +0000 (16:11 +0000)
Type: improvement
Ticket: VPP-1894

Change-Id: I5a24a48416bca2ffbd346cdaa813fb25801e6c9b
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
src/plugins/ikev2/ikev2.c
src/plugins/ikev2/ikev2.h
src/plugins/ikev2/ikev2_priv.h
src/plugins/ikev2/test/test_ikev2.py

index 3ce9114..05dfb60 100644 (file)
@@ -2352,7 +2352,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
        {
          if (sa->del[0].protocol_id == IKEV2_PROTOCOL_IKE)
            {
-             if (sa->is_initiator)
+             if (ike_hdr_is_request (ike))
                ikev2_payload_add_delete (chain, sa->del);
 
              /* The response to a request that deletes the IKE SA is an empty
@@ -2446,16 +2446,14 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
   ike->version = IKE_VERSION_2;
   ike->nextpayload = IKEV2_PAYLOAD_SK;
   tlen = sizeof (*ike);
+
   if (sa->is_initiator)
+    ike->flags |= IKEV2_HDR_FLAG_INITIATOR;
+
+  if (ike_hdr_is_request (ike))
     {
-      ike->flags = IKEV2_HDR_FLAG_INITIATOR;
       sa->last_init_msg_id = clib_net_to_host_u32 (ike->msgid);
     }
-  else
-    {
-      ike->flags = IKEV2_HDR_FLAG_RESPONSE;
-    }
-
 
   if (ike->exchange == IKEV2_EXCHANGE_SA_INIT)
     {
@@ -2638,6 +2636,9 @@ ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr,
 static u32
 ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike)
 {
+  if (ike_hdr_is_response (ike))
+    return 0;
+
   u32 msg_id = clib_net_to_host_u32 (ike->msgid);
 
   /* new req */
@@ -2852,7 +2853,7 @@ ikev2_node_internal (vlib_main_t * vm,
          sa0 = &sa;
          clib_memset (sa0, 0, sizeof (*sa0));
 
-         if (ike0->flags & IKEV2_HDR_FLAG_INITIATOR)
+         if (ike_hdr_is_initiator (ike0))
            {
              if (ike0->rspi == 0)
                {
@@ -2898,6 +2899,7 @@ ikev2_node_internal (vlib_main_t * vm,
                  if (sa0->state == IKEV2_STATE_SA_INIT
                      || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE)
                    {
+                     ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                      slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
@@ -2945,6 +2947,7 @@ ikev2_node_internal (vlib_main_t * vm,
                          ikev2_complete_sa_data (sa0, sai);
                          ikev2_calc_keys (sa0);
                          ikev2_sa_auth_init (sa0);
+                         ike0->flags = IKEV2_HDR_FLAG_INITIATOR;
                          slen =
                            ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                          if (~0 == slen)
@@ -3086,9 +3089,9 @@ ikev2_node_internal (vlib_main_t * vm,
                        }
                    }
                }
-             if (!(ike0->flags & IKEV2_HDR_FLAG_RESPONSE))
+             if (ike_hdr_is_request (ike0))
                {
-                 ike0->flags |= IKEV2_HDR_FLAG_RESPONSE;
+                 ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                  slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                  if (~0 == slen)
                    vlib_node_increment_counter (vm, node->node_index,
@@ -3149,6 +3152,7 @@ ikev2_node_internal (vlib_main_t * vm,
                    }
                  else
                    {
+                     ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                      slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
@@ -3615,7 +3619,7 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
       ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
       ike0->ispi = clib_host_to_net_u64 (sa->ispi);
       ike0->rspi = clib_host_to_net_u64 (sa->rspi);
-
+      ike0->flags = 0;
       ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
       sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
       len = ikev2_generate_message (b0, sa, ike0, 0, 0);
@@ -4269,6 +4273,7 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
   ike0->ispi = clib_host_to_net_u64 (sa->ispi);
   ike0->rspi = clib_host_to_net_u64 (sa->rspi);
+  ike0->flags = 0;
   vec_resize (sa->del, 1);
   sa->del->protocol_id = IKEV2_PROTOCOL_ESP;
   sa->del->spi = csa->i_proposals->spi;
@@ -4837,6 +4842,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
   ike0->ispi = clib_host_to_net_u64 (sa->ispi);
   ike0->rspi = clib_host_to_net_u64 (sa->rspi);
   ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
+  ike0->flags = 0;
   sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
   len = ikev2_generate_message (b0, sa, ike0, 0, 0);
   if (~0 == len)
index 47c301f..893d954 100644 (file)
@@ -44,6 +44,11 @@ typedef CLIB_PACKED (struct {
 }) ike_header_t;
 /* *INDENT-ON* */
 
+#define ike_hdr_is_response(_h) ((_h)->flags & IKEV2_HDR_FLAG_RESPONSE)
+#define ike_hdr_is_request(_h) (!ike_hdr_is_response(_h))
+#define ike_hdr_is_initiator(_h) ((_h)->flags & IKEV2_HDR_FLAG_INITIATOR)
+#define ike_hdr_is_responder(_h) (!(ike_hdr_is_initiator(_h)))
+
 /* *INDENT-OFF* */
 typedef CLIB_PACKED (struct {
   u8 nextpayload;
index 115a5b2..2b89b66 100644 (file)
@@ -400,10 +400,12 @@ typedef struct
   u8 *last_sa_init_res_packet_data;
 
   /* retransmit */
+  /* message id expected in the request from the other peer */
   u32 last_msg_id;
   u8 *last_res_packet_data;
 
   u8 is_initiator;
+  /* last message id that was used for an initiated request */
   u32 last_init_msg_id;
   u32 profile_index;
   u8 is_tun_itf_set;
index f75a517..ec68658 100644 (file)
@@ -113,9 +113,7 @@ class CryptoAlgo(object):
                                self.mode(nonce, icv, len(icv)),
                                default_backend()).decryptor()
             decryptor.authenticate_additional_data(aad)
-            pt = decryptor.update(ct) + decryptor.finalize()
-            pad_len = pt[-1] + 1
-            return pt[:-pad_len]
+            return decryptor.update(ct) + decryptor.finalize()
 
     def pad(self, data):
         pad_len = (len(data) // self.bs + 1) * self.bs - len(data)
@@ -435,14 +433,17 @@ class IKEv2SA(object):
             aad_len = len(ikev2.IKEv2_payload_Encrypted()) + len(ikev2.IKEv2())
             ct = ep.load[:-GCM_ICV_SIZE]
             tag = ep.load[-GCM_ICV_SIZE:]
-            return self.decrypt(ct, raw(ike)[:aad_len], tag)
+            plain = self.decrypt(ct, raw(ike)[:aad_len], tag)
         else:
             self.verify_hmac(raw(ike))
             integ_trunc = self.ike_integ_alg.trunc_len
 
             # remove ICV and decrypt payload
             ct = ep.load[:-integ_trunc]
-            return self.decrypt(ct)
+            plain = self.decrypt(ct)
+        # remove padding
+        pad_len = plain[-1]
+        return plain[:-pad_len - 1]
 
     def build_ts_addr(self, ts, version):
         return {'starting_address_v' + version: ts['start_addr'],
@@ -540,6 +541,19 @@ class IkePeer(VppTestCase):
     def tearDownClass(cls):
         super(IkePeer, cls).tearDownClass()
 
+    def tearDown(self):
+        super(IkePeer, self).tearDown()
+        if self.del_sa_from_responder:
+            self.initiate_del_sa_from_responder()
+        else:
+            self.initiate_del_sa_from_initiator()
+        r = self.vapi.ikev2_sa_dump()
+        self.assertEqual(len(r), 0)
+        sas = self.vapi.ipsec_sa_dump()
+        self.assertEqual(len(sas), 0)
+        self.p.remove_vpp_config()
+        self.assertIsNone(self.p.query_vpp_config())
+
     def setUp(self):
         super(IkePeer, self).setUp()
         self.config_tc()
@@ -580,6 +594,7 @@ class IkePeer(VppTestCase):
             esp = packet[ESP]
             ih = self.verify_and_remove_non_esp_marker(esp)
         self.assertEqual(ih.version, 0x20)
+        self.assertNotIn('Version', ih.flags)
         return ih
 
     def verify_and_remove_non_esp_marker(self, packet):
@@ -775,8 +790,49 @@ class IkePeer(VppTestCase):
 class TemplateInitiator(IkePeer):
     """ initiator test template """
 
-    def tearDown(self):
-        super(TemplateInitiator, self).tearDown()
+    def initiate_del_sa_from_initiator(self):
+        ispi = int.from_bytes(self.sa.ispi, 'little')
+        self.pg0.enable_capture()
+        self.pg_start()
+        self.vapi.ikev2_initiate_del_ike_sa(ispi=ispi)
+        capture = self.pg0.get_capture(1)
+        ih = self.get_ike_header(capture[0])
+        self.assertNotIn('Response', ih.flags)
+        self.assertIn('Initiator', ih.flags)
+        self.assertEqual(ih.init_SPI, self.sa.ispi)
+        self.assertEqual(ih.resp_SPI, self.sa.rspi)
+        plain = self.sa.hmac_and_decrypt(ih)
+        d = ikev2.IKEv2_payload_Delete(plain)
+        self.assertEqual(d.proto, 1)  # proto=IKEv2
+        header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
+                             flags='Response', exch_type='INFORMATIONAL',
+                             id=ih.id, next_payload='Encrypted')
+        resp = self.encrypt_ike_msg(header, b'', None)
+        self.send_and_assert_no_replies(self.pg0, resp)
+
+    def verify_del_sa(self, packet):
+        ih = self.get_ike_header(packet)
+        self.assertEqual(ih.id, self.sa.msg_id)
+        self.assertEqual(ih.exch_type, 37)  # exchange informational
+        self.assertIn('Response', ih.flags)
+        self.assertIn('Initiator', ih.flags)
+        plain = self.sa.hmac_and_decrypt(ih)
+        self.assertEqual(plain, b'')
+
+    def initiate_del_sa_from_responder(self):
+        header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
+                             exch_type='INFORMATIONAL',
+                             id=self.sa.new_msg_id())
+        del_sa = ikev2.IKEv2_payload_Delete(proto='IKEv2')
+        ike_msg = self.encrypt_ike_msg(header, del_sa, 'Delete')
+        packet = self.create_packet(self.pg0, ike_msg,
+                                    self.sa.sport, self.sa.dport,
+                                    self.sa.natt, self.ip6)
+        self.pg0.add_stream(packet)
+        self.pg0.enable_capture()
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        self.verify_del_sa(capture[0])
 
     @staticmethod
     def find_notify_payload(packet, notify_type):
@@ -946,22 +1002,41 @@ class TemplateInitiator(IkePeer):
 class TemplateResponder(IkePeer):
     """ responder test template """
 
-    def tearDown(self):
-        super(TemplateResponder, self).tearDown()
-        if self.sa.is_initiator:
-            self.initiate_del_sa()
-            r = self.vapi.ikev2_sa_dump()
-            self.assertEqual(len(r), 0)
-
-        self.p.remove_vpp_config()
-        self.assertIsNone(self.p.query_vpp_config())
+    def initiate_del_sa_from_responder(self):
+        self.pg0.enable_capture()
+        self.pg_start()
+        self.vapi.ikev2_initiate_del_ike_sa(
+                ispi=int.from_bytes(self.sa.ispi, 'little'))
+        capture = self.pg0.get_capture(1)
+        ih = self.get_ike_header(capture[0])
+        self.assertNotIn('Response', ih.flags)
+        self.assertNotIn('Initiator', ih.flags)
+        self.assertEqual(ih.exch_type, 37)  # INFORMATIONAL
+        plain = self.sa.hmac_and_decrypt(ih)
+        d = ikev2.IKEv2_payload_Delete(plain)
+        self.assertEqual(d.proto, 1)  # proto=IKEv2
+        self.assertEqual(ih.init_SPI, self.sa.ispi)
+        self.assertEqual(ih.resp_SPI, self.sa.rspi)
+        header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
+                             flags='Initiator+Response',
+                             exch_type='INFORMATIONAL',
+                             id=ih.id, next_payload='Encrypted')
+        resp = self.encrypt_ike_msg(header, b'', None)
+        self.send_and_assert_no_replies(self.pg0, resp)
 
     def verify_del_sa(self, packet):
         ih = self.get_ike_header(packet)
         self.assertEqual(ih.id, self.sa.msg_id)
         self.assertEqual(ih.exch_type, 37)  # exchange informational
+        self.assertIn('Response', ih.flags)
+        self.assertNotIn('Initiator', ih.flags)
+        self.assertEqual(ih.next_payload, 46)  # Encrypted
+        self.assertEqual(ih.init_SPI, self.sa.ispi)
+        self.assertEqual(ih.resp_SPI, self.sa.rspi)
+        plain = self.sa.hmac_and_decrypt(ih)
+        self.assertEqual(plain, b'')
 
-    def initiate_del_sa(self):
+    def initiate_del_sa_from_initiator(self):
         header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
                              flags='Initiator', exch_type='INFORMATIONAL',
                              id=self.sa.new_msg_id())
@@ -1081,7 +1156,7 @@ class TemplateResponder(IkePeer):
 
         self.assertEqual(ih.id, self.sa.msg_id)
         self.assertEqual(ih.exch_type, 34)
-        self.assertTrue('Response' in ih.flags)
+        self.assertIn('Response', ih.flags)
         self.assertEqual(ih.init_SPI, self.sa.ispi)
         self.assertNotEqual(ih.resp_SPI, 0)
         self.sa.rspi = ih.resp_SPI
@@ -1129,6 +1204,8 @@ class Ikev2Params(object):
                 'SHA2-384-192': ei.IPSEC_API_INTEG_ALG_SHA_384_192,
                 'SHA2-512-256': ei.IPSEC_API_INTEG_ALG_SHA_512_256}
 
+        self.del_sa_from_responder = False if 'del_sa_from_responder'\
+            not in params else params['del_sa_from_responder']
         is_natt = 'natt' in params and params['natt'] or False
         self.p = Profile(self, 'pr1')
         self.ip6 = False if 'ip6' not in params else params['ip6']
@@ -1392,8 +1469,34 @@ class TestApi(VppTestCase):
 
 class TestInitiatorPsk(TemplateInitiator, Ikev2Params):
     """ test ikev2 initiator - pre shared key auth """
+
+    def config_tc(self):
+        self.config_params({
+            'is_initiator': False,  # seen from test case perspective
+                                    # thus vpp is initiator
+            'responder': {'sw_if_index': self.pg0.sw_if_index,
+                           'addr': self.pg0.remote_ip4},
+            'ike-crypto': ('AES-GCM-16ICV', 32),
+            'ike-integ': 'NULL',
+            'ike-dh': '3072MODPgr',
+            'ike_transforms': {
+                'crypto_alg': 20,  # "aes-gcm-16"
+                'crypto_key_size': 256,
+                'dh_group': 15,  # "modp-3072"
+            },
+            'esp_transforms': {
+                'crypto_alg': 12,  # "aes-cbc"
+                'crypto_key_size': 256,
+                # "hmac-sha2-256-128"
+                'integ_alg': 12}})
+
+
+class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
+    """ test ikev2 initiator - delete IKE SA from responder """
+
     def config_tc(self):
         self.config_params({
+            'del_sa_from_responder': True,
             'is_initiator': False,  # seen from test case perspective
                                     # thus vpp is initiator
             'responder': {'sw_if_index': self.pg0.sw_if_index,
@@ -1471,6 +1574,7 @@ class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
     """
     def config_tc(self):
         self.config_params({
+            'del_sa_from_responder': True,
             'ip6': True,
             'natt': True,
             'ike-crypto': ('AES-GCM-16ICV', 32),