From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Phil Sutter <phil@nwl.cc>
Cc: netfilter-devel@vger.kernel.org, Florian Westphal <fw@strlen.de>,
Dan Winship <danwinship@redhat.com>
Subject: Re: [nft PATCH] table: Embed creating nft version into userdata
Date: Mon, 11 Aug 2025 20:58:48 +0200 [thread overview]
Message-ID: <aJo9aH0KUxB67dRU@calendula> (raw)
In-Reply-To: <20250808124624.30768-1-phil@nwl.cc>
Hi Phil,
Thanks for your patch, comments below.
On Fri, Aug 08, 2025 at 02:46:18PM +0200, Phil Sutter wrote:
> Upon listing a table which was created by a newer version of nftables,
> warn about the potentially incomplete content.
>
> Suggested-by: Florian Westphal <fw@strlen.de>
> Cc: Dan Winship <danwinship@redhat.com>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> Changes since RFC:
> - Change NFTNL_UDATA_TABLE_NFTVER content from string (PACKAGE_VERSION)
> to 12Byte binary data consisting of:
> - the version 3-tuple
> - a stable release number specified at configure-time
> - the build time in seconds since epoch (a 64bit value - could scrap
> some bytes, but maybe worth leaving some space)
> ---
> .gitignore | 1 +
> Makefile.am | 3 +++
> configure.ac | 22 ++++++++++++++++++++++
> include/nft.h | 2 ++
> include/rule.h | 1 +
> src/mnl.c | 19 +++++++++++++------
> src/netlink.c | 23 ++++++++++++++++++++++-
> src/rule.c | 4 ++++
> 8 files changed, 68 insertions(+), 7 deletions(-)
>
> diff --git a/.gitignore b/.gitignore
> index a62e31f31c6b5..1e3bc5146b2f1 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -14,6 +14,7 @@ autom4te.cache
> build-aux/
> libnftables.pc
> libtool
> +nftversion.h
>
> # cscope files
> /cscope.*
> diff --git a/Makefile.am b/Makefile.am
> index b5580b5451fca..ca6af2e393bed 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -33,6 +33,7 @@ sbin_PROGRAMS =
> check_PROGRAMS =
> dist_man_MANS =
> CLEANFILES =
> +DISTCLEANFILES =
>
> ###############################################################################
>
> @@ -106,6 +107,8 @@ noinst_HEADERS = \
> \
> $(NULL)
>
> +DISTCLEANFILES += nftversion.h
> +
> ###############################################################################
>
> AM_CPPFLAGS = \
> diff --git a/configure.ac b/configure.ac
> index 550913ef04964..2c68c2b8e0560 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -114,6 +114,28 @@ AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r, getservbyport_r], [], [],
> #include <netdb.h>
> ]])
>
> +AC_ARG_WITH([stable-release], [AS_HELP_STRING([--with-stable-release],
> + [Stable release number])],
> + [], [with_stable_release=0])
> +AC_CONFIG_COMMANDS([stable_release],
> + [STABLE_RELEASE=$stable_release],
> + [stable_release=$with_stable_release])
> +AC_CONFIG_COMMANDS([nftversion.h], [
> +(
> + echo "static char nftversion[[]] = {"
> + echo " ${VERSION}," | tr '.' ','
> + echo " ${STABLE_RELEASE},"
> + for ((i = 56; i >= 0; i-= 8)); do
> + echo " ((uint64_t)MAKE_STAMP >> $i) & 0xff,"
> + done
> + echo "};"
> +) >nftversion.h
> +])
> +# Current date should be fetched exactly once per build,
> +# so have 'make' call date and pass the value to every 'gcc' call
> +AC_SUBST([MAKE_STAMP], ["\$(shell date +%s)"])
> +CFLAGS="${CFLAGS} -DMAKE_STAMP=\${MAKE_STAMP}"
> +
> AC_CONFIG_FILES([ \
> Makefile \
> libnftables.pc \
> diff --git a/include/nft.h b/include/nft.h
> index a2d62dbf4808a..b406a68ffeb18 100644
> --- a/include/nft.h
> +++ b/include/nft.h
> @@ -15,4 +15,6 @@
> * something we frequently need to do and it's intentional. */
> #define free_const(ptr) free((void *)(ptr))
>
> +#define NFTNL_UDATA_TABLE_NFTVER 1
Better define this in libnftnl?
libnftnl$ git grep NFTNL_UDATA_TABLE_COMMENT
include/libnftnl/udata.h: NFTNL_UDATA_TABLE_COMMENT,
> #endif /* NFTABLES_NFT_H */
> diff --git a/include/rule.h b/include/rule.h
> index 470ae10754ba5..319f9c39f1107 100644
> --- a/include/rule.h
> +++ b/include/rule.h
> @@ -170,6 +170,7 @@ struct table {
> uint32_t owner;
> const char *comment;
> bool has_xt_stmts;
> + bool is_from_future;
> };
>
> extern struct table *table_alloc(void);
> diff --git a/src/mnl.c b/src/mnl.c
> index 43229f2498e55..67ec60a6fee00 100644
> --- a/src/mnl.c
> +++ b/src/mnl.c
> @@ -10,6 +10,7 @@
>
> #include <nft.h>
> #include <iface.h>
> +#include <nftversion.h>
>
> #include <libmnl/libmnl.h>
> #include <libnftnl/common.h>
> @@ -1074,24 +1075,30 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
> if (nlt == NULL)
> memory_allocation_error();
>
> + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
> + if (!udbuf)
> + memory_allocation_error();
> +
> nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
> if (cmd->table) {
> nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags);
>
> if (cmd->table->comment) {
> - udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
> - if (!udbuf)
> - memory_allocation_error();
> if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_TABLE_COMMENT, cmd->table->comment))
> memory_allocation_error();
> - nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, nftnl_udata_buf_data(udbuf),
> - nftnl_udata_buf_len(udbuf));
I suggest two separated TLVs, one for version and another for build time.
> - nftnl_udata_buf_free(udbuf);
> }
> } else {
> nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, 0);
> }
>
> + if (!nftnl_udata_put(udbuf, NFTNL_UDATA_TABLE_NFTVER,
> + sizeof(nftversion), nftversion))
> + memory_allocation_error();
> + nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA,
> + nftnl_udata_buf_data(udbuf),
> + nftnl_udata_buf_len(udbuf));
> + nftnl_udata_buf_free(udbuf);
> +
> nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
> NFT_MSG_NEWTABLE,
> cmd->handle.family,
> diff --git a/src/netlink.c b/src/netlink.c
> index 94cbcbfc6c094..97a49c08b1e82 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -10,6 +10,7 @@
> */
>
> #include <nft.h>
> +#include <nftversion.h>
>
> #include <errno.h>
> #include <libmnl/libmnl.h>
> @@ -799,6 +800,10 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
> if (value[len - 1] != '\0')
> return -1;
> break;
> + case NFTNL_UDATA_TABLE_NFTVER:
> + if (len != sizeof(nftversion))
> + return -1;
> + break;
> default:
> return 0;
> }
> @@ -806,10 +811,23 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
> return 0;
> }
>
> +static int version_cmp(const struct nftnl_udata *ud)
> +{
> + const char *udbuf = nftnl_udata_get(ud);
> + size_t i;
> +
> + /* udbuf length checked by table_parse_udata_cb() */
> + for (i = 0; i < sizeof(nftversion); i++) {
> + if (nftversion[i] != udbuf[i])
> + return nftversion[i] - udbuf[i];
> + }
Interesting trick.
> + return 0;
> +}
> +
> struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
> const struct nftnl_table *nlt)
> {
> - const struct nftnl_udata *ud[NFTNL_UDATA_TABLE_MAX + 1] = {};
> + const struct nftnl_udata *ud[NFTNL_UDATA_TABLE_MAX + 2] = {};
> struct table *table;
> const char *udata;
> uint32_t ulen;
> @@ -830,6 +848,9 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
> }
> if (ud[NFTNL_UDATA_TABLE_COMMENT])
> table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT]));
> + if (ud[NFTNL_UDATA_TABLE_NFTVER] &&
> + version_cmp(ud[NFTNL_UDATA_TABLE_NFTVER]) < 0)
> + table->is_from_future = true;
> }
>
> return table;
> diff --git a/src/rule.c b/src/rule.c
> index 0ad948ea87f2f..fd69c622cfe75 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -1298,6 +1298,10 @@ static void table_print(const struct table *table, struct output_ctx *octx)
> fprintf(octx->error_fp,
> "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
> family, table->handle.table.name);
> + if (table->is_from_future)
> + fprintf(octx->error_fp,
> + "# Warning: table %s %s was created by a newer version of nftables, content may be incomplete!\n",
+ "# Warning: this table %s %s was created by a newer version of nftables? Content may be incomplete!\n",
Maybe rise it as a question? This is just an indication, I was
considering you could write a program to push anything in the userdata
area. But not a deal breaker if you prefer this sentence.
> + family, table->handle.table.name);
>
> nft_print(octx, "table %s %s {", family, table->handle.table.name);
> if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
> --
> 2.49.0
>
next prev parent reply other threads:[~2025-08-11 18:58 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-08 12:46 [nft PATCH] table: Embed creating nft version into userdata Phil Sutter
2025-08-11 18:58 ` Pablo Neira Ayuso [this message]
2025-08-12 13:06 ` Phil Sutter
2025-08-12 13:17 ` Pablo Neira Ayuso
-- strict thread matches above, loose matches on Subject: below --
2025-08-13 17:07 Phil Sutter
2025-08-13 17:21 ` Phil Sutter
2025-08-27 22:46 ` Pablo Neira Ayuso
2025-08-28 10:48 ` Phil Sutter
2025-08-28 12:53 ` Pablo Neira Ayuso
2025-08-28 13:53 ` Phil Sutter
2025-08-28 14:45 ` Pablo Neira Ayuso
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=aJo9aH0KUxB67dRU@calendula \
--to=pablo@netfilter.org \
--cc=danwinship@redhat.com \
--cc=fw@strlen.de \
--cc=netfilter-devel@vger.kernel.org \
--cc=phil@nwl.cc \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).