@@ -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 @@ echo "
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}
@@ -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 \
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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_ */
@@ -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);
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> --- configure.ac | 9 +++ iptables/Makefile.am | 1 + iptables/nft-compat.c | 148 +++++++++++++++++++++++++++++++++++++++ iptables/nft-compat.h | 29 ++++++++ iptables/nft-ruleparse.c | 17 +++++ 5 files changed, 204 insertions(+) create mode 100644 iptables/nft-compat.c create mode 100644 iptables/nft-compat.h