Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/klassert/ipsec

Steffen Klassert says:

====================
1) We used the wrong netlink attribute to verify the
   lenght of the replay window on async events. Fix this by
   using the right netlink attribute.

2) Policy lookups can not match the output interface on forwarding.
   Add the needed informations to the flow informations.

3) We update the pmtu when we receive a ICMPV6_DEST_UNREACH message
   on IPsec with ipv6. This is wrong and leads to strange fragmented
   packets, only ICMPV6_PKT_TOOBIG messages should update the pmtu.
   Fix this by removing the ICMPV6_DEST_UNREACH check from the IPsec
   protocol error handlers.

4) The legacy IPsec anti replay mechanism supports anti replay
   windows up to 32 packets. If a user requests for a bigger
   anti replay window, we use 32 packets but pretend that we use
   the requested window size. Fix from Fan Du.

5) If asynchronous events are enabled and replay_maxdiff is set to
   zero, we generate an async event for every received packet instead
   of checking whether a timeout occurred. Fix from Thomas Egerer.

6) Policies need a refcount when the state resolution timer is armed.
   Otherwise the timer can fire after the policy is deleted.

7) We might dreference a NULL pointer if the hold_queue is empty,
   add a check to avoid this.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Oct 9, 2013
2 parents cb03db9 + 2bb53e2 commit f606385
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 42 deletions.
1 change: 1 addition & 0 deletions net/ipv4/xfrm4_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)

memset(fl4, 0, sizeof(struct flowi4));
fl4->flowi4_mark = skb->mark;
fl4->flowi4_oif = skb_dst(skb)->dev->ifindex;

if (!ip_is_fragment(iph)) {
switch (iph->protocol) {
Expand Down
3 changes: 1 addition & 2 deletions net/ipv6/ah6.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
struct xfrm_state *x;

if (type != ICMPV6_DEST_UNREACH &&
type != ICMPV6_PKT_TOOBIG &&
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
return;

Expand Down
3 changes: 1 addition & 2 deletions net/ipv6/esp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
struct xfrm_state *x;

if (type != ICMPV6_DEST_UNREACH &&
type != ICMPV6_PKT_TOOBIG &&
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
return;

Expand Down
3 changes: 1 addition & 2 deletions net/ipv6/ipcomp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
(struct ip_comp_hdr *)(skb->data + offset);
struct xfrm_state *x;

if (type != ICMPV6_DEST_UNREACH &&
type != ICMPV6_PKT_TOOBIG &&
if (type != ICMPV6_PKT_TOOBIG &&
type != NDISC_REDIRECT)
return;

Expand Down
1 change: 1 addition & 0 deletions net/ipv6/xfrm6_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)

memset(fl6, 0, sizeof(struct flowi6));
fl6->flowi6_mark = skb->mark;
fl6->flowi6_oif = skb_dst(skb)->dev->ifindex;

fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
Expand Down
3 changes: 2 additions & 1 deletion net/key/af_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,

x->id.proto = proto;
x->id.spi = sa->sadb_sa_spi;
x->props.replay_window = sa->sadb_sa_replay;
x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay,
(sizeof(x->replay.bitmap) * 8));
if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
x->props.flags |= XFRM_STATE_NOECN;
if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP)
Expand Down
28 changes: 21 additions & 7 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)

atomic_inc(&policy->genid);

del_timer(&policy->polq.hold_timer);
if (del_timer(&policy->polq.hold_timer))
xfrm_pol_put(policy);
xfrm_queue_purge(&policy->polq.hold_queue);

if (del_timer(&policy->timer))
Expand Down Expand Up @@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,

spin_lock_bh(&pq->hold_queue.lock);
skb_queue_splice_init(&pq->hold_queue, &list);
del_timer(&pq->hold_timer);
if (del_timer(&pq->hold_timer))
xfrm_pol_put(old);
spin_unlock_bh(&pq->hold_queue.lock);

if (skb_queue_empty(&list))
Expand All @@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
spin_lock_bh(&pq->hold_queue.lock);
skb_queue_splice(&list, &pq->hold_queue);
pq->timeout = XFRM_QUEUE_TMO_MIN;
mod_timer(&pq->hold_timer, jiffies);
if (!mod_timer(&pq->hold_timer, jiffies))
xfrm_pol_hold(new);
spin_unlock_bh(&pq->hold_queue.lock);
}

Expand Down Expand Up @@ -1769,6 +1772,10 @@ static void xfrm_policy_queue_process(unsigned long arg)

spin_lock(&pq->hold_queue.lock);
skb = skb_peek(&pq->hold_queue);
if (!skb) {
spin_unlock(&pq->hold_queue.lock);
goto out;
}
dst = skb_dst(skb);
sk = skb->sk;
xfrm_decode_session(skb, &fl, dst->ops->family);
Expand All @@ -1787,8 +1794,9 @@ static void xfrm_policy_queue_process(unsigned long arg)
goto purge_queue;

pq->timeout = pq->timeout << 1;
mod_timer(&pq->hold_timer, jiffies + pq->timeout);
return;
if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
xfrm_pol_hold(pol);
goto out;
}

dst_release(dst);
Expand Down Expand Up @@ -1819,19 +1827,23 @@ static void xfrm_policy_queue_process(unsigned long arg)
err = dst_output(skb);
}

out:
xfrm_pol_put(pol);
return;

purge_queue:
pq->timeout = 0;
xfrm_queue_purge(&pq->hold_queue);
xfrm_pol_put(pol);
}

static int xdst_queue_output(struct sk_buff *skb)
{
unsigned long sched_next;
struct dst_entry *dst = skb_dst(skb);
struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
struct xfrm_policy_queue *pq = &xdst->pols[0]->polq;
struct xfrm_policy *pol = xdst->pols[0];
struct xfrm_policy_queue *pq = &pol->polq;

if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {
kfree_skb(skb);
Expand All @@ -1850,10 +1862,12 @@ static int xdst_queue_output(struct sk_buff *skb)
if (del_timer(&pq->hold_timer)) {
if (time_before(pq->hold_timer.expires, sched_next))
sched_next = pq->hold_timer.expires;
xfrm_pol_put(pol);
}

__skb_queue_tail(&pq->hold_queue, skb);
mod_timer(&pq->hold_timer, sched_next);
if (!mod_timer(&pq->hold_timer, sched_next))
xfrm_pol_hold(pol);

spin_unlock_bh(&pq->hold_queue.lock);

Expand Down
54 changes: 28 additions & 26 deletions net/xfrm/xfrm_replay.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)

switch (event) {
case XFRM_REPLAY_UPDATE:
if (x->replay_maxdiff &&
(x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
(x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) {
if (!x->replay_maxdiff ||
((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
(x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
if (x->xflags & XFRM_TIME_DEFER)
event = XFRM_REPLAY_TIMEOUT;
else
Expand Down Expand Up @@ -129,8 +129,7 @@ static int xfrm_replay_check(struct xfrm_state *x,
return 0;

diff = x->replay.seq - seq;
if (diff >= min_t(unsigned int, x->props.replay_window,
sizeof(x->replay.bitmap) * 8)) {
if (diff >= x->props.replay_window) {
x->stats.replay_window++;
goto err;
}
Expand Down Expand Up @@ -302,9 +301,10 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)

switch (event) {
case XFRM_REPLAY_UPDATE:
if (x->replay_maxdiff &&
(replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
(replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) {
if (!x->replay_maxdiff ||
((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
(replay_esn->oseq - preplay_esn->oseq
< x->replay_maxdiff))) {
if (x->xflags & XFRM_TIME_DEFER)
event = XFRM_REPLAY_TIMEOUT;
else
Expand Down Expand Up @@ -353,28 +353,30 @@ static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)

switch (event) {
case XFRM_REPLAY_UPDATE:
if (!x->replay_maxdiff)
break;

if (replay_esn->seq_hi == preplay_esn->seq_hi)
seq_diff = replay_esn->seq - preplay_esn->seq;
else
seq_diff = ~preplay_esn->seq + replay_esn->seq + 1;

if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
oseq_diff = replay_esn->oseq - preplay_esn->oseq;
else
oseq_diff = ~preplay_esn->oseq + replay_esn->oseq + 1;

if (seq_diff < x->replay_maxdiff &&
oseq_diff < x->replay_maxdiff) {
if (x->replay_maxdiff) {
if (replay_esn->seq_hi == preplay_esn->seq_hi)
seq_diff = replay_esn->seq - preplay_esn->seq;
else
seq_diff = ~preplay_esn->seq + replay_esn->seq
+ 1;

if (x->xflags & XFRM_TIME_DEFER)
event = XFRM_REPLAY_TIMEOUT;
if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
oseq_diff = replay_esn->oseq
- preplay_esn->oseq;
else
return;
oseq_diff = ~preplay_esn->oseq
+ replay_esn->oseq + 1;

if (seq_diff >= x->replay_maxdiff ||
oseq_diff >= x->replay_maxdiff)
break;
}

if (x->xflags & XFRM_TIME_DEFER)
event = XFRM_REPLAY_TIMEOUT;
else
return;

break;

case XFRM_REPLAY_TIMEOUT:
Expand Down
5 changes: 3 additions & 2 deletions net/xfrm/xfrm_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
memcpy(&x->sel, &p->sel, sizeof(x->sel));
memcpy(&x->lft, &p->lft, sizeof(x->lft));
x->props.mode = p->mode;
x->props.replay_window = p->replay_window;
x->props.replay_window = min_t(unsigned int, p->replay_window,
sizeof(x->replay.bitmap) * 8);
x->props.reqid = p->reqid;
x->props.family = p->family;
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
Expand Down Expand Up @@ -1856,7 +1857,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
if (x->km.state != XFRM_STATE_VALID)
goto out;

err = xfrm_replay_verify_len(x->replay_esn, rp);
err = xfrm_replay_verify_len(x->replay_esn, re);
if (err)
goto out;

Expand Down

0 comments on commit f606385

Please sign in to comment.