Copy ESN enabled replay state during update_sa, if supported

This commit is contained in:
Martin Willi 2011-04-18 17:57:59 +02:00
parent ee8c89e2ee
commit 05e9589783
1 changed files with 77 additions and 48 deletions

View File

@ -353,9 +353,14 @@ struct private_kernel_netlink_ipsec_t {
bool install_routes; bool install_routes;
/** /**
* Size of the replay window * Size of the replay window, in packets
*/ */
u_int32_t replay_window; u_int32_t replay_window;
/**
* Size of the replay window bitmap, in bytes
*/
u_int32_t replay_bmp;
}; };
/** /**
@ -1199,8 +1204,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr); replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr);
/* bmp_len contains number uf __u32's */ /* bmp_len contains number uf __u32's */
replay->bmp_len = (this->replay_window + sizeof(u_int32_t) * 8 - 1) replay->bmp_len = this->replay_bmp;
/ (sizeof(u_int32_t) * 8);
replay->replay_window = this->replay_window; replay->replay_window = this->replay_window;
rthdr = XFRM_RTA_NEXT(rthdr); rthdr = XFRM_RTA_NEXT(rthdr);
@ -1232,11 +1236,14 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
} }
/** /**
* Get the replay state (i.e. sequence numbers) of an SA. * Get the ESN replay state (i.e. sequence numbers) of an SA.
*
* Allocates into one the replay state structure we get from the kernel.
*/ */
static status_t get_replay_state(private_kernel_netlink_ipsec_t *this, static void get_replay_state(private_kernel_netlink_ipsec_t *this,
u_int32_t spi, u_int8_t protocol, host_t *dst, u_int32_t spi, u_int8_t protocol, host_t *dst,
struct xfrm_replay_state *replay) struct xfrm_replay_state_esn **replay_esn,
struct xfrm_replay_state **replay)
{ {
netlink_buf_t request; netlink_buf_t request;
struct nlmsghdr *hdr, *out = NULL; struct nlmsghdr *hdr, *out = NULL;
@ -1247,7 +1254,8 @@ static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
memset(&request, 0, sizeof(request)); memset(&request, 0, sizeof(request));
DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x", ntohl(spi)); DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x",
ntohl(spi));
hdr = (struct nlmsghdr*)request; hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_flags = NLM_F_REQUEST;
@ -1291,32 +1299,30 @@ static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
} }
} }
if (out_aevent == NULL) if (out_aevent)
{ {
DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI %.8x", rta = XFRM_RTA(out, struct xfrm_aevent_id);
ntohl(spi)); rtasize = XFRM_PAYLOAD(out, struct xfrm_aevent_id);
free(out); while (RTA_OK(rta, rtasize))
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 &&
RTA_PAYLOAD(rta) == sizeof(struct xfrm_replay_state))
{ {
memcpy(replay, RTA_DATA(rta), RTA_PAYLOAD(rta)); if (rta->rta_type == XFRMA_REPLAY_VAL &&
free(out); RTA_PAYLOAD(rta) == sizeof(**replay))
return SUCCESS; {
*replay = malloc(RTA_PAYLOAD(rta));
memcpy(*replay, RTA_DATA(rta), RTA_PAYLOAD(rta));
break;
}
if (rta->rta_type == XFRMA_REPLAY_ESN_VAL &&
RTA_PAYLOAD(rta) >= sizeof(**replay_esn) + this->replay_bmp)
{
*replay_esn = malloc(RTA_PAYLOAD(rta));
memcpy(*replay_esn, RTA_DATA(rta), RTA_PAYLOAD(rta));
break;
}
rta = RTA_NEXT(rta, rtasize);
} }
rta = RTA_NEXT(rta, rtasize);
} }
DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI %.8x",
ntohl(spi));
free(out); free(out);
return FAILED;
} }
METHOD(kernel_ipsec_t, query_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t,
@ -1515,8 +1521,9 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
struct rtattr *rta; struct rtattr *rta;
size_t rtasize; size_t rtasize;
struct xfrm_encap_tmpl* tmpl = NULL; struct xfrm_encap_tmpl* tmpl = NULL;
bool got_replay_state = FALSE; struct xfrm_replay_state *replay = NULL;
struct xfrm_replay_state replay; struct xfrm_replay_state_esn *replay_esn = NULL;
status_t status = FAILED;
/* if IPComp is used, we first update the IPComp SA */ /* if IPComp is used, we first update the IPComp SA */
if (cpi) if (cpi)
@ -1572,22 +1579,16 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
if (out_sa == NULL) if (out_sa == NULL)
{ {
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi)); DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi));
free(out); goto failed;
return FAILED;
} }
/* try to get the replay state */ get_replay_state(this, spi, protocol, dst, &replay_esn, &replay);
if (get_replay_state(this, spi, protocol, dst, &replay) == SUCCESS)
{
got_replay_state = TRUE;
}
/* delete the old SA (without affecting the IPComp SA) */ /* delete the old SA (without affecting the IPComp SA) */
if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS) if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS)
{ {
DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi)); DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi));
free(out); goto failed;
return FAILED;
} }
DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H", DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
@ -1640,7 +1641,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
if (hdr->nlmsg_len > sizeof(request)) if (hdr->nlmsg_len > sizeof(request))
{ {
return FAILED; goto failed;
} }
tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta);
@ -1652,30 +1653,55 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
rta = XFRM_RTA_NEXT(rta); rta = XFRM_RTA_NEXT(rta);
} }
if (got_replay_state) if (replay_esn)
{ /* copy the replay data if available */ {
rta->rta_type = XFRMA_REPLAY_ESN_VAL;
rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) +
this->replay_bmp);
hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
if (hdr->nlmsg_len > sizeof(request))
{
goto failed;
}
memcpy(RTA_DATA(rta), replay_esn,
sizeof(struct xfrm_replay_state_esn) + this->replay_bmp);
rta = XFRM_RTA_NEXT(rta);
}
else if (replay)
{
rta->rta_type = XFRMA_REPLAY_VAL; rta->rta_type = XFRMA_REPLAY_VAL;
rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state)); rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state));
hdr->nlmsg_len += RTA_ALIGN(rta->rta_len); hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
if (hdr->nlmsg_len > sizeof(request)) if (hdr->nlmsg_len > sizeof(request))
{ {
return FAILED; goto failed;
} }
memcpy(RTA_DATA(rta), &replay, sizeof(replay)); memcpy(RTA_DATA(rta), replay, sizeof(replay));
rta = XFRM_RTA_NEXT(rta); rta = XFRM_RTA_NEXT(rta);
} }
else
{
DBG1(DBG_KNL, "unable to copy replay state from old SAD entry "
"with SPI %.8x", ntohl(spi));
}
if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
{ {
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi)); DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi));
free(out); goto failed;
return FAILED;
} }
status = SUCCESS;
failed:
free(replay);
free(replay_esn);
free(out); free(out);
return SUCCESS; return status;
} }
METHOD(kernel_ipsec_t, add_policy, status_t, METHOD(kernel_ipsec_t, add_policy, status_t,
@ -2243,6 +2269,9 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
"%s.replay_window", DEFAULT_REPLAY_WINDOW, hydra->daemon), "%s.replay_window", DEFAULT_REPLAY_WINDOW, hydra->daemon),
); );
this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) /
(sizeof(u_int32_t) * 8);
if (streq(hydra->daemon, "pluto")) if (streq(hydra->daemon, "pluto"))
{ /* no routes for pluto, they are installed via updown script */ { /* no routes for pluto, they are installed via updown script */
this->install_routes = FALSE; this->install_routes = FALSE;