summaryrefslogtreecommitdiff
path: root/sbin/iked/ikev2_msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/iked/ikev2_msg.c')
-rw-r--r--sbin/iked/ikev2_msg.c180
1 files changed, 163 insertions, 17 deletions
diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c
index 43d047ac31e..43504723986 100644
--- a/sbin/iked/ikev2_msg.c
+++ b/sbin/iked/ikev2_msg.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2_msg.c,v 1.15 2012/05/30 09:18:14 mikeb Exp $ */
+/* $OpenBSD: ikev2_msg.c,v 1.16 2012/06/22 16:28:20 mikeb Exp $ */
/* $vantronix: ikev2.c,v 1.101 2010/06/03 07:57:33 reyk Exp $ */
/*
@@ -47,6 +47,12 @@
#include "eap.h"
#include "dh.h"
+void ikev2_msg_response_timeout(struct iked *, void *);
+void ikev2_msg_retransmit_timeout(struct iked *, void *);
+struct iked_message *
+ ikev2_msg_lookup_by_id(struct iked *, struct iked_msgqueue *,
+ u_int32_t);
+
void
ikev2_msg_cb(int fd, short event, void *arg)
{
@@ -128,6 +134,26 @@ ikev2_msg_init(struct iked *env, struct iked_message *msg,
return (msg->msg_data);
}
+struct iked_message *
+ikev2_msg_copy(struct iked *env, struct iked_message *msg)
+{
+ struct iked_message *m = NULL;
+ struct ibuf *buf;
+
+ if ((m = malloc(sizeof(*m))) == NULL ||
+ (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen,
+ &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL ||
+ ibuf_add(buf, ibuf_data(msg->msg_data), ibuf_size(msg->msg_data)))
+ return (NULL);
+
+ m->msg_fd = msg->msg_fd;
+ m->msg_msgid = msg->msg_msgid;
+ m->msg_offset = msg->msg_offset;
+ m->msg_sa = msg->msg_sa;
+
+ return (m);
+}
+
void
ikev2_msg_cleanup(struct iked *env, struct iked_message *msg)
{
@@ -190,8 +216,10 @@ ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
sa.sa_hdr.sh_ispi = betoh64(oldhdr->ike_ispi);
sa.sa_hdr.sh_rspi = betoh64(oldhdr->ike_rspi);
+ resp.msg_msgid = betoh32(oldhdr->ike_msgid);
+
/* IKE header */
- if ((hdr = ikev2_add_header(buf, &sa, betoh32(oldhdr->ike_msgid),
+ if ((hdr = ikev2_add_header(buf, &sa, resp.msg_msgid,
IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL,
IKEV2_FLAG_RESPONSE)) == NULL)
goto done;
@@ -225,9 +253,11 @@ ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
int
ikev2_msg_send(struct iked *env, struct iked_message *msg)
{
+ struct iked_sa *sa = msg->msg_sa;
struct ibuf *buf = msg->msg_data;
u_int32_t natt = 0x00000000;
struct ike_header *hdr;
+ struct iked_message *m;
if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
msg->msg_offset, sizeof(*hdr))) == NULL)
@@ -244,6 +274,7 @@ ikev2_msg_send(struct iked *env, struct iked_message *msg)
log_debug("%s: failed to set NAT-T", __func__);
return (-1);
}
+ msg->msg_offset += sizeof(natt);
}
if ((sendto(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
@@ -252,18 +283,34 @@ ikev2_msg_send(struct iked *env, struct iked_message *msg)
return (-1);
}
+ if (!sa)
+ return (0);
+
+ if ((m = ikev2_msg_copy(env, msg)) == NULL) {
+ log_debug("%s: failed to copy a message", __func__);
+ return (-1);
+ }
+
+ if (hdr->ike_flags & IKEV2_FLAG_RESPONSE) {
+ TAILQ_INSERT_TAIL(&sa->sa_responses, m, msg_entry);
+ timer_initialize(env, &m->msg_timer,
+ ikev2_msg_response_timeout, m);
+ timer_register(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT);
+ } else {
+ TAILQ_INSERT_TAIL(&sa->sa_requests, m, msg_entry);
+ timer_initialize(env, &m->msg_timer,
+ ikev2_msg_retransmit_timeout, m);
+ timer_register(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT);
+ }
+
return (0);
}
u_int32_t
-ikev2_msg_id(struct iked *env, struct iked_sa *sa, int response)
+ikev2_msg_id(struct iked *env, struct iked_sa *sa)
{
- u_int32_t id;
+ u_int32_t id = sa->sa_reqid;
- if (response)
- return (sa->sa_msgid);
-
- id = sa->sa_reqid;
if (++sa->sa_reqid == UINT32_MAX) {
/* XXX we should close and renegotiate the connection now */
log_debug("%s: IKEv2 message sequence overflow", __func__);
@@ -527,8 +574,8 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
}
int
-ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa,
- struct ibuf **ep, u_int8_t exchange, u_int8_t firstpayload, int response)
+ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep,
+ u_int8_t exchange, u_int8_t firstpayload, int response)
{
struct iked_message resp;
struct ike_header *hdr;
@@ -536,16 +583,16 @@ ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa,
struct ibuf *buf, *e = *ep;
int ret = -1;
- if ((buf = ikev2_msg_init(env, &resp,
- &sa->sa_peer.addr, sa->sa_peer.addr.ss_len,
- &sa->sa_local.addr, sa->sa_local.addr.ss_len, 1)) == NULL)
+ if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
+ sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
+ sa->sa_local.addr.ss_len, response)) == NULL)
goto done;
+ resp.msg_msgid = response ? sa->sa_msgid : ikev2_msg_id(env, sa);
+
/* IKE header */
- if ((hdr = ikev2_add_header(buf, sa,
- ikev2_msg_id(env, sa, response),
- IKEV2_PAYLOAD_SK, exchange,
- response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
+ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK,
+ exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
goto done;
if ((pld = ikev2_add_payload(buf)) == NULL)
@@ -841,3 +888,102 @@ ikev2_msg_getsocket(struct iked *env, int af)
log_debug("%s: af socket %d not available", __func__, af);
return (NULL);
}
+
+void
+ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue,
+ struct iked_message *msg)
+{
+ struct iked_message *m = NULL;
+
+ while ((m = TAILQ_FIRST(queue)) != NULL) {
+ if (m->msg_msgid < msg->msg_msgid)
+ ikev2_msg_dispose(env, queue, m);
+ }
+}
+
+void
+ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue,
+ struct iked_message *msg)
+{
+ TAILQ_REMOVE(queue, msg, msg_entry);
+ timer_deregister(env, &msg->msg_timer);
+ ikev2_msg_cleanup(env, msg);
+ free(msg);
+}
+
+void
+ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue)
+{
+ struct iked_message *m = NULL;
+
+ while ((m = TAILQ_FIRST(queue)) != NULL)
+ ikev2_msg_dispose(env, queue, m);
+}
+
+struct iked_message *
+ikev2_msg_lookup_by_id(struct iked *env, struct iked_msgqueue *queue,
+ u_int32_t msgid)
+{
+ struct iked_message *m = NULL;
+
+ TAILQ_FOREACH(m, queue, msg_entry) {
+ if (m->msg_msgid == msgid)
+ break;
+ }
+ return (m);
+}
+
+struct iked_message *
+ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue,
+ struct iked_message *msg)
+{
+ return (ikev2_msg_lookup_by_id(env, queue, msg->msg_msgid));
+}
+
+int
+ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa,
+ struct iked_message *msg)
+{
+ if ((sendto(msg->msg_fd, ibuf_data(msg->msg_data),
+ ibuf_size(msg->msg_data), 0, (struct sockaddr *)&msg->msg_peer,
+ msg->msg_peerlen)) == -1) {
+ log_warn("%s: sendto", __func__);
+ sa_free(env, sa);
+ return (-1);
+ }
+
+ timer_register(env, &msg->msg_timer, IKED_RESPONSE_TIMEOUT);
+ return (0);
+}
+
+void
+ikev2_msg_response_timeout(struct iked *env, void *arg)
+{
+ struct iked_message *msg = arg;
+ struct iked_sa *sa = msg->msg_sa;
+
+ ikev2_msg_dispose(env, &sa->sa_responses, msg);
+}
+
+void
+ikev2_msg_retransmit_timeout(struct iked *env, void *arg)
+{
+ struct iked_message *msg = arg;
+ struct iked_sa *sa = msg->msg_sa;
+
+ if ((sendto(msg->msg_fd, ibuf_data(msg->msg_data),
+ ibuf_size(msg->msg_data), 0,
+ (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) {
+ log_warn("%s: sendto", __func__);
+ sa_free(env, sa);
+ return;
+ }
+
+ if (msg->msg_tries < IKED_RETRANSMIT_TRIES) {
+ TAILQ_INSERT_TAIL(&sa->sa_requests, msg, msg_entry);
+ /* Exponential timeout */
+ timer_register(env, &msg->msg_timer,
+ IKED_RETRANSMIT_TIMEOUT * (2 << (msg->msg_tries++)));
+ } else
+ sa_free(env, sa);
+}