]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft-ruleparse: Fallback to compat expressions in userdata
authorPhil Sutter <phil@nwl.cc>
Fri, 4 Oct 2024 19:23:25 +0000 (21:23 +0200)
committerPhil Sutter <phil@nwl.cc>
Thu, 10 Apr 2025 16:45:45 +0000 (18:45 +0200)
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
iptables/Makefile.am
iptables/nft-compat.c [new file with mode: 0644]
iptables/nft-compat.h [new file with mode: 0644]
iptables/nft-ruleparse.c

index 0106b3165a25c2a1eb410c54811d917cb10385cc..d42588c8a4b6d8dde79a2cb7be2b339635a6a665 100644 (file)
@@ -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}
index 2007cd10260bd7259bd0eec61f6c590732633853..4855c9a7c2911be173fcafb6f095eb73e935e058 100644 (file)
@@ -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 (file)
index 0000000..1edf088
--- /dev/null
@@ -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 (file)
index 0000000..1147f08
--- /dev/null
@@ -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_ */
index 757d3c29fc8166f2f14e320f80ae9445f4c07a24..34270e46ae888a17e724ad9f4950cbdca060dd53 100644 (file)
@@ -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);