diff options
Diffstat (limited to 'net/ipv6/xfrm6_output.c')
-rw-r--r-- | net/ipv6/xfrm6_output.c | 75 |
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 | ||
95 | int xfrm6_output(struct sk_buff *skb) | 97 | static 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 | ||
137 | out_exit: | 144 | out_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 | |||
153 | static 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 | |||
177 | int 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 | } | ||