netfilter: nft_meta: add inner match support
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 17 Oct 2022 11:03:33 +0000 (13:03 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 25 Oct 2022 11:48:42 +0000 (13:48 +0200)
Add support for inner meta matching on:

- NFT_META_PROTOCOL: to match on the ethertype, this can be used
  regardless tunnel protocol provides no link layer header, in that case
  nft_inner sets on the ethertype based on the IP header version field.
- NFT_META_L4PROTO: to match on the layer 4 protocol.

These meta expression are usually autogenerated as dependencies by
userspace nftables.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nft_meta.h
net/netfilter/nft_inner.c
net/netfilter/nft_meta.c

index 9b51cc6..f3a5285 100644 (file)
@@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,
 
 bool nft_meta_get_reduce(struct nft_regs_track *track,
                         const struct nft_expr *expr);
+
+struct nft_inner_tun_ctx;
+void nft_meta_inner_eval(const struct nft_expr *expr,
+                        struct nft_regs *regs, const struct nft_pktinfo *pkt,
+                        struct nft_inner_tun_ctx *tun_ctx);
+
 #endif
index 29f2eef..c43a2fe 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables_core.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_meta.h>
 #include <net/netfilter/nf_tables_offload.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
@@ -28,11 +29,13 @@ struct __nft_expr {
        const struct nft_expr_ops       *ops;
        union {
                struct nft_payload      payload;
+               struct nft_meta         meta;
        } __attribute__((aligned(__alignof__(u64))));
 };
 
 enum {
        NFT_INNER_EXPR_PAYLOAD,
+       NFT_INNER_EXPR_META,
 };
 
 struct nft_inner {
@@ -237,6 +240,9 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
        case NFT_INNER_EXPR_PAYLOAD:
                nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
                break;
+       case NFT_INNER_EXPR_META:
+               nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
+               break;
        default:
                WARN_ON_ONCE(1);
                goto err;
@@ -306,6 +312,8 @@ static int nft_inner_init(const struct nft_ctx *ctx,
 
        if (!strcmp(expr_info.ops->type->name, "payload"))
                priv->expr_type = NFT_INNER_EXPR_PAYLOAD;
+       else if (!strcmp(expr_info.ops->type->name, "meta"))
+               priv->expr_type = NFT_INNER_EXPR_META;
        else
                return -EINVAL;
 
index 55d2d49..8c39ade 100644 (file)
@@ -831,9 +831,71 @@ nft_meta_select_ops(const struct nft_ctx *ctx,
        return ERR_PTR(-EINVAL);
 }
 
+static int nft_meta_inner_init(const struct nft_ctx *ctx,
+                              const struct nft_expr *expr,
+                              const struct nlattr * const tb[])
+{
+       struct nft_meta *priv = nft_expr_priv(expr);
+       unsigned int len;
+
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
+       case NFT_META_PROTOCOL:
+               len = sizeof(u16);
+               break;
+       case NFT_META_L4PROTO:
+               len = sizeof(u32);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       priv->len = len;
+
+       return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
+                                       NULL, NFT_DATA_VALUE, len);
+}
+
+void nft_meta_inner_eval(const struct nft_expr *expr,
+                        struct nft_regs *regs,
+                        const struct nft_pktinfo *pkt,
+                        struct nft_inner_tun_ctx *tun_ctx)
+{
+       const struct nft_meta *priv = nft_expr_priv(expr);
+       u32 *dest = &regs->data[priv->dreg];
+
+       switch (priv->key) {
+       case NFT_META_PROTOCOL:
+               nft_reg_store16(dest, (__force u16)tun_ctx->llproto);
+               break;
+       case NFT_META_L4PROTO:
+               if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH))
+                       goto err;
+
+               nft_reg_store8(dest, tun_ctx->l4proto);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               goto err;
+       }
+       return;
+
+err:
+       regs->verdict.code = NFT_BREAK;
+}
+EXPORT_SYMBOL_GPL(nft_meta_inner_eval);
+
+static const struct nft_expr_ops nft_meta_inner_ops = {
+       .type           = &nft_meta_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .init           = nft_meta_inner_init,
+       .dump           = nft_meta_get_dump,
+       /* direct call to nft_meta_inner_eval(). */
+};
+
 struct nft_expr_type nft_meta_type __read_mostly = {
        .name           = "meta",
        .select_ops     = nft_meta_select_ops,
+       .inner_ops      = &nft_meta_inner_ops,
        .policy         = nft_meta_policy,
        .maxattr        = NFTA_META_MAX,
        .owner          = THIS_MODULE,