>From 7aa47cdff60f5a1a7b48bc6100daea710f0ffa37 Mon Sep 17 00:00:00 2001 From: Alex Badea Date: Thu, 18 Jun 2009 17:03:39 +0300 Subject: [PATCH] xfrm: add generic support for replay protection with Extended Sequence Numbers --- src/include/net/xfrm.h | 2 + src/net/ipv4/xfrm4_input.c | 2 +- src/net/ipv6/xfrm6_input.c | 2 +- src/net/xfrm/xfrm_export.c | 1 + src/net/xfrm/xfrm_replay.c | 77 +++++++++++++++++++++++++++++++++++++++---- 5 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/include/net/xfrm.h b/src/include/net/xfrm.h index bf0daff..1f9feb5 100644 --- a/src/include/net/xfrm.h +++ b/src/include/net/xfrm.h @@ -132,6 +132,7 @@ struct xfrm_state /* State for replay detection */ struct xfrm_replay_state replay; + struct xfrm_replay_state_ext replay_ext; /* Statistics */ struct xfrm_stats stats; @@ -819,6 +820,7 @@ extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq); extern void xfrm_state_delete(struct xfrm_state *x); extern void xfrm_state_flush(u8 proto); extern int xfrm_replay_check(struct xfrm_state *x, u32 seq); +extern u32 xfrm_replay_seqhi(struct xfrm_state *x, u32 seq); extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq); extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl); extern int xfrm_check_output(struct xfrm_state *x, struct sk_buff *skb, unsigned short family); diff --git a/src/net/ipv4/xfrm4_input.c b/src/net/ipv4/xfrm4_input.c index d3284b1..d2d58df 100644 --- a/src/net/ipv4/xfrm4_input.c +++ b/src/net/ipv4/xfrm4_input.c @@ -91,7 +91,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) /* only the first xfrm gets the encap type */ encap_type = 0; - if (x->props.replay_window) + if (x->props.replay_window || (x->props.flags & XFRM_STATE_ESN)) xfrm_replay_advance(x, seq); x->curlft.bytes += skb->len; diff --git a/src/net/ipv6/xfrm6_input.c b/src/net/ipv6/xfrm6_input.c index 075f8b4..be7f4d7 100644 --- a/src/net/ipv6/xfrm6_input.c +++ b/src/net/ipv6/xfrm6_input.c @@ -75,7 +75,7 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) if (nexthdr <= 0) goto drop_unlock; - if (x->props.replay_window) + if (x->props.replay_window || (x->props.flags & XFRM_STATE_ESN)) xfrm_replay_advance(x, seq); x->curlft.bytes += skb->len; diff --git a/src/net/xfrm/xfrm_export.c b/src/net/xfrm/xfrm_export.c index fdd4d0e..25cb97e 100644 --- a/src/net/xfrm/xfrm_export.c +++ b/src/net/xfrm/xfrm_export.c @@ -25,6 +25,7 @@ EXPORT_SYMBOL(xfrm_state_get_afinfo); EXPORT_SYMBOL(xfrm_state_put_afinfo); EXPORT_SYMBOL(xfrm_state_delete_tunnel); EXPORT_SYMBOL(xfrm_replay_check); +EXPORT_SYMBOL(xfrm_replay_seqhi); EXPORT_SYMBOL(xfrm_replay_advance); EXPORT_SYMBOL(xfrm_check_selectors); EXPORT_SYMBOL(xfrm_check_output); diff --git a/src/net/xfrm/xfrm_replay.c b/src/net/xfrm/xfrm_replay.c index 6a7350d..96ccdb3 100644 --- a/src/net/xfrm/xfrm_replay.c +++ b/src/net/xfrm/xfrm_replay.c @@ -1,18 +1,67 @@ #include +/** Given a state and a Seql, figure out Seqh (for ESN) */ +u32 xfrm_replay_seqhi(struct xfrm_state *x, u32 seq) +{ + if (unlikely(!(x->props.flags & XFRM_STATE_ESN))) { + BUG(); + return 0; + } + + seq = ntohl(seq); + + const u32 bottom = x->replay.seq - x->props.replay_window + 1; + u32 seq_hi = x->replay_ext.seq_hi; + + if (likely(x->replay.seq >= x->props.replay_window - 1)) { + /* A. same subspace */ + if (unlikely(seq < bottom)) + seq_hi++; + } else { + /* B. window spans two subspaces */ + if (unlikely(seq >= bottom)) + seq_hi--; + } + return seq_hi; +} + + int xfrm_replay_check(struct xfrm_state *x, u32 seq) { u32 diff; seq = ntohl(seq); - if (unlikely(seq == 0)) - return -EINVAL; + if (unlikely(seq == 0)) { + if (!(x->props.flags & XFRM_STATE_ESN)) + return -EINVAL; + if (x->replay_ext.seq_hi == 0 && (x->replay.seq < x->props.replay_window - 1)) + return -EINVAL; + } - if (likely(seq > x->replay.seq)) - return 0; + if (x->props.flags & XFRM_STATE_ESN) { + const u32 wsize = x->props.replay_window; + const u32 top = x->replay.seq; + const u32 bottom = top - wsize + 1; + + diff = top - seq; + if (likely(top >= wsize - 1)) { + /* A. same subspace */ + if (likely(seq > top) || seq < bottom) + return 0; + } else { + /* B. window spans two subspaces */ + if (likely(seq > top && seq < bottom)) + return 0; + if (seq >= bottom) + diff = ~seq + top + 1; + } + } else { + if (likely(seq > x->replay.seq)) + return 0; + diff = x->replay.seq - seq; + } - diff = x->replay.seq - seq; if (diff >= x->props.replay_window) { x->stats.replay_window++; return -EINVAL; @@ -28,19 +77,31 @@ int xfrm_replay_check(struct xfrm_state *x, u32 seq) void xfrm_replay_advance(struct xfrm_state *x, u32 seq) { u32 diff; + int wrap = 0; + + if (x->props.flags & XFRM_STATE_ESN) { + u32 seq_hi = xfrm_replay_seqhi(x, seq); + wrap = seq_hi - x->replay_ext.seq_hi; + } seq = ntohl(seq); - if (seq > x->replay.seq) { - diff = seq - x->replay.seq; + if ((!wrap && seq > x->replay.seq) || wrap > 0) { + if (likely(!wrap)) + diff = seq - x->replay.seq; + else + diff = ~x->replay.seq + seq + 1; + if (diff < x->props.replay_window) x->replay.bitmap = ((x->replay.bitmap) << diff) | 1; else x->replay.bitmap = 1; x->replay.seq = seq; + + if (unlikely(wrap > 0)) + x->replay_ext.seq_hi++; } else { diff = x->replay.seq - seq; x->replay.bitmap |= (1U << diff); } } - -- 1.5.4.3