]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: Introduce nft-ruleparse.{c,h}
authorPhil Sutter <phil@nwl.cc>
Wed, 29 Mar 2023 15:53:11 +0000 (17:53 +0200)
committerPhil Sutter <phil@nwl.cc>
Wed, 3 May 2023 17:09:29 +0000 (19:09 +0200)
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
iptables/nft-ruleparse.c [new file with mode: 0644]
iptables/nft-ruleparse.h [new file with mode: 0644]
iptables/nft-shared.c
iptables/nft-shared.h

index 1f37640f263c99a1760cb90702eb39bc3700baff..d5922da6a2d848c20859a08e3c88f3019f498f32 100644 (file)
@@ -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 (file)
index 0000000..2d84241
--- /dev/null
@@ -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 (file)
index 0000000..7fac6c7
--- /dev/null
@@ -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_ */
index 79f6a7d3fbb8500372b9012284b386a31ce0d3fd..12860fbf6d5750844bccacab2a58cf057a1f1116 100644 (file)
 
 #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 */
index 2c4c0d90cd077a803fc7ca41498757ad4cd6da73..2edee64920e8bbb9d01a074350c23466356e7c62 100644 (file)
@@ -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