From patchwork Fri Apr 21 17:40:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Sutter X-Patchwork-Id: 1772080 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2620:137:e000::1:20; helo=out1.vger.email; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nwl.cc header.i=@nwl.cc header.a=rsa-sha256 header.s=mail2022 header.b=C/UkWsy/; dkim-atps=neutral Received: from out1.vger.email (out1.vger.email [IPv6:2620:137:e000::1:20]) by legolas.ozlabs.org (Postfix) with ESMTP id 4Q31y05SRNz23rW for ; Sat, 22 Apr 2023 03:40:08 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229534AbjDURkG (ORCPT ); Fri, 21 Apr 2023 13:40:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40102 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233140AbjDURj7 (ORCPT ); Fri, 21 Apr 2023 13:39:59 -0400 Received: from orbyte.nwl.cc (orbyte.nwl.cc [IPv6:2001:41d0:e:133a::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BB67A12582 for ; Fri, 21 Apr 2023 10:39:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nwl.cc; s=mail2022; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=0gnhBos42CEcw7g9FyeW/dSh8EXMUpzSdTBilBnin4Q=; b=C/UkWsy/h0xK7+etnne0C9zGrv YzTwAnJp8bt3QCBYYt6NHJncv0wxUXM61frr2vAp5epLaWSZxCtumR5ZAYAfe3ocQ+4dD5Oq5iDrl tdY7/DYzmNyBbhrMky4t8mloZalLzEntL1GoMv9vKdkqF+QQnxXoLH0nQHo4DKR4cgtAvZt9GuuJO h9hXWTouQr1pkpkvRUXjWWxML3U0kBipNRlKbGdmUtVQMe5reRHaPtFooxF78PSWjj4fC2ool/wT7 xPM2diYUhCw/znDqxtWtQZv9Pjibnph1x7nSlpqvaX8l291PBBkcSTUASOf8XJkY3UIPs++zepifO WcA3GgTg==; Received: from localhost ([::1] helo=xic) by orbyte.nwl.cc with esmtp (Exim 4.94.2) (envelope-from ) id 1ppujd-00086F-Rv; Fri, 21 Apr 2023 19:39:50 +0200 From: Phil Sutter To: netfilter-devel@vger.kernel.org Cc: Pablo Neira Ayuso , Florian Westphal Subject: [iptables PATCH 1/3] nft: Introduce nft-ruleparse.{c,h} Date: Fri, 21 Apr 2023 19:40:12 +0200 Message-Id: <20230421174014.17014-2-phil@nwl.cc> X-Mailer: git-send-email 2.40.0 In-Reply-To: <20230421174014.17014-1-phil@nwl.cc> References: <20230421174014.17014-1-phil@nwl.cc> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org 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 --- 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 + * (C) 2013 by Tomasz Bursztyka + * + * 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 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#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 + +#include + +#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 -#include -#include -#include -#include -#include - -#include - #include #include #include @@ -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 #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 From patchwork Fri Apr 21 17:40:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Sutter X-Patchwork-Id: 1772083 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2620:137:e000::1:20; helo=out1.vger.email; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nwl.cc header.i=@nwl.cc header.a=rsa-sha256 header.s=mail2022 header.b=oCK6Jbuh; dkim-atps=neutral Received: from out1.vger.email (out1.vger.email [IPv6:2620:137:e000::1:20]) by legolas.ozlabs.org (Postfix) with ESMTP id 4Q31y445cqz1ybF for ; Sat, 22 Apr 2023 03:40:12 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233156AbjDURkL (ORCPT ); Fri, 21 Apr 2023 13:40:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40234 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232911AbjDURkK (ORCPT ); Fri, 21 Apr 2023 13:40:10 -0400 Received: from orbyte.nwl.cc (orbyte.nwl.cc [IPv6:2001:41d0:e:133a::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B24D613864 for ; Fri, 21 Apr 2023 10:40:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nwl.cc; s=mail2022; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=wCLfagmr/0IHGbagbWgQHERO3yVCdf/D+7f9Pe7YEeA=; b=oCK6Jbuhb9lty1/WQs9Kh2Zgce MEBH1teYj3y9c4IvpVkkodjk9YHdNnD1Yt/D4kfsa2bgsedookmDqi5bJxm14xGTEsYfgu/WOEDvK 6EsCJ1hMWJS8uXvsBci1u3SzFKfm8+hQtq+wjYq7FQ3nHbo4IqjMxO0UrboH01TPFgJMo6UQfSEg6 u6/1ZFQ+tGtWb2UN1wVdJMb0UD18qDoeAUibuaXD+VGwoAuRHW+pOYq10oXWa3M7+yaw8+HnuIDvw DvBdrelkLVGl3Q7D11gBmWqYJReUSo4LwIHL7you41EbP4qFW1TI6e+U355M2qq5sFaPTlgP9x0Bw d4laZt6g==; Received: from localhost ([::1] helo=xic) by orbyte.nwl.cc with esmtp (Exim 4.94.2) (envelope-from ) id 1ppuju-00089P-JT; Fri, 21 Apr 2023 19:40:06 +0200 From: Phil Sutter To: netfilter-devel@vger.kernel.org Cc: Pablo Neira Ayuso , Florian Westphal Subject: [iptables PATCH 2/3] nft: Extract rule parsing callbacks from nft_family_ops Date: Fri, 21 Apr 2023 19:40:13 +0200 Message-Id: <20230421174014.17014-3-phil@nwl.cc> X-Mailer: git-send-email 2.40.0 In-Reply-To: <20230421174014.17014-1-phil@nwl.cc> References: <20230421174014.17014-1-phil@nwl.cc> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org 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 --- 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); From patchwork Fri Apr 21 17:40:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Phil Sutter X-Patchwork-Id: 1772082 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2620:137:e000::1:20; helo=out1.vger.email; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nwl.cc header.i=@nwl.cc header.a=rsa-sha256 header.s=mail2022 header.b=fYg+SbAz; dkim-atps=neutral Received: from out1.vger.email (out1.vger.email [IPv6:2620:137:e000::1:20]) by legolas.ozlabs.org (Postfix) with ESMTP id 4Q31y26N4Yz23tm for ; Sat, 22 Apr 2023 03:40:10 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232834AbjDURkH (ORCPT ); Fri, 21 Apr 2023 13:40:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40136 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233170AbjDURkD (ORCPT ); Fri, 21 Apr 2023 13:40:03 -0400 Received: from orbyte.nwl.cc (orbyte.nwl.cc [IPv6:2001:41d0:e:133a::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D718C1258D for ; Fri, 21 Apr 2023 10:39:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nwl.cc; s=mail2022; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=NngMboLe69LKcRNwzNViWaNjkid+HHVnFFpXkOCchqM=; b=fYg+SbAzvl7iUkLjZuLX2oCbq3 471GFy5pXgwH+4mjLGdxUfQMZ8YnlGtib7zp+66AX5Pz7TrBpwumBKTE6yuQINT0GxUCymnKWVunL IEiXKL6iXyMHXaRnqKgAnLSjlwgCYJk+01ubaI8flRDeARrVaaPpjGcfItzo8lV1czPv4A+ONhYiG TJ/Q9LU89oOifhCG1QZiFZPlfnoyskOapswc8MLnAXJbWnGp/ryFfDQTep77hcyMWbZ/JC7AJOuaq NIVnrSgQV3tAzXhjIr9vUAF/q3uaqYiNVXQMJHmTbfKVfn1CX5qzorFZXEewIGleaKIRRrC/M7Cv4 832OlLwQ==; Received: from localhost ([::1] helo=xic) by orbyte.nwl.cc with esmtp (Exim 4.94.2) (envelope-from ) id 1ppujj-00086N-QB; Fri, 21 Apr 2023 19:39:55 +0200 From: Phil Sutter To: netfilter-devel@vger.kernel.org Cc: Pablo Neira Ayuso , Florian Westphal Subject: [iptables PATCH 3/3] nft: ruleparse: Create family-specific source files Date: Fri, 21 Apr 2023 19:40:14 +0200 Message-Id: <20230421174014.17014-4-phil@nwl.cc> X-Mailer: git-send-email 2.40.0 In-Reply-To: <20230421174014.17014-1-phil@nwl.cc> References: <20230421174014.17014-1-phil@nwl.cc> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org Extract the remaining nftnl rule parsing code from nft-.c sources into dedicated ones to complete the separation. Signed-off-by: Phil Sutter --- 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, ðproto, 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 + 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 + * (C) 2013 by Giuseppe Longo + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include +#include +//#include +#include + +#include +#include +#include + +#include + +#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, ðproto, 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 + * (C) 2013 by Tomasz Bursztyka + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + * (C) 2013 by Tomasz Bursztyka + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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);