netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [nft PATCH] table: Embed creating nft version into userdata
@ 2025-08-13 17:07 Phil Sutter
  2025-08-13 17:21 ` Phil Sutter
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Phil Sutter @ 2025-08-13 17:07 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Dan Winship

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 v1:
- Keep version and build timestamp separate in nftversion.h (separate
  variables) and netlink (separate attributes)
- Drop NFTNL_UDATA_TABLE_NFTVER define, rely upon updated libnftnl
  headers instead (we need a correct value in NFTNL_UDATA_TABLE_MAX as
  well, so no point in introducing hacks)
- Perform attribute existence checks in version_cmp() itself, kernel
  data may contain one or the other or both or none at all
- Minor warning adjustment as requested

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   | 24 ++++++++++++++++++++++++
 include/rule.h |  1 +
 src/mnl.c      | 21 +++++++++++++++------
 src/netlink.c  | 33 +++++++++++++++++++++++++++++++++
 src/rule.c     |  4 ++++
 7 files changed, 81 insertions(+), 6 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..55fecbc56660a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,30 @@ 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}"
+	echo "};"
+	echo "static char nftbuildstamp[[]] = {"
+	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/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..ec4d73d12460a 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,32 @@ 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));
-			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) ||
+	    !nftnl_udata_put(udbuf, NFTNL_UDATA_TABLE_NFTBLD,
+	                     sizeof(nftbuildstamp), nftbuildstamp))
+		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..b5da33e5a1e53 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,14 @@ 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;
+		case NFTNL_UDATA_TABLE_NFTBLD:
+			if (len != sizeof(nftbuildstamp))
+				return -1;
+			break;
 		default:
 			return 0;
 	}
@@ -806,6 +815,29 @@ 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;
+	size_t i;
+
+	/* netlink attribute lengths checked by table_parse_udata_cb() */
+	if (ud[NFTNL_UDATA_TABLE_NFTVER]) {
+		udbuf = nftnl_udata_get(ud[NFTNL_UDATA_TABLE_NFTVER]);
+		for (i = 0; i < sizeof(nftversion); i++) {
+			if (nftversion[i] != udbuf[i])
+				return nftversion[i] - udbuf[i];
+		}
+	}
+	if (ud[NFTNL_UDATA_TABLE_NFTBLD]) {
+		udbuf = nftnl_udata_get(ud[NFTNL_UDATA_TABLE_NFTBLD]);
+		for (i = 0; i < sizeof(nftbuildstamp); i++) {
+			if (nftbuildstamp[i] != udbuf[i])
+				return nftbuildstamp[i] - udbuf[i];
+		}
+	}
+	return 0;
+}
+
 struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
 					const struct nftnl_table *nlt)
 {
@@ -830,6 +862,7 @@ 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]));
+		table->is_from_future = version_cmp(ud) < 0;
 	}
 
 	return table;
diff --git a/src/rule.c b/src/rule.c
index 0ad948ea87f2f..398e5bdd4c64e 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",
+			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


^ permalink raw reply related	[flat|nested] 11+ messages in thread
* [nft PATCH] table: Embed creating nft version into userdata
@ 2025-08-08 12:46 Phil Sutter
  2025-08-11 18:58 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 11+ messages in thread
From: Phil Sutter @ 2025-08-08 12:46 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel, Florian Westphal, Dan Winship

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
+
 #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));
-			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];
+	}
+	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",
+			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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-08-28 14:45 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-13 17:07 [nft PATCH] table: Embed creating nft version into userdata 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
  -- strict thread matches above, loose matches on Subject: below --
2025-08-08 12:46 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 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).