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 | |
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>
-rw-r--r-- | include/linux/netfilter.h | 61 | ||||
-rw-r--r-- | include/net/dst.h | 11 | ||||
-rw-r--r-- | net/ipv4/xfrm4_output.c | 71 | ||||
-rw-r--r-- | net/ipv6/xfrm6_output.c | 75 |
4 files changed, 148 insertions, 70 deletions
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index be365e70ee99..79bb977afeac 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h | |||
@@ -168,6 +168,37 @@ void nf_log_packet(int pf, | |||
168 | const struct net_device *out, | 168 | const struct net_device *out, |
169 | struct nf_loginfo *li, | 169 | struct nf_loginfo *li, |
170 | const char *fmt, ...); | 170 | const char *fmt, ...); |
171 | |||
172 | int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb, | ||
173 | struct net_device *indev, struct net_device *outdev, | ||
174 | int (*okfn)(struct sk_buff *), int thresh); | ||
175 | |||
176 | /** | ||
177 | * nf_hook_thresh - call a netfilter hook | ||
178 | * | ||
179 | * Returns 1 if the hook has allowed the packet to pass. The function | ||
180 | * okfn must be invoked by the caller in this case. Any other return | ||
181 | * value indicates the packet has been consumed by the hook. | ||
182 | */ | ||
183 | static inline int nf_hook_thresh(int pf, unsigned int hook, | ||
184 | struct sk_buff **pskb, | ||
185 | struct net_device *indev, | ||
186 | struct net_device *outdev, | ||
187 | int (*okfn)(struct sk_buff *), int thresh) | ||
188 | { | ||
189 | #ifndef CONFIG_NETFILTER_DEBUG | ||
190 | if (list_empty(&nf_hooks[pf][hook])) | ||
191 | return 1; | ||
192 | #endif | ||
193 | return nf_hook_slow(pf, hook, pskb, indev, outdev, okfn, thresh); | ||
194 | } | ||
195 | |||
196 | static inline int nf_hook(int pf, unsigned int hook, struct sk_buff **pskb, | ||
197 | struct net_device *indev, struct net_device *outdev, | ||
198 | int (*okfn)(struct sk_buff *)) | ||
199 | { | ||
200 | return nf_hook_thresh(pf, hook, pskb, indev, outdev, okfn, INT_MIN); | ||
201 | } | ||
171 | 202 | ||
172 | /* Activate hook; either okfn or kfree_skb called, unless a hook | 203 | /* Activate hook; either okfn or kfree_skb called, unless a hook |
173 | returns NF_STOLEN (in which case, it's up to the hook to deal with | 204 | returns NF_STOLEN (in which case, it's up to the hook to deal with |
@@ -188,35 +219,17 @@ void nf_log_packet(int pf, | |||
188 | 219 | ||
189 | /* This is gross, but inline doesn't cut it for avoiding the function | 220 | /* This is gross, but inline doesn't cut it for avoiding the function |
190 | call in fast path: gcc doesn't inline (needs value tracking?). --RR */ | 221 | call in fast path: gcc doesn't inline (needs value tracking?). --RR */ |
191 | #ifdef CONFIG_NETFILTER_DEBUG | 222 | |
192 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ | 223 | /* HX: It's slightly less gross now. */ |
193 | ({int __ret; \ | 224 | |
194 | if ((__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN)) == 1) \ | ||
195 | __ret = (okfn)(skb); \ | ||
196 | __ret;}) | ||
197 | #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ | ||
198 | ({int __ret; \ | ||
199 | if ((__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1) \ | ||
200 | __ret = (okfn)(skb); \ | ||
201 | __ret;}) | ||
202 | #else | ||
203 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ | ||
204 | ({int __ret; \ | ||
205 | if (list_empty(&nf_hooks[pf][hook]) || \ | ||
206 | (__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN)) == 1) \ | ||
207 | __ret = (okfn)(skb); \ | ||
208 | __ret;}) | ||
209 | #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ | 225 | #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ |
210 | ({int __ret; \ | 226 | ({int __ret; \ |
211 | if (list_empty(&nf_hooks[pf][hook]) || \ | 227 | if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1)\ |
212 | (__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1) \ | ||
213 | __ret = (okfn)(skb); \ | 228 | __ret = (okfn)(skb); \ |
214 | __ret;}) | 229 | __ret;}) |
215 | #endif | ||
216 | 230 | ||
217 | int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb, | 231 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ |
218 | struct net_device *indev, struct net_device *outdev, | 232 | NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN) |
219 | int (*okfn)(struct sk_buff *), int thresh); | ||
220 | 233 | ||
221 | /* Call setsockopt() */ | 234 | /* Call setsockopt() */ |
222 | int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, | 235 | int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, |
diff --git a/include/net/dst.h b/include/net/dst.h index bee8b84d329d..5161e89017f9 100644 --- a/include/net/dst.h +++ b/include/net/dst.h | |||
@@ -225,16 +225,7 @@ static inline void dst_set_expires(struct dst_entry *dst, int timeout) | |||
225 | /* Output packet to network from transport. */ | 225 | /* Output packet to network from transport. */ |
226 | static inline int dst_output(struct sk_buff *skb) | 226 | static inline int dst_output(struct sk_buff *skb) |
227 | { | 227 | { |
228 | int err; | 228 | return skb->dst->output(skb); |
229 | |||
230 | for (;;) { | ||
231 | err = skb->dst->output(skb); | ||
232 | |||
233 | if (likely(err == 0)) | ||
234 | return err; | ||
235 | if (unlikely(err != NET_XMIT_BYPASS)) | ||
236 | return err; | ||
237 | } | ||
238 | } | 229 | } |
239 | 230 | ||
240 | /* Input packet from network to transport. */ | 231 | /* Input packet from network to transport. */ |
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 | } | ||
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 | } | ||