From 50afb84d7064806ad7acc8364455062fc0751528 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 9 Sep 2025 16:49:27 +0200 Subject: [PATCH] table: Embed creating nft version into userdata JIRA: https://issues.redhat.com/browse/RHEL-108851 Upstream Status: nftables commit 64c07e38f0494093a399a68a31056f5866c4d705 Conflicts: Context change due to missing --with-unitdir option. commit 64c07e38f0494093a399a68a31056f5866c4d705 Author: Phil Sutter Date: Mon May 12 22:59:26 2025 +0200 table: Embed creating nft version into userdata Upon listing a table which was created by a newer version of nftables, warn about the potentially incomplete content. Suggested-by: Florian Westphal Cc: Dan Winship Signed-off-by: Phil Sutter Acked-by: Pablo Neira Ayuso Signed-off-by: Phil Sutter --- Makefile.am | 3 +++ configure.ac | 24 ++++++++++++++++++++++++ include/rule.h | 1 + src/mnl.c | 21 +++++++++++++++------ src/netlink.c | 33 +++++++++++++++++++++++++++++++++ src/rule.c | 4 ++++ 6 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index ba09e7f..c2a6908 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ sbin_PROGRAMS = check_PROGRAMS = dist_man_MANS = CLEANFILES = +DISTCLEANFILES = ############################################################################### @@ -105,6 +106,8 @@ noinst_HEADERS = \ \ $(NULL) +DISTCLEANFILES += nftversion.h + ############################################################################### AM_CPPFLAGS = \ diff --git a/configure.ac b/configure.ac index 816e920..bac8319 100644 --- a/configure.ac +++ b/configure.ac @@ -114,6 +114,30 @@ AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r, getservbyport_r], [], [], #include ]]) +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 238be23..1b52972 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 12a6345..e748ab6 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -1054,24 +1055,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 2ced863..7f9730d 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -728,6 +729,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; } @@ -735,6 +744,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) { @@ -759,6 +791,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 151ed53..e4d6f53 100644 --- a/src/rule.c +++ b/src/rule.c @@ -1274,6 +1274,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)