xfrm: Add user interface for esn and big anti-replay windows

This patch adds a netlink based user interface to configure
esn and big anti-replay windows. The new netlink attribute
XFRMA_REPLAY_ESN_VAL is used to configure the new implementation.
If the XFRM_STATE_ESN flag is set, we use esn and support for big
anti-replay windows for the configured state. If this flag is not
set we use the new implementation with 32 bit sequence numbers.
A big anti-replay window can be configured in this case anyway.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Steffen Klassert 2011-03-08 00:10:27 +00:00 committed by David S. Miller
parent 2cd084678f
commit d8647b79c3
2 changed files with 87 additions and 14 deletions

View File

@ -356,6 +356,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
kfree(x->calg); kfree(x->calg);
kfree(x->encap); kfree(x->encap);
kfree(x->coaddr); kfree(x->coaddr);
kfree(x->replay_esn);
kfree(x->preplay_esn);
if (x->inner_mode) if (x->inner_mode)
xfrm_put_mode(x->inner_mode); xfrm_put_mode(x->inner_mode);
if (x->inner_mode_iaf) if (x->inner_mode_iaf)

View File

@ -119,6 +119,19 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
return 0; return 0;
} }
static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
if (!rt)
return 0;
if (p->replay_window != 0)
return -EINVAL;
return 0;
}
static int verify_newsa_info(struct xfrm_usersa_info *p, static int verify_newsa_info(struct xfrm_usersa_info *p,
struct nlattr **attrs) struct nlattr **attrs)
@ -214,6 +227,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
goto out; goto out;
if ((err = verify_sec_ctx_len(attrs))) if ((err = verify_sec_ctx_len(attrs)))
goto out; goto out;
if ((err = verify_replay(p, attrs)))
goto out;
err = -EINVAL; err = -EINVAL;
switch (p->mode) { switch (p->mode) {
@ -345,6 +360,33 @@ static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
return 0; return 0;
} }
static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
struct xfrm_replay_state_esn **preplay_esn,
struct nlattr *rta)
{
struct xfrm_replay_state_esn *p, *pp, *up;
if (!rta)
return 0;
up = nla_data(rta);
p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
if (!p)
return -ENOMEM;
pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
if (!pp) {
kfree(p);
return -ENOMEM;
}
*replay_esn = p;
*preplay_esn = pp;
return 0;
}
static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx) static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
{ {
int len = 0; int len = 0;
@ -380,10 +422,20 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs) static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
{ {
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
struct nlattr *et = attrs[XFRMA_ETIMER_THRESH]; struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH]; struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
if (re) {
struct xfrm_replay_state_esn *replay_esn;
replay_esn = nla_data(re);
memcpy(x->replay_esn, replay_esn,
xfrm_replay_state_esn_len(replay_esn));
memcpy(x->preplay_esn, replay_esn,
xfrm_replay_state_esn_len(replay_esn));
}
if (rp) { if (rp) {
struct xfrm_replay_state *replay; struct xfrm_replay_state *replay;
replay = nla_data(rp); replay = nla_data(rp);
@ -467,13 +519,14 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX]))) security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX])))
goto error; goto error;
if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn,
attrs[XFRMA_REPLAY_ESN_VAL])))
goto error;
x->km.seq = p->seq; x->km.seq = p->seq;
x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth; x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth;
/* sysctl_xfrm_aevent_etime is in 100ms units */ /* sysctl_xfrm_aevent_etime is in 100ms units */
x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
x->preplay.bitmap = 0;
x->preplay.seq = x->replay.seq+x->replay_maxdiff;
x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;
if ((err = xfrm_init_replay(x))) if ((err = xfrm_init_replay(x)))
goto error; goto error;
@ -709,6 +762,10 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
if (xfrm_mark_put(skb, &x->mark)) if (xfrm_mark_put(skb, &x->mark))
goto nla_put_failure; goto nla_put_failure;
if (x->replay_esn)
NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL,
xfrm_replay_state_esn_len(x->replay_esn), x->replay_esn);
if (x->security && copy_sec_ctx(x->security, skb) < 0) if (x->security && copy_sec_ctx(x->security, skb) < 0)
goto nla_put_failure; goto nla_put_failure;
@ -1578,10 +1635,14 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
return 0; return 0;
} }
static inline size_t xfrm_aevent_msgsize(void) static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x)
{ {
size_t replay_size = x->replay_esn ?
xfrm_replay_state_esn_len(x->replay_esn) :
sizeof(struct xfrm_replay_state);
return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id)) return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id))
+ nla_total_size(sizeof(struct xfrm_replay_state)) + nla_total_size(replay_size)
+ nla_total_size(sizeof(struct xfrm_lifetime_cur)) + nla_total_size(sizeof(struct xfrm_lifetime_cur))
+ nla_total_size(sizeof(struct xfrm_mark)) + nla_total_size(sizeof(struct xfrm_mark))
+ nla_total_size(4) /* XFRM_AE_RTHR */ + nla_total_size(4) /* XFRM_AE_RTHR */
@ -1606,7 +1667,13 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
id->reqid = x->props.reqid; id->reqid = x->props.reqid;
id->flags = c->data.aevent; id->flags = c->data.aevent;
NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay); if (x->replay_esn)
NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL,
xfrm_replay_state_esn_len(x->replay_esn),
x->replay_esn);
else
NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay);
NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft);
if (id->flags & XFRM_AE_RTHR) if (id->flags & XFRM_AE_RTHR)
@ -1639,16 +1706,16 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_aevent_id *p = nlmsg_data(nlh); struct xfrm_aevent_id *p = nlmsg_data(nlh);
struct xfrm_usersa_id *id = &p->sa_id; struct xfrm_usersa_id *id = &p->sa_id;
r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
if (r_skb == NULL)
return -ENOMEM;
mark = xfrm_mark_get(attrs, &m); mark = xfrm_mark_get(attrs, &m);
x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family); x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family);
if (x == NULL) { if (x == NULL)
kfree_skb(r_skb);
return -ESRCH; return -ESRCH;
r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
if (r_skb == NULL) {
xfrm_state_put(x);
return -ENOMEM;
} }
/* /*
@ -1680,9 +1747,10 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_mark m; struct xfrm_mark m;
struct xfrm_aevent_id *p = nlmsg_data(nlh); struct xfrm_aevent_id *p = nlmsg_data(nlh);
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL]; struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
struct nlattr *lt = attrs[XFRMA_LTIME_VAL]; struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
if (!lt && !rp) if (!lt && !rp && !re)
return err; return err;
/* pedantic mode - thou shalt sayeth replaceth */ /* pedantic mode - thou shalt sayeth replaceth */
@ -2147,6 +2215,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) }, [XFRMA_KMADDRESS] = { .len = sizeof(struct xfrm_user_kmaddress) },
[XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) },
[XFRMA_TFCPAD] = { .type = NLA_U32 }, [XFRMA_TFCPAD] = { .type = NLA_U32 },
[XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) },
}; };
static struct xfrm_link { static struct xfrm_link {
@ -2274,7 +2343,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event
struct net *net = xs_net(x); struct net *net = xs_net(x);
struct sk_buff *skb; struct sk_buff *skb;
skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC); skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
if (skb == NULL) if (skb == NULL)
return -ENOMEM; return -ENOMEM;
@ -2328,6 +2397,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
l += nla_total_size(sizeof(*x->encap)); l += nla_total_size(sizeof(*x->encap));
if (x->tfcpad) if (x->tfcpad)
l += nla_total_size(sizeof(x->tfcpad)); l += nla_total_size(sizeof(x->tfcpad));
if (x->replay_esn)
l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn));
if (x->security) if (x->security)
l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) +
x->security->ctx_len); x->security->ctx_len);