18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/act_simple.c	Simple example of an action
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Jamal Hadi Salim (2005-8)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
138c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
148c2ecf20Sopenharmony_ci#include <net/netlink.h>
158c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
168c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/tc_act/tc_defact.h>
198c2ecf20Sopenharmony_ci#include <net/tc_act/tc_defact.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic unsigned int simp_net_id;
228c2ecf20Sopenharmony_cistatic struct tc_action_ops act_simp_ops;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SIMP_MAX_DATA	32
258c2ecf20Sopenharmony_cistatic int tcf_simp_act(struct sk_buff *skb, const struct tc_action *a,
268c2ecf20Sopenharmony_ci			struct tcf_result *res)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct tcf_defact *d = to_defact(a);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	spin_lock(&d->tcf_lock);
318c2ecf20Sopenharmony_ci	tcf_lastuse_update(&d->tcf_tm);
328c2ecf20Sopenharmony_ci	bstats_update(&d->tcf_bstats, skb);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* print policy string followed by _ then packet count
358c2ecf20Sopenharmony_ci	 * Example if this was the 3rd packet and the string was "hello"
368c2ecf20Sopenharmony_ci	 * then it would look like "hello_3" (without quotes)
378c2ecf20Sopenharmony_ci	 */
388c2ecf20Sopenharmony_ci	pr_info("simple: %s_%llu\n",
398c2ecf20Sopenharmony_ci	       (char *)d->tcfd_defdata, d->tcf_bstats.packets);
408c2ecf20Sopenharmony_ci	spin_unlock(&d->tcf_lock);
418c2ecf20Sopenharmony_ci	return d->tcf_action;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void tcf_simp_release(struct tc_action *a)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct tcf_defact *d = to_defact(a);
478c2ecf20Sopenharmony_ci	kfree(d->tcfd_defdata);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int alloc_defdata(struct tcf_defact *d, const struct nlattr *defdata)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	d->tcfd_defdata = kzalloc(SIMP_MAX_DATA, GFP_KERNEL);
538c2ecf20Sopenharmony_ci	if (unlikely(!d->tcfd_defdata))
548c2ecf20Sopenharmony_ci		return -ENOMEM;
558c2ecf20Sopenharmony_ci	nla_strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int reset_policy(struct tc_action *a, const struct nlattr *defdata,
608c2ecf20Sopenharmony_ci			struct tc_defact *p, struct tcf_proto *tp,
618c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
648c2ecf20Sopenharmony_ci	struct tcf_defact *d;
658c2ecf20Sopenharmony_ci	int err;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	err = tcf_action_check_ctrlact(p->action, tp, &goto_ch, extack);
688c2ecf20Sopenharmony_ci	if (err < 0)
698c2ecf20Sopenharmony_ci		return err;
708c2ecf20Sopenharmony_ci	d = to_defact(a);
718c2ecf20Sopenharmony_ci	spin_lock_bh(&d->tcf_lock);
728c2ecf20Sopenharmony_ci	goto_ch = tcf_action_set_ctrlact(a, p->action, goto_ch);
738c2ecf20Sopenharmony_ci	memset(d->tcfd_defdata, 0, SIMP_MAX_DATA);
748c2ecf20Sopenharmony_ci	nla_strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
758c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
768c2ecf20Sopenharmony_ci	if (goto_ch)
778c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
828c2ecf20Sopenharmony_ci	[TCA_DEF_PARMS]	= { .len = sizeof(struct tc_defact) },
838c2ecf20Sopenharmony_ci	[TCA_DEF_DATA]	= { .type = NLA_STRING, .len = SIMP_MAX_DATA },
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int tcf_simp_init(struct net *net, struct nlattr *nla,
878c2ecf20Sopenharmony_ci			 struct nlattr *est, struct tc_action **a,
888c2ecf20Sopenharmony_ci			 int ovr, int bind, bool rtnl_held,
898c2ecf20Sopenharmony_ci			 struct tcf_proto *tp, u32 flags,
908c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, simp_net_id);
938c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_DEF_MAX + 1];
948c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
958c2ecf20Sopenharmony_ci	struct tc_defact *parm;
968c2ecf20Sopenharmony_ci	struct tcf_defact *d;
978c2ecf20Sopenharmony_ci	bool exists = false;
988c2ecf20Sopenharmony_ci	int ret = 0, err;
998c2ecf20Sopenharmony_ci	u32 index;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (nla == NULL)
1028c2ecf20Sopenharmony_ci		return -EINVAL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_DEF_MAX, nla, simple_policy,
1058c2ecf20Sopenharmony_ci					  NULL);
1068c2ecf20Sopenharmony_ci	if (err < 0)
1078c2ecf20Sopenharmony_ci		return err;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (tb[TCA_DEF_PARMS] == NULL)
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	parm = nla_data(tb[TCA_DEF_PARMS]);
1138c2ecf20Sopenharmony_ci	index = parm->index;
1148c2ecf20Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
1158c2ecf20Sopenharmony_ci	if (err < 0)
1168c2ecf20Sopenharmony_ci		return err;
1178c2ecf20Sopenharmony_ci	exists = err;
1188c2ecf20Sopenharmony_ci	if (exists && bind)
1198c2ecf20Sopenharmony_ci		return 0;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (tb[TCA_DEF_DATA] == NULL) {
1228c2ecf20Sopenharmony_ci		if (exists)
1238c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
1248c2ecf20Sopenharmony_ci		else
1258c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!exists) {
1308c2ecf20Sopenharmony_ci		ret = tcf_idr_create(tn, index, est, a,
1318c2ecf20Sopenharmony_ci				     &act_simp_ops, bind, false, flags);
1328c2ecf20Sopenharmony_ci		if (ret) {
1338c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1348c2ecf20Sopenharmony_ci			return ret;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		d = to_defact(*a);
1388c2ecf20Sopenharmony_ci		err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
1398c2ecf20Sopenharmony_ci					       extack);
1408c2ecf20Sopenharmony_ci		if (err < 0)
1418c2ecf20Sopenharmony_ci			goto release_idr;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		err = alloc_defdata(d, tb[TCA_DEF_DATA]);
1448c2ecf20Sopenharmony_ci		if (err < 0)
1458c2ecf20Sopenharmony_ci			goto put_chain;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		tcf_action_set_ctrlact(*a, parm->action, goto_ch);
1488c2ecf20Sopenharmony_ci		ret = ACT_P_CREATED;
1498c2ecf20Sopenharmony_ci	} else {
1508c2ecf20Sopenharmony_ci		if (!ovr) {
1518c2ecf20Sopenharmony_ci			err = -EEXIST;
1528c2ecf20Sopenharmony_ci			goto release_idr;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		err = reset_policy(*a, tb[TCA_DEF_DATA], parm, tp, extack);
1568c2ecf20Sopenharmony_ci		if (err)
1578c2ecf20Sopenharmony_ci			goto release_idr;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return ret;
1618c2ecf20Sopenharmony_ciput_chain:
1628c2ecf20Sopenharmony_ci	if (goto_ch)
1638c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
1648c2ecf20Sopenharmony_cirelease_idr:
1658c2ecf20Sopenharmony_ci	tcf_idr_release(*a, bind);
1668c2ecf20Sopenharmony_ci	return err;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
1708c2ecf20Sopenharmony_ci			 int bind, int ref)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
1738c2ecf20Sopenharmony_ci	struct tcf_defact *d = to_defact(a);
1748c2ecf20Sopenharmony_ci	struct tc_defact opt = {
1758c2ecf20Sopenharmony_ci		.index   = d->tcf_index,
1768c2ecf20Sopenharmony_ci		.refcnt  = refcount_read(&d->tcf_refcnt) - ref,
1778c2ecf20Sopenharmony_ci		.bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
1788c2ecf20Sopenharmony_ci	};
1798c2ecf20Sopenharmony_ci	struct tcf_t t;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	spin_lock_bh(&d->tcf_lock);
1828c2ecf20Sopenharmony_ci	opt.action = d->tcf_action;
1838c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt) ||
1848c2ecf20Sopenharmony_ci	    nla_put_string(skb, TCA_DEF_DATA, d->tcfd_defdata))
1858c2ecf20Sopenharmony_ci		goto nla_put_failure;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	tcf_tm_dump(&t, &d->tcf_tm);
1888c2ecf20Sopenharmony_ci	if (nla_put_64bit(skb, TCA_DEF_TM, sizeof(t), &t, TCA_DEF_PAD))
1898c2ecf20Sopenharmony_ci		goto nla_put_failure;
1908c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return skb->len;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cinla_put_failure:
1958c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
1968c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
1978c2ecf20Sopenharmony_ci	return -1;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int tcf_simp_walker(struct net *net, struct sk_buff *skb,
2018c2ecf20Sopenharmony_ci			   struct netlink_callback *cb, int type,
2028c2ecf20Sopenharmony_ci			   const struct tc_action_ops *ops,
2038c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, simp_net_id);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int tcf_simp_search(struct net *net, struct tc_action **a, u32 index)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, simp_net_id);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return tcf_idr_search(tn, a, index);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic struct tc_action_ops act_simp_ops = {
2188c2ecf20Sopenharmony_ci	.kind		=	"simple",
2198c2ecf20Sopenharmony_ci	.id		=	TCA_ID_SIMP,
2208c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
2218c2ecf20Sopenharmony_ci	.act		=	tcf_simp_act,
2228c2ecf20Sopenharmony_ci	.dump		=	tcf_simp_dump,
2238c2ecf20Sopenharmony_ci	.cleanup	=	tcf_simp_release,
2248c2ecf20Sopenharmony_ci	.init		=	tcf_simp_init,
2258c2ecf20Sopenharmony_ci	.walk		=	tcf_simp_walker,
2268c2ecf20Sopenharmony_ci	.lookup		=	tcf_simp_search,
2278c2ecf20Sopenharmony_ci	.size		=	sizeof(struct tcf_defact),
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic __net_init int simp_init_net(struct net *net)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, simp_net_id);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return tc_action_net_init(net, tn, &act_simp_ops);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void __net_exit simp_exit_net(struct list_head *net_list)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	tc_action_net_exit(net_list, simp_net_id);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic struct pernet_operations simp_net_ops = {
2438c2ecf20Sopenharmony_ci	.init = simp_init_net,
2448c2ecf20Sopenharmony_ci	.exit_batch = simp_exit_net,
2458c2ecf20Sopenharmony_ci	.id   = &simp_net_id,
2468c2ecf20Sopenharmony_ci	.size = sizeof(struct tc_action_net),
2478c2ecf20Sopenharmony_ci};
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jamal Hadi Salim(2005)");
2508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simple example action");
2518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int __init simp_init_module(void)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	int ret = tcf_register_action(&act_simp_ops, &simp_net_ops);
2568c2ecf20Sopenharmony_ci	if (!ret)
2578c2ecf20Sopenharmony_ci		pr_info("Simple TC action Loaded\n");
2588c2ecf20Sopenharmony_ci	return ret;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic void __exit simp_cleanup_module(void)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	tcf_unregister_action(&act_simp_ops, &simp_net_ops);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cimodule_init(simp_init_module);
2678c2ecf20Sopenharmony_cimodule_exit(simp_cleanup_module);
268