All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Phil Sutter <phil@nwl.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: Tue, 12 Aug 2025 15:17:52 +0200	[thread overview]
Message-ID: <aJs_ACvoIndH-vdP@calendula> (raw)
In-Reply-To: <aJs8VtYSwB0V9hRz@orbyte.nwl.cc>

On Tue, Aug 12, 2025 at 03:06:30PM +0200, Phil Sutter wrote:
> Hi Pablo,
> 
> On Mon, Aug 11, 2025 at 08:58:48PM +0200, Pablo Neira Ayuso wrote:
> > Thanks for your patch, comments below.
> 
> Thanks for your review! (Replies 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?
> 
> ACK, you found my "quick hack", sorry for that.

If you prefer easier backporting, then add an #ifdef here to skip the
libnftnl dependency.

> > 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.
> 
> This seems wrong to me. Build time is just an extra to the version info.
> What benefit do you see with splitting them up?

It is the same as in Netlink. If someone shows up saying built time
and version is not enough, a new attribute should be sufficient.

I cannot see how that can happen, but I have regretted everytime I
added TLVs that embed multiple fields to save a header.

> > > -			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.
> 
> There's a German proverb about a blind hen and grains. But I like that
> function, too! :)

We don't have such proverb in Spanish :)

> > > +	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.
> 
> Fine with me!

Thanks.

  reply	other threads:[~2025-08-12 13:17 UTC|newest]

Thread overview: 13+ 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
2025-08-12 13:06   ` Phil Sutter
2025-08-12 13:17     ` Pablo Neira Ayuso [this message]
  -- 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
2025-09-08 12:03 ` Pablo Neira Ayuso
2025-09-08 12:07   ` 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=aJs_ACvoIndH-vdP@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 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.