diff options
| author | Patrick McHardy <kaber@trash.net> | 2006-01-06 23:01:48 -0800 | 
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2006-01-07 12:57:28 -0800 | 
| commit | 16a6677fdf1d1194f688f8291b06fbaff248c353 (patch) | |
| tree | 61badedc44ed88eb8f39e082d1abf114252cc686 /net/ipv4/xfrm4_output.c | |
| 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/xfrm4_output.c')
| -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 @@   * 2 of the License, or (at your option) any later version.   */ +#include <linux/compiler.h>  #include <linux/skbuff.h>  #include <linux/spinlock.h> +#include <linux/netfilter_ipv4.h>  #include <net/inet_ecn.h>  #include <net/ip.h>  #include <net/xfrm.h> @@ -95,7 +97,7 @@ out:  	return ret;  } -int xfrm4_output(struct sk_buff *skb) +static int xfrm4_output_one(struct sk_buff *skb)  {  	struct dst_entry *dst = skb->dst;  	struct xfrm_state *x = dst->xfrm; @@ -113,27 +115,32 @@ int xfrm4_output(struct sk_buff *skb)  			goto error_nolock;  	} -	spin_lock_bh(&x->lock); -	err = xfrm_state_check(x, skb); -	if (err) -		goto error; +	do { +		spin_lock_bh(&x->lock); +		err = xfrm_state_check(x, skb); +		if (err) +			goto error; -	xfrm4_encap(skb); +		xfrm4_encap(skb); -	err = x->type->output(x, skb); -	if (err) -		goto error; +		err = x->type->output(x, skb); +		if (err) +			goto error; -	x->curlft.bytes += skb->len; -	x->curlft.packets++; +		x->curlft.bytes += skb->len; +		x->curlft.packets++; -	spin_unlock_bh(&x->lock); +		spin_unlock_bh(&x->lock); -	if (!(skb->dst = dst_pop(dst))) { -		err = -EHOSTUNREACH; -		goto error_nolock; -	} -	err = NET_XMIT_BYPASS; +		if (!(skb->dst = dst_pop(dst))) { +			err = -EHOSTUNREACH; +			goto error_nolock; +		} +		dst = skb->dst; +		x = dst->xfrm; +	} while (x && !x->props.mode); + +	err = 0;  out_exit:  	return err; @@ -143,3 +150,33 @@ error_nolock:  	kfree_skb(skb);  	goto out_exit;  } + +static int xfrm4_output_finish(struct sk_buff *skb) +{ +	int err; + +	while (likely((err = xfrm4_output_one(skb)) == 0)) { +		nf_reset(skb); + +		err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL, +			      skb->dst->dev, dst_output); +		if (unlikely(err != 1)) +			break; + +		if (!skb->dst->xfrm) +			return dst_output(skb); + +		err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL, +			      skb->dst->dev, xfrm4_output_finish); +		if (unlikely(err != 1)) +			break; +	} + +	return err; +} + +int xfrm4_output(struct sk_buff *skb) +{ +	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, +		       xfrm4_output_finish); +} | 
