diff options
Diffstat (limited to 'sbin/iked/ikev2_msg.c')
-rw-r--r-- | sbin/iked/ikev2_msg.c | 180 |
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); +} |