Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.25-rc7 /net/xfrm/xfrm_output.c

Filename:/net/xfrm/xfrm_output.c
Lines Added:129
Lines Deleted:28
Also changed in: (Previous) 2.6.25-rc6  2.6.25-rc5  2.6.25-rc4  2.6.25-rc3  2.6.25-rc2  2.6.25-rc1 
(Following) 2.6.25-rc7-git3  2.6.25-rc7-git4  2.6.25-rc7-git5  2.6.25-rc7-git6  2.6.25-rc8  2.6.25-rc9 

Location
[  2.6.25-rc7
  [  net
    [  xfrm
       o  xfrm_output.c

Patch

diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index f4bfd6c..569d377 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -12,14 +12,18 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/netfilter.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <net/dst.h>
 #include <net/xfrm.h>
 
+static int xfrm_output2(struct sk_buff *skb);
+
 static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
 {
-   int nhead = x->props.header_len + LL_RESERVED_SPACE(skb->dst->dev)
+   struct dst_entry *dst = skb->dst;
+   int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
       - skb_headroom(skb);
 
    if (nhead > 0)
@@ -29,54 +33,64 @@ static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
    return 0;
 }
 
-static int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb)
-{
-   int err = xfrm_state_check_expire(x);
-   if (err < 0)
-      goto err;
-   err = xfrm_state_check_space(x, skb);
-err:
-   return err;
-}
-
-int xfrm_output(struct sk_buff *skb)
+static int xfrm_output_one(struct sk_buff *skb, int err)
 {
    struct dst_entry *dst = skb->dst;
    struct xfrm_state *x = dst->xfrm;
-   int err;
 
-   if (skb->ip_summed == CHECKSUM_PARTIAL) {
-      err = skb_checksum_help(skb);
-      if (err)
-         goto error_nolock;
-   }
+   if (err <= 0)
+      goto resume;
 
    do {
+      err = xfrm_state_check_space(x, skb);
+      if (err) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+         goto error_nolock;
+      }
+
+      err = x->outer_mode->output(x, skb);
+      if (err) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEMODEERROR);
+         goto error_nolock;
+      }
+
       spin_lock_bh(&x->lock);
-      err = xfrm_state_check(x, skb);
-      if (err)
+      err = xfrm_state_check_expire(x);
+      if (err) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEEXPIRED);
          goto error;
+      }
 
       if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
-         XFRM_SKB_CB(skb)->seq = ++x->replay.oseq;
+         XFRM_SKB_CB(skb)->seq.output = ++x->replay.oseq;
+         if (unlikely(x->replay.oseq == 0)) {
+            XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATESEQERROR);
+            x->replay.oseq--;
+            xfrm_audit_state_replay_overflow(x, skb);
+            err = -EOVERFLOW;
+            goto error;
+         }
          if (xfrm_aevent_is_on())
             xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
       }
 
-      err = x->outer_mode->output(x, skb);
-      if (err)
-         goto error;
-
       x->curlft.bytes += skb->len;
       x->curlft.packets++;
 
       spin_unlock_bh(&x->lock);
 
       err = x->type->output(x, skb);
-      if (err)
+      if (err == -EINPROGRESS)
+         goto out_exit;
+
+resume:
+      if (err) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEPROTOERROR);
          goto error_nolock;
+      }
 
       if (!(skb->dst = dst_pop(dst))) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
          err = -EHOSTUNREACH;
          goto error_nolock;
       }
@@ -86,10 +100,97 @@ int xfrm_output(struct sk_buff *skb)
 
    err = 0;
 
-error_nolock:
+out_exit:
    return err;
 error:
    spin_unlock_bh(&x->lock);
-   goto error_nolock;
+error_nolock:
+   kfree_skb(skb);
+   goto out_exit;
+}
+
+int xfrm_output_resume(struct sk_buff *skb, int err)
+{
+   while (likely((err = xfrm_output_one(skb, err)) == 0)) {
+      struct xfrm_state *x;
+
+      nf_reset(skb);
+
+      err = skb->dst->ops->local_out(skb);
+      if (unlikely(err != 1))
+         goto out;
+
+      x = skb->dst->xfrm;
+      if (!x)
+         return dst_output(skb);
+
+      err = nf_hook(x->inner_mode->afinfo->family,
+               NF_INET_POST_ROUTING, skb,
+               NULL, skb->dst->dev, xfrm_output2);
+      if (unlikely(err != 1))
+         goto out;
+   }
+
+   if (err == -EINPROGRESS)
+      err = 0;
+
+out:
+   return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_output_resume);
+
+static int xfrm_output2(struct sk_buff *skb)
+{
+   return xfrm_output_resume(skb, 1);
+}
+
+static int xfrm_output_gso(struct sk_buff *skb)
+{
+   struct sk_buff *segs;
+
+   segs = skb_gso_segment(skb, 0);
+   kfree_skb(skb);
+   if (unlikely(IS_ERR(segs)))
+      return PTR_ERR(segs);
+
+   do {
+      struct sk_buff *nskb = segs->next;
+      int err;
+
+      segs->next = NULL;
+      err = xfrm_output2(segs);
+
+      if (unlikely(err)) {
+         while ((segs = nskb)) {
+            nskb = segs->next;
+            segs->next = NULL;
+            kfree_skb(segs);
+         }
+         return err;
+      }
+
+      segs = nskb;
+   } while (segs);
+
+   return 0;
+}
+
+int xfrm_output(struct sk_buff *skb)
+{
+   int err;
+
+   if (skb_is_gso(skb))
+      return xfrm_output_gso(skb);
+
+   if (skb->ip_summed == CHECKSUM_PARTIAL) {
+      err = skb_checksum_help(skb);
+      if (err) {
+         XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+         kfree_skb(skb);
+         return err;
+      }
+   }
+
+   return xfrm_output2(skb);
 }
 EXPORT_SYMBOL_GPL(xfrm_output);


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.