summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Tehlar <ftehlar@cisco.com>2020-10-10 04:39:11 +0000
committerBeno�t Ganne <bganne@cisco.com>2020-10-21 16:11:28 +0000
commitedf2900ac633ae0d8575b04094b1bca40e1a221f (patch)
treec6afab1f6a9162d07aee9835628b90a030f2a33d
parent6ba4e41d33ffda2596d9d4b3a1d7fdd3c9a6b870 (diff)
ikev2: support sending requests from responder
Type: improvement Ticket: VPP-1894 Change-Id: I5a24a48416bca2ffbd346cdaa813fb25801e6c9b Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
-rw-r--r--src/plugins/ikev2/ikev2.c28
-rw-r--r--src/plugins/ikev2/ikev2.h5
-rw-r--r--src/plugins/ikev2/ikev2_priv.h2
-rw-r--r--src/plugins/ikev2/test/test_ikev2.py140
4 files changed, 146 insertions, 29 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 3ce9114e358..05dfb604006 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -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)
diff --git a/src/plugins/ikev2/ikev2.h b/src/plugins/ikev2/ikev2.h
index 47c301f33a1..893d9544aa8 100644
--- a/src/plugins/ikev2/ikev2.h
+++ b/src/plugins/ikev2/ikev2.h
@@ -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;
diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h
index 115a5b2a6b3..2b89b66efa6 100644
--- a/src/plugins/ikev2/ikev2_priv.h
+++ b/src/plugins/ikev2/ikev2_priv.h
@@ -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;
diff --git a/src/plugins/ikev2/test/test_ikev2.py b/src/plugins/ikev2/test/test_ikev2.py
index f75a517f824..ec68658f564 100644
--- a/src/plugins/ikev2/test/test_ikev2.py
+++ b/src/plugins/ikev2/test/test_ikev2.py
@@ -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),
="p">: is_member; } if (insert) { *insert = !is_member; if (!is_member) { uword j; w |= b; h->is_member_bitmap[i] = w; for (j = i + 1; j < vec_len (h->member_counts); j++) h->member_counts[j] += 1; } return 1 + d; } d = is_member ? d : 0; return is_member + d; } always_inline uword sparse_vec_index (void *v, uword sparse_index) { return sparse_vec_index_internal (v, sparse_index, /* maybe range */ 0, /* insert? */ 0); } always_inline void sparse_vec_index2 (void *v, u32 si0, u32 si1, u32 * i0_return, u32 * i1_return) { sparse_vec_header_t *h; uword b0, b1, w0, w1, v0, v1; u32 i0, i1, d0, d1; u8 is_member0, is_member1; h = sparse_vec_header (v); i0 = si0 / BITS (h->is_member_bitmap[0]); i1 = si1 / BITS (h->is_member_bitmap[0]); b0 = (uword) 1 << (uword) (si0 % BITS (h->is_member_bitmap[0])); b1 = (uword) 1 << (uword) (si1 % BITS (h->is_member_bitmap[0])); ASSERT (i0 < vec_len (h->is_member_bitmap)); ASSERT (i1 < vec_len (h->is_member_bitmap)); ASSERT (i0 < vec_len (h->member_counts)); ASSERT (i1 < vec_len (h->member_counts)); w0 = h->is_member_bitmap[i0]; w1 = h->is_member_bitmap[i1]; v0 = w0 & (b0 - 1); v1 = w1 & (b1 - 1); /* Speculate that masks will have zero or one bits set. */ d0 = h->member_counts[i0] + (v0 != 0); d1 = h->member_counts[i1] + (v1 != 0); /* Validate speculation. */ if (PREDICT_FALSE (!is_pow2 (v0) || !is_pow2 (v1))) { d0 += count_set_bits (v0) - (v0 != 0); d1 += count_set_bits (v1) - (v1 != 0); } is_member0 = (w0 & b0) != 0; is_member1 = (w1 & b1) != 0; d0 = is_member0 ? d0 : 0; d1 = is_member1 ? d1 : 0; *i0_return = is_member0 + d0; *i1_return = is_member1 + d1; } #define sparse_vec_free(v) vec_free(v) #define sparse_vec_elt_at_index(v,i) \ vec_elt_at_index ((v), sparse_vec_index ((v), (i))) #define sparse_vec_validate(v,i) \ ({ \ uword _i; \ u32 _insert; \ \ if (! (v)) \ (v) = sparse_vec_new (sizeof ((v)[0]), BITS (u16)); \ \ _i = sparse_vec_index_internal ((v), (i), \ /* maybe range */ 0, \ /* insert? */ &_insert); \ if (_insert) \ vec_insert_ha ((v), 1, _i, \ /* header size */ sizeof (sparse_vec_header_t), \ /* align */ 0); \ \ /* Invalid index is 0. */ \ ASSERT (_i > 0); \ \ (v) + _i; \ }) #endif /* included_sparse_vec_h */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */