diff options
Diffstat (limited to 'net/xfrm/xfrm_output.c')
-rw-r--r-- | net/xfrm/xfrm_output.c | 70 |
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 | ||
43 | int xfrm_output(struct sk_buff *skb) | 44 | static 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 | ||
90 | error_nolock: | 91 | out_exit: |
91 | return err; | 92 | return err; |
92 | error: | 93 | error: |
93 | spin_unlock_bh(&x->lock); | 94 | spin_unlock_bh(&x->lock); |
94 | goto error_nolock; | 95 | error_nolock: |
96 | kfree_skb(skb); | ||
97 | goto out_exit; | ||
98 | } | ||
99 | |||
100 | static 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 | |||
127 | int 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 | } |
96 | EXPORT_SYMBOL_GPL(xfrm_output); | 160 | EXPORT_SYMBOL_GPL(xfrm_output); |