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