diff mbox series

[nf-next,1/4] netfilter: nf_tables: allow to dump all registered base hooks

Message ID 20210521113922.20798-2-fw@strlen.de
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series netfilter: add hook dump feature | expand

Commit Message

Florian Westphal May 21, 2021, 11:39 a.m. UTC
This dumps the active hook pipeline to userspace.

Userspace needs to pass the address family and the hook point.

For the netdev family/ingress hook, a device name must be
provided as well.

This allows 'nft' to display the priority of each hook.

Example:
family ip hook prerouting {
        -0000000300
        -0000000150
        -0000000100
}

... so this shows 3 active hooks with -300/-150/-100.

With manual work this is enough to figure out which hooks these are.

Followup patch will include the hook and module name (if any).

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/uapi/linux/netfilter/nf_tables.h |   1 +
 net/netfilter/nf_queue.c                 |   4 +-
 net/netfilter/nf_tables_api.c            | 216 +++++++++++++++++++++++
 3 files changed, 219 insertions(+), 2 deletions(-)

Comments

kernel test robot May 22, 2021, 10:49 p.m. UTC | #1
Hi Florian,

I love your patch! Perhaps something to improve:

[auto build test WARNING on nf-next/master]

url:    https://github.com/0day-ci/linux/commits/Florian-Westphal/netfilter-add-hook-dump-feature/20210522-225655
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master
config: x86_64-randconfig-r026-20210522 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project e84a9b9bb3051c35dea993cdad7b3d2575638f85)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/0d9a4c5b44ef1ad2d9ed5c4c8cea7eae29560706
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Florian-Westphal/netfilter-add-hook-dump-feature/20210522-225655
        git checkout 0d9a4c5b44ef1ad2d9ed5c4c8cea7eae29560706
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/netfilter/nf_tables_api.c:7991:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (!nlh)
               ^~~~
   net/netfilter/nf_tables_api.c:8006:9: note: uninitialized use occurs here
           return ret;
                  ^~~
   net/netfilter/nf_tables_api.c:7991:2: note: remove the 'if' if its condition is always false
           if (!nlh)
           ^~~~~~~~~
   net/netfilter/nf_tables_api.c:7987:9: note: initialize the variable 'ret' to silence this warning
           int ret;
                  ^
                   = 0
   1 warning generated.


vim +7991 net/netfilter/nf_tables_api.c

  7979	
  7980	static int nf_tables_dump_one_hook(struct sk_buff *nlskb,
  7981					   const struct nft_dump_hooks_data *ctx,
  7982					   const struct nf_hook_ops *ops)
  7983	{
  7984		unsigned int portid = NETLINK_CB(nlskb).portid;
  7985		struct net *net = sock_net(nlskb->sk);
  7986		struct nlmsghdr *nlh;
  7987		int ret;
  7988	
  7989		nlh = nfnl_msg_put(nlskb, portid, ctx->seq, NFT_MSG_GETNFHOOKS,
  7990				   NLM_F_MULTI, ops->pf, NFNETLINK_V0, nft_base_seq(net));
> 7991		if (!nlh)
  7992			goto nla_put_failure;
  7993	
  7994		ret = nla_put_be32(nlskb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum));
  7995		if (ret)
  7996			goto nla_put_failure;
  7997	
  7998		ret = nla_put_be32(nlskb, NFTA_HOOK_PRIORITY, htonl(ops->priority));
  7999		if (ret)
  8000			goto nla_put_failure;
  8001	
  8002		nlmsg_end(nlskb, nlh);
  8003		return 0;
  8004	nla_put_failure:
  8005		nlmsg_trim(nlskb, nlh);
  8006		return ret;
  8007	}
  8008	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 19715e2679d1..5810e41eff33 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -124,6 +124,7 @@  enum nf_tables_msg_types {
 	NFT_MSG_NEWFLOWTABLE,
 	NFT_MSG_GETFLOWTABLE,
 	NFT_MSG_DELFLOWTABLE,
+	NFT_MSG_GETNFHOOKS,
 	NFT_MSG_MAX,
 };
 
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index bbd1209694b8..224238b20825 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -259,7 +259,7 @@  static unsigned int nf_iterate(struct sk_buff *skb,
 	return NF_ACCEPT;
 }
 
-static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf, u8 hooknum)
+static struct nf_hook_entries *nf_queue_hook_entries_head(const struct net *net, u8 pf, u8 hooknum)
 {
 	switch (pf) {
 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
@@ -292,7 +292,7 @@  void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
 	net = entry->state.net;
 	pf = entry->state.pf;
 
-	hooks = nf_hook_entries_head(net, pf, entry->state.hook);
+	hooks = nf_queue_hook_entries_head(net, pf, entry->state.hook);
 
 	i = entry->hook_index;
 	if (WARN_ON_ONCE(!hooks || i >= hooks->num_hook_entries)) {
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index d63d2d8f769c..2bfa80e93658 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -7911,6 +7911,216 @@  static int nf_tables_getgen(struct sk_buff *skb, const struct nfnl_info *info,
 	return err;
 }
 
+static const struct nf_hook_entries *
+nf_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
+{
+	const struct nf_hook_entries *hook_head = NULL;
+	struct net_device *netdev;
+
+	switch (pf) {
+	case NFPROTO_IPV4:
+		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
+			return ERR_PTR(-EINVAL);
+		hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
+		break;
+	case NFPROTO_IPV6:
+		hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
+		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
+			return ERR_PTR(-EINVAL);
+		break;
+	case NFPROTO_ARP:
+#ifdef CONFIG_NETFILTER_FAMILY_ARP
+		if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
+			return ERR_PTR(-EINVAL);
+		hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
+#endif
+		break;
+	case NFPROTO_BRIDGE:
+#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+		if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
+			return ERR_PTR(-EINVAL);
+		hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
+#endif
+		break;
+#if IS_ENABLED(CONFIG_DECNET)
+	case NFPROTO_DECNET:
+		if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
+			return ERR_PTR(-EINVAL);
+		hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
+		break;
+#endif
+#ifdef CONFIG_NETFILTER_INGRESS
+	case NFPROTO_NETDEV:
+		if (hook != NF_NETDEV_INGRESS)
+			return ERR_PTR(-EOPNOTSUPP);
+
+		if (!dev)
+			return ERR_PTR(-ENODEV);
+
+		netdev = dev_get_by_name_rcu(net, dev);
+		if (!netdev)
+			return ERR_PTR(-ENODEV);
+
+		return rcu_dereference(netdev->nf_hooks_ingress);
+#endif
+	default:
+		return ERR_PTR(-EPROTONOSUPPORT);
+	}
+
+	return hook_head;
+}
+
+struct nft_dump_hooks_data {
+	char devname[IFNAMSIZ];
+	unsigned long headv;
+	unsigned int seq;
+	u8 hook;
+};
+
+static int nf_tables_dump_one_hook(struct sk_buff *nlskb,
+				   const struct nft_dump_hooks_data *ctx,
+				   const struct nf_hook_ops *ops)
+{
+	unsigned int portid = NETLINK_CB(nlskb).portid;
+	struct net *net = sock_net(nlskb->sk);
+	struct nlmsghdr *nlh;
+	int ret;
+
+	nlh = nfnl_msg_put(nlskb, portid, ctx->seq, NFT_MSG_GETNFHOOKS,
+			   NLM_F_MULTI, ops->pf, NFNETLINK_V0, nft_base_seq(net));
+	if (!nlh)
+		goto nla_put_failure;
+
+	ret = nla_put_be32(nlskb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum));
+	if (ret)
+		goto nla_put_failure;
+
+	ret = nla_put_be32(nlskb, NFTA_HOOK_PRIORITY, htonl(ops->priority));
+	if (ret)
+		goto nla_put_failure;
+
+	nlmsg_end(nlskb, nlh);
+	return 0;
+nla_put_failure:
+	nlmsg_trim(nlskb, nlh);
+	return ret;
+}
+
+static int nf_tables_dump_basehooks(struct sk_buff *nlskb,
+				    struct netlink_callback *cb)
+{
+	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+	struct nft_dump_hooks_data *ctx = cb->data;
+	int err, family = nfmsg->nfgen_family;
+	struct net *net = sock_net(nlskb->sk);
+	struct nf_hook_ops * const *ops;
+	const struct nf_hook_entries *e;
+	unsigned int i = cb->args[0];
+
+	rcu_read_lock();
+	cb->seq = ctx->seq;
+
+	e = nf_hook_entries_head(family, ctx->hook, net, ctx->devname);
+	if (!e)
+		goto done;
+
+	if (IS_ERR(e)) {
+		ctx->seq++;
+		goto done;
+	}
+
+	if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
+		ctx->seq++;
+
+	ops = nf_hook_entries_get_hook_ops(e);
+
+	for (; i < e->num_hook_entries; i++) {
+		err = nf_tables_dump_one_hook(nlskb, ctx, ops[i]);
+		if (err)
+			break;
+	}
+
+done:
+	nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
+	rcu_read_unlock();
+	cb->args[0] = i;
+	return nlskb->len;
+}
+
+static int nf_tables_dump_basehooks_start(struct netlink_callback *cb)
+{
+	const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+	const struct nlattr * const *nla = cb->data;
+	struct nft_dump_hooks_data *ctx = NULL;
+	struct net *net = sock_net(cb->skb->sk);
+	u8 family = nfmsg->nfgen_family;
+	char name[IFNAMSIZ] = "";
+	const void *head;
+	u32 hooknum;
+
+	hooknum = ntohl(nla_get_be32(nla[NFTA_HOOK_HOOKNUM]));
+	if (hooknum > 255)
+		return -EINVAL;
+
+	if (family == NFPROTO_NETDEV) {
+		if (!nla[NFTA_HOOK_DEV])
+			return -EINVAL;
+
+		nla_strscpy(name, nla[NFTA_HOOK_DEV], sizeof(name));
+	}
+
+	rcu_read_lock();
+	/* Not dereferenced; for consistency check only */
+	head = nf_hook_entries_head(family, hooknum, net, name);
+	rcu_read_unlock();
+
+	if (head && IS_ERR(head))
+		return PTR_ERR(head);
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->headv = (unsigned long)head;
+
+	ctx->hook = hooknum;
+	strscpy(ctx->devname, name, sizeof(ctx->devname));
+	cb->data = ctx;
+	cb->seq = 1;
+	return 0;
+}
+
+static int nf_tables_dump_basehooks_stop(struct netlink_callback *cb)
+{
+	kfree(cb->data);
+	return 0;
+}
+
+static int nft_get_basehooks(struct sk_buff *skb,
+			     const struct nfnl_info *info,
+			     const struct nlattr * const nla[])
+{
+	if (!nla[NFTA_HOOK_HOOKNUM])
+		return -EINVAL;
+
+	if (ntohl(nla_get_be32(nla[NFTA_HOOK_HOOKNUM])) > 255)
+		return -EINVAL;
+
+	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
+		struct netlink_dump_control c = {
+			.start = nf_tables_dump_basehooks_start,
+			.done = nf_tables_dump_basehooks_stop,
+			.dump = nf_tables_dump_basehooks,
+			.module = THIS_MODULE,
+			.data = (void *)nla,
+		};
+
+		return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
+	}
+
+	return -EOPNOTSUPP;
+}
+
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 	[NFT_MSG_NEWTABLE] = {
 		.call		= nf_tables_newtable,
@@ -8048,6 +8258,12 @@  static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
 		.attr_count	= NFTA_FLOWTABLE_MAX,
 		.policy		= nft_flowtable_policy,
 	},
+	[NFT_MSG_GETNFHOOKS] = {
+		.call		= nft_get_basehooks,
+		.type		= NFNL_CB_RCU,
+		.attr_count	= NFTA_HOOK_MAX,
+		.policy		= nft_hook_policy,
+	},
 };
 
 static int nf_tables_validate(struct net *net)