OSDN Git Service

netfilter: nft_{fwd,dup}_netdev: add offload support
authorPablo Neira Ayuso <pablo@netfilter.org>
Sun, 8 Sep 2019 17:32:05 +0000 (19:32 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 10 Sep 2019 20:44:29 +0000 (22:44 +0200)
This patch adds support for packet mirroring and redirection. The
nft_fwd_dup_netdev_offload() function configures the flow_action object
for the fwd and the dup actions.

Extend nft_flow_rule_destroy() to release the net_device object when the
flow_rule object is released, since nft_fwd_dup_netdev_offload() bumps
the net_device reference counter.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Acked-by: wenxu <wenxu@ucloud.cn>
include/net/netfilter/nf_dup_netdev.h
include/net/netfilter/nf_tables_offload.h
net/netfilter/nf_dup_netdev.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_offload.c
net/netfilter/nft_dup_netdev.c
net/netfilter/nft_fwd_netdev.c

index 1816726..b175d27 100644 (file)
@@ -7,4 +7,10 @@
 void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
 void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif);
 
+struct nft_offload_ctx;
+struct nft_flow_rule;
+
+int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
+                              struct nft_flow_rule *flow,
+                              enum flow_action_id id, int oif);
 #endif
index 6de896e..ddd048b 100644 (file)
@@ -26,6 +26,7 @@ struct nft_offload_ctx {
                u8                              protonum;
        } dep;
        unsigned int                            num_actions;
+       struct net                              *net;
        struct nft_offload_reg                  regs[NFT_REG32_15 + 1];
 };
 
@@ -61,7 +62,7 @@ struct nft_flow_rule {
 #define NFT_OFFLOAD_F_ACTION   (1 << 0)
 
 struct nft_rule;
-struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule);
+struct nft_flow_rule *nft_flow_rule_create(struct net *net, const struct nft_rule *rule);
 void nft_flow_rule_destroy(struct nft_flow_rule *flow);
 int nft_flow_rule_offload_commit(struct net *net);
 
index 5a35ef0..f108a76 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_offload.h>
 #include <net/netfilter/nf_dup_netdev.h>
 
 static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev)
@@ -50,5 +51,25 @@ void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif)
 }
 EXPORT_SYMBOL_GPL(nf_dup_netdev_egress);
 
+int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
+                              struct nft_flow_rule *flow,
+                              enum flow_action_id id, int oif)
+{
+       struct flow_action_entry *entry;
+       struct net_device *dev;
+
+       /* nft_flow_rule_destroy() releases the reference on this device. */
+       dev = dev_get_by_index(ctx->net, oif);
+       if (!dev)
+               return -EOPNOTSUPP;
+
+       entry = &flow->rule->action.entries[ctx->num_actions++];
+       entry->id = id;
+       entry->dev = dev;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nft_fwd_dup_netdev_offload);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
index efd0c97..c6f59ef 100644 (file)
@@ -2853,7 +2853,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
                return nft_table_validate(net, table);
 
        if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
-               flow = nft_flow_rule_create(rule);
+               flow = nft_flow_rule_create(net, rule);
                if (IS_ERR(flow))
                        return PTR_ERR(flow);
 
index 8abf193..239cb78 100644 (file)
@@ -28,7 +28,8 @@ static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
        return flow;
 }
 
-struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule)
+struct nft_flow_rule *nft_flow_rule_create(struct net *net,
+                                          const struct nft_rule *rule)
 {
        struct nft_offload_ctx *ctx;
        struct nft_flow_rule *flow;
@@ -54,6 +55,7 @@ struct nft_flow_rule *nft_flow_rule_create(const struct nft_rule *rule)
                err = -ENOMEM;
                goto err_out;
        }
+       ctx->net = net;
        ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
 
        while (expr->ops && expr != nft_expr_last(rule)) {
@@ -80,6 +82,19 @@ err_out:
 
 void nft_flow_rule_destroy(struct nft_flow_rule *flow)
 {
+       struct flow_action_entry *entry;
+       int i;
+
+       flow_action_for_each(i, entry, &flow->rule->action) {
+               switch (entry->id) {
+               case FLOW_ACTION_REDIRECT:
+               case FLOW_ACTION_MIRRED:
+                       dev_put(entry->dev);
+                       break;
+               default:
+                       break;
+               }
+       }
        kfree(flow->rule);
        kfree(flow);
 }
index c6052fd..c2e78c1 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_offload.h>
 #include <net/netfilter/nf_dup_netdev.h>
 
 struct nft_dup_netdev {
@@ -56,6 +57,16 @@ nla_put_failure:
        return -1;
 }
 
+static int nft_dup_netdev_offload(struct nft_offload_ctx *ctx,
+                                 struct nft_flow_rule *flow,
+                                 const struct nft_expr *expr)
+{
+       const struct nft_dup_netdev *priv = nft_expr_priv(expr);
+       int oif = ctx->regs[priv->sreg_dev].data.data[0];
+
+       return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_MIRRED, oif);
+}
+
 static struct nft_expr_type nft_dup_netdev_type;
 static const struct nft_expr_ops nft_dup_netdev_ops = {
        .type           = &nft_dup_netdev_type,
@@ -63,6 +74,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = {
        .eval           = nft_dup_netdev_eval,
        .init           = nft_dup_netdev_init,
        .dump           = nft_dup_netdev_dump,
+       .offload        = nft_dup_netdev_offload,
 };
 
 static struct nft_expr_type nft_dup_netdev_type __read_mostly = {
index 61b7f93..aba11c2 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_offload.h>
 #include <net/netfilter/nf_dup_netdev.h>
 #include <net/neighbour.h>
 #include <net/ip.h>
@@ -63,6 +64,16 @@ nla_put_failure:
        return -1;
 }
 
+static int nft_fwd_netdev_offload(struct nft_offload_ctx *ctx,
+                                 struct nft_flow_rule *flow,
+                                 const struct nft_expr *expr)
+{
+       const struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+       int oif = ctx->regs[priv->sreg_dev].data.data[0];
+
+       return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_REDIRECT, oif);
+}
+
 struct nft_fwd_neigh {
        enum nft_registers      sreg_dev:8;
        enum nft_registers      sreg_addr:8;
@@ -194,6 +205,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = {
        .eval           = nft_fwd_netdev_eval,
        .init           = nft_fwd_netdev_init,
        .dump           = nft_fwd_netdev_dump,
+       .offload        = nft_fwd_netdev_offload,
 };
 
 static const struct nft_expr_ops *