summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Belopuhov <mikeb@cvs.openbsd.org>2012-06-22 16:28:21 +0000
committerMike Belopuhov <mikeb@cvs.openbsd.org>2012-06-22 16:28:21 +0000
commit8a9bcae3da71cb6d59e5b362f3d825f6bdbf76e8 (patch)
tree1f1b84ac3599b3b605733285a3b59fc128a1a26d
parent707f71f072ef5342a75159bb0c14b5a58fad2f8f (diff)
Add initial support for retransmition timeouts and response retries.
This should still be considered an experimental work in progress.
-rw-r--r--sbin/iked/config.c7
-rw-r--r--sbin/iked/iked.h34
-rw-r--r--sbin/iked/ikev2.c155
-rw-r--r--sbin/iked/ikev2_msg.c180
4 files changed, 297 insertions, 79 deletions
diff --git a/sbin/iked/config.c b/sbin/iked/config.c
index b44a00eb3e0..2141b4eb37b 100644
--- a/sbin/iked/config.c
+++ b/sbin/iked/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.13 2011/07/05 19:59:00 tedu Exp $ */
+/* $OpenBSD: config.c,v 1.14 2012/06/22 16:28:20 mikeb Exp $ */
/* $vantronix: config.c,v 1.30 2010/05/28 15:34:35 reyk Exp $ */
/*
@@ -56,6 +56,8 @@ config_new_sa(struct iked *env, int initiator)
TAILQ_INIT(&sa->sa_proposals);
TAILQ_INIT(&sa->sa_childsas);
TAILQ_INIT(&sa->sa_flows);
+ TAILQ_INIT(&sa->sa_requests);
+ TAILQ_INIT(&sa->sa_responses);
sa->sa_hdr.sh_initiator = initiator;
sa->sa_type = IKED_SATYPE_LOCAL;
@@ -96,6 +98,9 @@ config_free_sa(struct iked *env, struct iked_sa *sa)
policy_unref(env, sa->sa_policy);
}
+ ikev2_msg_flushqueue(env, &sa->sa_requests);
+ ikev2_msg_flushqueue(env, &sa->sa_responses);
+
ibuf_release(sa->sa_inonce);
ibuf_release(sa->sa_rnonce);
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index 3aa52867836..72f3147f6c7 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: iked.h,v 1.48 2012/06/22 16:06:31 mikeb Exp $ */
+/* $OpenBSD: iked.h,v 1.49 2012/06/22 16:28:20 mikeb Exp $ */
/* $vantronix: iked.h,v 1.61 2010/06/03 07:57:33 reyk Exp $ */
/*
@@ -325,6 +325,8 @@ struct iked_id {
#define IKED_REQ_BITS \
"\10\01CERT\02VALID\03AUTH\04SA"
+TAILQ_HEAD(iked_msgqueue, iked_message);
+
struct iked_sahdr {
u_int64_t sh_ispi; /* Initiator SPI */
u_int64_t sh_rspi; /* Responder SPI */
@@ -398,6 +400,12 @@ struct iked_sa {
struct iked_childsas sa_childsas; /* IPSec Child SAs */
struct iked_saflows sa_flows; /* IPSec flows */
+ struct iked_msgqueue sa_requests; /* request queue */
+#define IKED_RETRANSMIT_TIMEOUT 2 /* 2 seconds */
+
+ struct iked_msgqueue sa_responses; /* response queue */
+#define IKED_RESPONSE_TIMEOUT 120 /* 2 minutes */
+
RB_ENTRY(iked_sa) sa_peer_entry;
RB_ENTRY(iked_sa) sa_entry;
};
@@ -426,6 +434,8 @@ struct iked_message {
struct iked_policy *msg_policy;
struct iked_sa *msg_sa;
+ u_int32_t msg_msgid;
+
/* Parsed information */
struct iked_proposals msg_proposals;
struct iked_spi msg_rekey;
@@ -438,6 +448,14 @@ struct iked_message {
/* Parse stack */
struct iked_proposal *msg_prop;
u_int16_t msg_attrlength;
+
+ /* Retransmit queue */
+ struct iked_timer msg_timer;
+ TAILQ_ENTRY(iked_message)
+ msg_entry;
+ int msg_tries; /* retransmits sent */
+#define IKED_RETRANSMIT_TRIES 5 /* try 5 times */
+
};
struct iked_user {
@@ -691,9 +709,11 @@ struct ibuf *
ikev2_msg_init(struct iked *, struct iked_message *,
struct sockaddr_storage *, socklen_t,
struct sockaddr_storage *, socklen_t, int);
+struct iked_message *
+ ikev2_msg_copy(struct iked *, struct iked_message *);
void ikev2_msg_cleanup(struct iked *, struct iked_message *);
u_int32_t
- ikev2_msg_id(struct iked *, struct iked_sa *, int);
+ ikev2_msg_id(struct iked *, struct iked_sa *);
struct ibuf
*ikev2_msg_auth(struct iked *, struct iked_sa *, int);
int ikev2_msg_authsign(struct iked *, struct iked_sa *,
@@ -714,6 +734,16 @@ int ikev2_msg_integr(struct iked *, struct iked_sa *, struct ibuf *);
int ikev2_msg_frompeer(struct iked_message *);
struct iked_socket *
ikev2_msg_getsocket(struct iked *, int);
+int ikev2_msg_retransmit_response(struct iked *, struct iked_sa *,
+ struct iked_message *);
+void ikev2_msg_prevail(struct iked *, struct iked_msgqueue *,
+ struct iked_message *);
+void ikev2_msg_dispose(struct iked *, struct iked_msgqueue *,
+ struct iked_message *);
+void ikev2_msg_flushqueue(struct iked *, struct iked_msgqueue *);
+struct iked_message *
+ ikev2_msg_lookup(struct iked *, struct iked_msgqueue *,
+ struct iked_message *);
/* ikev2_pld.c */
int ikev2_pld_parse(struct iked *, struct ike_header *,
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index e3102c65746..6d56b5224c9 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.66 2012/06/22 16:06:31 mikeb Exp $ */
+/* $OpenBSD: ikev2.c,v 1.67 2012/06/22 16:28:20 mikeb Exp $ */
/* $vantronix: ikev2.c,v 1.101 2010/06/03 07:57:33 reyk Exp $ */
/*
@@ -340,9 +340,17 @@ ikev2_getimsgdata(struct iked *env, struct imsg *imsg, struct iked_sahdr *sh,
void
ikev2_recv(struct iked *env, struct iked_message *msg)
{
+ enum {
+ ST_START,
+ ST_REQUEST,
+ ST_RESPONSE,
+ ST_FINISH
+ } state = ST_START;
struct ike_header *hdr;
+ struct iked_message *m;
struct iked_sa *sa;
u_int initiator, flag = 0;
+ int ignore = 0, response = 0;
hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr));
@@ -354,16 +362,20 @@ ikev2_recv(struct iked *env, struct iked_message *msg)
msg->msg_sa = sa_lookup(env,
betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi),
initiator);
+ msg->msg_msgid = betoh32(hdr->ike_msgid);
if (policy_lookup(env, msg) != 0)
return;
- log_info("%s: %s from %s %s to %s policy '%s', %ld bytes", __func__,
- print_map(hdr->ike_exchange, ikev2_exchange_map),
+ log_info("%s: %s from %s %s to %s policy '%s' id %u, %ld bytes",
+ __func__, print_map(hdr->ike_exchange, ikev2_exchange_map),
initiator ? "responder" : "initiator",
print_host(&msg->msg_peer, NULL, 0),
print_host(&msg->msg_local, NULL, 0),
- msg->msg_policy->pol_name,
+ msg->msg_policy->pol_name, msg->msg_msgid,
ibuf_length(msg->msg_data));
+ log_debug("%s: ispi %s rspi %s", __func__,
+ print_spi(betoh64(hdr->ike_ispi), 8),
+ print_spi(betoh64(hdr->ike_rspi), 8));
if ((sa = msg->msg_sa) == NULL)
goto done;
@@ -373,53 +385,77 @@ ikev2_recv(struct iked *env, struct iked_message *msg)
if (msg->msg_natt)
sa->sa_natt = 1;
- switch (hdr->ike_exchange) {
- case IKEV2_EXCHANGE_CREATE_CHILD_SA:
- flag = IKED_REQ_CHILDSA;
- goto xchgcommon;
-
- case IKEV2_EXCHANGE_INFORMATIONAL:
- flag = IKED_REQ_INF;
- xchgcommon:
- if ((sa->sa_stateflags & flag)) {
- /* response */
- if (betoh32(hdr->ike_msgid) == sa->sa_reqid - 1)
- /* we initiated the exchange */
- initiator = 1;
- else {
- if (flag == IKED_REQ_CHILDSA)
- ikev2_disable_rekeying(env, sa);
- goto errout; /* unexpected id */
- }
- } else {
- /* request */
- if (betoh32(hdr->ike_msgid) >= sa->sa_msgid) {
- /* we are responding */
+ do switch (state) {
+ case ST_START:
+ if (hdr->ike_exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA)
+ flag = IKED_REQ_CHILDSA;
+ if (hdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL)
+ flag = IKED_REQ_INF;
+ if ((flag && (sa->sa_stateflags & flag)) ||
+ (!flag && initiator))
+ state = ST_RESPONSE;
+ else
+ state = ST_REQUEST;
+ break;
+ case ST_REQUEST:
+ if (msg->msg_msgid >= sa->sa_msgid) {
+ /* Update if we've initiated this exchange */
+ if (flag)
initiator = 0;
- sa->sa_msgid = betoh32(hdr->ike_msgid);
- } else
- goto errout; /* unexpected id */
+ state = ST_FINISH;
+ } else if (flag) {
+ flag = 0; /* Prevent endless looping */
+ state = ST_RESPONSE;
+ } else {
+ ignore = 1;
+ state = ST_FINISH;
}
break;
-
- default:
- if (initiator) {
- if (betoh32(hdr->ike_msgid) != sa->sa_reqid - 1)
- goto errout;
+ case ST_RESPONSE:
+ if (msg->msg_msgid < sa->sa_reqid) {
+ response = 1;
+ /* Update if we've initiated this exchange */
+ if (flag)
+ initiator = 1;
+ state = ST_FINISH;
+ } else if (flag) {
+ flag = 0; /* Prevent endless looping */
+ state = ST_REQUEST;
} else {
- if (betoh32(hdr->ike_msgid) < sa->sa_msgid)
- goto errout;
- else
- sa->sa_msgid = betoh32(hdr->ike_msgid);
+ ignore = 1;
+ state = ST_FINISH;
}
break;
- errout:
- sa->sa_stateflags &= ~flag;
- log_debug("%s: invalid sequence number %d "
- "(SA msgid %d reqid %d)", __func__,
- betoh32(hdr->ike_msgid), sa->sa_msgid,
- sa->sa_reqid);
- return;
+ case ST_FINISH:
+ if (ignore)
+ return;
+ break;
+ } while (state != ST_FINISH);
+
+ if (response) {
+ /*
+ * There's no need to keep the request around anymore
+ */
+ if ((m = ikev2_msg_lookup(env, &sa->sa_requests, msg)))
+ ikev2_msg_dispose(env, &sa->sa_requests, m);
+ } else {
+ /*
+ * See if we have responded to this request before
+ */
+ if ((m = ikev2_msg_lookup(env, &sa->sa_responses, msg))) {
+ if (ikev2_msg_retransmit_response(env, sa, m)) {
+ log_warn("%s: failed to retransmit a "
+ "response", __func__);
+ sa_free(env, sa);
+ }
+ return;
+ }
+ /*
+ * If it's a new request, make sure to update the peer's
+ * message ID and dispose of all previous responses
+ */
+ sa->sa_msgid = msg->msg_msgid;
+ ikev2_msg_prevail(env, &sa->sa_responses, msg);
}
if (sa_address(sa, &sa->sa_peer, &msg->msg_peer, initiator) == -1 ||
@@ -590,6 +626,9 @@ ikev2_init_recv(struct iked *env, struct iked_message *msg,
break;
case IKEV2_EXCHANGE_IKE_AUTH:
case IKEV2_EXCHANGE_CREATE_CHILD_SA:
+ if (ikev2_msg_valid_ike_sa(env, hdr, msg) == -1)
+ return;
+ break;
case IKEV2_EXCHANGE_INFORMATIONAL:
break;
default:
@@ -700,9 +739,10 @@ ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol,
req.msg_fd = sock->sock_fd;
req.msg_sa = sa;
req.msg_sock = sock;
+ req.msg_msgid = ikev2_msg_id(env, sa);
/* IKE header */
- if ((hdr = ikev2_add_header(buf, sa, ikev2_msg_id(env, sa, 0),
+ if ((hdr = ikev2_add_header(buf, sa, req.msg_msgid,
IKEV2_PAYLOAD_SA, IKEV2_EXCHANGE_IKE_SA_INIT, 0)) == NULL)
goto done;
@@ -1695,9 +1735,10 @@ ikev2_resp_ike_sa_init(struct iked *env, struct iked_message *msg)
resp.msg_sa = sa;
resp.msg_fd = msg->msg_fd;
+ resp.msg_msgid = 0;
/* IKE header */
- if ((hdr = ikev2_add_header(buf, sa, 0,
+ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
IKEV2_PAYLOAD_SA, IKEV2_EXCHANGE_IKE_SA_INIT,
IKEV2_FLAG_RESPONSE)) == NULL)
goto done;
@@ -2053,8 +2094,8 @@ ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf,
if (ikev2_next_payload(pld, ibuf_size(buf), IKEV2_PAYLOAD_NONE) == -1)
goto done;
- ret = ikev2_msg_send_encrypt(env, sa, &e,
- exchange, firstpayload, response);
+ ret = ikev2_msg_send_encrypt(env, sa, &e, exchange, firstpayload,
+ response);
done:
ibuf_release(e);
@@ -2536,9 +2577,10 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg)
goto done;
if (sa != NULL && msg->msg_e) {
+ resp.msg_msgid = ikev2_msg_id(env, sa);
+
/* IKE header */
- if ((hdr = ikev2_add_header(buf, sa,
- ikev2_msg_id(env, sa, 0),
+ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
IKEV2_PAYLOAD_SK, IKEV2_EXCHANGE_INFORMATIONAL,
0)) == NULL)
goto done;
@@ -2574,11 +2616,11 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg)
sah.sa_hdr.sh_ispi = betoh64(hdr->ike_ispi);
sah.sa_hdr.sh_initiator =
hdr->ike_flags & IKEV2_FLAG_INITIATOR ? 0 : 1;
- sa = &sah;
+
+ resp.msg_msgid = ikev2_msg_id(env, &sah);
/* IKE header */
- if ((hdr = ikev2_add_header(buf, &sah,
- ikev2_msg_id(env, &sah, 0),
+ if ((hdr = ikev2_add_header(buf, &sah, resp.msg_msgid,
IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL,
0)) == NULL)
goto done;
@@ -2589,14 +2631,9 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg)
}
resp.msg_data = buf;
- resp.msg_sa = sa;
resp.msg_fd = msg->msg_fd;
TAILQ_INIT(&resp.msg_proposals);
- sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
- (void)ikev2_pld_parse(env, hdr, &resp, 0);
- sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1;
-
ret = ikev2_msg_send(env, &resp);
done:
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);
+}