aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/xfrm4_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/xfrm4_output.c')
-rw-r--r--net/ipv4/xfrm4_output.c71
1 files changed, 54 insertions, 17 deletions
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 66620a95942a..51fabb8f7c54 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -8,8 +8,10 @@
8 * 2 of the License, or (at your option) any later version. 8 * 2 of the License, or (at your option) any later version.
9 */ 9 */
10 10
11#include <linux/compiler.h>
11#include <linux/skbuff.h> 12#include <linux/skbuff.h>
12#include <linux/spinlock.h> 13#include <linux/spinlock.h>
14#include <linux/netfilter_ipv4.h>
13#include <net/inet_ecn.h> 15#include <net/inet_ecn.h>
14#include <net/ip.h> 16#include <net/ip.h>
15#include <net/xfrm.h> 17#include <net/xfrm.h>
@@ -95,7 +97,7 @@ out:
95 return ret; 97 return ret;
96} 98}
97 99
98int xfrm4_output(struct sk_buff *skb) 100static int xfrm4_output_one(struct sk_buff *skb)
99{ 101{
100 struct dst_entry *dst = skb->dst; 102 struct dst_entry *dst = skb->dst;
101 struct xfrm_state *x = dst->xfrm; 103 struct xfrm_state *x = dst->xfrm;
@@ -113,27 +115,32 @@ int xfrm4_output(struct sk_buff *skb)
113 goto error_nolock; 115 goto error_nolock;
114 } 116 }
115 117
116 spin_lock_bh(&x->lock); 118 do {
117 err = xfrm_state_check(x, skb); 119 spin_lock_bh(&x->lock);
118 if (err) 120 err = xfrm_state_check(x, skb);
119 goto error; 121 if (err)
122 goto error;
120 123
121 xfrm4_encap(skb); 124 xfrm4_encap(skb);
122 125
123 err = x->type->output(x, skb); 126 err = x->type->output(x, skb);
124 if (err) 127 if (err)
125 goto error; 128 goto error;
126 129
127 x->curlft.bytes += skb->len; 130 x->curlft.bytes += skb->len;
128 x->curlft.packets++; 131 x->curlft.packets++;
129 132
130 spin_unlock_bh(&x->lock); 133 spin_unlock_bh(&x->lock);
131 134
132 if (!(skb->dst = dst_pop(dst))) { 135 if (!(skb->dst = dst_pop(dst))) {
133 err = -EHOSTUNREACH; 136 err = -EHOSTUNREACH;
134 goto error_nolock; 137 goto error_nolock;
135 } 138 }
136 err = NET_XMIT_BYPASS; 139 dst = skb->dst;
140 x = dst->xfrm;
141 } while (x && !x->props.mode);
142
143 err = 0;
137 144
138out_exit: 145out_exit:
139 return err; 146 return err;
@@ -143,3 +150,33 @@ error_nolock:
143 kfree_skb(skb); 150 kfree_skb(skb);
144 goto out_exit; 151 goto out_exit;
145} 152}
153
154static int xfrm4_output_finish(struct sk_buff *skb)
155{
156 int err;
157
158 while (likely((err = xfrm4_output_one(skb)) == 0)) {
159 nf_reset(skb);
160
161 err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL,
162 skb->dst->dev, dst_output);
163 if (unlikely(err != 1))
164 break;
165
166 if (!skb->dst->xfrm)
167 return dst_output(skb);
168
169 err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL,
170 skb->dst->dev, xfrm4_output_finish);
171 if (unlikely(err != 1))
172 break;
173 }
174
175 return err;
176}
177
178int xfrm4_output(struct sk_buff *skb)
179{
180 return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev,
181 xfrm4_output_finish);
182}