aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Tehlar <ftehlar@cisco.com>2020-09-23 11:20:12 +0000
committerAndrew Yourtchenko <ayourtch@gmail.com>2020-11-26 15:02:41 +0000
commit8b69f7546e205470623d0ea1542e0488eb3f625f (patch)
tree10eecdf0bd8a6c671290420f7cc772f5292ae954
parent3ff88076d3e5c1a172217f8e7070bfc7ef2f3d04 (diff)
ikev2: fix initial contact cleanup
When looking for existing SA connection to clean up search all per thread data, not only current one. Type: fix Change-Id: I59312e08a07ca1f474b6389999e59320c5128e7d Signed-off-by: Filip Tehlar <ftehlar@cisco.com> (cherry picked from commit e7c8396982607634b4c747870499671ffa53868e)
-rw-r--r--src/plugins/ikev2/ikev2.c210
-rw-r--r--src/plugins/ikev2/test/test_ikev2.py658
2 files changed, 562 insertions, 306 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index f45c2180093..49fb6b66f0a 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -344,19 +344,17 @@ ikev2_sa_free_all_vec (ikev2_sa_t * sa)
}
static void
-ikev2_delete_sa (ikev2_sa_t * sa)
+ikev2_delete_sa (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa)
{
- ikev2_main_t *km = &ikev2_main;
- u32 thread_index = vlib_get_thread_index ();
uword *p;
ikev2_sa_free_all_vec (sa);
- p = hash_get (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi);
+ p = hash_get (ptd->sa_by_rspi, sa->rspi);
if (p)
{
- hash_unset (km->per_thread_data[thread_index].sa_by_rspi, sa->rspi);
- pool_put (km->per_thread_data[thread_index].sas, sa);
+ hash_unset (ptd->sa_by_rspi, sa->rspi);
+ pool_put (ptd->sas, sa);
}
}
@@ -1027,43 +1025,61 @@ ikev2_is_id_equal (ikev2_id_t * i1, ikev2_id_t * i2)
}
static void
-ikev2_initial_contact_cleanup (ikev2_sa_t * sa)
+ikev2_initial_contact_cleanup_internal (ikev2_main_per_thread_data_t * ptd,
+ ikev2_sa_t * sa)
{
ikev2_main_t *km = &ikev2_main;
ikev2_sa_t *tmp;
u32 i, *delete = 0;
ikev2_child_sa_t *c;
- u32 thread_index = vlib_get_thread_index ();
-
- if (!sa->initial_contact)
- return;
/* find old IKE SAs with the same authenticated identity */
/* *INDENT-OFF* */
- pool_foreach (tmp, km->per_thread_data[thread_index].sas, ({
- if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id)
- || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id))
- continue;
+ pool_foreach (tmp, ptd->sas, ({
+ if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id)
+ || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id))
+ continue;
- if (sa->rspi != tmp->rspi)
- vec_add1(delete, tmp - km->per_thread_data[thread_index].sas);
+ if (sa->rspi != tmp->rspi)
+ vec_add1(delete, tmp - ptd->sas);
}));
/* *INDENT-ON* */
for (i = 0; i < vec_len (delete); i++)
{
- tmp =
- pool_elt_at_index (km->per_thread_data[thread_index].sas, delete[i]);
- vec_foreach (c,
- tmp->childs) ikev2_delete_tunnel_interface (km->vnet_main,
- tmp, c);
- ikev2_delete_sa (tmp);
+ tmp = pool_elt_at_index (ptd->sas, delete[i]);
+ vec_foreach (c, tmp->childs)
+ {
+ ikev2_delete_tunnel_interface (km->vnet_main, tmp, c);
+ }
+ ikev2_delete_sa (ptd, tmp);
}
vec_free (delete);
sa->initial_contact = 0;
}
+static void
+ikev2_initial_contact_cleanup (ikev2_main_per_thread_data_t * ptd,
+ ikev2_sa_t * sa)
+{
+ ikev2_main_t *km = &ikev2_main;
+
+ if (!sa->initial_contact)
+ return;
+
+ if (ptd)
+ {
+ ikev2_initial_contact_cleanup_internal (ptd, sa);
+ }
+ else
+ {
+ vec_foreach (ptd, km->per_thread_data)
+ ikev2_initial_contact_cleanup_internal (ptd, sa);
+ }
+ sa->initial_contact = 0;
+}
+
static int
ikev2_parse_id_payload (const void *p, u16 rlen, ikev2_id_t * sa_id)
{
@@ -2532,74 +2548,87 @@ done:
}
static u32
+ikev2_retransmit_sa_init_one (ikev2_sa_t * sa, ike_header_t * ike,
+ ip_address_t iaddr, ip_address_t raddr,
+ u32 rlen)
+{
+ int p = 0;
+ ike_header_t *tmp;
+ u8 payload = ike->nextpayload;
+
+ if (sa->ispi != clib_net_to_host_u64 (ike->ispi) ||
+ ip_address_cmp (&sa->iaddr, &iaddr) ||
+ ip_address_cmp (&sa->raddr, &raddr))
+ {
+ return 0;
+ }
+
+ while (p < rlen && payload != IKEV2_PAYLOAD_NONE)
+ {
+ ike_payload_header_t *ikep = (ike_payload_header_t *) & ike->payload[p];
+ u32 plen = clib_net_to_host_u16 (ikep->length);
+
+ if (plen < sizeof (ike_payload_header_t))
+ return ~0;
+
+ if (payload == IKEV2_PAYLOAD_NONCE &&
+ !clib_memcmp (sa->i_nonce, ikep->payload, plen - sizeof (*ikep)))
+ {
+ /* req is retransmit */
+ if (sa->state == IKEV2_STATE_SA_INIT)
+ {
+ tmp = (ike_header_t *) sa->last_sa_init_res_packet_data;
+ u32 slen = clib_net_to_host_u32 (tmp->length);
+ ike->ispi = tmp->ispi;
+ ike->rspi = tmp->rspi;
+ ike->nextpayload = tmp->nextpayload;
+ ike->version = tmp->version;
+ ike->exchange = tmp->exchange;
+ ike->flags = tmp->flags;
+ ike->msgid = tmp->msgid;
+ ike->length = tmp->length;
+ clib_memcpy_fast (ike->payload, tmp->payload,
+ slen - sizeof (*ike));
+ ikev2_elog_uint_peers (IKEV2_LOG_DEBUG,
+ "ispi %lx IKE_SA_INIT retransmit "
+ "from %d.%d.%d.%d to %d.%d.%d.%d",
+ ike->ispi,
+ ip_addr_v4 (&raddr).as_u32,
+ ip_addr_v4 (&iaddr).as_u32);
+ return slen;
+ }
+ /* else ignore req */
+ else
+ {
+ ikev2_elog_uint_peers (IKEV2_LOG_DEBUG,
+ "ispi %lx IKE_SA_INIT ignore "
+ "from %d.%d.%d.%d to %d.%d.%d.%d",
+ ike->ispi,
+ ip_addr_v4 (&raddr).as_u32,
+ ip_addr_v4 (&iaddr).as_u32);
+ return ~0;
+ }
+ }
+ payload = ikep->nextpayload;
+ p += plen;
+ }
+
+ return 0;
+}
+
+static u32
ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr,
ip_address_t raddr, u32 rlen)
{
ikev2_sa_t *sa;
+ u32 res;
ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
/* *INDENT-OFF* */
pool_foreach (sa, ptd->sas, ({
- if (sa->ispi == clib_net_to_host_u64(ike->ispi) &&
- !ip_address_cmp(&sa->iaddr, &iaddr) &&
- !ip_address_cmp(&sa->raddr, &raddr))
- {
- int p = 0;
- u8 payload = ike->nextpayload;
-
- while (p < rlen && payload!= IKEV2_PAYLOAD_NONE) {
- ike_payload_header_t * ikep = (ike_payload_header_t *) &ike->payload[p];
- u32 plen = clib_net_to_host_u16 (ikep->length);
- if (plen > p + sizeof (*ike))
- return ~0;
-
- if (plen < sizeof(ike_payload_header_t))
- return ~0;
-
- if (payload == IKEV2_PAYLOAD_NONCE)
- {
- if (!clib_memcmp(sa->i_nonce, ikep->payload,
- plen - sizeof(*ikep)))
- {
- /* req is retransmit */
- if (sa->state == IKEV2_STATE_SA_INIT)
- {
- ike_header_t * tmp = (ike_header_t*)sa->last_sa_init_res_packet_data;
- u32 slen = clib_net_to_host_u32(tmp->length);
- ike->ispi = tmp->ispi;
- ike->rspi = tmp->rspi;
- ike->nextpayload = tmp->nextpayload;
- ike->version = tmp->version;
- ike->exchange = tmp->exchange;
- ike->flags = tmp->flags;
- ike->msgid = tmp->msgid;
- ike->length = tmp->length;
- clib_memcpy_fast(ike->payload, tmp->payload, slen - sizeof(*ike));
- ikev2_elog_uint_peers (IKEV2_LOG_DEBUG,
- "ispi %lx IKE_SA_INIT retransmit "
- "from %d.%d.%d.%d to %d.%d.%d.%d",
- ike->ispi,
- ip_addr_v4(&raddr).as_u32,
- ip_addr_v4 (&iaddr).as_u32);
- return slen;
- }
- /* else ignore req */
- else
- {
- ikev2_elog_uint_peers (IKEV2_LOG_DEBUG,
- "ispi %lx IKE_SA_INIT ignore "
- "from %d.%d.%d.%d to %d.%d.%d.%d",
- ike->ispi,
- ip_addr_v4(&raddr).as_u32,
- ip_addr_v4(&iaddr).as_u32);
- return ~0;
- }
- }
- }
- payload = ikep->nextpayload;
- p+=plen;
- }
- }
+ res = ikev2_retransmit_sa_init_one (sa, ike, iaddr, raddr, rlen);
+ if (res)
+ return res;
}));
/* *INDENT-ON* */
@@ -2982,7 +3011,7 @@ ikev2_node_internal (vlib_main_t * vm,
IKEV2_ERROR_MALFORMED_PACKET, 1);
if (sa0->state == IKEV2_STATE_AUTHENTICATED)
{
- ikev2_initial_contact_cleanup (sa0);
+ ikev2_initial_contact_cleanup (ptd, sa0);
ikev2_sa_match_ts (sa0);
if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE)
ikev2_create_tunnel_interface (vm, sa0,
@@ -3202,7 +3231,7 @@ ikev2_node_internal (vlib_main_t * vm,
vec_foreach (c, sa0->childs)
ikev2_delete_tunnel_interface (km->vnet_main, sa0, c);
- ikev2_delete_sa (sa0);
+ ikev2_delete_sa (ptd, sa0);
}
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
&& (b0->flags & VLIB_BUFFER_IS_TRACED)))
@@ -3596,6 +3625,9 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
if (~0 == len)
return;
+ if (sa->natt)
+ len = ikev2_insert_non_esp_marker (ike0, len);
+
if (sa->is_initiator)
{
src = &sa->iaddr;
@@ -4178,7 +4210,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
vec_add (sa.childs[0].tsi, &p->loc_ts, 1);
vec_add (sa.childs[0].tsr, &p->rem_ts, 1);
- ikev2_initial_contact_cleanup (&sa);
+ ikev2_initial_contact_cleanup (0, &sa);
/* add SA to the pool */
ikev2_sa_t *sa0 = 0;
@@ -4662,8 +4694,6 @@ ikev2_process_pending_sa_init (ikev2_main_t * km)
/* *INDENT-ON* */
}
-static vlib_node_registration_t ikev2_mngr_process_node;
-
static void
ikev2_send_informational_request (ikev2_sa_t * sa)
{
@@ -4761,6 +4791,10 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
pool_foreach (sa, tkm->sas, ({
ikev2_child_sa_t *c;
u8 del_old_ids = 0;
+
+ if (sa->state != IKEV2_STATE_AUTHENTICATED)
+ continue;
+
if (sa->old_remote_id_present && 0 > sa->old_id_expiration)
{
sa->old_remote_id_present = 0;
diff --git a/src/plugins/ikev2/test/test_ikev2.py b/src/plugins/ikev2/test/test_ikev2.py
index a47c59f578f..4f64b56d853 100644
--- a/src/plugins/ikev2/test/test_ikev2.py
+++ b/src/plugins/ikev2/test/test_ikev2.py
@@ -193,38 +193,52 @@ class IKEv2SA(object):
else:
self.rspi = spi
self.ispi = 8 * b'\x00'
- self.r_nonce = None
+ self.r_nonce = nonce
self.child_sas = [IKEv2ChildSA(local_ts, remote_ts)]
def new_msg_id(self):
self.msg_id += 1
return self.msg_id
- def dh_pub_key(self):
+ @property
+ def my_dh_pub_key(self):
+ if self.is_initiator:
+ return self.i_dh_data
+ return self.r_dh_data
+
+ @property
+ def peer_dh_pub_key(self):
+ if self.is_initiator:
+ return self.r_dh_data
return self.i_dh_data
def compute_secret(self):
priv = self.dh_private_key
- peer = self.r_dh_data
+ peer = self.peer_dh_pub_key
p, g, l = self.ike_group
return pow(int.from_bytes(peer, 'big'),
int.from_bytes(priv, 'big'), p).to_bytes(l, 'big')
def generate_dh_data(self):
# generate DH keys
+ if self.ike_dh not in DH:
+ raise NotImplementedError('%s not in DH group' % self.ike_dh)
+
+ if self.dh_params is None:
+ dhg = DH[self.ike_dh]
+ pn = dh.DHParameterNumbers(dhg[0], dhg[1])
+ self.dh_params = pn.parameters(default_backend())
+
+ priv = self.dh_params.generate_private_key()
+ pub = priv.public_key()
+ x = priv.private_numbers().x
+ self.dh_private_key = x.to_bytes(priv.key_size // 8, 'big')
+ y = pub.public_numbers().y
+
if self.is_initiator:
- if self.ike_dh not in DH:
- raise NotImplementedError('%s not in DH group' % self.ike_dh)
- if self.dh_params is None:
- dhg = DH[self.ike_dh]
- pn = dh.DHParameterNumbers(dhg[0], dhg[1])
- self.dh_params = pn.parameters(default_backend())
- priv = self.dh_params.generate_private_key()
- pub = priv.public_key()
- x = priv.private_numbers().x
- self.dh_private_key = x.to_bytes(priv.key_size // 8, 'big')
- y = pub.public_numbers().y
self.i_dh_data = y.to_bytes(pub.key_size // 8, 'big')
+ else:
+ self.r_dh_data = y.to_bytes(pub.key_size // 8, 'big')
def complete_dh_data(self):
self.dh_shared_secret = self.compute_secret()
@@ -335,13 +349,21 @@ class IKEv2SA(object):
id = self.i_id
nonce = self.r_nonce
key = self.sk_pi
+ else:
+ id = self.r_id
+ nonce = self.i_nonce
+ key = self.sk_pr
data = bytes([self.id_type, 0, 0, 0]) + id
id_hash = self.calc_prf(prf, key, data)
return packet + nonce + id_hash
def auth_init(self):
prf = self.ike_prf_alg.mod()
- authmsg = self.generate_authmsg(prf, raw(self.init_req_packet))
+ if self.is_initiator:
+ packet = self.init_req_packet
+ else:
+ packet = self.init_resp_packet
+ authmsg = self.generate_authmsg(prf, raw(packet))
if self.auth_method == 'shared-key':
psk = self.calc_prf(prf, self.auth_data, KEY_PAD)
self.auth_data = self.calc_prf(prf, psk, authmsg)
@@ -440,7 +462,10 @@ class IKEv2SA(object):
ts1 = ikev2.IPv6TrafficSelector(**ts_data)
ts_data.update(self.build_ts_addr(c.remote_ts, '6'))
ts2 = ikev2.IPv6TrafficSelector(**ts_data)
- return ([ts1], [ts2])
+
+ if self.is_initiator:
+ return ([ts1], [ts2])
+ return ([ts2], [ts1])
def set_ike_props(self, crypto, crypto_key_len, integ, prf, dh):
if crypto not in CRYPTO_ALGOS:
@@ -492,14 +517,14 @@ class IKEv2SA(object):
return digest.finalize()
-class TemplateResponder(VppTestCase):
- """ responder test template """
+class IkePeer(VppTestCase):
+ """ common class for initiator and responder """
@classmethod
def setUpClass(cls):
import scapy.contrib.ikev2 as _ikev2
globals()['ikev2'] = _ikev2
- super(TemplateResponder, cls).setUpClass()
+ super(IkePeer, cls).setUpClass()
cls.create_pg_interfaces(range(2))
for i in cls.pg_interfaces:
i.admin_up()
@@ -510,10 +535,10 @@ class TemplateResponder(VppTestCase):
@classmethod
def tearDownClass(cls):
- super(TemplateResponder, cls).tearDownClass()
+ super(IkePeer, cls).tearDownClass()
def setUp(self):
- super(TemplateResponder, self).setUp()
+ super(IkePeer, self).setUp()
self.config_tc()
self.p.add_vpp_config()
self.assertIsNotNone(self.p.query_vpp_config())
@@ -521,36 +546,6 @@ class TemplateResponder(VppTestCase):
self.vapi.cli('ikev2 set logging level 4')
self.vapi.cli('event-lo clear')
- 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 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
-
- def initiate_del_sa(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())
- 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])
-
def create_packet(self, src_if, msg, sport=500, dport=500, natt=False,
use_ip6=False):
if use_ip6:
@@ -569,60 +564,30 @@ class TemplateResponder(VppTestCase):
res = res / Raw(b'\x00' * 4)
return res / msg
- def send_sa_init(self, behind_nat=False):
- tr_attr = self.sa.ike_crypto_attr()
- trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
- transform_id=self.sa.ike_crypto, length=tr_attr[1],
- key_length=tr_attr[0]) /
- ikev2.IKEv2_payload_Transform(transform_type='Integrity',
- transform_id=self.sa.ike_integ) /
- ikev2.IKEv2_payload_Transform(transform_type='PRF',
- transform_id=self.sa.ike_prf_alg.name) /
- ikev2.IKEv2_payload_Transform(transform_type='GroupDesc',
- transform_id=self.sa.ike_dh))
-
- props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2',
- trans_nb=4, trans=trans))
-
- if behind_nat:
- next_payload = 'Notify'
- else:
- next_payload = None
+ def verify_udp(self, udp):
+ self.assertEqual(udp.sport, self.sa.sport)
+ self.assertEqual(udp.dport, self.sa.dport)
- self.sa.init_req_packet = (
- ikev2.IKEv2(init_SPI=self.sa.ispi,
- flags='Initiator', exch_type='IKE_SA_INIT') /
- ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) /
- ikev2.IKEv2_payload_KE(next_payload='Nonce',
- group=self.sa.ike_dh,
- load=self.sa.dh_pub_key()) /
- ikev2.IKEv2_payload_Nonce(next_payload=next_payload,
- load=self.sa.i_nonce))
+ def get_ike_header(self, packet):
+ try:
+ ih = packet[ikev2.IKEv2]
+ except IndexError as e:
+ # this is a workaround for getting IKEv2 layer as both ikev2 and
+ # ipsec register for port 4500
+ esp = packet[ESP]
+ ih = self.verify_and_remove_non_esp_marker(esp)
+ self.assertEqual(ih.version, 0x20)
+ return ih
- if behind_nat:
- src_address = b'\x0a\x0a\x0a\x01'
+ def verify_and_remove_non_esp_marker(self, packet):
+ if self.sa.natt:
+ # if we are in nat traversal mode check for non esp marker
+ # and remove it
+ data = raw(packet)
+ self.assertEqual(data[:4], b'\x00' * 4)
+ return ikev2.IKEv2(data[4:])
else:
- src_address = bytes(self.pg0.local_ip4, 'ascii')
-
- src_nat = self.sa.compute_nat_sha1(src_address, self.sa.sport)
- dst_nat = self.sa.compute_nat_sha1(bytes(self.pg0.remote_ip4, 'ascii'),
- self.sa.sport)
- nat_src_detection = ikev2.IKEv2_payload_Notify(
- type='NAT_DETECTION_SOURCE_IP', load=src_nat)
- nat_dst_detection = ikev2.IKEv2_payload_Notify(
- type='NAT_DETECTION_DESTINATION_IP', load=dst_nat)
- self.sa.init_req_packet = (self.sa.init_req_packet /
- nat_src_detection /
- nat_dst_detection)
-
- ike_msg = self.create_packet(self.pg0, self.sa.init_req_packet,
- self.sa.sport, self.sa.dport,
- self.sa.natt, self.ip6)
- self.pg0.add_stream(ike_msg)
- self.pg0.enable_capture()
- self.pg_start()
- capture = self.pg0.get_capture(1)
- self.verify_sa_init(capture[0])
+ return packet
def encrypt_ike_msg(self, header, plain, first_payload):
if self.sa.ike_crypto == 'AES-GCM-16ICV':
@@ -658,114 +623,17 @@ class TemplateResponder(VppTestCase):
assert(len(res) == tlen)
return res
- def send_sa_auth(self):
- tr_attr = self.sa.esp_crypto_attr()
- trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
- transform_id=self.sa.esp_crypto, length=tr_attr[1],
- key_length=tr_attr[0]) /
- ikev2.IKEv2_payload_Transform(transform_type='Integrity',
- transform_id=self.sa.esp_integ) /
- ikev2.IKEv2_payload_Transform(
- transform_type='Extended Sequence Number',
- transform_id='No ESN') /
- ikev2.IKEv2_payload_Transform(
- transform_type='Extended Sequence Number',
- transform_id='ESN'))
-
- props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP',
- SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans))
-
- tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
- plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
- IDtype=self.sa.id_type, load=self.sa.i_id) /
- ikev2.IKEv2_payload_IDr(next_payload='AUTH',
- IDtype=self.sa.id_type, load=self.sa.r_id) /
- ikev2.IKEv2_payload_AUTH(next_payload='SA',
- auth_type=AuthMethod.value(self.sa.auth_method),
- load=self.sa.auth_data) /
- ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) /
- ikev2.IKEv2_payload_TSi(next_payload='TSr',
- number_of_TSs=len(tsi),
- traffic_selector=tsi) /
- ikev2.IKEv2_payload_TSr(next_payload='Notify',
- number_of_TSs=len(tsr),
- traffic_selector=tsr) /
- ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT'))
-
- header = ikev2.IKEv2(
- init_SPI=self.sa.ispi,
- resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(),
- flags='Initiator', exch_type='IKE_AUTH')
-
- ike_msg = self.encrypt_ike_msg(header, plain, 'IDi')
- 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_sa_auth(capture[0])
-
- def get_ike_header(self, packet):
- try:
- ih = packet[ikev2.IKEv2]
- except IndexError as e:
- # this is a workaround for getting IKEv2 layer as both ikev2 and
- # ipsec register for port 4500
- esp = packet[ESP]
- ih = self.verify_and_remove_non_esp_marker(esp)
-
- self.assertEqual(ih.version, 0x20)
- return ih
-
- def verify_sa_init(self, packet):
- ih = self.get_ike_header(packet)
-
- self.assertEqual(ih.id, self.sa.msg_id)
- self.assertEqual(ih.exch_type, 34)
- self.assertTrue('Response' in ih.flags)
- self.assertEqual(ih.init_SPI, self.sa.ispi)
- self.assertNotEqual(ih.resp_SPI, 0)
- self.sa.rspi = ih.resp_SPI
- try:
- sa = ih[ikev2.IKEv2_payload_SA]
- self.sa.r_nonce = ih[ikev2.IKEv2_payload_Nonce].load
- self.sa.r_dh_data = ih[ikev2.IKEv2_payload_KE].load
- except IndexError as e:
- self.logger.error("unexpected reply: SA/Nonce/KE payload found!")
- self.logger.error(ih.show())
- raise
- self.sa.complete_dh_data()
- self.sa.calc_keys()
- self.sa.auth_init()
-
- def verify_and_remove_non_esp_marker(self, packet):
- if self.sa.natt:
- # if we are in nat traversal mode check for non esp marker
- # and remove it
- data = raw(packet)
- self.assertEqual(data[:4], b'\x00' * 4)
- return ikev2.IKEv2(data[4:])
- else:
- return packet
-
- def verify_udp(self, udp):
- self.assertEqual(udp.sport, self.sa.sport)
- self.assertEqual(udp.dport, self.sa.dport)
-
- def verify_sa_auth(self, packet):
- ike = self.get_ike_header(packet)
- udp = packet[UDP]
- self.verify_udp(udp)
- self.assertEqual(ike.id, self.sa.msg_id)
- plain = self.sa.hmac_and_decrypt(ike)
- self.sa.calc_child_keys()
-
def verify_ipsec_sas(self):
sas = self.vapi.ipsec_sa_dump()
self.assertEqual(len(sas), 2)
- sa0 = sas[0].entry
- sa1 = sas[1].entry
+ e = VppEnum.vl_api_ipsec_sad_flags_t
+ if self.sa.is_initiator:
+ sa0 = sas[0].entry
+ sa1 = sas[1].entry
+ else:
+ sa1 = sas[0].entry
+ sa0 = sas[1].entry
+
c = self.sa.child_sas[0]
vpp_crypto_alg = self.vpp_enums[self.sa.vpp_esp_cypto_alg]
@@ -814,11 +682,19 @@ class TemplateResponder(VppTestCase):
self.assertEqual(self.sa.ispi, (sa.ispi).to_bytes(8, 'big'))
self.assertEqual(self.sa.rspi, (sa.rspi).to_bytes(8, 'big'))
if self.ip6:
- self.assertEqual(sa.iaddr, IPv6Address(self.pg0.remote_ip6))
- self.assertEqual(sa.raddr, IPv6Address(self.pg0.local_ip6))
+ if self.sa.is_initiator:
+ self.assertEqual(sa.iaddr, IPv6Address(self.pg0.remote_ip6))
+ self.assertEqual(sa.raddr, IPv6Address(self.pg0.local_ip6))
+ else:
+ self.assertEqual(sa.iaddr, IPv6Address(self.pg0.local_ip6))
+ self.assertEqual(sa.raddr, IPv6Address(self.pg0.remote_ip6))
else:
- self.assertEqual(sa.iaddr, IPv4Address(self.pg0.remote_ip4))
- self.assertEqual(sa.raddr, IPv4Address(self.pg0.local_ip4))
+ if self.sa.is_initiator:
+ self.assertEqual(sa.iaddr, IPv4Address(self.pg0.remote_ip4))
+ self.assertEqual(sa.raddr, IPv4Address(self.pg0.local_ip4))
+ else:
+ self.assertEqual(sa.iaddr, IPv4Address(self.pg0.local_ip4))
+ self.assertEqual(sa.raddr, IPv4Address(self.pg0.remote_ip4))
self.verify_keymat(sa.keys, self.sa, 'sk_d')
self.verify_keymat(sa.keys, self.sa, 'sk_ai')
self.verify_keymat(sa.keys, self.sa, 'sk_ar')
@@ -892,8 +768,314 @@ class TemplateResponder(VppTestCase):
self.assertEqual(api_ts.end_port, ts.end_port)
self.assertEqual(api_ts.protocol_id, ts.IP_protocol_ID)
+
+class TemplateInitiator(IkePeer):
+ """ initiator test template """
+
+ def tearDown(self):
+ super(TemplateInitiator, self).tearDown()
+
+ def verify_sa_init_request(self, packet):
+ ih = packet[ikev2.IKEv2]
+ self.assertNotEqual(ih.init_SPI, 8 * b'\x00')
+ self.assertEqual(ih.exch_type, 34) # SA_INIT
+ self.sa.ispi = ih.init_SPI
+ self.assertEqual(ih.resp_SPI, 8 * b'\x00')
+ self.assertIn('Initiator', ih.flags)
+ self.assertNotIn('Response', ih.flags)
+ self.sa.i_nonce = ih[ikev2.IKEv2_payload_Nonce].load
+ self.sa.i_dh_data = ih[ikev2.IKEv2_payload_KE].load
+
+ prop = packet[ikev2.IKEv2_payload_Proposal]
+ self.assertEqual(prop.proto, 1) # proto = ikev2
+ self.assertEqual(prop.proposal, 1)
+ self.assertEqual(prop.trans[0].transform_type, 1) # encryption
+ self.assertEqual(prop.trans[0].transform_id,
+ self.p.ike_transforms['crypto_alg'])
+ self.assertEqual(prop.trans[1].transform_type, 2) # prf
+ self.assertEqual(prop.trans[1].transform_id, 5) # "hmac-sha2-256"
+ self.assertEqual(prop.trans[2].transform_type, 4) # dh
+ self.assertEqual(prop.trans[2].transform_id,
+ self.p.ike_transforms['dh_group'])
+
+ self.sa.complete_dh_data()
+ self.sa.calc_keys()
+
+ def verify_sa_auth_req(self, packet):
+ ih = self.get_ike_header(packet)
+ self.assertEqual(ih.resp_SPI, self.sa.rspi)
+ self.assertEqual(ih.init_SPI, self.sa.ispi)
+ self.assertEqual(ih.exch_type, 35) # IKE_AUTH
+ self.assertIn('Initiator', ih.flags)
+ self.assertNotIn('Response', ih.flags)
+
+ udp = packet[UDP]
+ self.verify_udp(udp)
+ self.assertEqual(ih.id, self.sa.msg_id + 1)
+ self.sa.msg_id += 1
+ plain = self.sa.hmac_and_decrypt(ih)
+ idi = ikev2.IKEv2_payload_IDi(plain)
+ idr = ikev2.IKEv2_payload_IDr(idi.payload)
+ self.assertEqual(idi.load, self.sa.i_id)
+ self.assertEqual(idr.load, self.sa.r_id)
+
+ def send_init_response(self):
+ tr_attr = self.sa.ike_crypto_attr()
+ trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
+ transform_id=self.sa.ike_crypto, length=tr_attr[1],
+ key_length=tr_attr[0]) /
+ ikev2.IKEv2_payload_Transform(transform_type='Integrity',
+ transform_id=self.sa.ike_integ) /
+ ikev2.IKEv2_payload_Transform(transform_type='PRF',
+ transform_id=self.sa.ike_prf_alg.name) /
+ ikev2.IKEv2_payload_Transform(transform_type='GroupDesc',
+ transform_id=self.sa.ike_dh))
+ props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2',
+ trans_nb=4, trans=trans))
+ self.sa.init_resp_packet = (
+ ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
+ exch_type='IKE_SA_INIT', flags='Response') /
+ ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) /
+ ikev2.IKEv2_payload_KE(next_payload='Nonce',
+ group=self.sa.ike_dh,
+ load=self.sa.my_dh_pub_key) /
+ ikev2.IKEv2_payload_Nonce(load=self.sa.r_nonce))
+
+ ike_msg = self.create_packet(self.pg0, self.sa.init_resp_packet,
+ self.sa.sport, self.sa.dport,
+ self.sa.natt, self.ip6)
+ self.pg_send(self.pg0, ike_msg)
+ capture = self.pg0.get_capture(1)
+ self.verify_sa_auth_req(capture[0])
+
+ def initiate_sa_init(self):
+ self.pg0.enable_capture()
+ self.pg_start()
+ self.vapi.ikev2_initiate_sa_init(name=self.p.profile_name)
+
+ capture = self.pg0.get_capture(1)
+ self.verify_sa_init_request(capture[0])
+ self.send_init_response()
+
+ def send_auth_response(self):
+ tr_attr = self.sa.esp_crypto_attr()
+ trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
+ transform_id=self.sa.esp_crypto, length=tr_attr[1],
+ key_length=tr_attr[0]) /
+ ikev2.IKEv2_payload_Transform(transform_type='Integrity',
+ transform_id=self.sa.esp_integ) /
+ ikev2.IKEv2_payload_Transform(
+ transform_type='Extended Sequence Number',
+ transform_id='No ESN') /
+ ikev2.IKEv2_payload_Transform(
+ transform_type='Extended Sequence Number',
+ transform_id='ESN'))
+
+ props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP',
+ SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans))
+
+ tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
+ plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
+ IDtype=self.sa.id_type, load=self.sa.i_id) /
+ ikev2.IKEv2_payload_IDr(next_payload='AUTH',
+ IDtype=self.sa.id_type, load=self.sa.r_id) /
+ ikev2.IKEv2_payload_AUTH(next_payload='SA',
+ auth_type=AuthMethod.value(self.sa.auth_method),
+ load=self.sa.auth_data) /
+ ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) /
+ ikev2.IKEv2_payload_TSi(next_payload='TSr',
+ number_of_TSs=len(tsi),
+ traffic_selector=tsi) /
+ ikev2.IKEv2_payload_TSr(next_payload='Notify',
+ number_of_TSs=len(tsr),
+ traffic_selector=tsr) /
+ ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT'))
+
+ header = ikev2.IKEv2(
+ init_SPI=self.sa.ispi,
+ resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(),
+ flags='Response', exch_type='IKE_AUTH')
+
+ ike_msg = self.encrypt_ike_msg(header, plain, 'IDi')
+ packet = self.create_packet(self.pg0, ike_msg, self.sa.sport,
+ self.sa.dport, self.sa.natt, self.ip6)
+ self.pg_send(self.pg0, packet)
+
+ def test_initiator(self):
+ self.initiate_sa_init()
+ self.sa.auth_init()
+ self.sa.calc_child_keys()
+ self.send_auth_response()
+ self.verify_ike_sas()
+
+
+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 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
+
+ def initiate_del_sa(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())
+ 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])
+
+ def send_sa_init_req(self, behind_nat=False):
+ tr_attr = self.sa.ike_crypto_attr()
+ trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
+ transform_id=self.sa.ike_crypto, length=tr_attr[1],
+ key_length=tr_attr[0]) /
+ ikev2.IKEv2_payload_Transform(transform_type='Integrity',
+ transform_id=self.sa.ike_integ) /
+ ikev2.IKEv2_payload_Transform(transform_type='PRF',
+ transform_id=self.sa.ike_prf_alg.name) /
+ ikev2.IKEv2_payload_Transform(transform_type='GroupDesc',
+ transform_id=self.sa.ike_dh))
+
+ props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='IKEv2',
+ trans_nb=4, trans=trans))
+
+ if behind_nat:
+ next_payload = 'Notify'
+ else:
+ next_payload = None
+
+ self.sa.init_req_packet = (
+ ikev2.IKEv2(init_SPI=self.sa.ispi,
+ flags='Initiator', exch_type='IKE_SA_INIT') /
+ ikev2.IKEv2_payload_SA(next_payload='KE', prop=props) /
+ ikev2.IKEv2_payload_KE(next_payload='Nonce',
+ group=self.sa.ike_dh,
+ load=self.sa.my_dh_pub_key) /
+ ikev2.IKEv2_payload_Nonce(next_payload=next_payload,
+ load=self.sa.i_nonce))
+
+ if behind_nat:
+ src_address = b'\x0a\x0a\x0a\x01'
+ else:
+ src_address = bytes(self.pg0.local_ip4, 'ascii')
+
+ src_nat = self.sa.compute_nat_sha1(src_address, self.sa.sport)
+ dst_nat = self.sa.compute_nat_sha1(bytes(self.pg0.remote_ip4, 'ascii'),
+ self.sa.sport)
+ nat_src_detection = ikev2.IKEv2_payload_Notify(
+ type='NAT_DETECTION_SOURCE_IP', load=src_nat)
+ nat_dst_detection = ikev2.IKEv2_payload_Notify(
+ type='NAT_DETECTION_DESTINATION_IP', load=dst_nat)
+ self.sa.init_req_packet = (self.sa.init_req_packet /
+ nat_src_detection /
+ nat_dst_detection)
+
+ ike_msg = self.create_packet(self.pg0, self.sa.init_req_packet,
+ self.sa.sport, self.sa.dport,
+ self.sa.natt, self.ip6)
+ self.pg0.add_stream(ike_msg)
+ self.pg0.enable_capture()
+ self.pg_start()
+ capture = self.pg0.get_capture(1)
+ self.verify_sa_init(capture[0])
+
+ def send_sa_auth(self):
+ tr_attr = self.sa.esp_crypto_attr()
+ trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
+ transform_id=self.sa.esp_crypto, length=tr_attr[1],
+ key_length=tr_attr[0]) /
+ ikev2.IKEv2_payload_Transform(transform_type='Integrity',
+ transform_id=self.sa.esp_integ) /
+ ikev2.IKEv2_payload_Transform(
+ transform_type='Extended Sequence Number',
+ transform_id='No ESN') /
+ ikev2.IKEv2_payload_Transform(
+ transform_type='Extended Sequence Number',
+ transform_id='ESN'))
+
+ props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP',
+ SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans))
+
+ tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
+ plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
+ IDtype=self.sa.id_type, load=self.sa.i_id) /
+ ikev2.IKEv2_payload_IDr(next_payload='AUTH',
+ IDtype=self.sa.id_type, load=self.sa.r_id) /
+ ikev2.IKEv2_payload_AUTH(next_payload='SA',
+ auth_type=AuthMethod.value(self.sa.auth_method),
+ load=self.sa.auth_data) /
+ ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) /
+ ikev2.IKEv2_payload_TSi(next_payload='TSr',
+ number_of_TSs=len(tsi),
+ traffic_selector=tsi) /
+ ikev2.IKEv2_payload_TSr(next_payload='Notify',
+ number_of_TSs=len(tsr),
+ traffic_selector=tsr) /
+ ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT'))
+
+ header = ikev2.IKEv2(
+ init_SPI=self.sa.ispi,
+ resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(),
+ flags='Initiator', exch_type='IKE_AUTH')
+
+ ike_msg = self.encrypt_ike_msg(header, plain, 'IDi')
+ 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_sa_auth_resp(capture[0])
+
+ def verify_sa_init(self, packet):
+ ih = self.get_ike_header(packet)
+
+ self.assertEqual(ih.id, self.sa.msg_id)
+ self.assertEqual(ih.exch_type, 34)
+ self.assertTrue('Response' in ih.flags)
+ self.assertEqual(ih.init_SPI, self.sa.ispi)
+ self.assertNotEqual(ih.resp_SPI, 0)
+ self.sa.rspi = ih.resp_SPI
+ try:
+ sa = ih[ikev2.IKEv2_payload_SA]
+ self.sa.r_nonce = ih[ikev2.IKEv2_payload_Nonce].load
+ self.sa.r_dh_data = ih[ikev2.IKEv2_payload_KE].load
+ except IndexError as e:
+ self.logger.error("unexpected reply: SA/Nonce/KE payload found!")
+ self.logger.error(ih.show())
+ raise
+ self.sa.complete_dh_data()
+ self.sa.calc_keys()
+ self.sa.auth_init()
+
+ def verify_sa_auth_resp(self, packet):
+ ike = self.get_ike_header(packet)
+ udp = packet[UDP]
+ self.verify_udp(udp)
+ self.assertEqual(ike.id, self.sa.msg_id)
+ plain = self.sa.hmac_and_decrypt(ike)
+ self.sa.calc_child_keys()
+
def test_responder(self):
- self.send_sa_init(self.sa.natt)
+ self.send_sa_init_req(self.sa.natt)
self.send_sa_auth()
self.verify_ipsec_sas()
self.verify_ike_sas()
@@ -942,17 +1124,33 @@ class Ikev2Params(object):
auth_method = 'shared-key'
client_priv = None
- self.p.add_local_id(id_type='fqdn', data=b'vpp.home')
- self.p.add_remote_id(id_type='fqdn', data=b'roadwarrior.example.com')
+ is_init = True if 'is_initiator' not in params else\
+ params['is_initiator']
+
+ idr = {'id_type': 'fqdn', 'data': b'vpp.home'}
+ idi = {'id_type': 'fqdn', 'data': b'roadwarrior.example.com'}
+ if is_init:
+ self.p.add_local_id(**idr)
+ self.p.add_remote_id(**idi)
+ else:
+ self.p.add_local_id(**idi)
+ self.p.add_remote_id(**idr)
+
loc_ts = {'start_addr': '10.10.10.0', 'end_addr': '10.10.10.255'} if\
'loc_ts' not in params else params['loc_ts']
rem_ts = {'start_addr': '10.0.0.0', 'end_addr': '10.0.0.255'} if\
'rem_ts' not in params else params['rem_ts']
self.p.add_local_ts(**loc_ts)
self.p.add_remote_ts(**rem_ts)
-
- self.sa = IKEv2SA(self, i_id=self.p.remote_id['data'],
- r_id=self.p.local_id['data'],
+ if 'responder' in params:
+ self.p.add_responder(params['responder'])
+ if 'ike_transforms' in params:
+ self.p.add_ike_transforms(params['ike_transforms'])
+ if 'esp_transforms' in params:
+ self.p.add_esp_transforms(params['esp_transforms'])
+
+ self.sa = IKEv2SA(self, i_id=idi['data'], r_id=idr['data'],
+ is_initiator=is_init,
id_type=self.p.local_id['id_type'], natt=is_natt,
priv_key=client_priv, auth_method=auth_method,
auth_data=auth_data,
@@ -962,7 +1160,8 @@ class Ikev2Params(object):
params['ike-crypto']
ike_integ = 'HMAC-SHA1-96' if 'ike-integ' not in params else\
params['ike-integ']
- ike_dh = '2048MODPgr' if 'ike-dh' not in params else params['ike-dh']
+ ike_dh = '2048MODPgr' if 'ike-dh' not in params else\
+ params['ike-dh']
esp_crypto = ('AES-CBC', 32) if 'esp-crypto' not in params else\
params['esp-crypto']
@@ -1160,6 +1359,29 @@ class TestApi(VppTestCase):
self.assertEqual(ap.tun_itf, 0xffffffff)
+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 TestResponderNATT(TemplateResponder, Ikev2Params):
""" test ikev2 responder - nat traversal """
def config_tc(self):