diff options
author | Patrick McHardy <kaber@trash.net> | 2006-01-07 02:01:48 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-01-07 15:57:28 -0500 |
commit | 16a6677fdf1d1194f688f8291b06fbaff248c353 (patch) | |
tree | 61badedc44ed88eb8f39e082d1abf114252cc686 /net/ipv4 | |
parent | ee2e6841b934d76cb944a3390bbea84da777d4fa (diff) |
[XFRM]: Netfilter IPsec output hooks
Call netfilter hooks before IPsec transforms. Packets visit the
FORWARD/LOCAL_OUT and POST_ROUTING hook before the first encapsulation
and the LOCAL_OUT and POST_ROUTING hook before each following tunnel mode
transform.
Patch from Herbert Xu <herbert@gondor.apana.org.au>:
Move the loop from dst_output into xfrm4_output/xfrm6_output since they're
the only ones who need to it. xfrm{4,6}_output_one() processes the first SA
all subsequent transport mode SAs and is called in a loop that calls the
netfilter hooks between each two calls.
In order to avoid the tail call issue, I've added the inline function
nf_hook which is nf_hook_slow plus the empty list check.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/xfrm4_output.c | 71 |
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 | ||
98 | int xfrm4_output(struct sk_buff *skb) | 100 | static 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 | ||
138 | out_exit: | 145 | out_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 | |||
154 | static 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 | |||
178 | int 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 | } | ||