xref: /openbmc/linux/net/sched/act_simple.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24bba3925SPatrick McHardy /*
30c6965ddSJiri Pirko  * net/sched/act_simple.c	Simple example of an action
44bba3925SPatrick McHardy  *
5fa1b1cffSJamal Hadi Salim  * Authors:	Jamal Hadi Salim (2005-8)
64bba3925SPatrick McHardy  */
74bba3925SPatrick McHardy 
84bba3925SPatrick McHardy #include <linux/module.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
104bba3925SPatrick McHardy #include <linux/init.h>
114bba3925SPatrick McHardy #include <linux/kernel.h>
124bba3925SPatrick McHardy #include <linux/skbuff.h>
134bba3925SPatrick McHardy #include <linux/rtnetlink.h>
14dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h>
154bba3925SPatrick McHardy #include <net/pkt_sched.h>
164b006b0cSDavide Caratti #include <net/pkt_cls.h>
17*871cf386SPedro Tammela #include <net/tc_wrapper.h>
184bba3925SPatrick McHardy 
194bba3925SPatrick McHardy #include <linux/tc_act/tc_defact.h>
204bba3925SPatrick McHardy #include <net/tc_act/tc_defact.h>
214bba3925SPatrick McHardy 
22a85a970aSWANG Cong static struct tc_action_ops act_simp_ops;
23ddf97ccdSWANG Cong 
24fa1b1cffSJamal Hadi Salim #define SIMP_MAX_DATA	32
tcf_simp_act(struct sk_buff * skb,const struct tc_action * a,struct tcf_result * res)25*871cf386SPedro Tammela TC_INDIRECT_SCOPE int tcf_simp_act(struct sk_buff *skb,
26*871cf386SPedro Tammela 				   const struct tc_action *a,
27dc7f9f6eSEric Dumazet 				   struct tcf_result *res)
284bba3925SPatrick McHardy {
29a85a970aSWANG Cong 	struct tcf_defact *d = to_defact(a);
304bba3925SPatrick McHardy 
31e9ce1cd3SDavid S. Miller 	spin_lock(&d->tcf_lock);
329c4a4e48SJamal Hadi Salim 	tcf_lastuse_update(&d->tcf_tm);
33bfe0d029SEric Dumazet 	bstats_update(&d->tcf_bstats, skb);
344bba3925SPatrick McHardy 
354bba3925SPatrick McHardy 	/* print policy string followed by _ then packet count
364bba3925SPatrick McHardy 	 * Example if this was the 3rd packet and the string was "hello"
374bba3925SPatrick McHardy 	 * then it would look like "hello_3" (without quotes)
38cc7ec456SEric Dumazet 	 */
39d0083d98SEric Dumazet 	pr_info("simple: %s_%llu\n",
4050dc9a85SAhmed S. Darwish 		(char *)d->tcfd_defdata,
4150dc9a85SAhmed S. Darwish 		u64_stats_read(&d->tcf_bstats.packets));
42e9ce1cd3SDavid S. Miller 	spin_unlock(&d->tcf_lock);
43e9ce1cd3SDavid S. Miller 	return d->tcf_action;
44e9ce1cd3SDavid S. Miller }
45e9ce1cd3SDavid S. Miller 
tcf_simp_release(struct tc_action * a)469a63b255SCong Wang static void tcf_simp_release(struct tc_action *a)
47e9ce1cd3SDavid S. Miller {
4886062033SWANG Cong 	struct tcf_defact *d = to_defact(a);
49e9ce1cd3SDavid S. Miller 	kfree(d->tcfd_defdata);
50e9ce1cd3SDavid S. Miller }
51e9ce1cd3SDavid S. Miller 
alloc_defdata(struct tcf_defact * d,const struct nlattr * defdata)528d499533SDavide Caratti static int alloc_defdata(struct tcf_defact *d, const struct nlattr *defdata)
53e9ce1cd3SDavid S. Miller {
540eff683fSDan Carpenter 	d->tcfd_defdata = kzalloc(SIMP_MAX_DATA, GFP_KERNEL);
55e9ce1cd3SDavid S. Miller 	if (unlikely(!d->tcfd_defdata))
56e9ce1cd3SDavid S. Miller 		return -ENOMEM;
57872f6903SFrancis Laniel 	nla_strscpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
58e9ce1cd3SDavid S. Miller 	return 0;
59e9ce1cd3SDavid S. Miller }
60e9ce1cd3SDavid S. Miller 
reset_policy(struct tc_action * a,const struct nlattr * defdata,struct tc_defact * p,struct tcf_proto * tp,struct netlink_ext_ack * extack)614b006b0cSDavide Caratti static int reset_policy(struct tc_action *a, const struct nlattr *defdata,
624b006b0cSDavide Caratti 			struct tc_defact *p, struct tcf_proto *tp,
634b006b0cSDavide Caratti 			struct netlink_ext_ack *extack)
64e9ce1cd3SDavid S. Miller {
654b006b0cSDavide Caratti 	struct tcf_chain *goto_ch = NULL;
664b006b0cSDavide Caratti 	struct tcf_defact *d;
674b006b0cSDavide Caratti 	int err;
684b006b0cSDavide Caratti 
694b006b0cSDavide Caratti 	err = tcf_action_check_ctrlact(p->action, tp, &goto_ch, extack);
704b006b0cSDavide Caratti 	if (err < 0)
714b006b0cSDavide Caratti 		return err;
724b006b0cSDavide Caratti 	d = to_defact(a);
739d1045adSJamal Hadi Salim 	spin_lock_bh(&d->tcf_lock);
744b006b0cSDavide Caratti 	goto_ch = tcf_action_set_ctrlact(a, p->action, goto_ch);
759d1045adSJamal Hadi Salim 	memset(d->tcfd_defdata, 0, SIMP_MAX_DATA);
76872f6903SFrancis Laniel 	nla_strscpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA);
779d1045adSJamal Hadi Salim 	spin_unlock_bh(&d->tcf_lock);
784b006b0cSDavide Caratti 	if (goto_ch)
794b006b0cSDavide Caratti 		tcf_chain_put_by_act(goto_ch);
804b006b0cSDavide Caratti 	return 0;
81e9ce1cd3SDavid S. Miller }
82e9ce1cd3SDavid S. Miller 
8353b2bf3fSPatrick McHardy static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
8453b2bf3fSPatrick McHardy 	[TCA_DEF_PARMS]	= { .len = sizeof(struct tc_defact) },
85fa1b1cffSJamal Hadi Salim 	[TCA_DEF_DATA]	= { .type = NLA_STRING, .len = SIMP_MAX_DATA },
8653b2bf3fSPatrick McHardy };
8753b2bf3fSPatrick McHardy 
tcf_simp_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)88c1b52739SBenjamin LaHaise static int tcf_simp_init(struct net *net, struct nlattr *nla,
89a85a970aSWANG Cong 			 struct nlattr *est, struct tc_action **a,
90abbb0d33SVlad Buslov 			 struct tcf_proto *tp, u32 flags,
91abbb0d33SVlad Buslov 			 struct netlink_ext_ack *extack)
92e9ce1cd3SDavid S. Miller {
93acd0a7abSZhengchao Shao 	struct tc_action_net *tn = net_generic(net, act_simp_ops.net_id);
94695176bfSCong Wang 	bool bind = flags & TCA_ACT_FLAGS_BIND;
957ba699c6SPatrick McHardy 	struct nlattr *tb[TCA_DEF_MAX + 1];
964b006b0cSDavide Caratti 	struct tcf_chain *goto_ch = NULL;
97e9ce1cd3SDavid S. Miller 	struct tc_defact *parm;
98e9ce1cd3SDavid S. Miller 	struct tcf_defact *d;
99b2313077SWANG Cong 	bool exists = false;
100b2313077SWANG Cong 	int ret = 0, err;
1017be8ef2cSDmytro Linkin 	u32 index;
102e9ce1cd3SDavid S. Miller 
103cee63723SPatrick McHardy 	if (nla == NULL)
104e9ce1cd3SDavid S. Miller 		return -EINVAL;
105e9ce1cd3SDavid S. Miller 
1068cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, TCA_DEF_MAX, nla, simple_policy,
1078cb08174SJohannes Berg 					  NULL);
108cee63723SPatrick McHardy 	if (err < 0)
109cee63723SPatrick McHardy 		return err;
110cee63723SPatrick McHardy 
11153b2bf3fSPatrick McHardy 	if (tb[TCA_DEF_PARMS] == NULL)
112e9ce1cd3SDavid S. Miller 		return -EINVAL;
113e9ce1cd3SDavid S. Miller 
114fa1b1cffSJamal Hadi Salim 	parm = nla_data(tb[TCA_DEF_PARMS]);
1157be8ef2cSDmytro Linkin 	index = parm->index;
1167be8ef2cSDmytro Linkin 	err = tcf_idr_check_alloc(tn, &index, a, bind);
1170190c1d4SVlad Buslov 	if (err < 0)
1180190c1d4SVlad Buslov 		return err;
1190190c1d4SVlad Buslov 	exists = err;
1200e5538abSJamal Hadi Salim 	if (exists && bind)
1210e5538abSJamal Hadi Salim 		return 0;
1220e5538abSJamal Hadi Salim 
1230e5538abSJamal Hadi Salim 	if (tb[TCA_DEF_DATA] == NULL) {
1240e5538abSJamal Hadi Salim 		if (exists)
12565a206c0SChris Mi 			tcf_idr_release(*a, bind);
1260190c1d4SVlad Buslov 		else
1277be8ef2cSDmytro Linkin 			tcf_idr_cleanup(tn, index);
1280e5538abSJamal Hadi Salim 		return -EINVAL;
1290e5538abSJamal Hadi Salim 	}
1300e5538abSJamal Hadi Salim 
1310e5538abSJamal Hadi Salim 	if (!exists) {
1327be8ef2cSDmytro Linkin 		ret = tcf_idr_create(tn, index, est, a,
13340bd094dSBaowen Zheng 				     &act_simp_ops, bind, false, flags);
1340190c1d4SVlad Buslov 		if (ret) {
1357be8ef2cSDmytro Linkin 			tcf_idr_cleanup(tn, index);
13686062033SWANG Cong 			return ret;
1370190c1d4SVlad Buslov 		}
138e9ce1cd3SDavid S. Miller 
139a85a970aSWANG Cong 		d = to_defact(*a);
1404b006b0cSDavide Caratti 		err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch,
1414b006b0cSDavide Caratti 					       extack);
1424b006b0cSDavide Caratti 		if (err < 0)
1434b006b0cSDavide Caratti 			goto release_idr;
1444b006b0cSDavide Caratti 
1454b006b0cSDavide Caratti 		err = alloc_defdata(d, tb[TCA_DEF_DATA]);
1464b006b0cSDavide Caratti 		if (err < 0)
1474b006b0cSDavide Caratti 			goto put_chain;
1484b006b0cSDavide Caratti 
1494b006b0cSDavide Caratti 		tcf_action_set_ctrlact(*a, parm->action, goto_ch);
150e9ce1cd3SDavid S. Miller 		ret = ACT_P_CREATED;
151e9ce1cd3SDavid S. Miller 	} else {
152695176bfSCong Wang 		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
1534b006b0cSDavide Caratti 			err = -EEXIST;
1544b006b0cSDavide Caratti 			goto release_idr;
1554e8ddd7fSVlad Buslov 		}
1561a29321eSJamal Hadi Salim 
1574b006b0cSDavide Caratti 		err = reset_policy(*a, tb[TCA_DEF_DATA], parm, tp, extack);
1584b006b0cSDavide Caratti 		if (err)
1594b006b0cSDavide Caratti 			goto release_idr;
160e9ce1cd3SDavid S. Miller 	}
161e9ce1cd3SDavid S. Miller 
162e9ce1cd3SDavid S. Miller 	return ret;
1634b006b0cSDavide Caratti put_chain:
1644b006b0cSDavide Caratti 	if (goto_ch)
1654b006b0cSDavide Caratti 		tcf_chain_put_by_act(goto_ch);
1664b006b0cSDavide Caratti release_idr:
1674b006b0cSDavide Caratti 	tcf_idr_release(*a, bind);
1684b006b0cSDavide Caratti 	return err;
169e9ce1cd3SDavid S. Miller }
170e9ce1cd3SDavid S. Miller 
tcf_simp_dump(struct sk_buff * skb,struct tc_action * a,int bind,int ref)171cc7ec456SEric Dumazet static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
172e9ce1cd3SDavid S. Miller 			 int bind, int ref)
173e9ce1cd3SDavid S. Miller {
17427a884dcSArnaldo Carvalho de Melo 	unsigned char *b = skb_tail_pointer(skb);
175a85a970aSWANG Cong 	struct tcf_defact *d = to_defact(a);
1761c40be12SEric Dumazet 	struct tc_defact opt = {
1771c40be12SEric Dumazet 		.index   = d->tcf_index,
178036bb443SVlad Buslov 		.refcnt  = refcount_read(&d->tcf_refcnt) - ref,
179036bb443SVlad Buslov 		.bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
1801c40be12SEric Dumazet 	};
181e9ce1cd3SDavid S. Miller 	struct tcf_t t;
182e9ce1cd3SDavid S. Miller 
1835e48180eSVlad Buslov 	spin_lock_bh(&d->tcf_lock);
1845e48180eSVlad Buslov 	opt.action = d->tcf_action;
1851b34ec43SDavid S. Miller 	if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt) ||
1861b34ec43SDavid S. Miller 	    nla_put_string(skb, TCA_DEF_DATA, d->tcfd_defdata))
1871b34ec43SDavid S. Miller 		goto nla_put_failure;
18848d8ee16SJamal Hadi Salim 
18948d8ee16SJamal Hadi Salim 	tcf_tm_dump(&t, &d->tcf_tm);
1909854518eSNicolas Dichtel 	if (nla_put_64bit(skb, TCA_DEF_TM, sizeof(t), &t, TCA_DEF_PAD))
1911b34ec43SDavid S. Miller 		goto nla_put_failure;
1925e48180eSVlad Buslov 	spin_unlock_bh(&d->tcf_lock);
1935e48180eSVlad Buslov 
194e9ce1cd3SDavid S. Miller 	return skb->len;
195e9ce1cd3SDavid S. Miller 
1967ba699c6SPatrick McHardy nla_put_failure:
1975e48180eSVlad Buslov 	spin_unlock_bh(&d->tcf_lock);
198dc5fc579SArnaldo Carvalho de Melo 	nlmsg_trim(skb, b);
199e9ce1cd3SDavid S. Miller 	return -1;
2004bba3925SPatrick McHardy }
2014bba3925SPatrick McHardy 
2024bba3925SPatrick McHardy static struct tc_action_ops act_simp_ops = {
2034bba3925SPatrick McHardy 	.kind		=	"simple",
204eddd2cf1SEli Cohen 	.id		=	TCA_ID_SIMP,
2054bba3925SPatrick McHardy 	.owner		=	THIS_MODULE,
206798de374SJamal Hadi Salim 	.act		=	tcf_simp_act,
207e9ce1cd3SDavid S. Miller 	.dump		=	tcf_simp_dump,
20886062033SWANG Cong 	.cleanup	=	tcf_simp_release,
209e9ce1cd3SDavid S. Miller 	.init		=	tcf_simp_init,
210a85a970aSWANG Cong 	.size		=	sizeof(struct tcf_defact),
211ddf97ccdSWANG Cong };
212ddf97ccdSWANG Cong 
simp_init_net(struct net * net)213ddf97ccdSWANG Cong static __net_init int simp_init_net(struct net *net)
214ddf97ccdSWANG Cong {
215acd0a7abSZhengchao Shao 	struct tc_action_net *tn = net_generic(net, act_simp_ops.net_id);
216ddf97ccdSWANG Cong 
217981471bdSCong Wang 	return tc_action_net_init(net, tn, &act_simp_ops);
218ddf97ccdSWANG Cong }
219ddf97ccdSWANG Cong 
simp_exit_net(struct list_head * net_list)220039af9c6SCong Wang static void __net_exit simp_exit_net(struct list_head *net_list)
221ddf97ccdSWANG Cong {
222acd0a7abSZhengchao Shao 	tc_action_net_exit(net_list, act_simp_ops.net_id);
223ddf97ccdSWANG Cong }
224ddf97ccdSWANG Cong 
225ddf97ccdSWANG Cong static struct pernet_operations simp_net_ops = {
226ddf97ccdSWANG Cong 	.init = simp_init_net,
227039af9c6SCong Wang 	.exit_batch = simp_exit_net,
228acd0a7abSZhengchao Shao 	.id   = &act_simp_ops.net_id,
229ddf97ccdSWANG Cong 	.size = sizeof(struct tc_action_net),
2304bba3925SPatrick McHardy };
2314bba3925SPatrick McHardy 
2324bba3925SPatrick McHardy MODULE_AUTHOR("Jamal Hadi Salim(2005)");
2334bba3925SPatrick McHardy MODULE_DESCRIPTION("Simple example action");
2344bba3925SPatrick McHardy MODULE_LICENSE("GPL");
2354bba3925SPatrick McHardy 
simp_init_module(void)2364bba3925SPatrick McHardy static int __init simp_init_module(void)
2374bba3925SPatrick McHardy {
238ddf97ccdSWANG Cong 	int ret = tcf_register_action(&act_simp_ops, &simp_net_ops);
2394bba3925SPatrick McHardy 	if (!ret)
2406ff9c364Sstephen hemminger 		pr_info("Simple TC action Loaded\n");
2414bba3925SPatrick McHardy 	return ret;
2424bba3925SPatrick McHardy }
2434bba3925SPatrick McHardy 
simp_cleanup_module(void)2444bba3925SPatrick McHardy static void __exit simp_cleanup_module(void)
2454bba3925SPatrick McHardy {
246ddf97ccdSWANG Cong 	tcf_unregister_action(&act_simp_ops, &simp_net_ops);
2474bba3925SPatrick McHardy }
2484bba3925SPatrick McHardy 
2494bba3925SPatrick McHardy module_init(simp_init_module);
2504bba3925SPatrick McHardy module_exit(simp_cleanup_module);
251