aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm/xfrm_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/xfrm/xfrm_output.c')
-rw-r--r--net/xfrm/xfrm_output.c70
1 files changed, 67 insertions, 3 deletions
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index b1efdc8850a7..bcb3701c5cf3 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -12,6 +12,7 @@
12#include <linux/errno.h> 12#include <linux/errno.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/netdevice.h> 14#include <linux/netdevice.h>
15#include <linux/netfilter.h>
15#include <linux/skbuff.h> 16#include <linux/skbuff.h>
16#include <linux/spinlock.h> 17#include <linux/spinlock.h>
17#include <net/dst.h> 18#include <net/dst.h>
@@ -40,7 +41,7 @@ err:
40 return err; 41 return err;
41} 42}
42 43
43int xfrm_output(struct sk_buff *skb) 44static int xfrm_output_one(struct sk_buff *skb)
44{ 45{
45 struct dst_entry *dst = skb->dst; 46 struct dst_entry *dst = skb->dst;
46 struct xfrm_state *x = dst->xfrm; 47 struct xfrm_state *x = dst->xfrm;
@@ -87,10 +88,73 @@ int xfrm_output(struct sk_buff *skb)
87 88
88 err = 0; 89 err = 0;
89 90
90error_nolock: 91out_exit:
91 return err; 92 return err;
92error: 93error:
93 spin_unlock_bh(&x->lock); 94 spin_unlock_bh(&x->lock);
94 goto error_nolock; 95error_nolock:
96 kfree_skb(skb);
97 goto out_exit;
98}
99
100static int xfrm_output2(struct sk_buff *skb)
101{
102 int err;
103
104 while (likely((err = xfrm_output_one(skb)) == 0)) {
105 struct xfrm_state *x;
106
107 nf_reset(skb);
108
109 err = skb->dst->ops->local_out(skb);
110 if (unlikely(err != 1))
111 break;
112
113 x = skb->dst->xfrm;
114 if (!x)
115 return dst_output(skb);
116
117 err = nf_hook(x->inner_mode->afinfo->family,
118 x->inner_mode->afinfo->nf_post_routing, skb,
119 NULL, skb->dst->dev, xfrm_output2);
120 if (unlikely(err != 1))
121 break;
122 }
123
124 return err;
125}
126
127int xfrm_output(struct sk_buff *skb)
128{
129 struct sk_buff *segs;
130
131 if (!skb_is_gso(skb))
132 return xfrm_output2(skb);
133
134 segs = skb_gso_segment(skb, 0);
135 kfree_skb(skb);
136 if (unlikely(IS_ERR(segs)))
137 return PTR_ERR(segs);
138
139 do {
140 struct sk_buff *nskb = segs->next;
141 int err;
142
143 segs->next = NULL;
144 err = xfrm_output2(segs);
145
146 if (unlikely(err)) {
147 while ((segs = nskb)) {
148 nskb = segs->next;
149 segs->next = NULL;
150 kfree_skb(segs);
151 }
152 return err;
153 }
154
155 segs = nskb;
156 } while (segs);
157
158 return 0;
95} 159}
96EXPORT_SYMBOL_GPL(xfrm_output); 160EXPORT_SYMBOL_GPL(xfrm_output);