Commit 60a3815d authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

netfilter: add inet ingress support



This patch adds the NF_INET_INGRESS pseudohook for the NFPROTO_INET
family. This is a mapping this new hook to the existing NFPROTO_NETDEV
and NF_NETDEV_INGRESS hook. The hook does not guarantee that packets are
inet only, users must filter out non-ip traffic explicitly.

This infrastructure makes it easier to support this new hook in nf_tables.

Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent ddcfa710
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ enum nf_inet_hooks {
	NF_INET_FORWARD,
	NF_INET_LOCAL_OUT,
	NF_INET_POST_ROUTING,
	NF_INET_INGRESS,
	NF_INET_NUMHOOKS
};

+82 −21
Original line number Diff line number Diff line
@@ -281,6 +281,16 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
		if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum))
			return NULL;
		return net->nf.hooks_bridge + hooknum;
#endif
#ifdef CONFIG_NETFILTER_INGRESS
	case NFPROTO_INET:
		if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS))
			return NULL;
		if (!dev || dev_net(dev) != net) {
			WARN_ON_ONCE(1);
			return NULL;
		}
		return &dev->nf_hooks_ingress;
#endif
	case NFPROTO_IPV4:
		if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum))
@@ -311,22 +321,56 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
	return NULL;
}

static int nf_ingress_check(struct net *net, const struct nf_hook_ops *reg,
			    int hooknum)
{
#ifndef CONFIG_NETFILTER_INGRESS
	if (reg->hooknum == hooknum)
		return -EOPNOTSUPP;
#endif
	if (reg->hooknum != hooknum ||
	    !reg->dev || dev_net(reg->dev) != net)
		return -EINVAL;

	return 0;
}

static inline bool nf_ingress_hook(const struct nf_hook_ops *reg, int pf)
{
	return pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS;
	if ((pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) ||
	    (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS))
		return true;

	return false;
}

static void nf_static_key_inc(const struct nf_hook_ops *reg, int pf)
{
#ifdef CONFIG_JUMP_LABEL
       static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]);
	int hooknum;

	if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
		pf = NFPROTO_NETDEV;
		hooknum = NF_NETDEV_INGRESS;
	} else {
		hooknum = reg->hooknum;
	}
	static_key_slow_inc(&nf_hooks_needed[pf][hooknum]);
#endif
}

static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf)
{
#ifdef CONFIG_JUMP_LABEL
       static_key_slow_dec(&nf_hooks_needed[pf][reg->hooknum]);
	int hooknum;

	if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
		pf = NFPROTO_NETDEV;
		hooknum = NF_NETDEV_INGRESS;
	} else {
		hooknum = reg->hooknum;
	}
	static_key_slow_dec(&nf_hooks_needed[pf][hooknum]);
#endif
}

@@ -335,15 +379,22 @@ static int __nf_register_net_hook(struct net *net, int pf,
{
	struct nf_hook_entries *p, *new_hooks;
	struct nf_hook_entries __rcu **pp;
	int err;

	if (pf == NFPROTO_NETDEV) {
#ifndef CONFIG_NETFILTER_INGRESS
		if (reg->hooknum == NF_NETDEV_INGRESS)
			return -EOPNOTSUPP;
#endif
		if (reg->hooknum != NF_NETDEV_INGRESS ||
		    !reg->dev || dev_net(reg->dev) != net)
			return -EINVAL;
	switch (pf) {
	case NFPROTO_NETDEV:
		err = nf_ingress_check(net, reg, NF_NETDEV_INGRESS);
		if (err < 0)
			return err;
		break;
	case NFPROTO_INET:
		if (reg->hooknum != NF_INET_INGRESS)
			break;

		err = nf_ingress_check(net, reg, NF_INET_INGRESS);
		if (err < 0)
			return err;
		break;
	}

	pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
@@ -441,8 +492,12 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
{
	if (reg->pf == NFPROTO_INET) {
		if (reg->hooknum == NF_INET_INGRESS) {
			__nf_unregister_net_hook(net, NFPROTO_INET, reg);
		} else {
			__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
			__nf_unregister_net_hook(net, NFPROTO_IPV6, reg);
		}
	} else {
		__nf_unregister_net_hook(net, reg->pf, reg);
	}
@@ -467,6 +522,11 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
	int err;

	if (reg->pf == NFPROTO_INET) {
		if (reg->hooknum == NF_INET_INGRESS) {
			err = __nf_register_net_hook(net, NFPROTO_INET, reg);
			if (err < 0)
				return err;
		} else {
			err = __nf_register_net_hook(net, NFPROTO_IPV4, reg);
			if (err < 0)
				return err;
@@ -476,6 +536,7 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
				__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
				return err;
			}
		}
	} else {
		err = __nf_register_net_hook(net, reg->pf, reg);
		if (err < 0)