diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2014-01-22 09:25:42 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2014-01-22 09:25:42 +0000 |
commit | 0651b494b818a97537cd6bea97fde8e69dfce489 (patch) | |
tree | abba94a96fc2222079baed8bcedd87ed2d6e8c39 | |
parent | 4bd0d7246014bd6044332675260aa56bb13dbed9 (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.h | 5 | ||||
-rw-r--r-- | sbin/iked/ikev2.c | 79 | ||||
-rw-r--r-- | sbin/iked/ikev2_pld.c | 4 | ||||
-rw-r--r-- | sbin/iked/pfkey.c | 114 |
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) { |