Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16a6677f authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ee2e6841
Loading
Loading
Loading
Loading
+37 −24
Original line number Diff line number Diff line
@@ -169,6 +169,37 @@ void nf_log_packet(int pf,
		   struct nf_loginfo *li,
		   const char *fmt, ...);

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,
		 struct net_device *indev, struct net_device *outdev,
		 int (*okfn)(struct sk_buff *), int thresh);

/**
 *	nf_hook_thresh - call a netfilter hook
 *	
 *	Returns 1 if the hook has allowed the packet to pass.  The function
 *	okfn must be invoked by the caller in this case.  Any other return
 *	value indicates the packet has been consumed by the hook.
 */
static inline int nf_hook_thresh(int pf, unsigned int hook,
				 struct sk_buff **pskb,
				 struct net_device *indev,
				 struct net_device *outdev,
				 int (*okfn)(struct sk_buff *), int thresh)
{
#ifndef CONFIG_NETFILTER_DEBUG
	if (list_empty(&nf_hooks[pf][hook]))
		return 1;
#endif
	return nf_hook_slow(pf, hook, pskb, indev, outdev, okfn, thresh);
}

static inline int nf_hook(int pf, unsigned int hook, struct sk_buff **pskb,
			  struct net_device *indev, struct net_device *outdev,
			  int (*okfn)(struct sk_buff *))
{
	return nf_hook_thresh(pf, hook, pskb, indev, outdev, okfn, INT_MIN);
}
                   
/* Activate hook; either okfn or kfree_skb called, unless a hook
   returns NF_STOLEN (in which case, it's up to the hook to deal with
   the consequences).
@@ -188,35 +219,17 @@ void nf_log_packet(int pf,

/* This is gross, but inline doesn't cut it for avoiding the function
   call in fast path: gcc doesn't inline (needs value tracking?). --RR */
#ifdef CONFIG_NETFILTER_DEBUG
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			       \
({int __ret;								       \
if ((__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN)) == 1) \
	__ret = (okfn)(skb);						       \
__ret;})
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	       \
({int __ret;								       \
if ((__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1)  \
	__ret = (okfn)(skb);						       \
__ret;})
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			       \
({int __ret;								       \
if (list_empty(&nf_hooks[pf][hook]) ||					       \
    (__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, INT_MIN)) == 1) \
	__ret = (okfn)(skb);						       \
__ret;})

/* HX: It's slightly less gross now. */

#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	       \
({int __ret;								       \
if (list_empty(&nf_hooks[pf][hook]) ||					       \
    (__ret=nf_hook_slow(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1)  \
if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh)) == 1)\
	__ret = (okfn)(skb);						       \
__ret;})
#endif

int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,
		 struct net_device *indev, struct net_device *outdev,
		 int (*okfn)(struct sk_buff *), int thresh);
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
	NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)

/* Call setsockopt() */
int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, 
+1 −10
Original line number Diff line number Diff line
@@ -225,16 +225,7 @@ static inline void dst_set_expires(struct dst_entry *dst, int timeout)
/* Output packet to network from transport.  */
static inline int dst_output(struct sk_buff *skb)
{
	int err;

	for (;;) {
		err = skb->dst->output(skb);

		if (likely(err == 0))
			return err;
		if (unlikely(err != NET_XMIT_BYPASS))
			return err;
	}
	return skb->dst->output(skb);
}

/* Input packet from network to transport.  */
+54 −17
Original line number Diff line number Diff line
@@ -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 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
	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,6 +115,7 @@ int xfrm4_output(struct sk_buff *skb)
			goto error_nolock;
	}

	do {
		spin_lock_bh(&x->lock);
		err = xfrm_state_check(x, skb);
		if (err)
@@ -133,7 +136,11 @@ int xfrm4_output(struct sk_buff *skb)
			err = -EHOSTUNREACH;
			goto error_nolock;
		}
	err = NET_XMIT_BYPASS;
		dst = skb->dst;
		x = dst->xfrm;
	} while (x && !x->props.mode);

	err = 0;

out_exit:
	return err;
@@ -143,3 +150,33 @@ int xfrm4_output(struct sk_buff *skb)
	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);
}
+56 −19
Original line number Diff line number Diff line
@@ -9,9 +9,11 @@
 * 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/icmpv6.h>
#include <linux/netfilter_ipv6.h>
#include <net/dsfield.h>
#include <net/inet_ecn.h>
#include <net/ipv6.h>
@@ -92,7 +94,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
	return ret;
}

int xfrm6_output(struct sk_buff *skb)
static int xfrm6_output_one(struct sk_buff *skb)
{
	struct dst_entry *dst = skb->dst;
	struct xfrm_state *x = dst->xfrm;
@@ -110,6 +112,7 @@ int xfrm6_output(struct sk_buff *skb)
			goto error_nolock;
	}

	do {
		spin_lock_bh(&x->lock);
		err = xfrm_state_check(x, skb);
		if (err)
@@ -132,7 +135,11 @@ int xfrm6_output(struct sk_buff *skb)
			err = -EHOSTUNREACH;
			goto error_nolock;
		}
	err = NET_XMIT_BYPASS;
		dst = skb->dst;
		x = dst->xfrm;
	} while (x && !x->props.mode);

	err = 0;

out_exit:
	return err;
@@ -142,3 +149,33 @@ int xfrm6_output(struct sk_buff *skb)
	kfree_skb(skb);
	goto out_exit;
}

static int xfrm6_output_finish(struct sk_buff *skb)
{
	int err;

	while (likely((err = xfrm6_output_one(skb)) == 0)) {
		nf_reset(skb);
	
		err = nf_hook(PF_INET6, NF_IP6_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_INET6, NF_IP6_POST_ROUTING, &skb, NULL,
			      skb->dst->dev, xfrm6_output_finish);
		if (unlikely(err != 1))
			break;
	}

	return err;
}

int xfrm6_output(struct sk_buff *skb)
{
	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev,
		       xfrm6_output_finish);
}