Commit 7a3f5b0d authored by Ryoga Saito's avatar Ryoga Saito Committed by Pablo Neira Ayuso
Browse files

netfilter: add netfilter hooks to SRv6 data plane



This patch introduces netfilter hooks for solving the problem that
conntrack couldn't record both inner flows and outer flows.

This patch also introduces a new sysctl toggle for enabling lightweight
tunnel netfilter hooks.

Signed-off-by: default avatarRyoga Saito <contact@proelbtn.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 7bc416f1
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -184,6 +184,13 @@ nf_conntrack_gre_timeout_stream - INTEGER (seconds)
	This extended timeout will be used in case there is an GRE stream
	detected.

nf_hooks_lwtunnel - BOOLEAN
	- 0 - disabled (default)
	- not 0 - enabled

	If this option is enabled, the lightweight tunnel netfilter hooks are
	enabled. This option cannot be disabled once it is enabled.

nf_flowtable_tcp_timeout - INTEGER (seconds)
        default 30

+3 −0
Original line number Diff line number Diff line
@@ -51,6 +51,9 @@ struct lwtunnel_encap_ops {
};

#ifdef CONFIG_LWTUNNEL

DECLARE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);

void lwtstate_free(struct lwtunnel_state *lws);

static inline struct lwtunnel_state *
+7 −0
Original line number Diff line number Diff line
#include <linux/sysctl.h>
#include <linux/types.h>

#ifdef CONFIG_SYSCTL
int nf_hooks_lwtunnel_sysctl_handler(struct ctl_table *table, int write,
				     void *buffer, size_t *lenp, loff_t *ppos);
#endif
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
#include <net/ip6_fib.h>
#include <net/rtnh.h>

DEFINE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_enabled);

#ifdef CONFIG_MODULES

static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
+72 −3
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
#ifdef CONFIG_IPV6_SEG6_HMAC
#include <net/seg6_hmac.h>
#endif
#include <net/lwtunnel.h>
#include <linux/netfilter.h>

static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
{
@@ -295,11 +297,19 @@ static int seg6_do_srh(struct sk_buff *skb)

	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
	nf_reset_ct(skb);

	return 0;
}

static int seg6_input(struct sk_buff *skb)
static int seg6_input_finish(struct net *net, struct sock *sk,
			     struct sk_buff *skb)
{
	return dst_input(skb);
}

static int seg6_input_core(struct net *net, struct sock *sk,
			   struct sk_buff *skb)
{
	struct dst_entry *orig_dst = skb_dst(skb);
	struct dst_entry *dst = NULL;
@@ -337,10 +347,41 @@ static int seg6_input(struct sk_buff *skb)
	if (unlikely(err))
		return err;

	return dst_input(skb);
	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
			       dev_net(skb->dev), NULL, skb, NULL,
			       skb_dst(skb)->dev, seg6_input_finish);

	return seg6_input_finish(dev_net(skb->dev), NULL, skb);
}

static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
static int seg6_input_nf(struct sk_buff *skb)
{
	struct net_device *dev = skb_dst(skb)->dev;
	struct net *net = dev_net(skb->dev);

	switch (skb->protocol) {
	case htons(ETH_P_IP):
		return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, NULL,
			       skb, NULL, dev, seg6_input_core);
	case htons(ETH_P_IPV6):
		return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, NULL,
			       skb, NULL, dev, seg6_input_core);
	}

	return -EINVAL;
}

static int seg6_input(struct sk_buff *skb)
{
	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
		return seg6_input_nf(skb);

	return seg6_input_core(dev_net(skb->dev), NULL, skb);
}

static int seg6_output_core(struct net *net, struct sock *sk,
			    struct sk_buff *skb)
{
	struct dst_entry *orig_dst = skb_dst(skb);
	struct dst_entry *dst = NULL;
@@ -387,12 +428,40 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
	if (unlikely(err))
		goto drop;

	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
		return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
			       NULL, skb_dst(skb)->dev, dst_output);

	return dst_output(net, sk, skb);
drop:
	kfree_skb(skb);
	return err;
}

static int seg6_output_nf(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct net_device *dev = skb_dst(skb)->dev;

	switch (skb->protocol) {
	case htons(ETH_P_IP):
		return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb,
			       NULL, dev, seg6_output_core);
	case htons(ETH_P_IPV6):
		return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb,
			       NULL, dev, seg6_output_core);
	}

	return -EINVAL;
}

static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
		return seg6_output_nf(net, sk, skb);

	return seg6_output_core(net, sk, skb);
}

static int seg6_build_state(struct net *net, struct nlattr *nla,
			    unsigned int family, const void *cfg,
			    struct lwtunnel_state **ts,
Loading