aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-01-07 02:01:48 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-01-07 15:57:28 -0500
commit16a6677fdf1d1194f688f8291b06fbaff248c353 (patch)
tree61badedc44ed88eb8f39e082d1abf114252cc686
parentee2e6841b934d76cb944a3390bbea84da777d4fa (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.h61
-rw-r--r--include/net/dst.h11
-rw-r--r--net/ipv4/xfrm4_output.c71
-rw-r--r--net/ipv6/xfrm6_output.c75
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
172int 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 */
183static 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
196static 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
194if ((__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; \
199if ((__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; \
205if (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; \
211if (list_empty(&nf_hooks[pf][hook]) || \ 227if ((__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
217int 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() */
222int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, 235int 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. */
226static inline int dst_output(struct sk_buff *skb) 226static 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
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}
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}