summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2014-01-22 09:25:42 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2014-01-22 09:25:42 +0000
commit0651b494b818a97537cd6bea97fde8e69dfce489 (patch)
treeabba94a96fc2222079baed8bcedd87ed2d6e8c39
parent4bd0d7246014bd6044332675260aa56bb13dbed9 (diff)
implement DPD similar to isakmpd, but only send DPD-messages 'on-demand'
(less aggressive, only if the ESP-SAs are actually used); feedback & ok mikeb@
-rw-r--r--sbin/iked/iked.h5
-rw-r--r--sbin/iked/ikev2.c79
-rw-r--r--sbin/iked/ikev2_pld.c4
-rw-r--r--sbin/iked/pfkey.c114
4 files changed, 192 insertions, 10 deletions
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index b261a2b725c..5da7f835795 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: iked.h,v 1.62 2013/12/09 15:22:32 markus Exp $ */
+/* $OpenBSD: iked.h,v 1.63 2014/01/22 09:25:41 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -406,6 +406,7 @@ struct iked_sa {
struct iked_timer sa_timer; /* SA timeouts */
#define IKED_IKE_SA_REKEY_TIMEOUT 300 /* 5 minutes */
+#define IKED_IKE_SA_ALIVE_TIMEOUT 60 /* 1 minute */
struct iked_msgqueue sa_requests; /* request queue */
#define IKED_RETRANSMIT_TIMEOUT 2 /* 2 seconds */
@@ -432,6 +433,7 @@ struct iked_message {
int msg_fd;
int msg_response;
+ int msg_responded;
int msg_natt;
int msg_error;
int msg_e;
@@ -769,6 +771,7 @@ int pfkey_block(int, int, u_int);
int pfkey_sa_init(int, struct iked_childsa *, u_int32_t *);
int pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa *);
int pfkey_sa_delete(int, struct iked_childsa *);
+int pfkey_sa_last_used(int, struct iked_childsa *, u_int64_t *);
int pfkey_flush(int);
int pfkey_socket(void);
void pfkey_init(struct iked *, int fd);
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 7e72f8b48b8..5b2150b39ea 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.88 2013/12/09 15:22:32 markus Exp $ */
+/* $OpenBSD: ikev2.c,v 1.89 2014/01/22 09:25:41 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -76,6 +76,7 @@ int ikev2_send_create_child_sa(struct iked *, struct iked_sa *,
int ikev2_init_create_child_sa(struct iked *, struct iked_message *);
int ikev2_resp_create_child_sa(struct iked *, struct iked_message *);
void ikev2_ike_sa_timeout(struct iked *env, void *);
+void ikev2_ike_sa_alive(struct iked *, void *);
int ikev2_sa_initiator(struct iked *, struct iked_sa *,
struct iked_message *);
@@ -1017,8 +1018,11 @@ ikev2_init_done(struct iked *env, struct iked_sa *sa)
ret = ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator);
if (ret == 0)
ret = ikev2_childsa_enable(env, sa);
- if (ret == 0)
+ if (ret == 0) {
sa_state(env, sa, IKEV2_STATE_ESTABLISHED);
+ timer_initialize(env, &sa->sa_timer, ikev2_ike_sa_alive, sa);
+ timer_register(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+ }
if (ret)
ikev2_childsa_delete(env, sa, 0, 0, NULL, 1);
@@ -1780,6 +1784,14 @@ ikev2_resp_recv(struct iked *env, struct iked_message *msg,
case IKEV2_EXCHANGE_CREATE_CHILD_SA:
(void)ikev2_resp_create_child_sa(env, msg);
break;
+ case IKEV2_EXCHANGE_INFORMATIONAL:
+ if (!msg->msg_responded && !msg->msg_error) {
+ (void)ikev2_send_ike_e(env, sa, NULL,
+ IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL,
+ 1);
+ msg->msg_responded = 1;
+ }
+ break;
default:
break;
}
@@ -2056,8 +2068,11 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1);
if (ret == 0)
ret = ikev2_childsa_enable(env, sa);
- if (ret == 0)
+ if (ret == 0) {
sa_state(env, sa, IKEV2_STATE_ESTABLISHED);
+ timer_initialize(env, &sa->sa_timer, ikev2_ike_sa_alive, sa);
+ timer_register(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+ }
done:
if (ret)
@@ -2164,11 +2179,15 @@ ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf,
if ((pld = ikev2_add_payload(e)) == NULL)
goto done;
- if (ibuf_cat(e, buf) != 0)
- goto done;
- if (ikev2_next_payload(pld, ibuf_size(buf), IKEV2_PAYLOAD_NONE) == -1)
- goto done;
+ if (buf) {
+ if (ibuf_cat(e, buf) != 0)
+ goto done;
+
+ 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);
@@ -2606,8 +2625,13 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
log_debug("%s: activating new IKE SA", __func__);
sa_state(env, nsa, IKEV2_STATE_ESTABLISHED);
+ timer_initialize(env, &nsa->sa_timer, ikev2_ike_sa_alive, nsa);
+ timer_register(env, &nsa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
nsa->sa_stateflags = sa->sa_statevalid; /* XXX */
+ /* unregister DPD keep alive timer first */
+ if (sa->sa_state == IKEV2_STATE_ESTABLISHED)
+ timer_deregister(env, &sa->sa_timer);
timer_initialize(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa);
timer_register(env, &sa->sa_timer, IKED_IKE_SA_REKEY_TIMEOUT);
} else
@@ -2629,6 +2653,47 @@ ikev2_ike_sa_timeout(struct iked *env, void *arg)
sa_free(env, sa);
}
+void
+ikev2_ike_sa_alive(struct iked *env, void *arg)
+{
+ struct iked_sa *sa = arg;
+ struct iked_childsa *csa = NULL;
+ struct timeval tv;
+ u_int64_t last_used, diff;
+ int foundin = 0, foundout = 0;
+
+ /* check for incoming traffic on any child SA */
+ TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
+ if (pfkey_sa_last_used(env->sc_pfkey, csa, &last_used) != 0)
+ continue;
+ gettimeofday(&tv, NULL);
+ diff = (u_int32_t)(tv.tv_sec - last_used);
+ log_debug("%s: %s CHILD SA spi %s last used %u second(s) ago",
+ __func__,
+ csa->csa_dir == IPSP_DIRECTION_IN ? "incoming" : "outgoing",
+ print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), diff);
+ if (diff < IKED_IKE_SA_ALIVE_TIMEOUT) {
+ if (csa->csa_dir == IPSP_DIRECTION_IN) {
+ foundin = 1;
+ break;
+ } else {
+ foundout = 1;
+ }
+ }
+ }
+
+ /* send probe if any outging SA has been used, but no incoming SA */
+ if (!foundin && foundout) {
+ log_debug("%s: sending alive check", __func__);
+ ikev2_send_ike_e(env, sa, NULL, IKEV2_PAYLOAD_NONE,
+ IKEV2_EXCHANGE_INFORMATIONAL, 0);
+ sa->sa_stateflags |= IKED_REQ_INF;
+ }
+
+ /* re-register */
+ timer_register(env, &sa->sa_timer, IKED_IKE_SA_ALIVE_TIMEOUT);
+}
+
int
ikev2_send_informational(struct iked *env, struct iked_message *msg)
{
diff --git a/sbin/iked/ikev2_pld.c b/sbin/iked/ikev2_pld.c
index 5c591b48adb..f5180db199e 100644
--- a/sbin/iked/ikev2_pld.c
+++ b/sbin/iked/ikev2_pld.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2_pld.c,v 1.33 2013/12/03 13:55:39 markus Exp $ */
+/* $OpenBSD: ikev2_pld.c,v 1.34 2014/01/22 09:25:41 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -809,6 +809,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld,
ret = ikev2_send_ike_e(env, sa, resp,
IKEV2_PAYLOAD_NONE,
IKEV2_EXCHANGE_INFORMATIONAL, 1);
+ msg->msg_responded = 1;
ibuf_release(resp);
sa_state(env, sa, IKEV2_STATE_CLOSED);
return (ret);
@@ -911,6 +912,7 @@ ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld,
if (found) {
ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_DELETE,
IKEV2_EXCHANGE_INFORMATIONAL, 1);
+ msg->msg_responded = 1;
} else {
/* XXX should we send an INVALID_SPI notification? */
ret = 0;
diff --git a/sbin/iked/pfkey.c b/sbin/iked/pfkey.c
index 8ab38c6df7d..18510859c57 100644
--- a/sbin/iked/pfkey.c
+++ b/sbin/iked/pfkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkey.c,v 1.26 2013/12/03 13:55:39 markus Exp $ */
+/* $OpenBSD: pfkey.c,v 1.27 2014/01/22 09:25:41 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -710,6 +710,118 @@ pfkey_sa(int sd, u_int8_t satype, u_int8_t action, struct iked_childsa *sa)
}
int
+pfkey_sa_last_used(int sd, struct iked_childsa *sa, u_int64_t *last_used)
+{
+ struct sadb_msg *msg, smsg;
+ struct sadb_address sa_src, sa_dst;
+ struct sadb_sa sadb;
+ struct sadb_lifetime *sa_life;
+ struct sockaddr_storage ssrc, sdst;
+ struct iovec iov[IOV_CNT];
+ u_int8_t *data;
+ ssize_t n;
+ int iov_cnt, ret = -1;
+ u_int8_t satype;
+
+ *last_used = 0;
+
+ if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
+ return (-1);
+
+ bzero(&ssrc, sizeof(ssrc));
+ memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
+ if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
+ if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
+ log_warn("%s: invalid address", __func__);
+ return (-1);
+ }
+
+ bzero(&smsg, sizeof(smsg));
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = ++sadb_msg_seq;
+ smsg.sadb_msg_pid = getpid();
+ smsg.sadb_msg_len = sizeof(smsg) / 8;
+ smsg.sadb_msg_type = SADB_GET;
+ smsg.sadb_msg_satype = satype;
+
+ bzero(&sadb, sizeof(sadb));
+ sadb.sadb_sa_len = sizeof(sadb) / 8;
+ sadb.sadb_sa_exttype = SADB_EXT_SA;
+ sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
+ sadb.sadb_sa_state = SADB_SASTATE_MATURE;
+ sadb.sadb_sa_replay = 64;
+
+ bzero(&sa_src, sizeof(sa_src));
+ sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
+ sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+
+ bzero(&sa_dst, sizeof(sa_dst));
+ sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
+ sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+
+ iov_cnt = 0;
+
+ /* header */
+ iov[iov_cnt].iov_base = &smsg;
+ iov[iov_cnt].iov_len = sizeof(smsg);
+ iov_cnt++;
+
+ /* sa */
+ iov[iov_cnt].iov_base = &sadb;
+ iov[iov_cnt].iov_len = sizeof(sadb);
+ smsg.sadb_msg_len += sadb.sadb_sa_len;
+ iov_cnt++;
+
+ /* src addr */
+ iov[iov_cnt].iov_base = &sa_src;
+ iov[iov_cnt].iov_len = sizeof(sa_src);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &ssrc;
+ iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
+ smsg.sadb_msg_len += sa_src.sadb_address_len;
+ iov_cnt++;
+
+ /* dst addr */
+ iov[iov_cnt].iov_base = &sa_dst;
+ iov[iov_cnt].iov_len = sizeof(sa_dst);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = &sdst;
+ iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
+ smsg.sadb_msg_len += sa_dst.sadb_address_len;
+ iov_cnt++;
+
+ if ((ret = pfkey_write(sd, &smsg, iov, iov_cnt, &data, &n)) != 0)
+ return (-1);
+
+ msg = (struct sadb_msg *)data;
+ if (msg->sadb_msg_errno != 0) {
+ errno = msg->sadb_msg_errno;
+ ret = -1;
+ log_warn("%s: message", __func__);
+ goto done;
+ }
+ if ((sa_life = pfkey_find_ext(data, n, SADB_X_EXT_LIFETIME_LASTUSE))
+ == NULL) {
+ log_debug("%s: erronous reply", __func__);
+ ret = -1;
+ goto done;
+ }
+ *last_used = sa_life->sadb_lifetime_usetime;
+ log_debug("%s: last_used %llu", __func__, *last_used);
+
+done:
+ bzero(data, n);
+ free(data);
+ return (ret);
+}
+
+int
pfkey_sa_getspi(int sd, u_int8_t satype, struct iked_childsa *sa,
u_int32_t *spip)
{