[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <55c8780bd9c4e8bf9bffef3d8becf7eb094677dc.camel@redhat.com>
Date: Thu, 14 Sep 2023 09:56:28 +0200
From: Paolo Abeni <pabeni@...hat.com>
To: Victor Nogueira <victor@...atatu.com>, jhs@...atatu.com,
xiyou.wangcong@...il.com, jiri@...nulli.us, davem@...emloft.net,
edumazet@...gle.com, kuba@...nel.org
Cc: mleitner@...hat.com, vladbu@...dia.com, horms@...nel.org,
pctammela@...atatu.com, netdev@...r.kernel.org, kernel@...atatu.com
Subject: Re: [PATCH net-next v3 3/3] net/sched: act_blockcast: Introduce
blockcast tc action
On Mon, 2023-09-11 at 20:27 -0300, Victor Nogueira wrote:
> This action takes advantage of the presence of tc block ports set in the
> datapath and broadcast a packet to all ports on that set with exception of
> the port in which it arrived on.
>
> Example usage:
> $ tc qdisc add dev ens7 ingress block 22
> $ tc qdisc add dev ens8 ingress block 22
>
> Now we can add a filter using the block index:
> $ tc filter add block 22 protocol ip pref 25 \
> flower dst_ip 192.168.0.0/16 action blockcast
>
> Co-developed-by: Jamal Hadi Salim <jhs@...atatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@...atatu.com>
> Co-developed-by: Pedro Tammela <pctammela@...atatu.com>
> Signed-off-by: Pedro Tammela <pctammela@...atatu.com>
> Signed-off-by: Victor Nogueira <victor@...atatu.com>
> ---
> include/net/tc_wrapper.h | 5 +
> include/uapi/linux/pkt_cls.h | 1 +
> net/sched/Kconfig | 13 ++
> net/sched/Makefile | 1 +
> net/sched/act_blockcast.c | 297 +++++++++++++++++++++++++++++++++++
> 5 files changed, 317 insertions(+)
> create mode 100644 net/sched/act_blockcast.c
>
> diff --git a/include/net/tc_wrapper.h b/include/net/tc_wrapper.h
> index a6d481b5bcbc..8ef848968be7 100644
> --- a/include/net/tc_wrapper.h
> +++ b/include/net/tc_wrapper.h
> @@ -28,6 +28,7 @@ TC_INDIRECT_ACTION_DECLARE(tcf_csum_act);
> TC_INDIRECT_ACTION_DECLARE(tcf_ct_act);
> TC_INDIRECT_ACTION_DECLARE(tcf_ctinfo_act);
> TC_INDIRECT_ACTION_DECLARE(tcf_gact_act);
> +TC_INDIRECT_ACTION_DECLARE(tcf_blockcast_run);
> TC_INDIRECT_ACTION_DECLARE(tcf_gate_act);
> TC_INDIRECT_ACTION_DECLARE(tcf_ife_act);
> TC_INDIRECT_ACTION_DECLARE(tcf_ipt_act);
> @@ -57,6 +58,10 @@ static inline int tc_act(struct sk_buff *skb, const struct tc_action *a,
> if (a->ops->act == tcf_mirred_act)
> return tcf_mirred_act(skb, a, res);
> #endif
> +#if IS_BUILTIN(CONFIG_NET_ACT_BLOCKCAST)
> + if (a->ops->act == tcf_blockcast_run)
> + return tcf_blockcast_run(skb, a, res);
> +#endif
> #if IS_BUILTIN(CONFIG_NET_ACT_PEDIT)
> if (a->ops->act == tcf_pedit_act)
> return tcf_pedit_act(skb, a, res);
> diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
> index c7082cc60d21..e12fc51c1be1 100644
> --- a/include/uapi/linux/pkt_cls.h
> +++ b/include/uapi/linux/pkt_cls.h
> @@ -139,6 +139,7 @@ enum tca_id {
> TCA_ID_MPLS,
> TCA_ID_CT,
> TCA_ID_GATE,
> + TCA_ID_BLOCKCAST,
> /* other actions go here */
> __TCA_ID_MAX = 255
> };
> diff --git a/net/sched/Kconfig b/net/sched/Kconfig
> index 470c70deffe2..abf26f0c921f 100644
> --- a/net/sched/Kconfig
> +++ b/net/sched/Kconfig
> @@ -780,6 +780,19 @@ config NET_ACT_SIMP
> To compile this code as a module, choose M here: the
> module will be called act_simple.
>
> +config NET_ACT_BLOCKCAST
> + tristate "TC block Multicast"
> + depends on NET_CLS_ACT
> + help
> + Say Y here to add an action that will multicast an skb to egress of
> + all netdevs that belong to a tc block except for the netdev on which
> + the skb arrived on
> +
> + If unsure, say N.
> +
> + To compile this code as a module, choose M here: the
> + module will be called act_blockcast.
> +
> config NET_ACT_SKBEDIT
> tristate "SKB Editing"
> depends on NET_CLS_ACT
> diff --git a/net/sched/Makefile b/net/sched/Makefile
> index b5fd49641d91..2cdcf30645eb 100644
> --- a/net/sched/Makefile
> +++ b/net/sched/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_NET_ACT_IPT) += act_ipt.o
> obj-$(CONFIG_NET_ACT_NAT) += act_nat.o
> obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o
> obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o
> +obj-$(CONFIG_NET_ACT_BLOCKCAST) += act_blockcast.o
> obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o
> obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
> obj-$(CONFIG_NET_ACT_MPLS) += act_mpls.o
> diff --git a/net/sched/act_blockcast.c b/net/sched/act_blockcast.c
> new file mode 100644
> index 000000000000..823a7c209173
> --- /dev/null
> +++ b/net/sched/act_blockcast.c
> @@ -0,0 +1,297 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * net/sched/act_blockcast.c Block Cast action
> + * Copyright (c) 2023, Mojatatu Networks
> + * Authors: Jamal Hadi Salim <jhs@...atatu.com>
> + * Victor Nogueira <victor@...atatu.com>
> + * Pedro Tammela <pctammela@...atatu.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/skbuff.h>
> +#include <linux/rtnetlink.h>
> +#include <net/netlink.h>
> +#include <net/pkt_sched.h>
> +#include <net/pkt_cls.h>
> +#include <linux/if_arp.h>
> +#include <net/tc_wrapper.h>
> +
> +#include <linux/tc_act/tc_defact.h>
> +
> +static struct tc_action_ops act_blockcast_ops;
> +
> +struct tcf_blockcast_act {
> + struct tc_action common;
> +};
> +
> +#define to_blockcast_act(a) ((struct tcf_blockcast_act *)a)
> +
> +#define CAST_RECURSION_LIMIT 4
> +
> +static DEFINE_PER_CPU(unsigned int, redirect_rec_level);
> +
> +static int cast_one(struct sk_buff *skb, const u32 ifindex)
> +{
> + struct sk_buff *skb2 = skb;
> + int retval = TC_ACT_PIPE;
> + struct net_device *dev;
> + unsigned int rec_level;
> + bool expects_nh;
> + int mac_len;
> + bool at_nh;
> + int err;
> +
> + rec_level = __this_cpu_inc_return(redirect_rec_level);
> + if (unlikely(rec_level > CAST_RECURSION_LIMIT)) {
> + net_warn_ratelimited("blockcast: exceeded redirect recursion limit on dev %s\n",
> + netdev_name(skb->dev));
> + __this_cpu_dec(redirect_rec_level);
> + return TC_ACT_SHOT;
You can avoid a bunch of duplicate code replacing the error paths with:
goto out_shot;
> + }
> +
> + dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex);
> + if (unlikely(!dev)) {
> + __this_cpu_dec(redirect_rec_level);
> + return TC_ACT_SHOT;
> + }
> +
> + if (unlikely(!(dev->flags & IFF_UP) || !netif_carrier_ok(dev))) {
> + __this_cpu_dec(redirect_rec_level);
> + return TC_ACT_SHOT;
> + }
> +
> + skb2 = skb_clone(skb, GFP_ATOMIC);
> + if (!skb2) {
> + __this_cpu_dec(redirect_rec_level);
> + return retval;
> + }
> +
> + nf_reset_ct(skb2);
> +
> + expects_nh = !dev_is_mac_header_xmit(dev);
> + at_nh = skb->data == skb_network_header(skb);
> + if (at_nh != expects_nh) {
> + mac_len = skb_at_tc_ingress(skb) ?
> + skb->mac_len :
> + skb_network_header(skb) - skb_mac_header(skb);
> +
> + if (expects_nh) {
> + /* target device/action expect data at nh */
> + skb_pull_rcsum(skb2, mac_len);
> + } else {
> + /* target device/action expect data at mac */
> + skb_push_rcsum(skb2, mac_len);
> + }
> + }
> +
> + skb2->skb_iif = skb->dev->ifindex;
> + skb2->dev = dev;
> +
> + err = dev_queue_xmit(skb2);
> + if (err)
> + retval = TC_ACT_SHOT;
> +
Here with:
goto out_shot;
out:
> + __this_cpu_dec(redirect_rec_level);
> +
> + return retval;
And finally:
out_shot:
retval = TC_ACT_SHOT;
goto out;
> +}
> +
> +TC_INDIRECT_SCOPE int tcf_blockcast_run(struct sk_buff *skb,
> + const struct tc_action *a,
> + struct tcf_result *res)
> +{
> + u32 block_index = qdisc_skb_cb(skb)->block_index;
> + struct tcf_blockcast_act *p = to_blockcast_act(a);
> + int action = READ_ONCE(p->tcf_action);
> + struct net *net = dev_net(skb->dev);
> + struct tcf_block *block;
> + struct net_device *dev;
> + u32 exception_ifindex;
> + unsigned long index;
> +
> + block = tcf_block_lookup(net, block_index);
> + exception_ifindex = skb->dev->ifindex;
> +
> + tcf_action_update_bstats(&p->common, skb);
> + tcf_lastuse_update(&p->tcf_tm);
> +
> + if (!block || xa_empty(&block->ports))
> + goto act_done;
> +
> + /* we are already under rcu protection, so iterating block is safe*/
> + xa_for_each(&block->ports, index, dev) {
> + int err;
> +
> + if (index == exception_ifindex)
> + continue;
> +
> + err = cast_one(skb, dev->ifindex);
> + if (err != TC_ACT_PIPE)
> + tcf_action_inc_overlimit_qstats(&p->common);
> + }
> +
> +act_done:
> + if (action == TC_ACT_SHOT)
> + tcf_action_inc_drop_qstats(&p->common);
> + return action;
> +}
> +
> +static const struct nla_policy blockcast_policy[TCA_DEF_MAX + 1] = {
> + [TCA_DEF_PARMS] = { .len = sizeof(struct tc_defact) },
> +};
> +
> +static int tcf_blockcast_init(struct net *net, struct nlattr *nla,
> + struct nlattr *est, struct tc_action **a,
> + struct tcf_proto *tp, u32 flags,
> + struct netlink_ext_ack *extack)
> +{
> + struct tc_action_net *tn = net_generic(net, act_blockcast_ops.net_id);
> + struct tcf_blockcast_act *p = to_blockcast_act(a);
> + bool bind = flags & TCA_ACT_FLAGS_BIND;
> + struct nlattr *tb[TCA_DEF_MAX + 1];
> + struct tcf_chain *goto_ch = NULL;
> + struct tc_defact *parm;
> + bool exists = false;
> + int ret = 0, err;
> + u32 index;
> +
> + if (!nla)
> + return -EINVAL;
> +
> + err = nla_parse_nested(tb, TCA_DEF_MAX, nla,
> + blockcast_policy, extack);
> + if (err < 0)
> + return err;
> +
> + if (!tb[TCA_DEF_PARMS])
> + return -EINVAL;
> +
> + parm = nla_data(tb[TCA_DEF_PARMS]);
> + index = parm->index;
> +
> + err = tcf_idr_check_alloc(tn, &index, a, bind);
> + if (err < 0)
> + return err;
> +
> + exists = err;
> + if (exists && bind)
> + return 0;
> +
> + if (!exists) {
> + ret = tcf_idr_create_from_flags(tn, index, est, a,
> + &act_blockcast_ops, bind, flags);
> + if (ret) {
> + tcf_idr_cleanup(tn, index);
> + return ret;
> + }
> +
> + ret = ACT_P_CREATED;
> + } else {
> + if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
> + err = -EEXIST;
> + goto release_idr;
> + }
> + }
> +
> + err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
> + if (err < 0)
> + goto release_idr;
> +
> + if (exists) {
> + spin_lock_bh(&p->tcf_lock);
> + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
> + spin_unlock_bh(&p->tcf_lock);
> + } else {
> + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
> + }
> +
> + if (goto_ch)
> + tcf_chain_put_by_act(goto_ch);
> +
> + return ret;
> +release_idr:
> + tcf_idr_release(*a, bind);
> + return err;
> +}
> +
> +static int tcf_blockcast_dump(struct sk_buff *skb, struct tc_action *a,
> + int bind, int ref)
> +{
> + unsigned char *b = skb_tail_pointer(skb);
> + struct tcf_blockcast_act *p = to_blockcast_act(a);
> + struct tc_defact opt = {
> + .index = p->tcf_index,
> + .refcnt = refcount_read(&p->tcf_refcnt) - ref,
> + .bindcnt = atomic_read(&p->tcf_bindcnt) - bind,
> + };
> + struct tcf_t t;
> +
> + spin_lock_bh(&p->tcf_lock);
> + opt.action = p->tcf_action;
> + if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt))
> + goto nla_put_failure;
> +
> + tcf_tm_dump(&t, &p->tcf_tm);
> + if (nla_put_64bit(skb, TCA_DEF_TM, sizeof(t), &t, TCA_DEF_PAD))
> + goto nla_put_failure;
> + spin_unlock_bh(&p->tcf_lock);
> +
> + return skb->len;
> +
> +nla_put_failure:
> + spin_unlock_bh(&p->tcf_lock);
> + nlmsg_trim(skb, b);
> + return -1;
> +}
> +
> +static struct tc_action_ops act_blockcast_ops = {
> + .kind = "blockcast",
> + .id = TCA_ID_BLOCKCAST,
> + .owner = THIS_MODULE,
> + .act = tcf_blockcast_run,
> + .dump = tcf_blockcast_dump,
> + .init = tcf_blockcast_init,
> + .size = sizeof(struct tcf_blockcast_act),
> +};
> +
> +static __net_init int blockcast_init_net(struct net *net)
> +{
> + struct tc_action_net *tn = net_generic(net, act_blockcast_ops.net_id);
> +
> + return tc_action_net_init(net, tn, &act_blockcast_ops);
> +}
> +
> +static void __net_exit blockcast_exit_net(struct list_head *net_list)
> +{
> + tc_action_net_exit(net_list, act_blockcast_ops.net_id);
> +}
> +
> +static struct pernet_operations blockcast_net_ops = {
> + .init = blockcast_init_net,
> + .exit_batch = blockcast_exit_net,
> + .id = &act_blockcast_ops.net_id,
> + .size = sizeof(struct tc_action_net),
> +};
> +
> +MODULE_AUTHOR("Mojatatu Networks, Inc");
> +MODULE_LICENSE("GPL");
The CI is complaining about the lack of MODULE_DESCRIPTION()
Cheers,
Paolo
Powered by blists - more mailing lists