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