aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/xfrm6_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/xfrm6_output.c')
-rw-r--r--net/ipv6/xfrm6_output.c75
1 files changed, 56 insertions, 19 deletions
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 6b9867717d11..fc0ea38953c4 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -9,9 +9,11 @@
9 * 2 of the License, or (at your option) any later version. 9 * 2 of the License, or (at your option) any later version.
10 */ 10 */
11 11
12#include <linux/compiler.h>
12#include <linux/skbuff.h> 13#include <linux/skbuff.h>
13#include <linux/spinlock.h> 14#include <linux/spinlock.h>
14#include <linux/icmpv6.h> 15#include <linux/icmpv6.h>
16#include <linux/netfilter_ipv6.h>
15#include <net/dsfield.h> 17#include <net/dsfield.h>
16#include <net/inet_ecn.h> 18#include <net/inet_ecn.h>
17#include <net/ipv6.h> 19#include <net/ipv6.h>
@@ -92,7 +94,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
92 return ret; 94 return ret;
93} 95}
94 96
95int xfrm6_output(struct sk_buff *skb) 97static int xfrm6_output_one(struct sk_buff *skb)
96{ 98{
97 struct dst_entry *dst = skb->dst; 99 struct dst_entry *dst = skb->dst;
98 struct xfrm_state *x = dst->xfrm; 100 struct xfrm_state *x = dst->xfrm;
@@ -110,29 +112,34 @@ int xfrm6_output(struct sk_buff *skb)
110 goto error_nolock; 112 goto error_nolock;
111 } 113 }
112 114
113 spin_lock_bh(&x->lock); 115 do {
114 err = xfrm_state_check(x, skb); 116 spin_lock_bh(&x->lock);
115 if (err) 117 err = xfrm_state_check(x, skb);
116 goto error; 118 if (err)
119 goto error;
117 120
118 xfrm6_encap(skb); 121 xfrm6_encap(skb);
119 122
120 err = x->type->output(x, skb); 123 err = x->type->output(x, skb);
121 if (err) 124 if (err)
122 goto error; 125 goto error;
123 126
124 x->curlft.bytes += skb->len; 127 x->curlft.bytes += skb->len;
125 x->curlft.packets++; 128 x->curlft.packets++;
126 129
127 spin_unlock_bh(&x->lock); 130 spin_unlock_bh(&x->lock);
128 131
129 skb->nh.raw = skb->data; 132 skb->nh.raw = skb->data;
130 133
131 if (!(skb->dst = dst_pop(dst))) { 134 if (!(skb->dst = dst_pop(dst))) {
132 err = -EHOSTUNREACH; 135 err = -EHOSTUNREACH;
133 goto error_nolock; 136 goto error_nolock;
134 } 137 }
135 err = NET_XMIT_BYPASS; 138 dst = skb->dst;
139 x = dst->xfrm;
140 } while (x && !x->props.mode);
141
142 err = 0;
136 143
137out_exit: 144out_exit:
138 return err; 145 return err;
@@ -142,3 +149,33 @@ error_nolock:
142 kfree_skb(skb); 149 kfree_skb(skb);
143 goto out_exit; 150 goto out_exit;
144} 151}
152
153static int xfrm6_output_finish(struct sk_buff *skb)
154{
155 int err;
156
157 while (likely((err = xfrm6_output_one(skb)) == 0)) {
158 nf_reset(skb);
159
160 err = nf_hook(PF_INET6, NF_IP6_LOCAL_OUT, &skb, NULL,
161 skb->dst->dev, dst_output);
162 if (unlikely(err != 1))
163 break;
164
165 if (!skb->dst->xfrm)
166 return dst_output(skb);
167
168 err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, &skb, NULL,
169 skb->dst->dev, xfrm6_output_finish);
170 if (unlikely(err != 1))
171 break;
172 }
173
174 return err;
175}
176
177int xfrm6_output(struct sk_buff *skb)
178{
179 return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev,
180 xfrm6_output_finish);
181}