summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhil Sutter <phil@nwl.cc>2024-10-04 21:23:25 +0200
committerPhil Sutter <phil@nwl.cc>2025-04-10 18:45:45 +0200
commitff5f6a208efccd4e2bd8d4fdaf5d284ce54ff69e (patch)
tree61dc0b52d8e7812eabb044719b7f4e9a1c2074e1
parentf6f0f4f55794a5f1add6f728f80f29f12f36ecd5 (diff)
nft-ruleparse: Fallback to compat expressions in userdata
If parsing of a rule fails (e.g. due to an unknown native expression), check if userdata contains a UDATA_TYPE_COMPAT_EXT attribute and retry parsing the rule preferring the contained extensions instead of native expressions. Signed-off-by: Phil Sutter <phil@nwl.cc>
-rw-r--r--configure.ac9
-rw-r--r--iptables/Makefile.am1
-rw-r--r--iptables/nft-compat.c148
-rw-r--r--iptables/nft-compat.h29
-rw-r--r--iptables/nft-ruleparse.c17
5 files changed, 204 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 0106b316..d42588c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,14 @@ AC_ARG_WITH([xt-lock-name], AS_HELP_STRING([--with-xt-lock-name=PATH],
AC_ARG_ENABLE([profiling],
AS_HELP_STRING([--enable-profiling], [build for use of gcov/gprof]),
[enable_profiling="$enableval"], [enable_profiling="no"])
+AC_ARG_WITH([zlib], [AS_HELP_STRING([--without-zlib],
+ [Disable payload compression of rule compat expressions])],
+ [], [with_zlib=yes])
+AS_IF([test "x$with_zlib" != xno], [
+ AC_CHECK_LIB([z], [compress], ,
+ AC_MSG_ERROR([No suitable version of zlib found]))
+ AC_DEFINE([HAVE_ZLIB], [1], [Define if you have zlib])
+])
AC_MSG_CHECKING([whether $LD knows -Wl,--no-undefined])
saved_LDFLAGS="$LDFLAGS";
@@ -289,6 +297,7 @@ Iptables Configuration:
nftables support: ${enable_nftables}
connlabel support: ${enable_connlabel}
profiling support: ${enable_profiling}
+ compress rule compat expressions: ${with_zlib}
Build parameters:
Put plugins into executable (static): ${enable_static}
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 2007cd10..4855c9a7 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -57,6 +57,7 @@ xtables_nft_multi_SOURCES += nft.c nft.h \
nft-ruleparse-arp.c nft-ruleparse-bridge.c \
nft-ruleparse-ipv4.c nft-ruleparse-ipv6.c \
nft-shared.c nft-shared.h \
+ nft-compat.c nft-compat.h \
xtables-monitor.c \
xtables.c xtables-arp.c xtables-eb.c \
xtables-standalone.c xtables-eb-standalone.c \
diff --git a/iptables/nft-compat.c b/iptables/nft-compat.c
new file mode 100644
index 00000000..1edf0885
--- /dev/null
+++ b/iptables/nft-compat.c
@@ -0,0 +1,148 @@
+/*
+ * (C) 2024 Red Hat GmbH
+ * Author: Phil Sutter <phil@nwl.cc>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "config.h"
+#include "nft-compat.h"
+#include "nft-ruleparse.h"
+#include "nft.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <xtables.h>
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#include <libnftnl/udata.h>
+
+static struct rule_udata_ext *
+rule_get_udata_ext(const struct nftnl_rule *r, uint32_t *outlen)
+{
+ const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
+ struct nftnl_udata_buf *udata;
+ uint32_t udatalen;
+
+ udata = (void *)nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &udatalen);
+ if (!udata)
+ return NULL;
+
+ if (nftnl_udata_parse(udata, udatalen, parse_udata_cb, tb) < 0)
+ return NULL;
+
+ if (!tb[UDATA_TYPE_COMPAT_EXT])
+ return NULL;
+
+ if (outlen)
+ *outlen = nftnl_udata_len(tb[UDATA_TYPE_COMPAT_EXT]);
+ return nftnl_udata_get(tb[UDATA_TYPE_COMPAT_EXT]);
+}
+
+static struct nftnl_expr *
+__nftnl_expr_from_udata_ext(struct rule_udata_ext *rue, const void *data)
+{
+ struct nftnl_expr *expr = NULL;
+
+ switch (rue->flags & RUE_FLAG_TYPE_BITS) {
+ case RUE_FLAG_MATCH_TYPE:
+ expr = nftnl_expr_alloc("match");
+ __add_match(expr, data);
+ break;
+ case RUE_FLAG_TARGET_TYPE:
+ expr = nftnl_expr_alloc("target");
+ __add_target(expr, data);
+ break;
+ default:
+ fprintf(stderr,
+ "Warning: Unexpected udata extension type %d\n",
+ rue->flags & RUE_FLAG_TYPE_BITS);
+ }
+
+ return expr;
+}
+
+static struct nftnl_expr *
+nftnl_expr_from_zipped_udata_ext(struct rule_udata_ext *rue)
+{
+#ifdef HAVE_ZLIB
+ uLongf datalen = rue->orig_size;
+ struct nftnl_expr *expr = NULL;
+ void *data;
+
+ data = xtables_malloc(datalen);
+ if (uncompress(data, &datalen, rue->data, rue->size) != Z_OK) {
+ fprintf(stderr, "Warning: Failed to uncompress rule udata extension\n");
+ goto out;
+ }
+
+ expr = __nftnl_expr_from_udata_ext(rue, data);
+out:
+ free(data);
+ return expr;
+#else
+ fprintf(stderr, "Warning: Zipped udata extensions are not supported.\n");
+ return NULL;
+#endif
+}
+
+static struct nftnl_expr *nftnl_expr_from_udata_ext(struct rule_udata_ext *rue)
+{
+ if (rue->flags & RUE_FLAG_ZIP)
+ return nftnl_expr_from_zipped_udata_ext(rue);
+ else
+ return __nftnl_expr_from_udata_ext(rue, rue->data);
+}
+
+bool rule_has_udata_ext(const struct nftnl_rule *r)
+{
+ return rule_get_udata_ext(r, NULL) != NULL;
+}
+
+#define rule_udata_ext_foreach(rue, ext, extlen) \
+ for (rue = (void *)(ext); \
+ (char *)rue < (char *)(ext) + extlen; \
+ rue = (void *)((char *)rue + sizeof(*rue) + rue->size))
+
+bool rule_parse_udata_ext(struct nft_xt_ctx *ctx, const struct nftnl_rule *r)
+{
+ struct rule_udata_ext *rue;
+ struct nftnl_expr *expr;
+ uint32_t extlen;
+ bool ret = true;
+ int eidx = 0;
+ void *ext;
+
+ ext = rule_get_udata_ext(r, &extlen);
+ if (!ext)
+ return false;
+
+ rule_udata_ext_foreach(rue, ext, extlen) {
+ for (; eidx < rue->start_idx; eidx++) {
+ expr = nftnl_expr_iter_next(ctx->iter);
+ if (!nft_parse_rule_expr(ctx->h, expr, ctx))
+ ret = false;
+ }
+
+ expr = nftnl_expr_from_udata_ext(rue);
+ if (!nft_parse_rule_expr(ctx->h, expr, ctx))
+ ret = false;
+ nftnl_expr_free(expr);
+
+ for (; eidx < rue->end_idx; eidx++)
+ nftnl_expr_iter_next(ctx->iter);
+ }
+ expr = nftnl_expr_iter_next(ctx->iter);
+ while (expr != NULL) {
+ if (!nft_parse_rule_expr(ctx->h, expr, ctx))
+ ret = false;
+ expr = nftnl_expr_iter_next(ctx->iter);
+ }
+ return ret;
+}
+
diff --git a/iptables/nft-compat.h b/iptables/nft-compat.h
new file mode 100644
index 00000000..1147f08a
--- /dev/null
+++ b/iptables/nft-compat.h
@@ -0,0 +1,29 @@
+#ifndef _NFT_COMPAT_H_
+#define _NFT_COMPAT_H_
+
+#include <libnftnl/rule.h>
+
+#include <linux/netfilter/x_tables.h>
+
+enum rule_udata_ext_flags {
+ RUE_FLAG_MATCH_TYPE = (1 << 0),
+ RUE_FLAG_TARGET_TYPE = (1 << 1),
+ RUE_FLAG_ZIP = (1 << 7),
+};
+#define RUE_FLAG_TYPE_BITS (RUE_FLAG_MATCH_TYPE | RUE_FLAG_TARGET_TYPE)
+
+struct rule_udata_ext {
+ uint8_t start_idx;
+ uint8_t end_idx;
+ uint8_t flags;
+ uint16_t orig_size;
+ uint16_t size;
+ unsigned char data[];
+};
+
+struct nft_xt_ctx;
+
+bool rule_has_udata_ext(const struct nftnl_rule *r);
+bool rule_parse_udata_ext(struct nft_xt_ctx *ctx, const struct nftnl_rule *r);
+
+#endif /* _NFT_COMPAT_H_ */
diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
index 757d3c29..34270e46 100644
--- a/iptables/nft-ruleparse.c
+++ b/iptables/nft-ruleparse.c
@@ -10,6 +10,7 @@
* This code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
+#include "config.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -27,6 +28,7 @@
#include <xtables.h>
+#include "nft-compat.h"
#include "nft-ruleparse.h"
#include "nft.h"
@@ -948,6 +950,21 @@ bool nft_rule_to_iptables_command_state(struct nft_handle *h,
ret = false;
expr = nftnl_expr_iter_next(ctx.iter);
}
+ if (!ret && rule_has_udata_ext(r)) {
+ fprintf(stderr,
+ "Warning: Rule parser failed, trying compat fallback\n");
+
+ h->ops->clear_cs(cs);
+ if (h->ops->init_cs)
+ h->ops->init_cs(cs);
+
+ nftnl_expr_iter_destroy(ctx.iter);
+ ctx.iter = nftnl_expr_iter_create(r);
+ if (!ctx.iter)
+ return false;
+
+ ret = rule_parse_udata_ext(&ctx, r);
+ }
nftnl_expr_iter_destroy(ctx.iter);