netfilter: nft_inner: add percpu inner context
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 17 Oct 2022 11:03:32 +0000 (13:03 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 25 Oct 2022 11:48:42 +0000 (13:48 +0200)
Add NFT_PKTINFO_INNER_FULL flag to annotate that inner offsets are
available. Store nft_inner_tun_ctx object in percpu area to cache
existing inner offsets for this skbuff.

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

index 2dbfe75..38e2b39 100644 (file)
@@ -24,6 +24,7 @@ struct module;
 enum {
        NFT_PKTINFO_L4PROTO     = (1 << 0),
        NFT_PKTINFO_INNER       = (1 << 1),
+       NFT_PKTINFO_INNER_FULL  = (1 << 2),
 };
 
 struct nft_pktinfo {
index be2b2b5..3e82538 100644 (file)
@@ -149,6 +149,7 @@ enum {
 };
 
 struct nft_inner_tun_ctx {
+       u16     type;
        u16     inner_tunoff;
        u16     inner_lloff;
        u16     inner_nhoff;
index 1e4079b..29f2eef 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 
+static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx);
+
 /* Same layout as nft_expr but it embeds the private expression data area. */
 struct __nft_expr {
        const struct nft_expr_ops       *ops;
@@ -180,7 +182,7 @@ static int nft_inner_parse_tunhdr(const struct nft_inner *priv,
 }
 
 static int nft_inner_parse(const struct nft_inner *priv,
-                          const struct nft_pktinfo *pkt,
+                          struct nft_pktinfo *pkt,
                           struct nft_inner_tun_ctx *tun_ctx)
 {
        struct nft_inner_tun_ctx ctx = {};
@@ -199,25 +201,41 @@ static int nft_inner_parse(const struct nft_inner *priv,
        }
 
        *tun_ctx = ctx;
+       tun_ctx->type = priv->type;
+       pkt->flags |= NFT_PKTINFO_INNER_FULL;
 
        return 0;
 }
 
+static bool nft_inner_parse_needed(const struct nft_inner *priv,
+                                  const struct nft_pktinfo *pkt,
+                                  const struct nft_inner_tun_ctx *tun_ctx)
+{
+       if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
+               return true;
+
+       if (priv->type != tun_ctx->type)
+               return true;
+
+       return false;
+}
+
 static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
                           const struct nft_pktinfo *pkt)
 {
+       struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
        const struct nft_inner *priv = nft_expr_priv(expr);
-       struct nft_inner_tun_ctx tun_ctx = {};
 
        if (nft_payload_inner_offset(pkt) < 0)
                goto err;
 
-       if (nft_inner_parse(priv, pkt, &tun_ctx) < 0)
+       if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
+           nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
                goto err;
 
        switch (priv->expr_type) {
        case NFT_INNER_EXPR_PAYLOAD:
-               nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx);
+               nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
                break;
        default:
                WARN_ON_ONCE(1);