All of lore.kernel.org
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nft 2/3] src: add support for base hook dumping
Date: Fri, 21 May 2021 14:08:45 +0200	[thread overview]
Message-ID: <20210521120846.1140-3-fw@strlen.de> (raw)
In-Reply-To: <20210521120846.1140-1-fw@strlen.de>

Example output:
$ nft list hook ip input
family ip hook input {
        +0000000000 nft_do_chain_inet [nf_tables]       # nft table ip filter chain input
        +0000000010 nft_do_chain_inet [nf_tables]       # nft table ip firewalld chain filter_INPUT
        +0000000100 nf_nat_ipv4_local_in [nf_nat]
        +2147483647 ipv4_confirm [nf_conntrack]
}

$ nft list hooks netdev type ingress device lo
family netdev hook ingress device lo {
        +0000000000 nft_do_chain_netdev [nf_tables]
}

$ nft list hooks inet
family ip hook prerouting {
        -0000000400 ipv4_conntrack_defrag [nf_defrag_ipv4]
        -0000000300 iptable_raw_hook [iptable_raw]
        -0000000290 nft_do_chain_inet [nf_tables]       # nft table ip firewalld chain raw_PREROUTING
        -0000000200 ipv4_conntrack_in [nf_conntrack]
        -0000000140 nft_do_chain_inet [nf_tables]       # nft table ip firewalld chain mangle_PREROUTING
        -0000000100 nf_nat_ipv4_pre_routing [nf_nat]
}
...

'nft list hooks' will display everyting except the netdev family
via successive dump request for all family:hook combinations.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/linux/netfilter/nf_tables.h |   4 +
 include/mnl.h                       |   3 +
 include/rule.h                      |   1 +
 src/evaluate.c                      |  10 ++
 src/mnl.c                           | 269 +++++++++++++++++++++++++++-
 src/parser_bison.y                  |  48 ++++-
 src/rule.c                          |  13 ++
 src/scanner.l                       |   1 +
 8 files changed, 346 insertions(+), 3 deletions(-)

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 75df968d231b..c53b0383ebac 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/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_GETFNHOOKS,
 	NFT_MSG_MAX,
 };
 
@@ -153,6 +154,9 @@ enum nft_hook_attributes {
 	NFTA_HOOK_PRIORITY,
 	NFTA_HOOK_DEV,
 	NFTA_HOOK_DEVS,
+	NFTA_HOOK_FUNCTION_NAME,
+	NFTA_HOOK_MODULE_NAME,
+	NFTA_HOOK_NFT_CHAIN_INFO,
 	__NFTA_HOOK_MAX
 };
 #define NFTA_HOOK_MAX		(__NFTA_HOOK_MAX - 1)
diff --git a/include/mnl.h b/include/mnl.h
index 979929c31c17..68ec80cd2282 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -82,6 +82,9 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
 			  unsigned int flags);
 int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd);
 
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook,
+			  const char *devname);
+
 int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
 			   struct output_ctx *octx,
 			   int (*cb)(const struct nlmsghdr *nlh, void *data),
diff --git a/include/rule.h b/include/rule.h
index f469db55bf60..357326a3fceb 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -644,6 +644,7 @@ enum cmd_obj {
 	CMD_OBJ_CT_EXPECT,
 	CMD_OBJ_SYNPROXY,
 	CMD_OBJ_SYNPROXYS,
+	CMD_OBJ_HOOKS,
 };
 
 struct markup {
diff --git a/src/evaluate.c b/src/evaluate.c
index 006b04affbd7..d1c6dd84ec90 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4698,6 +4698,16 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
 	case CMD_OBJ_METERS:
 	case CMD_OBJ_MAPS:
 		return 0;
+	case CMD_OBJ_HOOKS:
+		if (cmd->handle.chain.name) {
+			int hooknum = str2hooknum(cmd->handle.family, cmd->handle.chain.name);
+
+			if (hooknum == NF_INET_NUMHOOKS)
+				return chain_not_found(ctx);
+
+			cmd->handle.chain_id = hooknum;
+		}
+		return 0;
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
 	}
diff --git a/src/mnl.c b/src/mnl.c
index ef45cbd193f9..83612962c98e 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -34,6 +34,17 @@
 #include <stdlib.h>
 #include <utils.h>
 #include <nftables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_arp.h>
+
+struct basehook {
+	struct list_head list;
+	const char *module_name;
+	const char *hookfn;
+	const char *table;
+	const char *chain;
+	int prio;
+};
 
 struct mnl_socket *nft_mnl_socket_open(void)
 {
@@ -1874,7 +1885,7 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
 			   void *cb_data)
 {
 	/* Set netlink socket buffer size to 16 Mbytes to reduce chances of
- 	 * message loss due to ENOBUFS.
+	 * message loss due to ENOBUFS.
 	 */
 	unsigned int bufsiz = NFTABLES_NLEVENT_BUFSIZ;
 	int fd = mnl_socket_get_fd(nf_sock);
@@ -1918,3 +1929,259 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
 	}
 	return ret;
 }
+
+static struct basehook *basehook_alloc(void)
+{
+	return xzalloc(sizeof(struct basehook));
+}
+
+static void basehook_free(struct basehook *b)
+{
+	list_del(&b->list);
+	xfree(b->module_name);
+	xfree(b->hookfn);
+	xfree(b->chain);
+	xfree(b->table);
+	xfree(b);
+}
+
+static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
+{
+	list_add_tail(&b->list, head);
+}
+
+static int dump_nf_attr_cb(const struct nlattr *attr, void *data)
+{
+        int type = mnl_attr_get_type(attr);
+	const struct nlattr **tb = data;
+
+        if (mnl_attr_type_valid(attr, NFTA_HOOK_MAX) < 0)
+                return MNL_CB_OK;
+
+        switch(type) {
+	case NFTA_HOOK_HOOKNUM:
+	case NFTA_HOOK_PRIORITY:
+                if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+                        return MNL_CB_ERROR;
+		break;
+	case NFTA_HOOK_DEV:
+	case NFTA_HOOK_FUNCTION_NAME:
+	case NFTA_HOOK_MODULE_NAME:
+                if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+                        return MNL_CB_ERROR;
+		break;
+	case NFTA_HOOK_NFT_CHAIN_INFO:
+                if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+                        return MNL_CB_ERROR;
+		break;
+	default:
+		return MNL_CB_OK;
+	}
+
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
+{
+        int type = mnl_attr_get_type(attr);
+	const struct nlattr **tb = data;
+
+        if (mnl_attr_type_valid(attr, NFTA_CHAIN_MAX) < 0)
+                return MNL_CB_OK;
+
+        switch(type) {
+	case NFTA_CHAIN_TABLE:
+	case NFTA_CHAIN_NAME:
+                if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+                        return MNL_CB_ERROR;
+		break;
+	default:
+		return MNL_CB_OK;
+	}
+
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int dump_nf_hooks(const struct nlmsghdr *nlh, void *data)
+{
+	const struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+	struct nlattr *tb[NFTA_HOOK_MAX + 1] = {};
+	struct list_head *head = data;
+	struct basehook *hook;
+
+	if (check_genid(nlh) < 0)
+		return MNL_CB_ERROR;
+
+        if (mnl_attr_parse(nlh, sizeof(*nfg), dump_nf_attr_cb, tb) < 0)
+                return -1;
+
+	hook = basehook_alloc();
+	hook->prio = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_PRIORITY]));
+
+	if (tb[NFTA_HOOK_FUNCTION_NAME])
+		hook->hookfn = xstrdup(mnl_attr_get_str(tb[NFTA_HOOK_FUNCTION_NAME]));
+
+	if (tb[NFTA_HOOK_MODULE_NAME])
+		hook->module_name = xstrdup(mnl_attr_get_str(tb[NFTA_HOOK_MODULE_NAME]));
+
+	if (tb[NFTA_HOOK_NFT_CHAIN_INFO]) {
+		struct nlattr *nested[NFTA_CHAIN_MAX + 1] = {};
+
+		if (mnl_attr_parse_nested(tb[NFTA_HOOK_NFT_CHAIN_INFO], dump_nf_attr_chain_cb, nested) < 0)
+			return -1;
+
+		hook->table = xstrdup(mnl_attr_get_str(nested[NFTA_CHAIN_TABLE]));
+		hook->chain = xstrdup(mnl_attr_get_str(nested[NFTA_CHAIN_NAME]));
+	}
+
+	basehook_list_add_tail(hook, head);
+	return MNL_CB_OK;
+}
+
+static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t family, uint8_t hooknum, const char *devname)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct basehook *hook, *tmp;
+	struct nlmsghdr *nlh;
+	LIST_HEAD(hook_list);
+	FILE *fp;
+	int ret;
+
+	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFNHOOKS, family,
+				    NLM_F_DUMP, ctx->seqnum);
+
+	if (devname)
+		mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, devname);
+
+	mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(hooknum));
+
+	ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, dump_nf_hooks, &hook_list);
+	if (ret)
+		return ret;
+
+	if (list_empty(&hook_list))
+		return 0;
+
+	fp = ctx->nft->output.output_fp;
+	fprintf(fp, "family %s hook %s", family2str(family), hooknum2str(family, hooknum));
+	if (devname)
+		fprintf(fp, " device %s", devname);
+
+	fprintf(fp, " {\n");
+
+	list_for_each_entry_safe(hook, tmp, &hook_list, list) {
+		int prio = hook->prio;
+
+		if (prio < 0)
+			fprintf(fp, "\t%011d", prio); /* outputs a '-' sign */
+		else
+			fprintf(fp, "\t+%010u", prio);
+
+		if (hook->hookfn) {
+			fprintf(fp, " %s", hook->hookfn);
+			if (hook->module_name)
+				fprintf(fp, " [%s]", hook->module_name);
+		}
+
+		if (hook->table && hook->chain)
+			fprintf(fp, "\t# nft table %s %s chain %s", family2str(family), hook->table, hook->chain);
+
+		fprintf(fp, "\n");
+		basehook_free(hook);
+	}
+
+	fprintf(fp, "}\n");
+	return ret;
+}
+
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname)
+{
+	unsigned int i;
+	int ret, err;
+
+	errno = 0;
+	ret = 0;
+
+	switch (family) {
+	case NFPROTO_UNSPEC:
+		if (devname)
+			return mnl_nft_dump_nf_hooks(ctx, NFPROTO_NETDEV, NF_INET_INGRESS, devname);
+
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_INET, hook, NULL);
+		if (err < 0 && errno != EPROTONOSUPPORT)
+			ret = err;
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_ARP, hook, NULL);
+		if (err < 0 && errno != EPROTONOSUPPORT)
+			ret = err;
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_BRIDGE, hook, NULL);
+		if (err < 0 && errno != EPROTONOSUPPORT)
+			ret = err;
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_DECNET, hook, NULL);
+		if (err < 0 && errno != EPROTONOSUPPORT)
+			ret = err;
+		ret = 0;
+		break;
+	case NFPROTO_INET:
+		if (devname) {
+			err = __mnl_nft_dump_nf_hooks(ctx, family, NF_INET_INGRESS, devname);
+			if (err < 0)
+				ret = err;
+		}
+
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV4, hook, NULL);
+		if (err < 0)
+			ret = err;
+		err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV6, hook, NULL);
+		if (err < 0)
+			ret = err;
+
+		break;
+	case NFPROTO_IPV4:
+	case NFPROTO_IPV6:
+	case NFPROTO_BRIDGE:
+		if (hook >= 0)
+			return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
+
+		for (i = 0; i <= NF_INET_POST_ROUTING; i++) {
+			err = __mnl_nft_dump_nf_hooks(ctx, family, i, NULL);
+			if (err < 0)
+				err = ret;
+		}
+		break;
+	case NFPROTO_ARP:
+		if (hook >= 0)
+			return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
+
+		err = __mnl_nft_dump_nf_hooks(ctx, family, NF_ARP_IN, devname);
+		if (err < 0)
+			ret = err;
+		err = __mnl_nft_dump_nf_hooks(ctx, family, NF_ARP_OUT, devname);
+		if (err < 0)
+			ret = err;
+		break;
+	case NFPROTO_NETDEV:
+		if (hook >= 0)
+			return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
+
+		err = __mnl_nft_dump_nf_hooks(ctx, family, NF_INET_INGRESS, devname);
+		if (err < 0)
+			ret = err;
+		break;
+	case NFPROTO_DECNET:
+		if (hook >= 0)
+			return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
+#define NF_DN_NUMHOOKS		7
+		for (i = 0; i < NF_DN_NUMHOOKS; i++) {
+			err = __mnl_nft_dump_nf_hooks(ctx, family, i, devname);
+			if (err < 0) {
+				ret = err;
+				break;
+			}
+		}
+		break;
+	}
+
+	return ret;
+}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index f6c92feb7661..136ae105f513 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -238,6 +238,7 @@ int nft_lex(void *, void *, void *);
 %token TYPEOF			"typeof"
 
 %token HOOK			"hook"
+%token HOOKS			"hooks"
 %token DEVICE			"device"
 %token DEVICES			"devices"
 %token TABLE			"table"
@@ -632,11 +633,15 @@ int nft_lex(void *, void *, void *);
 
 %type <handle>			set_identifier flowtableid_spec flowtable_identifier obj_identifier
 %destructor { handle_free(&$$); } set_identifier flowtableid_spec obj_identifier
+
+%type <handle>			basehook_spec
+%destructor { handle_free(&$$); } basehook_spec
+
 %type <val>			family_spec family_spec_explicit
 %type <val32>			int_num	chain_policy
 %type <prio_spec>		extended_prio_spec prio_spec
-%type <string>			extended_prio_name quota_unit
-%destructor { xfree($$); }	extended_prio_name quota_unit
+%type <string>			extended_prio_name quota_unit	basehook_device_name
+%destructor { xfree($$); }	extended_prio_name quota_unit	basehook_device_name
 
 %type <expr>			dev_spec
 %destructor { xfree($$); }	dev_spec
@@ -1456,6 +1461,45 @@ list_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, $2, &$4, &@$, NULL);
 			}
+			|	HOOKS	basehook_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_HOOKS, &$2, &@$, NULL);
+			}
+			;
+
+basehook_device_name	:	/* NULL */
+			{
+				$$ = NULL;
+			}
+			|	DEVICE STRING
+			{
+				$$ = $2;
+			}
+			;
+
+basehook_spec		:	ruleset_spec
+			{
+				$$ = $1;
+			}
+			|	ruleset_spec    STRING  basehook_device_name
+			{
+				const char *name = chain_hookname_lookup($2);
+
+				if (name == NULL) {
+					erec_queue(error(&@2, "unknown chain hook"),
+						   state->msgs);
+					xfree($3);
+					YYERROR;
+				}
+
+				$1.chain.name = $2;
+				$1.chain.location = @2;
+				if ($3) {
+					$1.obj.name = $3;
+					$1.obj.location = @3;
+				}
+				$$ = $1;
+			}
 			;
 
 reset_cmd		:	COUNTERS	ruleset_spec
diff --git a/src/rule.c b/src/rule.c
index dcf1646a9c7c..04b3451e917c 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2364,6 +2364,17 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
 	return 0;
 }
 
+static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+	const char *devname = cmd->handle.obj.name;
+	int hooknum = -1;
+
+	if (cmd->handle.chain.name)
+		hooknum = cmd->handle.chain_id;
+
+	return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname);
+}
+
 static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 {
 	struct table *table = NULL;
@@ -2424,6 +2435,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 		return do_list_flowtable(ctx, cmd, table);
 	case CMD_OBJ_FLOWTABLES:
 		return do_list_flowtables(ctx, cmd);
+	case CMD_OBJ_HOOKS:
+		return do_list_hooks(ctx, cmd);
 	default:
 		BUG("invalid command object type %u\n", cmd->obj);
 	}
diff --git a/src/scanner.l b/src/scanner.l
index c1bc21aa7ecc..6dc1be8908cf 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -354,6 +354,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 	"limits"		{ return LIMITS; }
 	"secmarks"		{ return SECMARKS; }
 	"synproxys"		{ return SYNPROXYS; }
+	"hooks"			{ return HOOKS; }
 }
 
 "counter"		{ scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; }
-- 
2.26.3


  parent reply	other threads:[~2021-05-21 12:09 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-21 12:08 [PATCH nft 0/3] hook dump support Florian Westphal
2021-05-21 12:08 ` [PATCH nft 1/3] scanner: add list cmd parser scope Florian Westphal
2021-05-21 12:08 ` Florian Westphal [this message]
2021-05-21 12:08 ` [PATCH nft 3/3] doc: add LISTING section Florian Westphal
2021-05-21 22:43   ` Duncan Roe

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210521120846.1140-3-fw@strlen.de \
    --to=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.