Linux Netfilter development
 help / color / mirror / Atom feed
* [iptables PATCH 0/3] Extract nftnl_rule parsing code
@ 2023-04-21 17:40 Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h} Phil Sutter
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Phil Sutter @ 2023-04-21 17:40 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Florian Westphal

nft-shared.c was already oversized, upcoming enhancement of the
nftnl_rule parser will add to that. So prepare any further work in that
field by creating a common 'nft-ruleparse.c' source and one for each
family to hold all the parsing code (basically the stack below
nft_rule_to_iptables_command_state).

Collect the existing expression parsing callbacks in a new struct
nft_ruleparse_ops and add a pointer to it into nft_family_ops. This way
the callbacks may be static and the nft-ruleparse-<family>.c sources
only export their ops object.

This series does things somewhat gradually:

* First pull everything from nft-shared.c into nft-ruleparse.c (likewise
  with header files)
* Then perform the *_ops struct changes which should not have a
  functional implication
* Finally weed parsers from nft-<family>.c files into
  nft-ruleparse-<family>.c ones.

Phil Sutter (3):
  nft: Introduce nft-ruleparse.{c,h}
  nft: Extract rule parsing callbacks from nft_family_ops
  nft: ruleparse: Create family-specific source files

 iptables/Makefile.am            |    3 +
 iptables/nft-arp.c              |  139 +---
 iptables/nft-bridge.c           |  390 +---------
 iptables/nft-cache.h            |    2 +
 iptables/nft-ipv4.c             |  106 +--
 iptables/nft-ipv6.c             |   83 +--
 iptables/nft-ruleparse-arp.c    |  168 +++++
 iptables/nft-ruleparse-bridge.c |  422 +++++++++++
 iptables/nft-ruleparse-ipv4.c   |  135 ++++
 iptables/nft-ruleparse-ipv6.c   |  112 +++
 iptables/nft-ruleparse.c        | 1208 +++++++++++++++++++++++++++++++
 iptables/nft-ruleparse.h        |  138 ++++
 iptables/nft-shared.c           | 1190 ------------------------------
 iptables/nft-shared.h           |  115 +--
 14 files changed, 2194 insertions(+), 2017 deletions(-)
 create mode 100644 iptables/nft-ruleparse-arp.c
 create mode 100644 iptables/nft-ruleparse-bridge.c
 create mode 100644 iptables/nft-ruleparse-ipv4.c
 create mode 100644 iptables/nft-ruleparse-ipv6.c
 create mode 100644 iptables/nft-ruleparse.c
 create mode 100644 iptables/nft-ruleparse.h

-- 
2.40.0


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

* [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h}
  2023-04-21 17:40 [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
@ 2023-04-21 17:40 ` Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 2/3] nft: Extract rule parsing callbacks from nft_family_ops Phil Sutter
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Phil Sutter @ 2023-04-21 17:40 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Florian Westphal

Extract all code dealing with parsing from struct nftnl_rule into struct
iptables_command_state from nft-shared.c into a separate source file.

Basically this is nft_rule_to_iptables_command_state() and the functions
it calls, plus family-independent parsers called from family-specific
callbacks.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 iptables/Makefile.am     |    1 +
 iptables/nft-ruleparse.c | 1208 ++++++++++++++++++++++++++++++++++++++
 iptables/nft-ruleparse.h |  117 ++++
 iptables/nft-shared.c    | 1190 -------------------------------------
 iptables/nft-shared.h    |  101 +---
 5 files changed, 1327 insertions(+), 1290 deletions(-)
 create mode 100644 iptables/nft-ruleparse.c
 create mode 100644 iptables/nft-ruleparse.h

diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 1f37640f263c9..d5922da6a2d84 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -46,6 +46,7 @@ xtables_nft_multi_SOURCES += nft.c nft.h \
 			     nft-cache.c nft-cache.h \
 			     nft-chain.c nft-chain.h \
 			     nft-cmd.c nft-cmd.h \
+			     nft-ruleparse.c nft-ruleparse.h \
 			     nft-shared.c nft-shared.h \
 			     xtables-monitor.c \
 			     xtables.c xtables-arp.c xtables-eb.c \
diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
new file mode 100644
index 0000000000000..2d84241a16819
--- /dev/null
+++ b/iptables/nft-ruleparse.c
@@ -0,0 +1,1208 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/netfilter/nf_log.h>
+#include <linux/netfilter/xt_limit.h>
+#include <linux/netfilter/xt_mark.h>
+#include <linux/netfilter/xt_NFLOG.h>
+#include <linux/netfilter/xt_pkttype.h>
+
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <xtables.h>
+
+#include "nft-ruleparse.h"
+#include "nft.h"
+
+static struct xtables_match *
+nft_find_match_in_cs(struct iptables_command_state *cs, const char *name)
+{
+	struct xtables_rule_match *rm;
+	struct ebt_match *ebm;
+
+	for (ebm = cs->match_list; ebm; ebm = ebm->next) {
+		if (ebm->ismatch &&
+		    !strcmp(ebm->u.match->m->u.user.name, name))
+			return ebm->u.match;
+	}
+	for (rm = cs->matches; rm; rm = rm->next) {
+		if (!strcmp(rm->match->m->u.user.name, name))
+			return rm->match;
+	}
+	return NULL;
+}
+
+void *
+nft_create_match(struct nft_xt_ctx *ctx,
+		 struct iptables_command_state *cs,
+		 const char *name, bool reuse)
+{
+	struct xtables_match *match;
+	struct xt_entry_match *m;
+	unsigned int size;
+
+	if (reuse) {
+		match = nft_find_match_in_cs(cs, name);
+		if (match)
+			return match->m->data;
+	}
+
+	match = xtables_find_match(name, XTF_TRY_LOAD,
+				   &cs->matches);
+	if (!match)
+		return NULL;
+
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+	m = xtables_calloc(1, size);
+	m->u.match_size = size;
+	m->u.user.revision = match->revision;
+
+	strcpy(m->u.user.name, match->name);
+	match->m = m;
+
+	xs_init_match(match);
+
+	if (ctx->h->ops->parse_match)
+		ctx->h->ops->parse_match(match, cs);
+
+	return match->m->data;
+}
+
+static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
+{
+	counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
+	counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
+}
+
+static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
+	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum);
+
+	if (!reg)
+		return;
+
+	reg->type = NFT_XT_REG_PAYLOAD;
+	reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
+	reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
+	reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
+}
+
+static bool nft_parse_meta_set_common(struct nft_xt_ctx* ctx,
+				      struct nft_xt_ctx_reg *sreg)
+{
+	if ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
+		ctx->errmsg = "meta sreg is not an immediate";
+		return false;
+	}
+
+	if (sreg->immediate.data[0] == 0) {
+		ctx->errmsg = "meta sreg immediate is 0";
+		return false;
+	}
+
+	return true;
+}
+
+static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
+			       struct nftnl_expr *e)
+{
+	struct xtables_target *target;
+	struct nft_xt_ctx_reg *sreg;
+	enum nft_registers sregnum;
+	struct xt_entry_target *t;
+	unsigned int size;
+	const char *targname;
+
+	sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG);
+	sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
+	if (!sreg)
+		return;
+
+	switch (nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY)) {
+	case NFT_META_NFTRACE:
+		if (!nft_parse_meta_set_common(ctx, sreg))
+			return;
+
+		targname = "TRACE";
+		break;
+	case NFT_META_BRI_BROUTE:
+		if (!nft_parse_meta_set_common(ctx, sreg))
+			return;
+
+		ctx->cs->jumpto = "DROP";
+		return;
+	default:
+		ctx->errmsg = "meta sreg key not supported";
+		return;
+	}
+
+	target = xtables_find_target(targname, XTF_TRY_LOAD);
+	if (target == NULL) {
+		ctx->errmsg = "target TRACE not found";
+		return;
+	}
+
+	size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
+
+	t = xtables_calloc(1, size);
+	t->u.target_size = size;
+	t->u.user.revision = target->revision;
+	strcpy(t->u.user.name, targname);
+
+	target->t = t;
+
+	ctx->h->ops->parse_target(target, ctx->cs);
+}
+
+static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+        struct nft_xt_ctx_reg *reg;
+
+	if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
+		nft_parse_meta_set(ctx, e);
+		return;
+	}
+
+	reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG));
+	if (!reg)
+		return;
+
+	reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
+	reg->type = NFT_XT_REG_META_DREG;
+}
+
+static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
+	enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
+	struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
+	struct nft_xt_ctx_reg *dreg = sreg;
+	const void *data;
+	uint32_t len;
+
+	if (!sreg)
+		return;
+
+	if (sregnum != dregnum) {
+		dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */
+		if (!dreg)
+			return;
+
+		*dreg = *sreg;  /* .. and copy content instead */
+	}
+
+	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
+
+	if (len > sizeof(dreg->bitwise.xor)) {
+		ctx->errmsg = "bitwise xor too large";
+		return;
+	}
+
+	memcpy(dreg->bitwise.xor, data, len);
+
+	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
+
+	if (len > sizeof(dreg->bitwise.mask)) {
+		ctx->errmsg = "bitwise mask too large";
+		return;
+	}
+
+	memcpy(dreg->bitwise.mask, data, len);
+
+	dreg->bitwise.set = true;
+}
+
+static void nft_parse_icmp(struct nft_xt_ctx *ctx,
+			   struct iptables_command_state *cs,
+			   struct nft_xt_ctx_reg *sreg,
+			   uint8_t op, const char *data, size_t dlen)
+{
+	struct ipt_icmp icmp = {
+		.type = UINT8_MAX,
+		.code = { 0, UINT8_MAX },
+	}, *icmpp;
+
+	if (dlen < 1)
+		goto out_err_len;
+
+	switch (sreg->payload.offset) {
+	case 0:
+		icmp.type = data[0];
+		if (dlen == 1)
+			break;
+		dlen--;
+		data++;
+		/* fall through */
+	case 1:
+		if (dlen > 1)
+			goto out_err_len;
+		icmp.code[0] = icmp.code[1] = data[0];
+		break;
+	default:
+		ctx->errmsg = "unexpected payload offset";
+		return;
+	}
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		icmpp = nft_create_match(ctx, cs, "icmp", false);
+		break;
+	case NFPROTO_IPV6:
+		if (icmp.type == UINT8_MAX) {
+			ctx->errmsg = "icmp6 code with any type match not supported";
+			return;
+		}
+		icmpp = nft_create_match(ctx, cs, "icmp6", false);
+		break;
+	default:
+		ctx->errmsg = "unexpected family for icmp match";
+		return;
+	}
+
+	if (!icmpp) {
+		ctx->errmsg = "icmp match extension not found";
+		return;
+	}
+	memcpy(icmpp, &icmp, sizeof(icmp));
+	return;
+
+out_err_len:
+	ctx->errmsg = "unexpected RHS data length";
+}
+
+static void port_match_single_to_range(__u16 *ports, __u8 *invflags,
+				       uint8_t op, int port, __u8 invflag)
+{
+	if (port < 0)
+		return;
+
+	switch (op) {
+	case NFT_CMP_NEQ:
+		*invflags |= invflag;
+		/* fallthrough */
+	case NFT_CMP_EQ:
+		ports[0] = port;
+		ports[1] = port;
+		break;
+	case NFT_CMP_LT:
+		ports[1] = max(port - 1, 1);
+		break;
+	case NFT_CMP_LTE:
+		ports[1] = port;
+		break;
+	case NFT_CMP_GT:
+		ports[0] = min(port + 1, UINT16_MAX);
+		break;
+	case NFT_CMP_GTE:
+		ports[0] = port;
+		break;
+	}
+}
+
+static void nft_parse_udp(struct nft_xt_ctx *ctx,
+			  struct iptables_command_state *cs,
+			  int sport, int dport,
+			  uint8_t op)
+{
+	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
+
+	if (!udp) {
+		ctx->errmsg = "udp match extension not found";
+		return;
+	}
+
+	port_match_single_to_range(udp->spts, &udp->invflags,
+				   op, sport, XT_UDP_INV_SRCPT);
+	port_match_single_to_range(udp->dpts, &udp->invflags,
+				   op, dport, XT_UDP_INV_DSTPT);
+}
+
+static void nft_parse_tcp(struct nft_xt_ctx *ctx,
+			  struct iptables_command_state *cs,
+			  int sport, int dport,
+			  uint8_t op)
+{
+	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+	if (!tcp) {
+		ctx->errmsg = "tcp match extension not found";
+		return;
+	}
+
+	port_match_single_to_range(tcp->spts, &tcp->invflags,
+				   op, sport, XT_TCP_INV_SRCPT);
+	port_match_single_to_range(tcp->dpts, &tcp->invflags,
+				   op, dport, XT_TCP_INV_DSTPT);
+}
+
+static void nft_parse_th_port(struct nft_xt_ctx *ctx,
+			      struct iptables_command_state *cs,
+			      uint8_t proto,
+			      int sport, int dport, uint8_t op)
+{
+	switch (proto) {
+	case IPPROTO_UDP:
+		nft_parse_udp(ctx, cs, sport, dport, op);
+		break;
+	case IPPROTO_TCP:
+		nft_parse_tcp(ctx, cs, sport, dport, op);
+		break;
+	default:
+		ctx->errmsg = "unknown layer 4 protocol for TH match";
+	}
+}
+
+static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
+				struct iptables_command_state *cs,
+				uint8_t op, uint8_t flags, uint8_t mask)
+{
+	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+	if (!tcp) {
+		ctx->errmsg = "tcp match extension not found";
+		return;
+	}
+
+	if (op == NFT_CMP_NEQ)
+		tcp->invflags |= XT_TCP_INV_FLAGS;
+	tcp->flg_cmp = flags;
+	tcp->flg_mask = mask;
+}
+
+static void nft_parse_transport(struct nft_xt_ctx *ctx,
+				struct nftnl_expr *e,
+				struct iptables_command_state *cs)
+{
+	struct nft_xt_ctx_reg *sreg;
+	enum nft_registers reg;
+	uint32_t sdport;
+	uint16_t port;
+	uint8_t proto, op;
+	unsigned int len;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		proto = ctx->cs->fw.ip.proto;
+		break;
+	case NFPROTO_IPV6:
+		proto = ctx->cs->fw6.ipv6.proto;
+		break;
+	default:
+		ctx->errmsg = "invalid family for TH match";
+		return;
+	}
+
+	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+
+	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
+	sreg = nft_xt_ctx_get_sreg(ctx, reg);
+	if (!sreg)
+		return;
+
+	if (sreg->type != NFT_XT_REG_PAYLOAD) {
+		ctx->errmsg = "sgreg not payload";
+		return;
+	}
+
+	switch (proto) {
+	case IPPROTO_UDP:
+	case IPPROTO_TCP:
+		break;
+	case IPPROTO_ICMP:
+	case IPPROTO_ICMPV6:
+		nft_parse_icmp(ctx, cs, sreg, op,
+			       nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len),
+			       len);
+		return;
+	default:
+		ctx->errmsg = "unsupported layer 4 protocol value";
+		return;
+	}
+
+	switch(sreg->payload.offset) {
+	case 0: /* th->sport */
+		switch (len) {
+		case 2: /* load sport only */
+			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+			nft_parse_th_port(ctx, cs, proto, port, -1, op);
+			return;
+		case 4: /* load both src and dst port */
+			sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
+			nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
+			return;
+		}
+		break;
+	case 2: /* th->dport */
+		switch (len) {
+		case 2:
+			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+			nft_parse_th_port(ctx, cs, proto, -1, port, op);
+			return;
+		}
+		break;
+	case 13: /* th->flags */
+		if (len == 1 && proto == IPPROTO_TCP) {
+			uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+			uint8_t mask = ~0;
+
+			if (sreg->bitwise.set)
+				memcpy(&mask, &sreg->bitwise.mask, sizeof(mask));
+
+			nft_parse_tcp_flags(ctx, cs, op, flags, mask);
+		}
+		return;
+	}
+}
+
+static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nft_xt_ctx_reg *sreg;
+	uint32_t reg;
+
+	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
+
+	sreg = nft_xt_ctx_get_sreg(ctx, reg);
+	if (!sreg)
+		return;
+
+	switch (sreg->type) {
+	case NFT_XT_REG_UNDEF:
+		ctx->errmsg = "cmp sreg undef";
+		break;
+	case NFT_XT_REG_META_DREG:
+		ctx->h->ops->parse_meta(ctx, sreg, e, ctx->cs);
+		break;
+	case NFT_XT_REG_PAYLOAD:
+		switch (sreg->payload.base) {
+		case NFT_PAYLOAD_LL_HEADER:
+			if (ctx->h->family == NFPROTO_BRIDGE)
+				ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
+			break;
+		case NFT_PAYLOAD_NETWORK_HEADER:
+			ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
+			break;
+		case NFT_PAYLOAD_TRANSPORT_HEADER:
+			nft_parse_transport(ctx, e, ctx->cs);
+			break;
+		}
+
+		break;
+	default:
+		ctx->errmsg = "cmp sreg has unknown type";
+		break;
+	}
+}
+
+static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
+	struct iptables_command_state *cs = ctx->cs;
+	struct xt_entry_target *t;
+	uint32_t size;
+	int verdict;
+
+	if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
+		struct nft_xt_ctx_reg *dreg;
+		const void *imm_data;
+		uint32_t len;
+
+		imm_data = nftnl_expr_get(e, NFTNL_EXPR_IMM_DATA, &len);
+		dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG));
+		if (!dreg)
+			return;
+
+		if (len > sizeof(dreg->immediate.data)) {
+			ctx->errmsg = "oversized immediate data";
+			return;
+		}
+
+		memcpy(dreg->immediate.data, imm_data, len);
+		dreg->immediate.len = len;
+		dreg->type = NFT_XT_REG_IMMEDIATE;
+
+		return;
+	}
+
+	verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
+	/* Standard target? */
+	switch(verdict) {
+	case NF_ACCEPT:
+		if (cs->jumpto && strcmp(ctx->table, "broute") == 0)
+			break;
+		cs->jumpto = "ACCEPT";
+		break;
+	case NF_DROP:
+		cs->jumpto = "DROP";
+		break;
+	case NFT_RETURN:
+		cs->jumpto = "RETURN";
+		break;;
+	case NFT_GOTO:
+		if (ctx->h->ops->set_goto_flag)
+			ctx->h->ops->set_goto_flag(cs);
+		/* fall through */
+	case NFT_JUMP:
+		cs->jumpto = chain;
+		/* fall through */
+	default:
+		return;
+	}
+
+	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+	if (!cs->target) {
+		ctx->errmsg = "verdict extension not found";
+		return;
+	}
+
+	size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
+	t = xtables_calloc(1, size);
+	t->u.target_size = size;
+	t->u.user.revision = cs->target->revision;
+	strcpy(t->u.user.name, cs->jumpto);
+	cs->target->t = t;
+}
+
+static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	uint32_t mt_len;
+	const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
+	const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
+	struct xtables_match *match;
+	struct xtables_rule_match **matches;
+	struct xt_entry_match *m;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+	case NFPROTO_IPV6:
+	case NFPROTO_BRIDGE:
+		matches = &ctx->cs->matches;
+		break;
+	default:
+		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
+			ctx->h->family);
+		exit(EXIT_FAILURE);
+	}
+
+	match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
+	if (match == NULL) {
+		ctx->errmsg = "match extension not found";
+		return;
+	}
+
+	m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
+	memcpy(&m->data, mt_info, mt_len);
+	m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
+	m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
+	strcpy(m->u.user.name, match->name);
+
+	match->m = m;
+
+	if (ctx->h->ops->parse_match != NULL)
+		ctx->h->ops->parse_match(match, ctx->cs);
+}
+
+static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	uint32_t tg_len;
+	const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
+	const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
+	struct xtables_target *target;
+	struct xt_entry_target *t;
+	size_t size;
+
+	target = xtables_find_target(targname, XTF_TRY_LOAD);
+	if (target == NULL) {
+		ctx->errmsg = "target extension not found";
+		return;
+	}
+
+	size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
+
+	t = xtables_calloc(1, size);
+	memcpy(&t->data, targinfo, tg_len);
+	t->u.target_size = size;
+	t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
+	strcpy(t->u.user.name, target->name);
+
+	target->t = t;
+
+	ctx->h->ops->parse_target(target, ctx->cs);
+}
+
+static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
+	__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
+	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
+	struct xt_rateinfo *rinfo;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+	case NFPROTO_IPV6:
+	case NFPROTO_BRIDGE:
+		break;
+	default:
+		fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
+			ctx->h->family);
+		exit(EXIT_FAILURE);
+	}
+
+	rinfo = nft_create_match(ctx, ctx->cs, "limit", false);
+	if (!rinfo) {
+		ctx->errmsg = "limit match extension not found";
+		return;
+	}
+
+	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
+	rinfo->burst = burst;
+}
+
+static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
+			     struct nftnl_expr *e)
+{
+	if (ctx->h->ops->parse_lookup)
+		ctx->h->ops->parse_lookup(ctx, e);
+}
+
+static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	struct xtables_target *target;
+	struct xt_entry_target *t;
+	size_t target_size;
+	/*
+	 * In order to handle the longer log-prefix supported by nft, instead of
+	 * using struct xt_nflog_info, we use a struct with a compatible layout, but
+	 * a larger buffer for the prefix.
+	 */
+	struct xt_nflog_info_nft {
+		__u32 len;
+		__u16 group;
+		__u16 threshold;
+		__u16 flags;
+		__u16 pad;
+		char  prefix[NF_LOG_PREFIXLEN];
+	} info = {
+		.group     = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
+		.threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
+	};
+	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
+		info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
+		info.flags = XT_NFLOG_F_COPY_LEN;
+	}
+	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
+		snprintf(info.prefix, sizeof(info.prefix), "%s",
+			 nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
+
+	target = xtables_find_target("NFLOG", XTF_TRY_LOAD);
+	if (target == NULL) {
+		ctx->errmsg = "NFLOG target extension not found";
+		return;
+	}
+
+	target_size = XT_ALIGN(sizeof(struct xt_entry_target)) +
+		      XT_ALIGN(sizeof(struct xt_nflog_info_nft));
+
+	t = xtables_calloc(1, target_size);
+	t->u.target_size = target_size;
+	strcpy(t->u.user.name, target->name);
+	t->u.user.revision = target->revision;
+
+	target->t = t;
+
+	memcpy(&target->t->data, &info, sizeof(info));
+
+	ctx->h->ops->parse_target(target, ctx->cs);
+}
+
+static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
+			        struct iptables_command_state *cs,
+			        int sport_from, int sport_to,
+			        int dport_from, int dport_to,
+				uint8_t op)
+{
+	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
+
+	if (!udp) {
+		ctx->errmsg = "udp match extension not found";
+		return;
+	}
+
+	if (sport_from >= 0) {
+		switch (op) {
+		case NFT_RANGE_NEQ:
+			udp->invflags |= XT_UDP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_RANGE_EQ:
+			udp->spts[0] = sport_from;
+			udp->spts[1] = sport_to;
+			break;
+		}
+	}
+
+	if (dport_to >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			udp->invflags |= XT_UDP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			udp->dpts[0] = dport_from;
+			udp->dpts[1] = dport_to;
+			break;
+		}
+	}
+}
+
+static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
+			        struct iptables_command_state *cs,
+			        int sport_from, int sport_to,
+			        int dport_from, int dport_to,
+				uint8_t op)
+{
+	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+	if (!tcp) {
+		ctx->errmsg = "tcp match extension not found";
+		return;
+	}
+
+	if (sport_from >= 0) {
+		switch (op) {
+		case NFT_RANGE_NEQ:
+			tcp->invflags |= XT_TCP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_RANGE_EQ:
+			tcp->spts[0] = sport_from;
+			tcp->spts[1] = sport_to;
+			break;
+		}
+	}
+
+	if (dport_to >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			tcp->invflags |= XT_TCP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			tcp->dpts[0] = dport_from;
+			tcp->dpts[1] = dport_to;
+			break;
+		}
+	}
+}
+
+static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
+				    struct iptables_command_state *cs,
+				    uint8_t proto,
+				    int sport_from, int sport_to,
+				    int dport_from, int dport_to, uint8_t op)
+{
+	switch (proto) {
+	case IPPROTO_UDP:
+		nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+		break;
+	case IPPROTO_TCP:
+		nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+		break;
+	}
+}
+
+static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
+				      const struct nft_xt_ctx_reg *sreg,
+				      struct nftnl_expr *e,
+				      struct iptables_command_state *cs)
+{
+	unsigned int len_from, len_to;
+	uint8_t proto, op;
+	uint16_t from, to;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		proto = ctx->cs->fw.ip.proto;
+		break;
+	case NFPROTO_IPV6:
+		proto = ctx->cs->fw6.ipv6.proto;
+		break;
+	default:
+		proto = 0;
+		break;
+	}
+
+	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
+	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
+	if (len_to != len_from || len_to != 2)
+		return;
+
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
+
+	from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
+	to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+
+	switch (sreg->payload.offset) {
+	case 0:
+		nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
+		return;
+	case 2:
+		to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+		nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
+		return;
+	}
+}
+
+static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nft_xt_ctx_reg *sreg;
+	uint32_t reg;
+
+	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
+	sreg = nft_xt_ctx_get_sreg(ctx, reg);
+
+	switch (sreg->type) {
+	case NFT_XT_REG_UNDEF:
+		ctx->errmsg = "range sreg undef";
+		break;
+	case NFT_XT_REG_PAYLOAD:
+		switch (sreg->payload.base) {
+		case NFT_PAYLOAD_TRANSPORT_HEADER:
+			nft_parse_transport_range(ctx, sreg, e, ctx->cs);
+			break;
+		default:
+			ctx->errmsg = "range with unknown payload base";
+			break;
+		}
+		break;
+	default:
+		ctx->errmsg = "range sreg type unsupported";
+		break;
+	}
+}
+
+bool nft_rule_to_iptables_command_state(struct nft_handle *h,
+					const struct nftnl_rule *r,
+					struct iptables_command_state *cs)
+{
+	struct nftnl_expr_iter *iter;
+	struct nftnl_expr *expr;
+	struct nft_xt_ctx ctx = {
+		.cs = cs,
+		.h = h,
+		.table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
+	};
+	bool ret = true;
+
+	iter = nftnl_expr_iter_create(r);
+	if (iter == NULL)
+		return false;
+
+	ctx.iter = iter;
+	expr = nftnl_expr_iter_next(iter);
+	while (expr != NULL) {
+		const char *name =
+			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+		if (strcmp(name, "counter") == 0)
+			nft_parse_counter(expr, &ctx.cs->counters);
+		else if (strcmp(name, "payload") == 0)
+			nft_parse_payload(&ctx, expr);
+		else if (strcmp(name, "meta") == 0)
+			nft_parse_meta(&ctx, expr);
+		else if (strcmp(name, "bitwise") == 0)
+			nft_parse_bitwise(&ctx, expr);
+		else if (strcmp(name, "cmp") == 0)
+			nft_parse_cmp(&ctx, expr);
+		else if (strcmp(name, "immediate") == 0)
+			nft_parse_immediate(&ctx, expr);
+		else if (strcmp(name, "match") == 0)
+			nft_parse_match(&ctx, expr);
+		else if (strcmp(name, "target") == 0)
+			nft_parse_target(&ctx, expr);
+		else if (strcmp(name, "limit") == 0)
+			nft_parse_limit(&ctx, expr);
+		else if (strcmp(name, "lookup") == 0)
+			nft_parse_lookup(&ctx, h, expr);
+		else if (strcmp(name, "log") == 0)
+			nft_parse_log(&ctx, expr);
+		else if (strcmp(name, "range") == 0)
+			nft_parse_range(&ctx, expr);
+
+		if (ctx.errmsg) {
+			fprintf(stderr, "Error: %s\n", ctx.errmsg);
+			ctx.errmsg = NULL;
+			ret = false;
+		}
+
+		expr = nftnl_expr_iter_next(iter);
+	}
+
+	nftnl_expr_iter_destroy(iter);
+
+	if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
+		const void *data;
+		uint32_t len, size;
+		const char *comment;
+
+		data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
+		comment = get_comment(data, len);
+		if (comment) {
+			struct xtables_match *match;
+			struct xt_entry_match *m;
+
+			match = xtables_find_match("comment", XTF_TRY_LOAD,
+						   &cs->matches);
+			if (match == NULL)
+				return false;
+
+			size = XT_ALIGN(sizeof(struct xt_entry_match))
+				+ match->size;
+			m = xtables_calloc(1, size);
+
+			strncpy((char *)m->data, comment, match->size - 1);
+			m->u.match_size = size;
+			m->u.user.revision = 0;
+			strcpy(m->u.user.name, match->name);
+
+			match->m = m;
+		}
+	}
+
+	if (!cs->jumpto)
+		cs->jumpto = "";
+
+	if (!ret)
+		xtables_error(VERSION_PROBLEM, "Parsing nftables rule failed");
+	return ret;
+}
+
+static void parse_ifname(const char *name, unsigned int len,
+			 char *dst, unsigned char *mask)
+{
+	if (len == 0)
+		return;
+
+	memcpy(dst, name, len);
+	if (name[len - 1] == '\0') {
+		if (mask)
+			memset(mask, 0xff, strlen(name) + 1);
+		return;
+	}
+
+	if (len >= IFNAMSIZ)
+		return;
+
+	/* wildcard */
+	dst[len++] = '+';
+	if (len >= IFNAMSIZ)
+		return;
+	dst[len++] = 0;
+	if (mask)
+		memset(mask, 0xff, len - 2);
+}
+
+static void parse_invalid_iface(char *iface, unsigned char *mask,
+				uint8_t *invflags, uint8_t invbit)
+{
+	if (*invflags & invbit || strcmp(iface, "INVAL/D"))
+		return;
+
+	/* nft's poor "! -o +" excuse */
+	*invflags |= invbit;
+	iface[0] = '+';
+	iface[1] = '\0';
+	mask[0] = 0xff;
+	mask[1] = 0xff;
+	memset(mask + 2, 0, IFNAMSIZ - 2);
+}
+
+static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg)
+{
+	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg);
+
+	if (reg->bitwise.set)
+		return reg->bitwise.mask[0];
+
+	return ~0u;
+}
+
+static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	struct xt_mark_mtinfo1 *mark;
+	uint32_t value;
+
+	mark = nft_create_match(ctx, ctx->cs, "mark", false);
+	if (!mark)
+		return -1;
+
+	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+		mark->invert = 1;
+
+	value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+	mark->mark = value;
+	mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG));
+
+	return 0;
+}
+
+static int parse_meta_pkttype(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	struct xt_pkttype_info *pkttype;
+	uint8_t value;
+
+	pkttype = nft_create_match(ctx, ctx->cs, "pkttype", false);
+	if (!pkttype)
+		return -1;
+
+	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+		pkttype->invert = 1;
+
+	value = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+	pkttype->pkttype = value;
+
+	return 0;
+}
+
+int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
+	       char *iniface, unsigned char *iniface_mask,
+	       char *outiface, unsigned char *outiface_mask, uint8_t *invflags)
+{
+	uint32_t value;
+	const void *ifname;
+	uint32_t len;
+
+	switch(key) {
+	case NFT_META_IIF:
+		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			*invflags |= IPT_INV_VIA_IN;
+
+		if_indextoname(value, iniface);
+
+		memset(iniface_mask, 0xff, strlen(iniface)+1);
+		break;
+	case NFT_META_OIF:
+		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			*invflags |= IPT_INV_VIA_OUT;
+
+		if_indextoname(value, outiface);
+
+		memset(outiface_mask, 0xff, strlen(outiface)+1);
+		break;
+	case NFT_META_BRI_IIFNAME:
+	case NFT_META_IIFNAME:
+		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			*invflags |= IPT_INV_VIA_IN;
+
+		parse_ifname(ifname, len, iniface, iniface_mask);
+		parse_invalid_iface(iniface, iniface_mask,
+				    invflags, IPT_INV_VIA_IN);
+		break;
+	case NFT_META_BRI_OIFNAME:
+	case NFT_META_OIFNAME:
+		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			*invflags |= IPT_INV_VIA_OUT;
+
+		parse_ifname(ifname, len, outiface, outiface_mask);
+		parse_invalid_iface(outiface, outiface_mask,
+				    invflags, IPT_INV_VIA_OUT);
+		break;
+	case NFT_META_MARK:
+		parse_meta_mark(ctx, e);
+		break;
+	case NFT_META_PKTTYPE:
+		parse_meta_pkttype(ctx, e);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+void nft_ipv46_parse_target(struct xtables_target *t,
+			    struct iptables_command_state *cs)
+{
+	cs->target = t;
+	cs->jumpto = t->name;
+}
+
+int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+		 struct iptables_command_state *cs)
+{
+	struct ip6t_hl_info *info;
+	uint8_t hl, mode;
+	int op;
+
+	hl = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+
+	switch (op) {
+	case NFT_CMP_NEQ:
+		mode = IP6T_HL_NE;
+		break;
+	case NFT_CMP_EQ:
+		mode = IP6T_HL_EQ;
+		break;
+	case NFT_CMP_LT:
+		mode = IP6T_HL_LT;
+		break;
+	case NFT_CMP_GT:
+		mode = IP6T_HL_GT;
+		break;
+	case NFT_CMP_LTE:
+		mode = IP6T_HL_LT;
+		if (hl == 255)
+			return -1;
+		hl++;
+		break;
+	case NFT_CMP_GTE:
+		mode = IP6T_HL_GT;
+		if (hl == 0)
+			return -1;
+		hl--;
+		break;
+	default:
+		return -1;
+	}
+
+	/* ipt_ttl_info and ip6t_hl_info have same layout,
+	 * IPT_TTL_x and IP6T_HL_x are aliases as well, so
+	 * just use HL for both ipv4 and ipv6.
+	 */
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		info = nft_create_match(ctx, ctx->cs, "ttl", false);
+		break;
+	case NFPROTO_IPV6:
+		info = nft_create_match(ctx, ctx->cs, "hl", false);
+		break;
+	default:
+		return -1;
+	}
+
+	if (!info)
+		return -1;
+
+	info->hop_limit = hl;
+	info->mode = mode;
+
+	return 0;
+}
diff --git a/iptables/nft-ruleparse.h b/iptables/nft-ruleparse.h
new file mode 100644
index 0000000000000..7fac6c7969645
--- /dev/null
+++ b/iptables/nft-ruleparse.h
@@ -0,0 +1,117 @@
+#ifndef _NFT_RULEPARSE_H_
+#define _NFT_RULEPARSE_H_
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+
+#include "xshared.h"
+
+enum nft_ctx_reg_type {
+	NFT_XT_REG_UNDEF,
+	NFT_XT_REG_PAYLOAD,
+	NFT_XT_REG_IMMEDIATE,
+	NFT_XT_REG_META_DREG,
+};
+
+struct nft_xt_ctx_reg {
+	enum nft_ctx_reg_type type:8;
+
+	union {
+		struct {
+			uint32_t base;
+			uint32_t offset;
+			uint32_t len;
+		} payload;
+		struct {
+			uint32_t data[4];
+			uint8_t len;
+		} immediate;
+		struct {
+			uint32_t key;
+		} meta_dreg;
+		struct {
+			uint32_t key;
+		} meta_sreg;
+	};
+
+	struct {
+		uint32_t mask[4];
+		uint32_t xor[4];
+		bool set;
+	} bitwise;
+};
+
+struct nft_xt_ctx {
+	struct iptables_command_state *cs;
+	struct nftnl_expr_iter *iter;
+	struct nft_handle *h;
+	uint32_t flags;
+	const char *table;
+
+	struct nft_xt_ctx_reg regs[1 + 16];
+
+	const char *errmsg;
+};
+
+static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_sreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
+{
+	switch (reg) {
+	case NFT_REG_VERDICT:
+		return &ctx->regs[0];
+	case NFT_REG_1:
+		return &ctx->regs[1];
+	case NFT_REG_2:
+		return &ctx->regs[5];
+	case NFT_REG_3:
+		return &ctx->regs[9];
+	case NFT_REG_4:
+		return &ctx->regs[13];
+	case NFT_REG32_00...NFT_REG32_15:
+		return &ctx->regs[reg - NFT_REG32_00];
+	default:
+		ctx->errmsg = "Unknown register requested";
+		break;
+	}
+
+	return NULL;
+}
+
+static inline void nft_xt_reg_clear(struct nft_xt_ctx_reg *r)
+{
+	r->type = 0;
+	r->bitwise.set = false;
+}
+
+static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_dreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
+{
+	struct nft_xt_ctx_reg *r = nft_xt_ctx_get_sreg(ctx, reg);
+
+	if (r)
+		nft_xt_reg_clear(r);
+
+	return r;
+}
+
+void *nft_create_match(struct nft_xt_ctx *ctx,
+		       struct iptables_command_state *cs,
+		       const char *name, bool reuse);
+
+bool nft_rule_to_iptables_command_state(struct nft_handle *h,
+					const struct nftnl_rule *r,
+					struct iptables_command_state *cs);
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#define max(x, y) ((x) > (y) ? (x) : (y))
+
+int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
+	       char *iniface, unsigned char *iniface_mask, char *outiface,
+	       unsigned char *outiface_mask, uint8_t *invflags);
+
+void nft_ipv46_parse_target(struct xtables_target *t,
+			    struct iptables_command_state *cs);
+
+int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+		 struct iptables_command_state *cs);
+
+#endif /* _NFT_RULEPARSE_H_ */
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 79f6a7d3fbb85..12860fbf6d575 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -21,14 +21,6 @@
 
 #include <xtables.h>
 
-#include <linux/netfilter/nf_log.h>
-#include <linux/netfilter/xt_limit.h>
-#include <linux/netfilter/xt_NFLOG.h>
-#include <linux/netfilter/xt_mark.h>
-#include <linux/netfilter/xt_pkttype.h>
-
-#include <linux/netfilter_ipv6/ip6t_hl.h>
-
 #include <libmnl/libmnl.h>
 #include <libnftnl/rule.h>
 #include <libnftnl/expr.h>
@@ -276,224 +268,6 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
 	return true;
 }
 
-static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned char *mask)
-{
-	if (len == 0)
-		return;
-
-	memcpy(dst, name, len);
-	if (name[len - 1] == '\0') {
-		if (mask)
-			memset(mask, 0xff, strlen(name) + 1);
-		return;
-	}
-
-	if (len >= IFNAMSIZ)
-		return;
-
-	/* wildcard */
-	dst[len++] = '+';
-	if (len >= IFNAMSIZ)
-		return;
-	dst[len++] = 0;
-	if (mask)
-		memset(mask, 0xff, len - 2);
-}
-
-static void *
-nft_create_match(struct nft_xt_ctx *ctx,
-		 struct iptables_command_state *cs,
-		 const char *name, bool reuse);
-
-static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg)
-{
-	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg);
-
-	if (reg->bitwise.set)
-		return reg->bitwise.mask[0];
-
-	return ~0u;
-}
-
-static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	struct xt_mark_mtinfo1 *mark;
-	uint32_t value;
-
-	mark = nft_create_match(ctx, ctx->cs, "mark", false);
-	if (!mark)
-		return -1;
-
-	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-		mark->invert = 1;
-
-	value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
-	mark->mark = value;
-	mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG));
-
-	return 0;
-}
-
-static int parse_meta_pkttype(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	struct xt_pkttype_info *pkttype;
-	uint8_t value;
-
-	pkttype = nft_create_match(ctx, ctx->cs, "pkttype", false);
-	if (!pkttype)
-		return -1;
-
-	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-		pkttype->invert = 1;
-
-	value = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
-	pkttype->pkttype = value;
-
-	return 0;
-}
-
-static void parse_invalid_iface(char *iface, unsigned char *mask,
-				uint8_t *invflags, uint8_t invbit)
-{
-	if (*invflags & invbit || strcmp(iface, "INVAL/D"))
-		return;
-
-	/* nft's poor "! -o +" excuse */
-	*invflags |= invbit;
-	iface[0] = '+';
-	iface[1] = '\0';
-	mask[0] = 0xff;
-	mask[1] = 0xff;
-	memset(mask + 2, 0, IFNAMSIZ - 2);
-}
-
-int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
-	       char *iniface, unsigned char *iniface_mask,
-	       char *outiface, unsigned char *outiface_mask, uint8_t *invflags)
-{
-	uint32_t value;
-	const void *ifname;
-	uint32_t len;
-
-	switch(key) {
-	case NFT_META_IIF:
-		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			*invflags |= IPT_INV_VIA_IN;
-
-		if_indextoname(value, iniface);
-
-		memset(iniface_mask, 0xff, strlen(iniface)+1);
-		break;
-	case NFT_META_OIF:
-		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			*invflags |= IPT_INV_VIA_OUT;
-
-		if_indextoname(value, outiface);
-
-		memset(outiface_mask, 0xff, strlen(outiface)+1);
-		break;
-	case NFT_META_BRI_IIFNAME:
-	case NFT_META_IIFNAME:
-		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			*invflags |= IPT_INV_VIA_IN;
-
-		parse_ifname(ifname, len, iniface, iniface_mask);
-		parse_invalid_iface(iniface, iniface_mask,
-				    invflags, IPT_INV_VIA_IN);
-		break;
-	case NFT_META_BRI_OIFNAME:
-	case NFT_META_OIFNAME:
-		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			*invflags |= IPT_INV_VIA_OUT;
-
-		parse_ifname(ifname, len, outiface, outiface_mask);
-		parse_invalid_iface(outiface, outiface_mask,
-				    invflags, IPT_INV_VIA_OUT);
-		break;
-	case NFT_META_MARK:
-		parse_meta_mark(ctx, e);
-		break;
-	case NFT_META_PKTTYPE:
-		parse_meta_pkttype(ctx, e);
-		break;
-	default:
-		return -1;
-	}
-
-	return 0;
-}
-
-static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	uint32_t tg_len;
-	const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
-	const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
-	struct xtables_target *target;
-	struct xt_entry_target *t;
-	size_t size;
-
-	target = xtables_find_target(targname, XTF_TRY_LOAD);
-	if (target == NULL) {
-		ctx->errmsg = "target extension not found";
-		return;
-	}
-
-	size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
-
-	t = xtables_calloc(1, size);
-	memcpy(&t->data, targinfo, tg_len);
-	t->u.target_size = size;
-	t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
-	strcpy(t->u.user.name, target->name);
-
-	target->t = t;
-
-	ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	uint32_t mt_len;
-	const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
-	const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
-	struct xtables_match *match;
-	struct xtables_rule_match **matches;
-	struct xt_entry_match *m;
-
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-	case NFPROTO_IPV6:
-	case NFPROTO_BRIDGE:
-		matches = &ctx->cs->matches;
-		break;
-	default:
-		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
-			ctx->h->family);
-		exit(EXIT_FAILURE);
-	}
-
-	match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
-	if (match == NULL) {
-		ctx->errmsg = "match extension not found";
-		return;
-	}
-
-	m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
-	memcpy(&m->data, mt_info, mt_len);
-	m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
-	m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
-	strcpy(m->u.user.name, match->name);
-
-	match->m = m;
-
-	if (ctx->h->ops->parse_match != NULL)
-		ctx->h->ops->parse_match(match, ctx->cs);
-}
-
 void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op)
 {
 	uint32_t len;
@@ -510,899 +284,6 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
 	*inv = (op == NFT_CMP_NEQ);
 }
 
-static bool nft_parse_meta_set_common(struct nft_xt_ctx* ctx,
-				      struct nft_xt_ctx_reg *sreg)
-{
-	if ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
-		ctx->errmsg = "meta sreg is not an immediate";
-		return false;
-	}
-
-	if (sreg->immediate.data[0] == 0) {
-		ctx->errmsg = "meta sreg immediate is 0";
-		return false;
-	}
-
-	return true;
-}
-
-static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
-			       struct nftnl_expr *e)
-{
-	struct xtables_target *target;
-	struct nft_xt_ctx_reg *sreg;
-	enum nft_registers sregnum;
-	struct xt_entry_target *t;
-	unsigned int size;
-	const char *targname;
-
-	sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG);
-	sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
-	if (!sreg)
-		return;
-
-	switch (nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY)) {
-	case NFT_META_NFTRACE:
-		if (!nft_parse_meta_set_common(ctx, sreg))
-			return;
-
-		targname = "TRACE";
-		break;
-	case NFT_META_BRI_BROUTE:
-		if (!nft_parse_meta_set_common(ctx, sreg))
-			return;
-
-		ctx->cs->jumpto = "DROP";
-		return;
-	default:
-		ctx->errmsg = "meta sreg key not supported";
-		return;
-	}
-
-	target = xtables_find_target(targname, XTF_TRY_LOAD);
-	if (target == NULL) {
-		ctx->errmsg = "target TRACE not found";
-		return;
-	}
-
-	size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
-
-	t = xtables_calloc(1, size);
-	t->u.target_size = size;
-	t->u.user.revision = target->revision;
-	strcpy(t->u.user.name, targname);
-
-	target->t = t;
-
-	ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-        struct nft_xt_ctx_reg *reg;
-
-	if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
-		nft_parse_meta_set(ctx, e);
-		return;
-	}
-
-	reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG));
-	if (!reg)
-		return;
-
-	reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
-	reg->type = NFT_XT_REG_META_DREG;
-}
-
-static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
-	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum);
-
-	if (!reg)
-		return;
-
-	reg->type = NFT_XT_REG_PAYLOAD;
-	reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
-	reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
-	reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
-}
-
-static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
-	enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
-	struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
-	struct nft_xt_ctx_reg *dreg = sreg;
-	const void *data;
-	uint32_t len;
-
-	if (!sreg)
-		return;
-
-	if (sregnum != dregnum) {
-		dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */
-		if (!dreg)
-			return;
-
-		*dreg = *sreg;  /* .. and copy content instead */
-	}
-
-	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
-
-	if (len > sizeof(dreg->bitwise.xor)) {
-		ctx->errmsg = "bitwise xor too large";
-		return;
-	}
-
-	memcpy(dreg->bitwise.xor, data, len);
-
-	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
-
-	if (len > sizeof(dreg->bitwise.mask)) {
-		ctx->errmsg = "bitwise mask too large";
-		return;
-	}
-
-	memcpy(dreg->bitwise.mask, data, len);
-
-	dreg->bitwise.set = true;
-}
-
-static struct xtables_match *
-nft_find_match_in_cs(struct iptables_command_state *cs, const char *name)
-{
-	struct xtables_rule_match *rm;
-	struct ebt_match *ebm;
-
-	for (ebm = cs->match_list; ebm; ebm = ebm->next) {
-		if (ebm->ismatch &&
-		    !strcmp(ebm->u.match->m->u.user.name, name))
-			return ebm->u.match;
-	}
-	for (rm = cs->matches; rm; rm = rm->next) {
-		if (!strcmp(rm->match->m->u.user.name, name))
-			return rm->match;
-	}
-	return NULL;
-}
-
-static void *
-nft_create_match(struct nft_xt_ctx *ctx,
-		 struct iptables_command_state *cs,
-		 const char *name, bool reuse)
-{
-	struct xtables_match *match;
-	struct xt_entry_match *m;
-	unsigned int size;
-
-	if (reuse) {
-		match = nft_find_match_in_cs(cs, name);
-		if (match)
-			return match->m->data;
-	}
-
-	match = xtables_find_match(name, XTF_TRY_LOAD,
-				   &cs->matches);
-	if (!match)
-		return NULL;
-
-	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
-	m = xtables_calloc(1, size);
-	m->u.match_size = size;
-	m->u.user.revision = match->revision;
-
-	strcpy(m->u.user.name, match->name);
-	match->m = m;
-
-	xs_init_match(match);
-
-	if (ctx->h->ops->parse_match)
-		ctx->h->ops->parse_match(match, cs);
-
-	return match->m->data;
-}
-
-static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
-			        struct iptables_command_state *cs,
-			        int sport_from, int sport_to,
-			        int dport_from, int dport_to,
-				uint8_t op)
-{
-	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
-
-	if (!udp) {
-		ctx->errmsg = "udp match extension not found";
-		return;
-	}
-
-	if (sport_from >= 0) {
-		switch (op) {
-		case NFT_RANGE_NEQ:
-			udp->invflags |= XT_UDP_INV_SRCPT;
-			/* fallthrough */
-		case NFT_RANGE_EQ:
-			udp->spts[0] = sport_from;
-			udp->spts[1] = sport_to;
-			break;
-		}
-	}
-
-	if (dport_to >= 0) {
-		switch (op) {
-		case NFT_CMP_NEQ:
-			udp->invflags |= XT_UDP_INV_DSTPT;
-			/* fallthrough */
-		case NFT_CMP_EQ:
-			udp->dpts[0] = dport_from;
-			udp->dpts[1] = dport_to;
-			break;
-		}
-	}
-}
-
-static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
-			        struct iptables_command_state *cs,
-			        int sport_from, int sport_to,
-			        int dport_from, int dport_to,
-				uint8_t op)
-{
-	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
-
-	if (!tcp) {
-		ctx->errmsg = "tcp match extension not found";
-		return;
-	}
-
-	if (sport_from >= 0) {
-		switch (op) {
-		case NFT_RANGE_NEQ:
-			tcp->invflags |= XT_TCP_INV_SRCPT;
-			/* fallthrough */
-		case NFT_RANGE_EQ:
-			tcp->spts[0] = sport_from;
-			tcp->spts[1] = sport_to;
-			break;
-		}
-	}
-
-	if (dport_to >= 0) {
-		switch (op) {
-		case NFT_CMP_NEQ:
-			tcp->invflags |= XT_TCP_INV_DSTPT;
-			/* fallthrough */
-		case NFT_CMP_EQ:
-			tcp->dpts[0] = dport_from;
-			tcp->dpts[1] = dport_to;
-			break;
-		}
-	}
-}
-
-static void port_match_single_to_range(__u16 *ports, __u8 *invflags,
-				       uint8_t op, int port, __u8 invflag)
-{
-	if (port < 0)
-		return;
-
-	switch (op) {
-	case NFT_CMP_NEQ:
-		*invflags |= invflag;
-		/* fallthrough */
-	case NFT_CMP_EQ:
-		ports[0] = port;
-		ports[1] = port;
-		break;
-	case NFT_CMP_LT:
-		ports[1] = max(port - 1, 1);
-		break;
-	case NFT_CMP_LTE:
-		ports[1] = port;
-		break;
-	case NFT_CMP_GT:
-		ports[0] = min(port + 1, UINT16_MAX);
-		break;
-	case NFT_CMP_GTE:
-		ports[0] = port;
-		break;
-	}
-}
-
-static void nft_parse_udp(struct nft_xt_ctx *ctx,
-			  struct iptables_command_state *cs,
-			  int sport, int dport,
-			  uint8_t op)
-{
-	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
-
-	if (!udp) {
-		ctx->errmsg = "udp match extension not found";
-		return;
-	}
-
-	port_match_single_to_range(udp->spts, &udp->invflags,
-				   op, sport, XT_UDP_INV_SRCPT);
-	port_match_single_to_range(udp->dpts, &udp->invflags,
-				   op, dport, XT_UDP_INV_DSTPT);
-}
-
-static void nft_parse_tcp(struct nft_xt_ctx *ctx,
-			  struct iptables_command_state *cs,
-			  int sport, int dport,
-			  uint8_t op)
-{
-	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
-
-	if (!tcp) {
-		ctx->errmsg = "tcp match extension not found";
-		return;
-	}
-
-	port_match_single_to_range(tcp->spts, &tcp->invflags,
-				   op, sport, XT_TCP_INV_SRCPT);
-	port_match_single_to_range(tcp->dpts, &tcp->invflags,
-				   op, dport, XT_TCP_INV_DSTPT);
-}
-
-static void nft_parse_icmp(struct nft_xt_ctx *ctx,
-			   struct iptables_command_state *cs,
-			   struct nft_xt_ctx_reg *sreg,
-			   uint8_t op, const char *data, size_t dlen)
-{
-	struct ipt_icmp icmp = {
-		.type = UINT8_MAX,
-		.code = { 0, UINT8_MAX },
-	}, *icmpp;
-
-	if (dlen < 1)
-		goto out_err_len;
-
-	switch (sreg->payload.offset) {
-	case 0:
-		icmp.type = data[0];
-		if (dlen == 1)
-			break;
-		dlen--;
-		data++;
-		/* fall through */
-	case 1:
-		if (dlen > 1)
-			goto out_err_len;
-		icmp.code[0] = icmp.code[1] = data[0];
-		break;
-	default:
-		ctx->errmsg = "unexpected payload offset";
-		return;
-	}
-
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-		icmpp = nft_create_match(ctx, cs, "icmp", false);
-		break;
-	case NFPROTO_IPV6:
-		if (icmp.type == UINT8_MAX) {
-			ctx->errmsg = "icmp6 code with any type match not supported";
-			return;
-		}
-		icmpp = nft_create_match(ctx, cs, "icmp6", false);
-		break;
-	default:
-		ctx->errmsg = "unexpected family for icmp match";
-		return;
-	}
-
-	if (!icmpp) {
-		ctx->errmsg = "icmp match extension not found";
-		return;
-	}
-	memcpy(icmpp, &icmp, sizeof(icmp));
-	return;
-
-out_err_len:
-	ctx->errmsg = "unexpected RHS data length";
-}
-
-static void nft_parse_th_port(struct nft_xt_ctx *ctx,
-			      struct iptables_command_state *cs,
-			      uint8_t proto,
-			      int sport, int dport, uint8_t op)
-{
-	switch (proto) {
-	case IPPROTO_UDP:
-		nft_parse_udp(ctx, cs, sport, dport, op);
-		break;
-	case IPPROTO_TCP:
-		nft_parse_tcp(ctx, cs, sport, dport, op);
-		break;
-	default:
-		ctx->errmsg = "unknown layer 4 protocol for TH match";
-	}
-}
-
-static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
-				    struct iptables_command_state *cs,
-				    uint8_t proto,
-				    int sport_from, int sport_to,
-				    int dport_from, int dport_to, uint8_t op)
-{
-	switch (proto) {
-	case IPPROTO_UDP:
-		nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
-		break;
-	case IPPROTO_TCP:
-		nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
-		break;
-	}
-}
-
-static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
-				struct iptables_command_state *cs,
-				uint8_t op, uint8_t flags, uint8_t mask)
-{
-	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
-
-	if (!tcp) {
-		ctx->errmsg = "tcp match extension not found";
-		return;
-	}
-
-	if (op == NFT_CMP_NEQ)
-		tcp->invflags |= XT_TCP_INV_FLAGS;
-	tcp->flg_cmp = flags;
-	tcp->flg_mask = mask;
-}
-
-static void nft_parse_transport(struct nft_xt_ctx *ctx,
-				struct nftnl_expr *e,
-				struct iptables_command_state *cs)
-{
-	struct nft_xt_ctx_reg *sreg;
-	enum nft_registers reg;
-	uint32_t sdport;
-	uint16_t port;
-	uint8_t proto, op;
-	unsigned int len;
-
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-		proto = ctx->cs->fw.ip.proto;
-		break;
-	case NFPROTO_IPV6:
-		proto = ctx->cs->fw6.ipv6.proto;
-		break;
-	default:
-		ctx->errmsg = "invalid family for TH match";
-		return;
-	}
-
-	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
-	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
-
-	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
-	sreg = nft_xt_ctx_get_sreg(ctx, reg);
-	if (!sreg)
-		return;
-
-	if (sreg->type != NFT_XT_REG_PAYLOAD) {
-		ctx->errmsg = "sgreg not payload";
-		return;
-	}
-
-	switch (proto) {
-	case IPPROTO_UDP:
-	case IPPROTO_TCP:
-		break;
-	case IPPROTO_ICMP:
-	case IPPROTO_ICMPV6:
-		nft_parse_icmp(ctx, cs, sreg, op,
-			       nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len),
-			       len);
-		return;
-	default:
-		ctx->errmsg = "unsupported layer 4 protocol value";
-		return;
-	}
-
-	switch(sreg->payload.offset) {
-	case 0: /* th->sport */
-		switch (len) {
-		case 2: /* load sport only */
-			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
-			nft_parse_th_port(ctx, cs, proto, port, -1, op);
-			return;
-		case 4: /* load both src and dst port */
-			sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
-			nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
-			return;
-		}
-		break;
-	case 2: /* th->dport */
-		switch (len) {
-		case 2:
-			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
-			nft_parse_th_port(ctx, cs, proto, -1, port, op);
-			return;
-		}
-		break;
-	case 13: /* th->flags */
-		if (len == 1 && proto == IPPROTO_TCP) {
-			uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
-			uint8_t mask = ~0;
-
-			if (sreg->bitwise.set)
-				memcpy(&mask, &sreg->bitwise.mask, sizeof(mask));
-
-			nft_parse_tcp_flags(ctx, cs, op, flags, mask);
-		}
-		return;
-	}
-}
-
-static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
-				      const struct nft_xt_ctx_reg *sreg,
-				      struct nftnl_expr *e,
-				      struct iptables_command_state *cs)
-{
-	unsigned int len_from, len_to;
-	uint8_t proto, op;
-	uint16_t from, to;
-
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-		proto = ctx->cs->fw.ip.proto;
-		break;
-	case NFPROTO_IPV6:
-		proto = ctx->cs->fw6.ipv6.proto;
-		break;
-	default:
-		proto = 0;
-		break;
-	}
-
-	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
-	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
-	if (len_to != len_from || len_to != 2)
-		return;
-
-	op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
-
-	from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
-	to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
-
-	switch (sreg->payload.offset) {
-	case 0:
-		nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
-		return;
-	case 2:
-		to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
-		nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
-		return;
-	}
-}
-
-static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	struct nft_xt_ctx_reg *sreg;
-	uint32_t reg;
-
-	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
-
-	sreg = nft_xt_ctx_get_sreg(ctx, reg);
-	if (!sreg)
-		return;
-
-	switch (sreg->type) {
-	case NFT_XT_REG_UNDEF:
-		ctx->errmsg = "cmp sreg undef";
-		break;
-	case NFT_XT_REG_META_DREG:
-		ctx->h->ops->parse_meta(ctx, sreg, e, ctx->cs);
-		break;
-	case NFT_XT_REG_PAYLOAD:
-		switch (sreg->payload.base) {
-		case NFT_PAYLOAD_LL_HEADER:
-			if (ctx->h->family == NFPROTO_BRIDGE)
-				ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
-			break;
-		case NFT_PAYLOAD_NETWORK_HEADER:
-			ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
-			break;
-		case NFT_PAYLOAD_TRANSPORT_HEADER:
-			nft_parse_transport(ctx, e, ctx->cs);
-			break;
-		}
-
-		break;
-	default:
-		ctx->errmsg = "cmp sreg has unknown type";
-		break;
-	}
-}
-
-static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
-{
-	counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
-	counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
-}
-
-static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
-	struct iptables_command_state *cs = ctx->cs;
-	struct xt_entry_target *t;
-	uint32_t size;
-	int verdict;
-
-	if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
-		struct nft_xt_ctx_reg *dreg;
-		const void *imm_data;
-		uint32_t len;
-
-		imm_data = nftnl_expr_get(e, NFTNL_EXPR_IMM_DATA, &len);
-		dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG));
-		if (!dreg)
-			return;
-
-		if (len > sizeof(dreg->immediate.data)) {
-			ctx->errmsg = "oversized immediate data";
-			return;
-		}
-
-		memcpy(dreg->immediate.data, imm_data, len);
-		dreg->immediate.len = len;
-		dreg->type = NFT_XT_REG_IMMEDIATE;
-
-		return;
-	}
-
-	verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
-	/* Standard target? */
-	switch(verdict) {
-	case NF_ACCEPT:
-		if (cs->jumpto && strcmp(ctx->table, "broute") == 0)
-			break;
-		cs->jumpto = "ACCEPT";
-		break;
-	case NF_DROP:
-		cs->jumpto = "DROP";
-		break;
-	case NFT_RETURN:
-		cs->jumpto = "RETURN";
-		break;;
-	case NFT_GOTO:
-		if (ctx->h->ops->set_goto_flag)
-			ctx->h->ops->set_goto_flag(cs);
-		/* fall through */
-	case NFT_JUMP:
-		cs->jumpto = chain;
-		/* fall through */
-	default:
-		return;
-	}
-
-	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
-	if (!cs->target) {
-		ctx->errmsg = "verdict extension not found";
-		return;
-	}
-
-	size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
-	t = xtables_calloc(1, size);
-	t->u.target_size = size;
-	t->u.user.revision = cs->target->revision;
-	strcpy(t->u.user.name, cs->jumpto);
-	cs->target->t = t;
-}
-
-static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
-	__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
-	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
-	struct xt_rateinfo *rinfo;
-
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-	case NFPROTO_IPV6:
-	case NFPROTO_BRIDGE:
-		break;
-	default:
-		fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
-			ctx->h->family);
-		exit(EXIT_FAILURE);
-	}
-
-	rinfo = nft_create_match(ctx, ctx->cs, "limit", false);
-	if (!rinfo) {
-		ctx->errmsg = "limit match extension not found";
-		return;
-	}
-
-	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
-	rinfo->burst = burst;
-}
-
-static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	struct xtables_target *target;
-	struct xt_entry_target *t;
-	size_t target_size;
-	/*
-	 * In order to handle the longer log-prefix supported by nft, instead of
-	 * using struct xt_nflog_info, we use a struct with a compatible layout, but
-	 * a larger buffer for the prefix.
-	 */
-	struct xt_nflog_info_nft {
-		__u32 len;
-		__u16 group;
-		__u16 threshold;
-		__u16 flags;
-		__u16 pad;
-		char  prefix[NF_LOG_PREFIXLEN];
-	} info = {
-		.group     = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
-		.threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
-	};
-	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
-		info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
-		info.flags = XT_NFLOG_F_COPY_LEN;
-	}
-	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
-		snprintf(info.prefix, sizeof(info.prefix), "%s",
-			 nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
-
-	target = xtables_find_target("NFLOG", XTF_TRY_LOAD);
-	if (target == NULL) {
-		ctx->errmsg = "NFLOG target extension not found";
-		return;
-	}
-
-	target_size = XT_ALIGN(sizeof(struct xt_entry_target)) +
-		      XT_ALIGN(sizeof(struct xt_nflog_info_nft));
-
-	t = xtables_calloc(1, target_size);
-	t->u.target_size = target_size;
-	strcpy(t->u.user.name, target->name);
-	t->u.user.revision = target->revision;
-
-	target->t = t;
-
-	memcpy(&target->t->data, &info, sizeof(info));
-
-	ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
-			     struct nftnl_expr *e)
-{
-	if (ctx->h->ops->parse_lookup)
-		ctx->h->ops->parse_lookup(ctx, e);
-}
-
-static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
-	struct nft_xt_ctx_reg *sreg;
-	uint32_t reg;
-
-	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
-	sreg = nft_xt_ctx_get_sreg(ctx, reg);
-
-	switch (sreg->type) {
-	case NFT_XT_REG_UNDEF:
-		ctx->errmsg = "range sreg undef";
-		break;
-	case NFT_XT_REG_PAYLOAD:
-		switch (sreg->payload.base) {
-		case NFT_PAYLOAD_TRANSPORT_HEADER:
-			nft_parse_transport_range(ctx, sreg, e, ctx->cs);
-			break;
-		default:
-			ctx->errmsg = "range with unknown payload base";
-			break;
-		}
-		break;
-	default:
-		ctx->errmsg = "range sreg type unsupported";
-		break;
-	}
-}
-
-bool nft_rule_to_iptables_command_state(struct nft_handle *h,
-					const struct nftnl_rule *r,
-					struct iptables_command_state *cs)
-{
-	struct nftnl_expr_iter *iter;
-	struct nftnl_expr *expr;
-	struct nft_xt_ctx ctx = {
-		.cs = cs,
-		.h = h,
-		.table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
-	};
-	bool ret = true;
-
-	iter = nftnl_expr_iter_create(r);
-	if (iter == NULL)
-		return false;
-
-	ctx.iter = iter;
-	expr = nftnl_expr_iter_next(iter);
-	while (expr != NULL) {
-		const char *name =
-			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
-
-		if (strcmp(name, "counter") == 0)
-			nft_parse_counter(expr, &ctx.cs->counters);
-		else if (strcmp(name, "payload") == 0)
-			nft_parse_payload(&ctx, expr);
-		else if (strcmp(name, "meta") == 0)
-			nft_parse_meta(&ctx, expr);
-		else if (strcmp(name, "bitwise") == 0)
-			nft_parse_bitwise(&ctx, expr);
-		else if (strcmp(name, "cmp") == 0)
-			nft_parse_cmp(&ctx, expr);
-		else if (strcmp(name, "immediate") == 0)
-			nft_parse_immediate(&ctx, expr);
-		else if (strcmp(name, "match") == 0)
-			nft_parse_match(&ctx, expr);
-		else if (strcmp(name, "target") == 0)
-			nft_parse_target(&ctx, expr);
-		else if (strcmp(name, "limit") == 0)
-			nft_parse_limit(&ctx, expr);
-		else if (strcmp(name, "lookup") == 0)
-			nft_parse_lookup(&ctx, h, expr);
-		else if (strcmp(name, "log") == 0)
-			nft_parse_log(&ctx, expr);
-		else if (strcmp(name, "range") == 0)
-			nft_parse_range(&ctx, expr);
-
-		if (ctx.errmsg) {
-			fprintf(stderr, "Error: %s\n", ctx.errmsg);
-			ctx.errmsg = NULL;
-			ret = false;
-		}
-
-		expr = nftnl_expr_iter_next(iter);
-	}
-
-	nftnl_expr_iter_destroy(iter);
-
-	if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
-		const void *data;
-		uint32_t len, size;
-		const char *comment;
-
-		data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
-		comment = get_comment(data, len);
-		if (comment) {
-			struct xtables_match *match;
-			struct xt_entry_match *m;
-
-			match = xtables_find_match("comment", XTF_TRY_LOAD,
-						   &cs->matches);
-			if (match == NULL)
-				return false;
-
-			size = XT_ALIGN(sizeof(struct xt_entry_match))
-				+ match->size;
-			m = xtables_calloc(1, size);
-
-			strncpy((char *)m->data, comment, match->size - 1);
-			m->u.match_size = size;
-			m->u.user.revision = 0;
-			strcpy(m->u.user.name, match->name);
-
-			match->m = m;
-		}
-	}
-
-	if (!cs->jumpto)
-		cs->jumpto = "";
-
-	if (!ret)
-		xtables_error(VERSION_PROBLEM, "Parsing nftables rule failed");
-	return ret;
-}
-
 void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
 {
 	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -1546,13 +427,6 @@ bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
 	return true;
 }
 
-void nft_ipv46_parse_target(struct xtables_target *t,
-			    struct iptables_command_state *cs)
-{
-	cs->target = t;
-	cs->jumpto = t->name;
-}
-
 void nft_check_xt_legacy(int family, bool is_ipt_save)
 {
 	static const char tables6[] = "/proc/net/ip6_tables_names";
@@ -1587,70 +461,6 @@ void nft_check_xt_legacy(int family, bool is_ipt_save)
 	fclose(fp);
 }
 
-int nft_parse_hl(struct nft_xt_ctx *ctx,
-		 struct nftnl_expr *e,
-		 struct iptables_command_state *cs)
-{
-	struct ip6t_hl_info *info;
-	uint8_t hl, mode;
-	int op;
-
-	hl = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
-	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
-
-	switch (op) {
-	case NFT_CMP_NEQ:
-		mode = IP6T_HL_NE;
-		break;
-	case NFT_CMP_EQ:
-		mode = IP6T_HL_EQ;
-		break;
-	case NFT_CMP_LT:
-		mode = IP6T_HL_LT;
-		break;
-	case NFT_CMP_GT:
-		mode = IP6T_HL_GT;
-		break;
-	case NFT_CMP_LTE:
-		mode = IP6T_HL_LT;
-		if (hl == 255)
-			return -1;
-		hl++;
-		break;
-	case NFT_CMP_GTE:
-		mode = IP6T_HL_GT;
-		if (hl == 0)
-			return -1;
-		hl--;
-		break;
-	default:
-		return -1;
-	}
-
-	/* ipt_ttl_info and ip6t_hl_info have same layout,
-	 * IPT_TTL_x and IP6T_HL_x are aliases as well, so
-	 * just use HL for both ipv4 and ipv6.
-	 */
-	switch (ctx->h->family) {
-	case NFPROTO_IPV4:
-		info = nft_create_match(ctx, ctx->cs, "ttl", false);
-		break;
-	case NFPROTO_IPV6:
-		info = nft_create_match(ctx, ctx->cs, "hl", false);
-		break;
-	default:
-		return -1;
-	}
-
-	if (!info)
-		return -1;
-
-	info->hop_limit = hl;
-	info->mode = mode;
-
-	return 0;
-}
-
 enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size)
 {
 	/* convert size to NETLINK_ALIGN-sized chunks */
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 2c4c0d90cd077..2edee64920e8b 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -11,6 +11,7 @@
 #include <linux/netfilter/nf_tables.h>
 
 #include "xshared.h"
+#include "nft-ruleparse.h"
 
 #ifdef DEBUG
 #define DEBUG_DEL
@@ -38,92 +39,6 @@ struct xtables_args;
 struct nft_handle;
 struct xt_xlate;
 
-enum nft_ctx_reg_type {
-	NFT_XT_REG_UNDEF,
-	NFT_XT_REG_PAYLOAD,
-	NFT_XT_REG_IMMEDIATE,
-	NFT_XT_REG_META_DREG,
-};
-
-struct nft_xt_ctx_reg {
-	enum nft_ctx_reg_type type:8;
-
-	union {
-		struct {
-			uint32_t base;
-			uint32_t offset;
-			uint32_t len;
-		} payload;
-		struct {
-			uint32_t data[4];
-			uint8_t len;
-		} immediate;
-		struct {
-			uint32_t key;
-		} meta_dreg;
-		struct {
-			uint32_t key;
-		} meta_sreg;
-	};
-
-	struct {
-		uint32_t mask[4];
-		uint32_t xor[4];
-		bool set;
-	} bitwise;
-};
-
-struct nft_xt_ctx {
-	struct iptables_command_state *cs;
-	struct nftnl_expr_iter *iter;
-	struct nft_handle *h;
-	uint32_t flags;
-	const char *table;
-
-	struct nft_xt_ctx_reg regs[1 + 16];
-
-	const char *errmsg;
-};
-
-static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_sreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
-{
-	switch (reg) {
-	case NFT_REG_VERDICT:
-		return &ctx->regs[0];
-	case NFT_REG_1:
-		return &ctx->regs[1];
-	case NFT_REG_2:
-		return &ctx->regs[5];
-	case NFT_REG_3:
-		return &ctx->regs[9];
-	case NFT_REG_4:
-		return &ctx->regs[13];
-	case NFT_REG32_00...NFT_REG32_15:
-		return &ctx->regs[reg - NFT_REG32_00];
-	default:
-		ctx->errmsg = "Unknown register requested";
-		break;
-	}
-
-	return NULL;
-}
-
-static inline void nft_xt_reg_clear(struct nft_xt_ctx_reg *r)
-{
-	r->type = 0;
-	r->bitwise.set = false;
-}
-
-static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_dreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
-{
-	struct nft_xt_ctx_reg *r = nft_xt_ctx_get_sreg(ctx, reg);
-
-	if (r)
-		nft_xt_reg_clear(r);
-
-	return r;
-}
-
 struct nft_family_ops {
 	int (*add)(struct nft_handle *h, struct nftnl_rule *r,
 		   struct iptables_command_state *cs);
@@ -207,14 +122,8 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
 			unsigned const char *b_iniface_mask,
 			unsigned const char *b_outiface_mask);
 
-int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
-	       char *iniface, unsigned char *iniface_mask, char *outiface,
-	       unsigned char *outiface_mask, uint8_t *invflags);
 void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op);
 void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv);
-bool nft_rule_to_iptables_command_state(struct nft_handle *h,
-					const struct nftnl_rule *r,
-					struct iptables_command_state *cs);
 void print_matches_and_target(struct iptables_command_state *cs,
 			      unsigned int format);
 void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy);
@@ -224,9 +133,6 @@ void save_matches_and_target(const struct iptables_command_state *cs,
 
 struct nft_family_ops *nft_family_ops_lookup(int family);
 
-void nft_ipv46_parse_target(struct xtables_target *t,
-			    struct iptables_command_state *cs);
-
 bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
 bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
 
@@ -263,11 +169,6 @@ void xtables_restore_parse(struct nft_handle *h,
 
 void nft_check_xt_legacy(int family, bool is_ipt_save);
 
-int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e, struct iptables_command_state *cs);
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#define max(x, y) ((x) > (y) ? (x) : (y))
-
 /* simplified nftables:include/netlink.h, netlink_padded_len() */
 #define NETLINK_ALIGN		4
 
-- 
2.40.0


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

* [iptables PATCH 2/3] nft: Extract rule parsing callbacks from nft_family_ops
  2023-04-21 17:40 [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h} Phil Sutter
@ 2023-04-21 17:40 ` Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 3/3] nft: ruleparse: Create family-specific source files Phil Sutter
  2023-05-03 17:36 ` [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
  3 siblings, 0 replies; 5+ messages in thread
From: Phil Sutter @ 2023-04-21 17:40 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Florian Westphal

Introduce struct nft_ruleparse_ops holding the family-specific
expression parsers and integrate it into nft_family_ops for now.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 iptables/nft-arp.c       |  9 ++++++---
 iptables/nft-bridge.c    | 18 +++++++++++-------
 iptables/nft-ipv4.c      | 10 +++++++---
 iptables/nft-ipv6.c      | 10 +++++++---
 iptables/nft-ruleparse.c | 24 ++++++++++++------------
 iptables/nft-ruleparse.h | 16 ++++++++++++++++
 iptables/nft-shared.h    | 14 +-------------
 7 files changed, 60 insertions(+), 41 deletions(-)

diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 8963573a72e9e..7c7122374bb63 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -779,23 +779,26 @@ nft_arp_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
+static struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
+	.meta		= nft_arp_parse_meta,
+	.payload	= nft_arp_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
 struct nft_family_ops nft_family_ops_arp = {
 	.add			= nft_arp_add,
 	.is_same		= nft_arp_is_same,
 	.print_payload		= NULL,
-	.parse_meta		= nft_arp_parse_meta,
-	.parse_payload		= nft_arp_parse_payload,
 	.print_header		= nft_arp_print_header,
 	.print_rule		= nft_arp_print_rule,
 	.save_rule		= nft_arp_save_rule,
 	.save_chain		= nft_arp_save_chain,
+	.rule_parse		= &nft_ruleparse_ops_arp,
 	.cmd_parse		= {
 		.post_parse	= nft_arp_post_parse,
 	},
 	.rule_to_cs		= nft_rule_to_iptables_command_state,
 	.init_cs		= nft_arp_init_cs,
 	.clear_cs		= xtables_clear_iptables_command_state,
-	.parse_target		= nft_ipv46_parse_target,
 	.add_entry		= nft_arp_add_entry,
 	.delete_entry		= nft_arp_delete_entry,
 	.check_entry		= nft_arp_check_entry,
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
index 22860d6b91a6f..0c9e1238f4c21 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -560,8 +560,8 @@ static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
 		match->m->u.user.revision = match->revision;
 		xs_init_match(match);
 
-		if (ctx->h->ops->parse_match != NULL)
-			ctx->h->ops->parse_match(match, ctx->cs);
+		if (ctx->h->ops->rule_parse->match != NULL)
+			ctx->h->ops->rule_parse->match(match, ctx->cs);
 	}
 	if (!match)
 		return;
@@ -984,15 +984,19 @@ static int nft_bridge_xlate(const struct iptables_command_state *cs,
 	return ret;
 }
 
+static struct nft_ruleparse_ops nft_ruleparse_ops_bridge = {
+	.meta		= nft_bridge_parse_meta,
+	.payload	= nft_bridge_parse_payload,
+	.lookup		= nft_bridge_parse_lookup,
+	.match		= nft_bridge_parse_match,
+	.target		= nft_bridge_parse_target,
+};
+
 struct nft_family_ops nft_family_ops_bridge = {
 	.add			= nft_bridge_add,
 	.is_same		= nft_bridge_is_same,
 	.print_payload		= NULL,
-	.parse_meta		= nft_bridge_parse_meta,
-	.parse_payload		= nft_bridge_parse_payload,
-	.parse_lookup		= nft_bridge_parse_lookup,
-	.parse_match		= nft_bridge_parse_match,
-	.parse_target		= nft_bridge_parse_target,
+	.rule_parse		= &nft_ruleparse_ops_bridge,
 	.print_table_header	= nft_bridge_print_table_header,
 	.print_header		= nft_bridge_print_header,
 	.print_rule		= nft_bridge_print_rule,
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index fadadd2eb9ed6..3f769e88663ac 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -440,21 +440,25 @@ nft_ipv4_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
+static struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
+	.meta		= nft_ipv4_parse_meta,
+	.payload	= nft_ipv4_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
+
 struct nft_family_ops nft_family_ops_ipv4 = {
 	.add			= nft_ipv4_add,
 	.is_same		= nft_ipv4_is_same,
-	.parse_meta		= nft_ipv4_parse_meta,
-	.parse_payload		= nft_ipv4_parse_payload,
 	.set_goto_flag		= nft_ipv4_set_goto_flag,
 	.print_header		= print_header,
 	.print_rule		= nft_ipv4_print_rule,
 	.save_rule		= nft_ipv4_save_rule,
 	.save_chain		= nft_ipv46_save_chain,
+	.rule_parse		= &nft_ruleparse_ops_ipv4,
 	.cmd_parse		= {
 		.proto_parse	= ipv4_proto_parse,
 		.post_parse	= ipv4_post_parse,
 	},
-	.parse_target		= nft_ipv46_parse_target,
 	.rule_to_cs		= nft_rule_to_iptables_command_state,
 	.clear_cs		= xtables_clear_iptables_command_state,
 	.xlate			= nft_ipv4_xlate,
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index 85bb683f4862b..962aaf0d13831 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -409,21 +409,25 @@ nft_ipv6_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
+static struct nft_ruleparse_ops nft_ruleparse_ops_ipv6 = {
+	.meta		= nft_ipv6_parse_meta,
+	.payload	= nft_ipv6_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
+
 struct nft_family_ops nft_family_ops_ipv6 = {
 	.add			= nft_ipv6_add,
 	.is_same		= nft_ipv6_is_same,
-	.parse_meta		= nft_ipv6_parse_meta,
-	.parse_payload		= nft_ipv6_parse_payload,
 	.set_goto_flag		= nft_ipv6_set_goto_flag,
 	.print_header		= print_header,
 	.print_rule		= nft_ipv6_print_rule,
 	.save_rule		= nft_ipv6_save_rule,
 	.save_chain		= nft_ipv46_save_chain,
+	.rule_parse		= &nft_ruleparse_ops_ipv6,
 	.cmd_parse		= {
 		.proto_parse	= ipv6_proto_parse,
 		.post_parse	= ipv6_post_parse,
 	},
-	.parse_target		= nft_ipv46_parse_target,
 	.rule_to_cs		= nft_rule_to_iptables_command_state,
 	.clear_cs		= xtables_clear_iptables_command_state,
 	.xlate			= nft_ipv6_xlate,
diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
index 2d84241a16819..edbbfa40e9c43 100644
--- a/iptables/nft-ruleparse.c
+++ b/iptables/nft-ruleparse.c
@@ -78,8 +78,8 @@ nft_create_match(struct nft_xt_ctx *ctx,
 
 	xs_init_match(match);
 
-	if (ctx->h->ops->parse_match)
-		ctx->h->ops->parse_match(match, cs);
+	if (ctx->h->ops->rule_parse->match)
+		ctx->h->ops->rule_parse->match(match, cs);
 
 	return match->m->data;
 }
@@ -168,7 +168,7 @@ static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
 
 	target->t = t;
 
-	ctx->h->ops->parse_target(target, ctx->cs);
+	ctx->h->ops->rule_parse->target(target, ctx->cs);
 }
 
 static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -488,16 +488,16 @@ static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 		ctx->errmsg = "cmp sreg undef";
 		break;
 	case NFT_XT_REG_META_DREG:
-		ctx->h->ops->parse_meta(ctx, sreg, e, ctx->cs);
+		ctx->h->ops->rule_parse->meta(ctx, sreg, e, ctx->cs);
 		break;
 	case NFT_XT_REG_PAYLOAD:
 		switch (sreg->payload.base) {
 		case NFT_PAYLOAD_LL_HEADER:
 			if (ctx->h->family == NFPROTO_BRIDGE)
-				ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
+				ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
 			break;
 		case NFT_PAYLOAD_NETWORK_HEADER:
-			ctx->h->ops->parse_payload(ctx, sreg, e, ctx->cs);
+			ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
 			break;
 		case NFT_PAYLOAD_TRANSPORT_HEADER:
 			nft_parse_transport(ctx, e, ctx->cs);
@@ -615,8 +615,8 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 	match->m = m;
 
-	if (ctx->h->ops->parse_match != NULL)
-		ctx->h->ops->parse_match(match, ctx->cs);
+	if (ctx->h->ops->rule_parse->match != NULL)
+		ctx->h->ops->rule_parse->match(match, ctx->cs);
 }
 
 static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -644,7 +644,7 @@ static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 	target->t = t;
 
-	ctx->h->ops->parse_target(target, ctx->cs);
+	ctx->h->ops->rule_parse->target(target, ctx->cs);
 }
 
 static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -678,8 +678,8 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
 			     struct nftnl_expr *e)
 {
-	if (ctx->h->ops->parse_lookup)
-		ctx->h->ops->parse_lookup(ctx, e);
+	if (ctx->h->ops->rule_parse->lookup)
+		ctx->h->ops->rule_parse->lookup(ctx, e);
 }
 
 static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -729,7 +729,7 @@ static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 	memcpy(&target->t->data, &info, sizeof(info));
 
-	ctx->h->ops->parse_target(target, ctx->cs);
+	ctx->h->ops->rule_parse->target(target, ctx->cs);
 }
 
 static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
diff --git a/iptables/nft-ruleparse.h b/iptables/nft-ruleparse.h
index 7fac6c7969645..69e98817bb6e1 100644
--- a/iptables/nft-ruleparse.h
+++ b/iptables/nft-ruleparse.h
@@ -93,6 +93,22 @@ static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_dreg(struct nft_xt_ctx *ctx,
 	return r;
 }
 
+struct nft_ruleparse_ops {
+	void (*meta)(struct nft_xt_ctx *ctx,
+		     const struct nft_xt_ctx_reg *sreg,
+		     struct nftnl_expr *e,
+		     struct iptables_command_state *cs);
+	void (*payload)(struct nft_xt_ctx *ctx,
+			const struct nft_xt_ctx_reg *sreg,
+			struct nftnl_expr *e,
+			struct iptables_command_state *cs);
+	void (*lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+	void (*match)(struct xtables_match *m,
+		      struct iptables_command_state *cs);
+	void (*target)(struct xtables_target *t,
+		       struct iptables_command_state *cs);
+};
+
 void *nft_create_match(struct nft_xt_ctx *ctx,
 		       struct iptables_command_state *cs,
 		       const char *name, bool reuse);
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 2edee64920e8b..a06b263d77c1d 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -46,15 +46,6 @@ struct nft_family_ops {
 			const struct iptables_command_state *cs_b);
 	void (*print_payload)(struct nftnl_expr *e,
 			      struct nftnl_expr_iter *iter);
-	void (*parse_meta)(struct nft_xt_ctx *ctx,
-			   const struct nft_xt_ctx_reg *sreg,
-			   struct nftnl_expr *e,
-			   struct iptables_command_state *cs);
-	void (*parse_payload)(struct nft_xt_ctx *ctx,
-			      const struct nft_xt_ctx_reg *sreg,
-			      struct nftnl_expr *e,
-			      struct iptables_command_state *cs);
-	void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
 	void (*set_goto_flag)(struct iptables_command_state *cs);
 
 	void (*print_table_header)(const char *tablename);
@@ -67,11 +58,8 @@ struct nft_family_ops {
 	void (*save_rule)(const struct iptables_command_state *cs,
 			  unsigned int format);
 	void (*save_chain)(const struct nftnl_chain *c, const char *policy);
+	struct nft_ruleparse_ops *rule_parse;
 	struct xt_cmd_parse_ops cmd_parse;
-	void (*parse_match)(struct xtables_match *m,
-			    struct iptables_command_state *cs);
-	void (*parse_target)(struct xtables_target *t,
-			     struct iptables_command_state *cs);
 	void (*init_cs)(struct iptables_command_state *cs);
 	bool (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
 			   struct iptables_command_state *cs);
-- 
2.40.0


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

* [iptables PATCH 3/3] nft: ruleparse: Create family-specific source files
  2023-04-21 17:40 [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h} Phil Sutter
  2023-04-21 17:40 ` [iptables PATCH 2/3] nft: Extract rule parsing callbacks from nft_family_ops Phil Sutter
@ 2023-04-21 17:40 ` Phil Sutter
  2023-05-03 17:36 ` [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
  3 siblings, 0 replies; 5+ messages in thread
From: Phil Sutter @ 2023-04-21 17:40 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Florian Westphal

Extract the remaining nftnl rule parsing code from
nft-<family>.c sources into dedicated ones to complete the separation.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 iptables/Makefile.am            |   2 +
 iptables/nft-arp.c              | 140 -----------
 iptables/nft-bridge.c           | 392 -----------------------------
 iptables/nft-cache.h            |   2 +
 iptables/nft-ipv4.c             | 108 --------
 iptables/nft-ipv6.c             |  85 -------
 iptables/nft-ruleparse-arp.c    | 168 +++++++++++++
 iptables/nft-ruleparse-bridge.c | 422 ++++++++++++++++++++++++++++++++
 iptables/nft-ruleparse-ipv4.c   | 135 ++++++++++
 iptables/nft-ruleparse-ipv6.c   | 112 +++++++++
 iptables/nft-ruleparse.h        |   5 +
 11 files changed, 846 insertions(+), 725 deletions(-)
 create mode 100644 iptables/nft-ruleparse-arp.c
 create mode 100644 iptables/nft-ruleparse-bridge.c
 create mode 100644 iptables/nft-ruleparse-ipv4.c
 create mode 100644 iptables/nft-ruleparse-ipv6.c

diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index d5922da6a2d84..8a7227024987f 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -47,6 +47,8 @@ xtables_nft_multi_SOURCES += nft.c nft.h \
 			     nft-chain.c nft-chain.h \
 			     nft-cmd.c nft-cmd.h \
 			     nft-ruleparse.c nft-ruleparse.h \
+			     nft-ruleparse-arp.c nft-ruleparse-bridge.c \
+			     nft-ruleparse-ipv4.c nft-ruleparse-ipv6.c \
 			     nft-shared.c nft-shared.h \
 			     xtables-monitor.c \
 			     xtables.c xtables-arp.c xtables-eb.c \
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 7c7122374bb63..ecb05472c62ae 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -160,141 +160,6 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r,
 	return ret;
 }
 
-static void nft_arp_parse_meta(struct nft_xt_ctx *ctx,
-			       const struct nft_xt_ctx_reg *reg,
-			       struct nftnl_expr *e,
-			       struct iptables_command_state *cs)
-{
-	struct arpt_entry *fw = &cs->arp;
-	uint8_t flags = 0;
-
-	if (parse_meta(ctx, e, reg->meta_dreg.key, fw->arp.iniface, fw->arp.iniface_mask,
-		   fw->arp.outiface, fw->arp.outiface_mask,
-		   &flags) == 0) {
-		fw->arp.invflags |= flags;
-		return;
-	}
-
-	ctx->errmsg = "Unknown arp meta key";
-}
-
-static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask)
-{
-	mask->s_addr = reg->bitwise.mask[0];
-}
-
-static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg,
-				  struct nftnl_expr *e,
-				  struct arpt_devaddr_info *info)
-{
-	uint32_t hlen;
-	bool inv;
-
-	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
-
-	if (hlen != ETH_ALEN)
-		return false;
-
-	get_cmp_data(e, info->addr, ETH_ALEN, &inv);
-
-	if (reg->bitwise.set)
-		memcpy(info->mask, reg->bitwise.mask, ETH_ALEN);
-	else
-		memset(info->mask, 0xff,
-		       min(reg->payload.len, ETH_ALEN));
-
-	return inv;
-}
-
-static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
-				  const struct nft_xt_ctx_reg *reg,
-				  struct nftnl_expr *e,
-				  struct iptables_command_state *cs)
-{
-	struct arpt_entry *fw = &cs->arp;
-	struct in_addr addr;
-	uint16_t ar_hrd, ar_pro, ar_op;
-	uint8_t ar_hln, ar_pln;
-	bool inv;
-
-	switch (reg->payload.offset) {
-	case offsetof(struct arphdr, ar_hrd):
-		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
-		fw->arp.arhrd = ar_hrd;
-		fw->arp.arhrd_mask = 0xffff;
-		if (inv)
-			fw->arp.invflags |= IPT_INV_ARPHRD;
-		break;
-	case offsetof(struct arphdr, ar_pro):
-		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
-		fw->arp.arpro = ar_pro;
-		fw->arp.arpro_mask = 0xffff;
-		if (inv)
-			fw->arp.invflags |= IPT_INV_PROTO;
-		break;
-	case offsetof(struct arphdr, ar_op):
-		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
-		fw->arp.arpop = ar_op;
-		fw->arp.arpop_mask = 0xffff;
-		if (inv)
-			fw->arp.invflags |= IPT_INV_ARPOP;
-		break;
-	case offsetof(struct arphdr, ar_hln):
-		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
-		fw->arp.arhln = ar_hln;
-		fw->arp.arhln_mask = 0xff;
-		if (inv)
-			fw->arp.invflags |= IPT_INV_ARPOP;
-		break;
-	case offsetof(struct arphdr, ar_pln):
-		get_cmp_data(e, &ar_pln, sizeof(ar_pln), &inv);
-		if (ar_pln != 4 || inv)
-			ctx->errmsg = "unexpected ARP protocol length match";
-		break;
-	default:
-		if (reg->payload.offset == sizeof(struct arphdr)) {
-			if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr))
-				fw->arp.invflags |= IPT_INV_SRCDEVADDR;
-		} else if (reg->payload.offset == sizeof(struct arphdr) +
-					   fw->arp.arhln) {
-			get_cmp_data(e, &addr, sizeof(addr), &inv);
-			fw->arp.src.s_addr = addr.s_addr;
-			if (reg->bitwise.set)
-				parse_mask_ipv4(reg, &fw->arp.smsk);
-			else
-				memset(&fw->arp.smsk, 0xff,
-				       min(reg->payload.len,
-					   sizeof(struct in_addr)));
-
-			if (inv)
-				fw->arp.invflags |= IPT_INV_SRCIP;
-		} else if (reg->payload.offset == sizeof(struct arphdr) +
-						  fw->arp.arhln +
-						  sizeof(struct in_addr)) {
-			if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr))
-				fw->arp.invflags |= IPT_INV_TGTDEVADDR;
-		} else if (reg->payload.offset == sizeof(struct arphdr) +
-						  fw->arp.arhln +
-						  sizeof(struct in_addr) +
-						  fw->arp.arhln) {
-			get_cmp_data(e, &addr, sizeof(addr), &inv);
-			fw->arp.tgt.s_addr = addr.s_addr;
-			if (reg->bitwise.set)
-				parse_mask_ipv4(reg, &fw->arp.tmsk);
-			else
-				memset(&fw->arp.tmsk, 0xff,
-				       min(reg->payload.len,
-					   sizeof(struct in_addr)));
-
-			if (inv)
-				fw->arp.invflags |= IPT_INV_DSTIP;
-		} else {
-			ctx->errmsg = "unknown payload offset";
-		}
-		break;
-	}
-}
-
 static void nft_arp_print_header(unsigned int format, const char *chain,
 				 const char *pol,
 				 const struct xt_counters *counters,
@@ -779,11 +644,6 @@ nft_arp_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
-static struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
-	.meta		= nft_arp_parse_meta,
-	.payload	= nft_arp_parse_payload,
-	.target		= nft_ipv46_parse_target,
-};
 struct nft_family_ops nft_family_ops_arp = {
 	.add			= nft_arp_add,
 	.is_same		= nft_arp_is_same,
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
index 0c9e1238f4c21..f3dfa488c6202 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -231,390 +231,6 @@ static int nft_bridge_add(struct nft_handle *h,
 	return _add_action(r, cs);
 }
 
-static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
-				  const struct nft_xt_ctx_reg *reg,
-				  struct nftnl_expr *e,
-				  struct iptables_command_state *cs)
-{
-	struct ebt_entry *fw = &cs->eb;
-	uint8_t invflags = 0;
-	char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
-
-	switch (reg->meta_dreg.key) {
-	case NFT_META_PROTOCOL:
-		return;
-	}
-
-	if (parse_meta(ctx, e, reg->meta_dreg.key, iifname, NULL, oifname, NULL, &invflags) < 0) {
-		ctx->errmsg = "unknown meta key";
-		return;
-	}
-
-	switch (reg->meta_dreg.key) {
-	case NFT_META_BRI_IIFNAME:
-		if (invflags & IPT_INV_VIA_IN)
-			cs->eb.invflags |= EBT_ILOGICALIN;
-		snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
-		break;
-	case NFT_META_IIFNAME:
-		if (invflags & IPT_INV_VIA_IN)
-			cs->eb.invflags |= EBT_IIN;
-		snprintf(fw->in, sizeof(fw->in), "%s", iifname);
-		break;
-	case NFT_META_BRI_OIFNAME:
-		if (invflags & IPT_INV_VIA_OUT)
-			cs->eb.invflags |= EBT_ILOGICALOUT;
-		snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
-		break;
-	case NFT_META_OIFNAME:
-		if (invflags & IPT_INV_VIA_OUT)
-			cs->eb.invflags |= EBT_IOUT;
-		snprintf(fw->out, sizeof(fw->out), "%s", oifname);
-		break;
-	default:
-		ctx->errmsg = "unknown bridge meta key";
-		break;
-	}
-}
-
-static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
-				     const struct nft_xt_ctx_reg *reg,
-				     struct nftnl_expr *e,
-				     struct iptables_command_state *cs)
-{
-	struct ebt_entry *fw = &cs->eb;
-	unsigned char addr[ETH_ALEN];
-	unsigned short int ethproto;
-	uint8_t op;
-	bool inv;
-	int i;
-
-	switch (reg->payload.offset) {
-	case offsetof(struct ethhdr, h_dest):
-		get_cmp_data(e, addr, sizeof(addr), &inv);
-		for (i = 0; i < ETH_ALEN; i++)
-			fw->destmac[i] = addr[i];
-		if (inv)
-			fw->invflags |= EBT_IDEST;
-
-		if (reg->bitwise.set)
-                        memcpy(fw->destmsk, reg->bitwise.mask, ETH_ALEN);
-                else
-			memset(&fw->destmsk, 0xff,
-			       min(reg->payload.len, ETH_ALEN));
-		fw->bitmask |= EBT_IDEST;
-		break;
-	case offsetof(struct ethhdr, h_source):
-		get_cmp_data(e, addr, sizeof(addr), &inv);
-		for (i = 0; i < ETH_ALEN; i++)
-			fw->sourcemac[i] = addr[i];
-		if (inv)
-			fw->invflags |= EBT_ISOURCE;
-		if (reg->bitwise.set)
-                        memcpy(fw->sourcemsk, reg->bitwise.mask, ETH_ALEN);
-                else
-			memset(&fw->sourcemsk, 0xff,
-			       min(reg->payload.len, ETH_ALEN));
-		fw->bitmask |= EBT_ISOURCE;
-		break;
-	case offsetof(struct ethhdr, h_proto):
-		__get_cmp_data(e, &ethproto, sizeof(ethproto), &op);
-		if (ethproto == htons(0x0600)) {
-			fw->bitmask |= EBT_802_3;
-			inv = (op == NFT_CMP_GTE);
-		} else {
-			fw->ethproto = ethproto;
-			inv = (op == NFT_CMP_NEQ);
-		}
-		if (inv)
-			fw->invflags |= EBT_IPROTO;
-		fw->bitmask &= ~EBT_NOPROTO;
-		break;
-	default:
-		DEBUGP("unknown payload offset %d\n", reg->payload.offset);
-		ctx->errmsg = "unknown payload offset";
-		break;
-	}
-}
-
-/* return 0 if saddr, 1 if daddr, -1 on error */
-static int
-lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
-{
-	if (base != 0 || len != ETH_ALEN)
-		return -1;
-
-	switch (offset) {
-	case offsetof(struct ether_header, ether_dhost):
-		return 1;
-	case offsetof(struct ether_header, ether_shost):
-		return 0;
-	default:
-		return -1;
-	}
-}
-
-/* return 0 if saddr, 1 if daddr, -1 on error */
-static int
-lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
-{
-	if (base != 1 || len != 4)
-		return -1;
-
-	switch (offset) {
-	case offsetof(struct iphdr, daddr):
-		return 1;
-	case offsetof(struct iphdr, saddr):
-		return 0;
-	default:
-		return -1;
-	}
-}
-
-/* Make sure previous payload expression(s) is/are consistent and extract if
- * matching on source or destination address and if matching on MAC and IP or
- * only MAC address. */
-static int lookup_analyze_payloads(struct nft_xt_ctx *ctx,
-				   enum nft_registers sreg,
-				   uint32_t key_len,
-				   bool *dst, bool *ip)
-{
-	const struct nft_xt_ctx_reg *reg;
-	int val, val2 = -1;
-
-	reg = nft_xt_ctx_get_sreg(ctx, sreg);
-	if (!reg)
-		return -1;
-
-	if (reg->type != NFT_XT_REG_PAYLOAD) {
-		ctx->errmsg = "lookup reg is not payload type";
-		return -1;
-	}
-
-	switch (key_len) {
-	case 12: /* ether + ipv4addr */
-		val = lookup_check_ether_payload(reg->payload.base,
-						 reg->payload.offset,
-						 reg->payload.len);
-		if (val < 0) {
-			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
-			       reg->payload.base, reg->payload.offset,
-			       reg->payload.len);
-			return -1;
-		}
-
-		sreg = nft_get_next_reg(sreg, ETH_ALEN);
-
-		reg = nft_xt_ctx_get_sreg(ctx, sreg);
-		if (!reg) {
-			ctx->errmsg = "next lookup register is invalid";
-			return -1;
-		}
-
-		if (reg->type != NFT_XT_REG_PAYLOAD) {
-			ctx->errmsg = "next lookup reg is not payload type";
-			return -1;
-		}
-
-		val2 = lookup_check_iphdr_payload(reg->payload.base,
-						  reg->payload.offset,
-						  reg->payload.len);
-		if (val2 < 0) {
-			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
-			       reg->payload.base, reg->payload.offset,
-			       reg->payload.len);
-			return -1;
-		} else if (val != val2) {
-			DEBUGP("mismatching payload match offsets\n");
-			return -1;
-		}
-		break;
-	case 6: /* ether */
-		val = lookup_check_ether_payload(reg->payload.base,
-						 reg->payload.offset,
-						 reg->payload.len);
-		if (val < 0) {
-			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
-			       reg->payload.base, reg->payload.offset,
-			       reg->payload.len);
-			return -1;
-		}
-		break;
-	default:
-		ctx->errmsg = "unsupported lookup key length";
-		return -1;
-	}
-
-	if (dst)
-		*dst = (val == 1);
-	if (ip)
-		*ip = (val2 != -1);
-	return 0;
-}
-
-static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
-				    const struct nftnl_set *s, int cnt)
-{
-	struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
-	struct nftnl_set_elem *elem;
-	size_t tmpcnt = 0;
-	const void *data;
-	uint32_t datalen;
-	int ret = -1;
-
-	if (!iter) {
-		fprintf(stderr, "BUG: set elems iter allocation failed\n");
-		return ret;
-	}
-
-	while ((elem = nftnl_set_elems_iter_next(iter))) {
-		data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
-		if (!data) {
-			fprintf(stderr, "BUG: set elem without key\n");
-			goto err;
-		}
-		if (datalen > sizeof(*pairs)) {
-			fprintf(stderr, "BUG: overlong set elem\n");
-			goto err;
-		}
-		nft_among_insert_pair(pairs, &tmpcnt, data);
-	}
-	ret = 0;
-err:
-	nftnl_set_elems_iter_destroy(iter);
-	return ret;
-}
-
-static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
-					      const struct nftnl_expr *e)
-{
-	const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
-	uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
-	struct nftnl_set_list *slist;
-	struct nftnl_set *set;
-
-	slist = nft_set_list_get(ctx->h, ctx->table, set_name);
-	if (slist) {
-		set = nftnl_set_list_lookup_byname(slist, set_name);
-		if (set)
-			return set;
-
-		set = nft_set_batch_lookup_byid(ctx->h, set_id);
-		if (set)
-			return set;
-	}
-
-	return NULL;
-}
-
-static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
-				    struct nftnl_expr *e)
-{
-	struct xtables_match *match = NULL;
-	struct nft_among_data *among_data;
-	bool is_dst, have_ip, inv;
-	struct ebt_match *ematch;
-	struct nftnl_set *s;
-	size_t poff, size;
-	uint32_t cnt;
-
-	s = set_from_lookup_expr(ctx, e);
-	if (!s)
-		xtables_error(OTHER_PROBLEM,
-			      "BUG: lookup expression references unknown set");
-
-	if (lookup_analyze_payloads(ctx,
-				    nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SREG),
-				    nftnl_set_get_u32(s, NFTNL_SET_KEY_LEN),
-				    &is_dst, &have_ip))
-		return;
-
-	cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
-
-	for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
-		if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
-			continue;
-
-		match = ematch->u.match;
-		among_data = (struct nft_among_data *)match->m->data;
-
-		size = cnt + among_data->src.cnt + among_data->dst.cnt;
-		size *= sizeof(struct nft_among_pair);
-
-		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
-			sizeof(struct nft_among_data);
-
-		match->m = xtables_realloc(match->m, size);
-		break;
-	}
-	if (!match) {
-		match = xtables_find_match("among", XTF_TRY_LOAD,
-					   &ctx->cs->matches);
-
-		size = cnt * sizeof(struct nft_among_pair);
-		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
-			sizeof(struct nft_among_data);
-
-		match->m = xtables_calloc(1, size);
-		strcpy(match->m->u.user.name, match->name);
-		match->m->u.user.revision = match->revision;
-		xs_init_match(match);
-
-		if (ctx->h->ops->rule_parse->match != NULL)
-			ctx->h->ops->rule_parse->match(match, ctx->cs);
-	}
-	if (!match)
-		return;
-
-	match->m->u.match_size = size;
-
-	inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
-				    NFT_LOOKUP_F_INV);
-
-	among_data = (struct nft_among_data *)match->m->data;
-	poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
-	if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
-		xtables_error(OTHER_PROBLEM,
-			      "ebtables among pair parsing failed");
-}
-
-static void parse_watcher(void *object, struct ebt_match **match_list,
-			  bool ismatch)
-{
-	struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
-
-	if (ismatch)
-		m->u.match = object;
-	else
-		m->u.watcher = object;
-
-	m->ismatch = ismatch;
-	if (*match_list == NULL)
-		*match_list = m;
-	else
-		(*match_list)->next = m;
-}
-
-static void nft_bridge_parse_match(struct xtables_match *m,
-				   struct iptables_command_state *cs)
-{
-	parse_watcher(m, &cs->match_list, true);
-}
-
-static void nft_bridge_parse_target(struct xtables_target *t,
-				    struct iptables_command_state *cs)
-{
-	/* harcoded names :-( */
-	if (strcmp(t->name, "log") == 0 ||
-	    strcmp(t->name, "nflog") == 0) {
-		parse_watcher(t, &cs->match_list, false);
-		return;
-	}
-
-	cs->target = t;
-	cs->jumpto = t->name;
-}
-
 static bool nft_rule_to_ebtables_command_state(struct nft_handle *h,
 					       const struct nftnl_rule *r,
 					       struct iptables_command_state *cs)
@@ -984,14 +600,6 @@ static int nft_bridge_xlate(const struct iptables_command_state *cs,
 	return ret;
 }
 
-static struct nft_ruleparse_ops nft_ruleparse_ops_bridge = {
-	.meta		= nft_bridge_parse_meta,
-	.payload	= nft_bridge_parse_payload,
-	.lookup		= nft_bridge_parse_lookup,
-	.match		= nft_bridge_parse_match,
-	.target		= nft_bridge_parse_target,
-};
-
 struct nft_family_ops nft_family_ops_bridge = {
 	.add			= nft_bridge_add,
 	.is_same		= nft_bridge_is_same,
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
index 58a015265056c..29ec6b5c3232b 100644
--- a/iptables/nft-cache.h
+++ b/iptables/nft-cache.h
@@ -1,6 +1,8 @@
 #ifndef _NFT_CACHE_H_
 #define _NFT_CACHE_H_
 
+#include <libnftnl/chain.h>
+
 struct nft_handle;
 struct nft_chain;
 struct nft_cmd;
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 3f769e88663ac..6df4e46bc3773 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -115,108 +115,6 @@ static bool nft_ipv4_is_same(const struct iptables_command_state *a,
 				  b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
 }
 
-static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
-{
-	uint8_t op;
-
-	/* we assume correct mask and xor */
-	if (!reg->bitwise.set)
-		return false;
-
-	/* we assume correct data */
-	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
-	if (op == NFT_CMP_EQ)
-		return true;
-
-	return false;
-}
-
-static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx,
-				const struct nft_xt_ctx_reg *reg,
-				struct nftnl_expr *e,
-				struct iptables_command_state *cs)
-{
-	switch (reg->meta_dreg.key) {
-	case NFT_META_L4PROTO:
-		cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			cs->fw.ip.invflags |= XT_INV_PROTO;
-		return;
-	default:
-		break;
-	}
-
-	if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
-		   cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
-		   &cs->fw.ip.invflags) == 0)
-		return;
-
-	ctx->errmsg = "unknown ipv4 meta key";
-}
-
-static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
-{
-	mask->s_addr = sreg->bitwise.mask[0];
-}
-
-static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
-				   const struct nft_xt_ctx_reg *sreg,
-				   struct nftnl_expr *e,
-				   struct iptables_command_state *cs)
-{
-	struct in_addr addr;
-	uint8_t proto;
-	bool inv;
-
-	switch (sreg->payload.offset) {
-	case offsetof(struct iphdr, saddr):
-		get_cmp_data(e, &addr, sizeof(addr), &inv);
-		cs->fw.ip.src.s_addr = addr.s_addr;
-		if (sreg->bitwise.set) {
-			parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
-		} else {
-			memset(&cs->fw.ip.smsk, 0xff,
-			       min(sreg->payload.len, sizeof(struct in_addr)));
-		}
-
-		if (inv)
-			cs->fw.ip.invflags |= IPT_INV_SRCIP;
-		break;
-	case offsetof(struct iphdr, daddr):
-		get_cmp_data(e, &addr, sizeof(addr), &inv);
-		cs->fw.ip.dst.s_addr = addr.s_addr;
-		if (sreg->bitwise.set)
-			parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
-		else
-			memset(&cs->fw.ip.dmsk, 0xff,
-			       min(sreg->payload.len, sizeof(struct in_addr)));
-
-		if (inv)
-			cs->fw.ip.invflags |= IPT_INV_DSTIP;
-		break;
-	case offsetof(struct iphdr, protocol):
-		get_cmp_data(e, &proto, sizeof(proto), &inv);
-		cs->fw.ip.proto = proto;
-		if (inv)
-			cs->fw.ip.invflags |= IPT_INV_PROTO;
-		break;
-	case offsetof(struct iphdr, frag_off):
-		cs->fw.ip.flags |= IPT_F_FRAG;
-		inv = get_frag(sreg, e);
-		if (inv)
-			cs->fw.ip.invflags |= IPT_INV_FRAG;
-		break;
-	case offsetof(struct iphdr, ttl):
-		if (nft_parse_hl(ctx, e, cs) < 0)
-			ctx->errmsg = "invalid ttl field match";
-		break;
-	default:
-		DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
-		ctx->errmsg = "unknown payload offset";
-		break;
-	}
-}
-
 static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs)
 {
 	cs->fw.ip.flags |= IPT_F_GOTO;
@@ -440,12 +338,6 @@ nft_ipv4_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
-static struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
-	.meta		= nft_ipv4_parse_meta,
-	.payload	= nft_ipv4_parse_payload,
-	.target		= nft_ipv46_parse_target,
-};
-
 struct nft_family_ops nft_family_ops_ipv4 = {
 	.add			= nft_ipv4_add,
 	.is_same		= nft_ipv4_is_same,
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index 962aaf0d13831..693a1c87b997d 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -104,85 +104,6 @@ static bool nft_ipv6_is_same(const struct iptables_command_state *a,
 				  b->fw6.ipv6.outiface_mask);
 }
 
-static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx,
-				const struct nft_xt_ctx_reg *reg,
-				struct nftnl_expr *e,
-				struct iptables_command_state *cs)
-{
-	switch (reg->meta_dreg.key) {
-	case NFT_META_L4PROTO:
-		cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
-		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
-			cs->fw6.ipv6.invflags |= XT_INV_PROTO;
-		return;
-	default:
-		break;
-	}
-
-	if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw6.ipv6.iniface,
-		   cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface,
-		   cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags) == 0)
-		return;
-
-	ctx->errmsg = "unknown ipv6 meta key";
-}
-
-static void parse_mask_ipv6(const struct nft_xt_ctx_reg *reg,
-			    struct in6_addr *mask)
-{
-	memcpy(mask, reg->bitwise.mask, sizeof(struct in6_addr));
-}
-
-static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
-				   const struct nft_xt_ctx_reg *reg,
-				   struct nftnl_expr *e,
-				   struct iptables_command_state *cs)
-{
-	struct in6_addr addr;
-	uint8_t proto;
-	bool inv;
-
-	switch (reg->payload.offset) {
-	case offsetof(struct ip6_hdr, ip6_src):
-		get_cmp_data(e, &addr, sizeof(addr), &inv);
-		memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
-		if (reg->bitwise.set)
-			parse_mask_ipv6(reg, &cs->fw6.ipv6.smsk);
-		else
-			memset(&cs->fw6.ipv6.smsk, 0xff,
-			       min(reg->payload.len, sizeof(struct in6_addr)));
-
-		if (inv)
-			cs->fw6.ipv6.invflags |= IP6T_INV_SRCIP;
-		break;
-	case offsetof(struct ip6_hdr, ip6_dst):
-		get_cmp_data(e, &addr, sizeof(addr), &inv);
-		memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
-		if (reg->bitwise.set)
-			parse_mask_ipv6(reg, &cs->fw6.ipv6.dmsk);
-		else
-			memset(&cs->fw6.ipv6.dmsk, 0xff,
-			       min(reg->payload.len, sizeof(struct in6_addr)));
-
-		if (inv)
-			cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
-		break;
-	case offsetof(struct ip6_hdr, ip6_nxt):
-		get_cmp_data(e, &proto, sizeof(proto), &inv);
-		cs->fw6.ipv6.proto = proto;
-		if (inv)
-			cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
-	case offsetof(struct ip6_hdr, ip6_hlim):
-		if (nft_parse_hl(ctx, e, cs) < 0)
-			ctx->errmsg = "invalid ttl field match";
-		break;
-	default:
-		DEBUGP("unknown payload offset %d\n", reg->payload.offset);
-		ctx->errmsg = "unknown payload offset";
-		break;
-	}
-}
-
 static void nft_ipv6_set_goto_flag(struct iptables_command_state *cs)
 {
 	cs->fw6.ipv6.flags |= IP6T_F_GOTO;
@@ -409,12 +330,6 @@ nft_ipv6_replace_entry(struct nft_handle *h,
 	return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
 
-static struct nft_ruleparse_ops nft_ruleparse_ops_ipv6 = {
-	.meta		= nft_ipv6_parse_meta,
-	.payload	= nft_ipv6_parse_payload,
-	.target		= nft_ipv46_parse_target,
-};
-
 struct nft_family_ops nft_family_ops_ipv6 = {
 	.add			= nft_ipv6_add,
 	.is_same		= nft_ipv6_is_same,
diff --git a/iptables/nft-ruleparse-arp.c b/iptables/nft-ruleparse-arp.c
new file mode 100644
index 0000000000000..2538b04e676ce
--- /dev/null
+++ b/iptables/nft-ruleparse-arp.c
@@ -0,0 +1,168 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_arp_parse_meta(struct nft_xt_ctx *ctx,
+			       const struct nft_xt_ctx_reg *reg,
+			       struct nftnl_expr *e,
+			       struct iptables_command_state *cs)
+{
+	struct arpt_entry *fw = &cs->arp;
+	uint8_t flags = 0;
+
+	if (parse_meta(ctx, e, reg->meta_dreg.key, fw->arp.iniface, fw->arp.iniface_mask,
+		   fw->arp.outiface, fw->arp.outiface_mask,
+		   &flags) == 0) {
+		fw->arp.invflags |= flags;
+		return;
+	}
+
+	ctx->errmsg = "Unknown arp meta key";
+}
+
+static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask)
+{
+	mask->s_addr = reg->bitwise.mask[0];
+}
+
+static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg,
+				  struct nftnl_expr *e,
+				  struct arpt_devaddr_info *info)
+{
+	uint32_t hlen;
+	bool inv;
+
+	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
+
+	if (hlen != ETH_ALEN)
+		return false;
+
+	get_cmp_data(e, info->addr, ETH_ALEN, &inv);
+
+	if (reg->bitwise.set)
+		memcpy(info->mask, reg->bitwise.mask, ETH_ALEN);
+	else
+		memset(info->mask, 0xff,
+		       min(reg->payload.len, ETH_ALEN));
+
+	return inv;
+}
+
+static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
+				  const struct nft_xt_ctx_reg *reg,
+				  struct nftnl_expr *e,
+				  struct iptables_command_state *cs)
+{
+	struct arpt_entry *fw = &cs->arp;
+	struct in_addr addr;
+	uint16_t ar_hrd, ar_pro, ar_op;
+	uint8_t ar_hln, ar_pln;
+	bool inv;
+
+	switch (reg->payload.offset) {
+	case offsetof(struct arphdr, ar_hrd):
+		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
+		fw->arp.arhrd = ar_hrd;
+		fw->arp.arhrd_mask = 0xffff;
+		if (inv)
+			fw->arp.invflags |= IPT_INV_ARPHRD;
+		break;
+	case offsetof(struct arphdr, ar_pro):
+		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
+		fw->arp.arpro = ar_pro;
+		fw->arp.arpro_mask = 0xffff;
+		if (inv)
+			fw->arp.invflags |= IPT_INV_PROTO;
+		break;
+	case offsetof(struct arphdr, ar_op):
+		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
+		fw->arp.arpop = ar_op;
+		fw->arp.arpop_mask = 0xffff;
+		if (inv)
+			fw->arp.invflags |= IPT_INV_ARPOP;
+		break;
+	case offsetof(struct arphdr, ar_hln):
+		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
+		fw->arp.arhln = ar_hln;
+		fw->arp.arhln_mask = 0xff;
+		if (inv)
+			fw->arp.invflags |= IPT_INV_ARPOP;
+		break;
+	case offsetof(struct arphdr, ar_pln):
+		get_cmp_data(e, &ar_pln, sizeof(ar_pln), &inv);
+		if (ar_pln != 4 || inv)
+			ctx->errmsg = "unexpected ARP protocol length match";
+		break;
+	default:
+		if (reg->payload.offset == sizeof(struct arphdr)) {
+			if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr))
+				fw->arp.invflags |= IPT_INV_SRCDEVADDR;
+		} else if (reg->payload.offset == sizeof(struct arphdr) +
+					   fw->arp.arhln) {
+			get_cmp_data(e, &addr, sizeof(addr), &inv);
+			fw->arp.src.s_addr = addr.s_addr;
+			if (reg->bitwise.set)
+				parse_mask_ipv4(reg, &fw->arp.smsk);
+			else
+				memset(&fw->arp.smsk, 0xff,
+				       min(reg->payload.len,
+					   sizeof(struct in_addr)));
+
+			if (inv)
+				fw->arp.invflags |= IPT_INV_SRCIP;
+		} else if (reg->payload.offset == sizeof(struct arphdr) +
+						  fw->arp.arhln +
+						  sizeof(struct in_addr)) {
+			if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr))
+				fw->arp.invflags |= IPT_INV_TGTDEVADDR;
+		} else if (reg->payload.offset == sizeof(struct arphdr) +
+						  fw->arp.arhln +
+						  sizeof(struct in_addr) +
+						  fw->arp.arhln) {
+			get_cmp_data(e, &addr, sizeof(addr), &inv);
+			fw->arp.tgt.s_addr = addr.s_addr;
+			if (reg->bitwise.set)
+				parse_mask_ipv4(reg, &fw->arp.tmsk);
+			else
+				memset(&fw->arp.tmsk, 0xff,
+				       min(reg->payload.len,
+					   sizeof(struct in_addr)));
+
+			if (inv)
+				fw->arp.invflags |= IPT_INV_DSTIP;
+		} else {
+			ctx->errmsg = "unknown payload offset";
+		}
+		break;
+	}
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
+	.meta		= nft_arp_parse_meta,
+	.payload	= nft_arp_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
diff --git a/iptables/nft-ruleparse-bridge.c b/iptables/nft-ruleparse-bridge.c
new file mode 100644
index 0000000000000..50fb92833046a
--- /dev/null
+++ b/iptables/nft-ruleparse-bridge.c
@@ -0,0 +1,422 @@
+/*
+ * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+//#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/set.h>
+
+#include <xtables.h>
+
+#include "nft.h" /* just for nft_set_batch_lookup_byid? */
+#include "nft-bridge.h"
+#include "nft-cache.h"
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
+				  const struct nft_xt_ctx_reg *reg,
+				  struct nftnl_expr *e,
+				  struct iptables_command_state *cs)
+{
+	struct ebt_entry *fw = &cs->eb;
+	uint8_t invflags = 0;
+	char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
+
+	switch (reg->meta_dreg.key) {
+	case NFT_META_PROTOCOL:
+		return;
+	}
+
+	if (parse_meta(ctx, e, reg->meta_dreg.key, iifname, NULL, oifname, NULL, &invflags) < 0) {
+		ctx->errmsg = "unknown meta key";
+		return;
+	}
+
+	switch (reg->meta_dreg.key) {
+	case NFT_META_BRI_IIFNAME:
+		if (invflags & IPT_INV_VIA_IN)
+			cs->eb.invflags |= EBT_ILOGICALIN;
+		snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
+		break;
+	case NFT_META_IIFNAME:
+		if (invflags & IPT_INV_VIA_IN)
+			cs->eb.invflags |= EBT_IIN;
+		snprintf(fw->in, sizeof(fw->in), "%s", iifname);
+		break;
+	case NFT_META_BRI_OIFNAME:
+		if (invflags & IPT_INV_VIA_OUT)
+			cs->eb.invflags |= EBT_ILOGICALOUT;
+		snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
+		break;
+	case NFT_META_OIFNAME:
+		if (invflags & IPT_INV_VIA_OUT)
+			cs->eb.invflags |= EBT_IOUT;
+		snprintf(fw->out, sizeof(fw->out), "%s", oifname);
+		break;
+	default:
+		ctx->errmsg = "unknown bridge meta key";
+		break;
+	}
+}
+
+static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
+				     const struct nft_xt_ctx_reg *reg,
+				     struct nftnl_expr *e,
+				     struct iptables_command_state *cs)
+{
+	struct ebt_entry *fw = &cs->eb;
+	unsigned char addr[ETH_ALEN];
+	unsigned short int ethproto;
+	uint8_t op;
+	bool inv;
+	int i;
+
+	switch (reg->payload.offset) {
+	case offsetof(struct ethhdr, h_dest):
+		get_cmp_data(e, addr, sizeof(addr), &inv);
+		for (i = 0; i < ETH_ALEN; i++)
+			fw->destmac[i] = addr[i];
+		if (inv)
+			fw->invflags |= EBT_IDEST;
+
+		if (reg->bitwise.set)
+                        memcpy(fw->destmsk, reg->bitwise.mask, ETH_ALEN);
+                else
+			memset(&fw->destmsk, 0xff,
+			       min(reg->payload.len, ETH_ALEN));
+		fw->bitmask |= EBT_IDEST;
+		break;
+	case offsetof(struct ethhdr, h_source):
+		get_cmp_data(e, addr, sizeof(addr), &inv);
+		for (i = 0; i < ETH_ALEN; i++)
+			fw->sourcemac[i] = addr[i];
+		if (inv)
+			fw->invflags |= EBT_ISOURCE;
+		if (reg->bitwise.set)
+                        memcpy(fw->sourcemsk, reg->bitwise.mask, ETH_ALEN);
+                else
+			memset(&fw->sourcemsk, 0xff,
+			       min(reg->payload.len, ETH_ALEN));
+		fw->bitmask |= EBT_ISOURCE;
+		break;
+	case offsetof(struct ethhdr, h_proto):
+		__get_cmp_data(e, &ethproto, sizeof(ethproto), &op);
+		if (ethproto == htons(0x0600)) {
+			fw->bitmask |= EBT_802_3;
+			inv = (op == NFT_CMP_GTE);
+		} else {
+			fw->ethproto = ethproto;
+			inv = (op == NFT_CMP_NEQ);
+		}
+		if (inv)
+			fw->invflags |= EBT_IPROTO;
+		fw->bitmask &= ~EBT_NOPROTO;
+		break;
+	default:
+		DEBUGP("unknown payload offset %d\n", reg->payload.offset);
+		ctx->errmsg = "unknown payload offset";
+		break;
+	}
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+	if (base != 0 || len != ETH_ALEN)
+		return -1;
+
+	switch (offset) {
+	case offsetof(struct ether_header, ether_dhost):
+		return 1;
+	case offsetof(struct ether_header, ether_shost):
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+	if (base != 1 || len != 4)
+		return -1;
+
+	switch (offset) {
+	case offsetof(struct iphdr, daddr):
+		return 1;
+	case offsetof(struct iphdr, saddr):
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/* Make sure previous payload expression(s) is/are consistent and extract if
+ * matching on source or destination address and if matching on MAC and IP or
+ * only MAC address. */
+static int lookup_analyze_payloads(struct nft_xt_ctx *ctx,
+				   enum nft_registers sreg,
+				   uint32_t key_len,
+				   bool *dst, bool *ip)
+{
+	const struct nft_xt_ctx_reg *reg;
+	int val, val2 = -1;
+
+	reg = nft_xt_ctx_get_sreg(ctx, sreg);
+	if (!reg)
+		return -1;
+
+	if (reg->type != NFT_XT_REG_PAYLOAD) {
+		ctx->errmsg = "lookup reg is not payload type";
+		return -1;
+	}
+
+	switch (key_len) {
+	case 12: /* ether + ipv4addr */
+		val = lookup_check_ether_payload(reg->payload.base,
+						 reg->payload.offset,
+						 reg->payload.len);
+		if (val < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       reg->payload.base, reg->payload.offset,
+			       reg->payload.len);
+			return -1;
+		}
+
+		sreg = nft_get_next_reg(sreg, ETH_ALEN);
+
+		reg = nft_xt_ctx_get_sreg(ctx, sreg);
+		if (!reg) {
+			ctx->errmsg = "next lookup register is invalid";
+			return -1;
+		}
+
+		if (reg->type != NFT_XT_REG_PAYLOAD) {
+			ctx->errmsg = "next lookup reg is not payload type";
+			return -1;
+		}
+
+		val2 = lookup_check_iphdr_payload(reg->payload.base,
+						  reg->payload.offset,
+						  reg->payload.len);
+		if (val2 < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       reg->payload.base, reg->payload.offset,
+			       reg->payload.len);
+			return -1;
+		} else if (val != val2) {
+			DEBUGP("mismatching payload match offsets\n");
+			return -1;
+		}
+		break;
+	case 6: /* ether */
+		val = lookup_check_ether_payload(reg->payload.base,
+						 reg->payload.offset,
+						 reg->payload.len);
+		if (val < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       reg->payload.base, reg->payload.offset,
+			       reg->payload.len);
+			return -1;
+		}
+		break;
+	default:
+		ctx->errmsg = "unsupported lookup key length";
+		return -1;
+	}
+
+	if (dst)
+		*dst = (val == 1);
+	if (ip)
+		*ip = (val2 != -1);
+	return 0;
+}
+
+static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
+				    const struct nftnl_set *s, int cnt)
+{
+	struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
+	struct nftnl_set_elem *elem;
+	size_t tmpcnt = 0;
+	const void *data;
+	uint32_t datalen;
+	int ret = -1;
+
+	if (!iter) {
+		fprintf(stderr, "BUG: set elems iter allocation failed\n");
+		return ret;
+	}
+
+	while ((elem = nftnl_set_elems_iter_next(iter))) {
+		data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
+		if (!data) {
+			fprintf(stderr, "BUG: set elem without key\n");
+			goto err;
+		}
+		if (datalen > sizeof(*pairs)) {
+			fprintf(stderr, "BUG: overlong set elem\n");
+			goto err;
+		}
+		nft_among_insert_pair(pairs, &tmpcnt, data);
+	}
+	ret = 0;
+err:
+	nftnl_set_elems_iter_destroy(iter);
+	return ret;
+}
+
+static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
+					      const struct nftnl_expr *e)
+{
+	const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+	uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
+	struct nftnl_set_list *slist;
+	struct nftnl_set *set;
+
+	slist = nft_set_list_get(ctx->h, ctx->table, set_name);
+	if (slist) {
+		set = nftnl_set_list_lookup_byname(slist, set_name);
+		if (set)
+			return set;
+
+		set = nft_set_batch_lookup_byid(ctx->h, set_id);
+		if (set)
+			return set;
+	}
+
+	return NULL;
+}
+
+static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
+				    struct nftnl_expr *e)
+{
+	struct xtables_match *match = NULL;
+	struct nft_among_data *among_data;
+	bool is_dst, have_ip, inv;
+	struct ebt_match *ematch;
+	struct nftnl_set *s;
+	size_t poff, size;
+	uint32_t cnt;
+
+	s = set_from_lookup_expr(ctx, e);
+	if (!s)
+		xtables_error(OTHER_PROBLEM,
+			      "BUG: lookup expression references unknown set");
+
+	if (lookup_analyze_payloads(ctx,
+				    nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SREG),
+				    nftnl_set_get_u32(s, NFTNL_SET_KEY_LEN),
+				    &is_dst, &have_ip))
+		return;
+
+	cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
+
+	for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
+		if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
+			continue;
+
+		match = ematch->u.match;
+		among_data = (struct nft_among_data *)match->m->data;
+
+		size = cnt + among_data->src.cnt + among_data->dst.cnt;
+		size *= sizeof(struct nft_among_pair);
+
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+			sizeof(struct nft_among_data);
+
+		match->m = xtables_realloc(match->m, size);
+		break;
+	}
+	if (!match) {
+		match = xtables_find_match("among", XTF_TRY_LOAD,
+					   &ctx->cs->matches);
+
+		size = cnt * sizeof(struct nft_among_pair);
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+			sizeof(struct nft_among_data);
+
+		match->m = xtables_calloc(1, size);
+		strcpy(match->m->u.user.name, match->name);
+		match->m->u.user.revision = match->revision;
+		xs_init_match(match);
+
+		if (ctx->h->ops->rule_parse->match != NULL)
+			ctx->h->ops->rule_parse->match(match, ctx->cs);
+	}
+	if (!match)
+		return;
+
+	match->m->u.match_size = size;
+
+	inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
+				    NFT_LOOKUP_F_INV);
+
+	among_data = (struct nft_among_data *)match->m->data;
+	poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
+	if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
+		xtables_error(OTHER_PROBLEM,
+			      "ebtables among pair parsing failed");
+}
+
+static void parse_watcher(void *object, struct ebt_match **match_list,
+			  bool ismatch)
+{
+	struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
+
+	if (ismatch)
+		m->u.match = object;
+	else
+		m->u.watcher = object;
+
+	m->ismatch = ismatch;
+	if (*match_list == NULL)
+		*match_list = m;
+	else
+		(*match_list)->next = m;
+}
+
+static void nft_bridge_parse_match(struct xtables_match *m,
+				   struct iptables_command_state *cs)
+{
+	parse_watcher(m, &cs->match_list, true);
+}
+
+static void nft_bridge_parse_target(struct xtables_target *t,
+				    struct iptables_command_state *cs)
+{
+	/* harcoded names :-( */
+	if (strcmp(t->name, "log") == 0 ||
+	    strcmp(t->name, "nflog") == 0) {
+		parse_watcher(t, &cs->match_list, false);
+		return;
+	}
+
+	cs->target = t;
+	cs->jumpto = t->name;
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_bridge = {
+	.meta		= nft_bridge_parse_meta,
+	.payload	= nft_bridge_parse_payload,
+	.lookup		= nft_bridge_parse_lookup,
+	.match		= nft_bridge_parse_match,
+	.target		= nft_bridge_parse_target,
+};
diff --git a/iptables/nft-ruleparse-ipv4.c b/iptables/nft-ruleparse-ipv4.c
new file mode 100644
index 0000000000000..c87e159cc5fec
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv4.c
@@ -0,0 +1,135 @@
+/*
+ * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx,
+				const struct nft_xt_ctx_reg *reg,
+				struct nftnl_expr *e,
+				struct iptables_command_state *cs)
+{
+	switch (reg->meta_dreg.key) {
+	case NFT_META_L4PROTO:
+		cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			cs->fw.ip.invflags |= XT_INV_PROTO;
+		return;
+	default:
+		break;
+	}
+
+	if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
+		   cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
+		   &cs->fw.ip.invflags) == 0)
+		return;
+
+	ctx->errmsg = "unknown ipv4 meta key";
+}
+
+static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
+{
+	mask->s_addr = sreg->bitwise.mask[0];
+}
+
+static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
+{
+	uint8_t op;
+
+	/* we assume correct mask and xor */
+	if (!reg->bitwise.set)
+		return false;
+
+	/* we assume correct data */
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+	if (op == NFT_CMP_EQ)
+		return true;
+
+	return false;
+}
+
+static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
+				   const struct nft_xt_ctx_reg *sreg,
+				   struct nftnl_expr *e,
+				   struct iptables_command_state *cs)
+{
+	struct in_addr addr;
+	uint8_t proto;
+	bool inv;
+
+	switch (sreg->payload.offset) {
+	case offsetof(struct iphdr, saddr):
+		get_cmp_data(e, &addr, sizeof(addr), &inv);
+		cs->fw.ip.src.s_addr = addr.s_addr;
+		if (sreg->bitwise.set) {
+			parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
+		} else {
+			memset(&cs->fw.ip.smsk, 0xff,
+			       min(sreg->payload.len, sizeof(struct in_addr)));
+		}
+
+		if (inv)
+			cs->fw.ip.invflags |= IPT_INV_SRCIP;
+		break;
+	case offsetof(struct iphdr, daddr):
+		get_cmp_data(e, &addr, sizeof(addr), &inv);
+		cs->fw.ip.dst.s_addr = addr.s_addr;
+		if (sreg->bitwise.set)
+			parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
+		else
+			memset(&cs->fw.ip.dmsk, 0xff,
+			       min(sreg->payload.len, sizeof(struct in_addr)));
+
+		if (inv)
+			cs->fw.ip.invflags |= IPT_INV_DSTIP;
+		break;
+	case offsetof(struct iphdr, protocol):
+		get_cmp_data(e, &proto, sizeof(proto), &inv);
+		cs->fw.ip.proto = proto;
+		if (inv)
+			cs->fw.ip.invflags |= IPT_INV_PROTO;
+		break;
+	case offsetof(struct iphdr, frag_off):
+		cs->fw.ip.flags |= IPT_F_FRAG;
+		inv = get_frag(sreg, e);
+		if (inv)
+			cs->fw.ip.invflags |= IPT_INV_FRAG;
+		break;
+	case offsetof(struct iphdr, ttl):
+		if (nft_parse_hl(ctx, e, cs) < 0)
+			ctx->errmsg = "invalid ttl field match";
+		break;
+	default:
+		DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
+		ctx->errmsg = "unknown payload offset";
+		break;
+	}
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
+	.meta		= nft_ipv4_parse_meta,
+	.payload	= nft_ipv4_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
diff --git a/iptables/nft-ruleparse-ipv6.c b/iptables/nft-ruleparse-ipv6.c
new file mode 100644
index 0000000000000..af55420b73766
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv6.c
@@ -0,0 +1,112 @@
+/*
+ * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx,
+				const struct nft_xt_ctx_reg *reg,
+				struct nftnl_expr *e,
+				struct iptables_command_state *cs)
+{
+	switch (reg->meta_dreg.key) {
+	case NFT_META_L4PROTO:
+		cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+			cs->fw6.ipv6.invflags |= XT_INV_PROTO;
+		return;
+	default:
+		break;
+	}
+
+	if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw6.ipv6.iniface,
+		   cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface,
+		   cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags) == 0)
+		return;
+
+	ctx->errmsg = "unknown ipv6 meta key";
+}
+
+static void parse_mask_ipv6(const struct nft_xt_ctx_reg *reg,
+			    struct in6_addr *mask)
+{
+	memcpy(mask, reg->bitwise.mask, sizeof(struct in6_addr));
+}
+
+static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
+				   const struct nft_xt_ctx_reg *reg,
+				   struct nftnl_expr *e,
+				   struct iptables_command_state *cs)
+{
+	struct in6_addr addr;
+	uint8_t proto;
+	bool inv;
+
+	switch (reg->payload.offset) {
+	case offsetof(struct ip6_hdr, ip6_src):
+		get_cmp_data(e, &addr, sizeof(addr), &inv);
+		memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
+		if (reg->bitwise.set)
+			parse_mask_ipv6(reg, &cs->fw6.ipv6.smsk);
+		else
+			memset(&cs->fw6.ipv6.smsk, 0xff,
+			       min(reg->payload.len, sizeof(struct in6_addr)));
+
+		if (inv)
+			cs->fw6.ipv6.invflags |= IP6T_INV_SRCIP;
+		break;
+	case offsetof(struct ip6_hdr, ip6_dst):
+		get_cmp_data(e, &addr, sizeof(addr), &inv);
+		memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
+		if (reg->bitwise.set)
+			parse_mask_ipv6(reg, &cs->fw6.ipv6.dmsk);
+		else
+			memset(&cs->fw6.ipv6.dmsk, 0xff,
+			       min(reg->payload.len, sizeof(struct in6_addr)));
+
+		if (inv)
+			cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
+		break;
+	case offsetof(struct ip6_hdr, ip6_nxt):
+		get_cmp_data(e, &proto, sizeof(proto), &inv);
+		cs->fw6.ipv6.proto = proto;
+		if (inv)
+			cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
+	case offsetof(struct ip6_hdr, ip6_hlim):
+		if (nft_parse_hl(ctx, e, cs) < 0)
+			ctx->errmsg = "invalid ttl field match";
+		break;
+	default:
+		DEBUGP("unknown payload offset %d\n", reg->payload.offset);
+		ctx->errmsg = "unknown payload offset";
+		break;
+	}
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_ipv6 = {
+	.meta		= nft_ipv6_parse_meta,
+	.payload	= nft_ipv6_parse_payload,
+	.target		= nft_ipv46_parse_target,
+};
diff --git a/iptables/nft-ruleparse.h b/iptables/nft-ruleparse.h
index 69e98817bb6e1..fd083c08ff343 100644
--- a/iptables/nft-ruleparse.h
+++ b/iptables/nft-ruleparse.h
@@ -109,6 +109,11 @@ struct nft_ruleparse_ops {
 		       struct iptables_command_state *cs);
 };
 
+extern struct nft_ruleparse_ops nft_ruleparse_ops_arp;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_bridge;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_ipv4;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_ipv6;
+
 void *nft_create_match(struct nft_xt_ctx *ctx,
 		       struct iptables_command_state *cs,
 		       const char *name, bool reuse);
-- 
2.40.0


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

* Re: [iptables PATCH 0/3] Extract nftnl_rule parsing code
  2023-04-21 17:40 [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
                   ` (2 preceding siblings ...)
  2023-04-21 17:40 ` [iptables PATCH 3/3] nft: ruleparse: Create family-specific source files Phil Sutter
@ 2023-05-03 17:36 ` Phil Sutter
  3 siblings, 0 replies; 5+ messages in thread
From: Phil Sutter @ 2023-05-03 17:36 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Pablo Neira Ayuso, Florian Westphal

On Fri, Apr 21, 2023 at 07:40:11PM +0200, Phil Sutter wrote:
> nft-shared.c was already oversized, upcoming enhancement of the
> nftnl_rule parser will add to that. So prepare any further work in that
> field by creating a common 'nft-ruleparse.c' source and one for each
> family to hold all the parsing code (basically the stack below
> nft_rule_to_iptables_command_state).
> 
> Collect the existing expression parsing callbacks in a new struct
> nft_ruleparse_ops and add a pointer to it into nft_family_ops. This way
> the callbacks may be static and the nft-ruleparse-<family>.c sources
> only export their ops object.
> 
> This series does things somewhat gradually:
> 
> * First pull everything from nft-shared.c into nft-ruleparse.c (likewise
>   with header files)
> * Then perform the *_ops struct changes which should not have a
>   functional implication
> * Finally weed parsers from nft-<family>.c files into
>   nft-ruleparse-<family>.c ones.

Series applied after rebasing it onto current HEAD - the previous
arptables-related fixes caused a minor conflict.

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

end of thread, other threads:[~2023-05-03 17:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-21 17:40 [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter
2023-04-21 17:40 ` [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h} Phil Sutter
2023-04-21 17:40 ` [iptables PATCH 2/3] nft: Extract rule parsing callbacks from nft_family_ops Phil Sutter
2023-04-21 17:40 ` [iptables PATCH 3/3] nft: ruleparse: Create family-specific source files Phil Sutter
2023-05-03 17:36 ` [iptables PATCH 0/3] Extract nftnl_rule parsing code Phil Sutter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox