netfilter: nf_tables: bail out early if hardware offload is not supported
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 6 Jun 2022 15:31:29 +0000 (17:31 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Jun 2022 16:36:17 +0000 (18:36 +0200)
[ Upstream commit 3a41c64d9c1185a2f3a184015e2a9b78bfc99c71 ]

If user requests for NFT_CHAIN_HW_OFFLOAD, then check if either device
provides the .ndo_setup_tc interface or there is an indirect flow block
that has been registered. Otherwise, bail out early from the preparation
phase. Moreover, validate that family == NFPROTO_NETDEV and hook is
NF_NETDEV_INGRESS.

Fixes: c9626a2cbdb2 ("netfilter: nf_tables: add hardware offload support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/flow_offload.h
include/net/netfilter/nf_tables_offload.h
net/core/flow_offload.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_offload.c

index 3961461d9c8bc0f1f2643c47c892dce6d531e087..7a2b0223a02c74ad7a6e4a4ba3bf61557b9521a9 100644 (file)
@@ -575,5 +575,6 @@ int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
                                enum tc_setup_type type, void *data,
                                struct flow_block_offload *bo,
                                void (*cleanup)(struct flow_block_cb *block_cb));
+bool flow_indr_dev_exists(void);
 
 #endif /* _NET_FLOW_OFFLOAD_H */
index 7971478439580c6a7db49892d021e1abfc9e9321..3568b6a2f5f0fcc918311a5b18daf0186b7b1016 100644 (file)
@@ -92,7 +92,7 @@ int nft_flow_rule_offload_commit(struct net *net);
        NFT_OFFLOAD_MATCH(__key, __base, __field, __len, __reg)         \
        memset(&(__reg)->mask, 0xff, (__reg)->len);
 
-int nft_chain_offload_priority(struct nft_base_chain *basechain);
+bool nft_chain_offload_support(const struct nft_base_chain *basechain);
 
 int nft_offload_init(void);
 void nft_offload_exit(void);
index 6beaea13564a8208f66004df24796c947065f08f..fb11103fa8afcd477c3a50c1275674caceb3960f 100644 (file)
@@ -565,3 +565,9 @@ int flow_indr_dev_setup_offload(struct net_device *dev,     struct Qdisc *sch,
        return list_empty(&bo->cb_list) ? -EOPNOTSUPP : 0;
 }
 EXPORT_SYMBOL(flow_indr_dev_setup_offload);
+
+bool flow_indr_dev_exists(void)
+{
+       return !list_empty(&flow_block_indr_dev_list);
+}
+EXPORT_SYMBOL(flow_indr_dev_exists);
index 1528620df34c19a7fe0d7734e60aab2c08e69326..1b4bc588f8d63d0bf58c097b1402cba3bcb503bd 100644 (file)
@@ -2072,7 +2072,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
        chain->flags |= NFT_CHAIN_BASE | flags;
        basechain->policy = NF_ACCEPT;
        if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
-           nft_chain_offload_priority(basechain) < 0)
+           !nft_chain_offload_support(basechain))
                return -EOPNOTSUPP;
 
        flow_block_init(&basechain->flow_block);
index 2d36952b13920d149110e7d90dff8d7069e78ff8..910ef881c3b853c35e4a2becf7eff31d666556fb 100644 (file)
@@ -208,7 +208,7 @@ static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
        return 0;
 }
 
-int nft_chain_offload_priority(struct nft_base_chain *basechain)
+static int nft_chain_offload_priority(const struct nft_base_chain *basechain)
 {
        if (basechain->ops.priority <= 0 ||
            basechain->ops.priority > USHRT_MAX)
@@ -217,6 +217,27 @@ int nft_chain_offload_priority(struct nft_base_chain *basechain)
        return 0;
 }
 
+bool nft_chain_offload_support(const struct nft_base_chain *basechain)
+{
+       struct net_device *dev;
+       struct nft_hook *hook;
+
+       if (nft_chain_offload_priority(basechain) < 0)
+               return false;
+
+       list_for_each_entry(hook, &basechain->hook_list, list) {
+               if (hook->ops.pf != NFPROTO_NETDEV ||
+                   hook->ops.hooknum != NF_NETDEV_INGRESS)
+                       return false;
+
+               dev = hook->ops.dev;
+               if (!dev->netdev_ops->ndo_setup_tc && !flow_indr_dev_exists())
+                       return false;
+       }
+
+       return true;
+}
+
 static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
                                       const struct nft_base_chain *basechain,
                                       const struct nft_rule *rule,