diff options
author | Tobias Brunner <tobias@strongswan.org> | 2008-06-24 15:35:09 +0000 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2008-06-24 15:35:09 +0000 |
commit | be33d1a51b0d9f60a156fea393effeeb4078aa25 (patch) | |
tree | 45dd45c0303f94e314e4c97c474d4151ebe12778 | |
parent | fae6e24dad0c3ac90399a2082a6bba9c0ea206d8 (diff) | |
download | strongswan-be33d1a51b0d9f60a156fea393effeeb4078aa25.tar.bz2 strongswan-be33d1a51b0d9f60a156fea393effeeb4078aa25.tar.xz |
merging the ESP sequence numbers of an SA in update_sa (fixing #52)
-rw-r--r-- | src/charon/kernel/kernel_interface.c | 132 |
1 files changed, 127 insertions, 5 deletions
diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index ad244b3c4..7d82a0723 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -2195,6 +2195,91 @@ static status_t add_sa(private_kernel_interface_t *this, } /** + * Get the replay state (i.e. sequence numbers) of an SA. + */ +static status_t get_replay_state(private_kernel_interface_t *this, + u_int32_t spi, protocol_id_t protocol, host_t *dst, + struct xfrm_replay_state *replay) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *out = NULL; + struct xfrm_aevent_id *out_aevent = NULL, *aevent_id; + size_t len; + struct rtattr *rta; + size_t rtasize; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying replay state from SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETAE; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_aevent_id)); + + aevent_id = (struct xfrm_aevent_id*)NLMSG_DATA(hdr); + aevent_id->flags = XFRM_AE_RVAL; + + host2xfrm(dst, &aevent_id->sa_id.daddr); + aevent_id->sa_id.spi = spi; + aevent_id->sa_id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : (protocol == PROTO_AH) ? KERNEL_AH : protocol; + aevent_id->sa_id.family = dst->get_family(dst); + + if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWAE: + { + out_aevent = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying replay state from SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (out_aevent == NULL) + { + DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + rta = XFRM_RTA(out, struct xfrm_aevent_id); + rtasize = XFRM_PAYLOAD(out, struct xfrm_aevent_id); + while(RTA_OK(rta, rtasize)) + { + if (rta->rta_type == XFRMA_REPLAY_VAL) + { + memcpy(replay, RTA_DATA(rta), rta->rta_len); + free(out); + return SUCCESS; + } + rta = RTA_NEXT(rta, rtasize); + } + + DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; +} + +/** * Implementation of kernel_interface_t.update_sa. */ static status_t update_sa(private_kernel_interface_t *this, @@ -2210,6 +2295,8 @@ static status_t update_sa(private_kernel_interface_t *this, struct rtattr *rta; size_t rtasize; struct xfrm_encap_tmpl* tmpl = NULL; + bool got_replay_state; + struct xfrm_replay_state replay; memset(&request, 0, sizeof(request)); @@ -2255,14 +2342,25 @@ static status_t update_sa(private_kernel_interface_t *this, break; } } - if (out_sa == NULL || - this->public.del_sa(&this->public, dst, spi, protocol) != SUCCESS) + if (out_sa == NULL) { DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); free(out); return FAILED; } + /* try to get the replay state */ + got_replay_state = (get_replay_state( + this, spi, protocol, dst, &replay) == SUCCESS); + + /* delete the old SA */ + if (this->public.del_sa(&this->public, dst, spi, protocol) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete old SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + DBG2(DBG_KNL, "updating SAD entry with SPI 0x%x from %#H..%#H to %#H..%#H", spi, src, dst, new_src, new_dst); @@ -2299,22 +2397,46 @@ static status_t update_sa(private_kernel_interface_t *this, tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); } memcpy(pos, rta, rta->rta_len); - pos += rta->rta_len; - hdr->nlmsg_len += rta->rta_len; + pos += RTA_ALIGN(rta->rta_len); + hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); } rta = RTA_NEXT(rta, rtasize); } + + rta = (struct rtattr*)pos; if (tmpl == NULL && encap) { /* add tmpl if we are enabling it */ - rta = (struct rtattr*)pos; rta->rta_type = XFRMA_ENCAP; rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + hdr->nlmsg_len += rta->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); tmpl->encap_type = UDP_ENCAP_ESPINUDP; tmpl->encap_sport = ntohs(new_src->get_port(new_src)); tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); + + rta = XFRM_RTA_NEXT(rta); + } + + if (got_replay_state) + { /* copy the replay data if available */ + rta->rta_type = XFRMA_REPLAY_VAL; + rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); + + hdr->nlmsg_len += rta->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + memcpy(RTA_DATA(rta), &replay, sizeof(replay)); + + rta = XFRM_RTA_NEXT(rta); } if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS) |