18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/sched/cls_cgroup.c Control Group Classifier 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 128c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 138c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 148c2ecf20Sopenharmony_ci#include <net/sock.h> 158c2ecf20Sopenharmony_ci#include <net/cls_cgroup.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct cls_cgroup_head { 188c2ecf20Sopenharmony_ci u32 handle; 198c2ecf20Sopenharmony_ci struct tcf_exts exts; 208c2ecf20Sopenharmony_ci struct tcf_ematch_tree ematches; 218c2ecf20Sopenharmony_ci struct tcf_proto *tp; 228c2ecf20Sopenharmony_ci struct rcu_work rwork; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp, 268c2ecf20Sopenharmony_ci struct tcf_result *res) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = rcu_dereference_bh(tp->root); 298c2ecf20Sopenharmony_ci u32 classid = task_get_classid(skb); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (unlikely(!head)) 328c2ecf20Sopenharmony_ci return -1; 338c2ecf20Sopenharmony_ci if (!classid) 348c2ecf20Sopenharmony_ci return -1; 358c2ecf20Sopenharmony_ci if (!tcf_em_tree_match(skb, &head->ematches, NULL)) 368c2ecf20Sopenharmony_ci return -1; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci res->classid = classid; 398c2ecf20Sopenharmony_ci res->class = 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return tcf_exts_exec(skb, &head->exts, res); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void *cls_cgroup_get(struct tcf_proto *tp, u32 handle) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int cls_cgroup_init(struct tcf_proto *tp) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { 558c2ecf20Sopenharmony_ci [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void __cls_cgroup_destroy(struct cls_cgroup_head *head) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci tcf_exts_destroy(&head->exts); 618c2ecf20Sopenharmony_ci tcf_em_tree_destroy(&head->ematches); 628c2ecf20Sopenharmony_ci tcf_exts_put_net(&head->exts); 638c2ecf20Sopenharmony_ci kfree(head); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void cls_cgroup_destroy_work(struct work_struct *work) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = container_of(to_rcu_work(work), 698c2ecf20Sopenharmony_ci struct cls_cgroup_head, 708c2ecf20Sopenharmony_ci rwork); 718c2ecf20Sopenharmony_ci rtnl_lock(); 728c2ecf20Sopenharmony_ci __cls_cgroup_destroy(head); 738c2ecf20Sopenharmony_ci rtnl_unlock(); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, 778c2ecf20Sopenharmony_ci struct tcf_proto *tp, unsigned long base, 788c2ecf20Sopenharmony_ci u32 handle, struct nlattr **tca, 798c2ecf20Sopenharmony_ci void **arg, bool ovr, bool rtnl_held, 808c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_CGROUP_MAX + 1]; 838c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 848c2ecf20Sopenharmony_ci struct cls_cgroup_head *new; 858c2ecf20Sopenharmony_ci int err; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!tca[TCA_OPTIONS]) 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!head && !handle) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (head && handle != head->handle) 948c2ecf20Sopenharmony_ci return -ENOENT; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci new = kzalloc(sizeof(*head), GFP_KERNEL); 978c2ecf20Sopenharmony_ci if (!new) 988c2ecf20Sopenharmony_ci return -ENOBUFS; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = tcf_exts_init(&new->exts, net, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); 1018c2ecf20Sopenharmony_ci if (err < 0) 1028c2ecf20Sopenharmony_ci goto errout; 1038c2ecf20Sopenharmony_ci new->handle = handle; 1048c2ecf20Sopenharmony_ci new->tp = tp; 1058c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CGROUP_MAX, 1068c2ecf20Sopenharmony_ci tca[TCA_OPTIONS], cgroup_policy, 1078c2ecf20Sopenharmony_ci NULL); 1088c2ecf20Sopenharmony_ci if (err < 0) 1098c2ecf20Sopenharmony_ci goto errout; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &new->exts, ovr, 1128c2ecf20Sopenharmony_ci true, extack); 1138c2ecf20Sopenharmony_ci if (err < 0) 1148c2ecf20Sopenharmony_ci goto errout; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &new->ematches); 1178c2ecf20Sopenharmony_ci if (err < 0) 1188c2ecf20Sopenharmony_ci goto errout; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rcu_assign_pointer(tp->root, new); 1218c2ecf20Sopenharmony_ci if (head) { 1228c2ecf20Sopenharmony_ci tcf_exts_get_net(&head->exts); 1238c2ecf20Sopenharmony_ci tcf_queue_work(&head->rwork, cls_cgroup_destroy_work); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_cierrout: 1278c2ecf20Sopenharmony_ci tcf_exts_destroy(&new->exts); 1288c2ecf20Sopenharmony_ci kfree(new); 1298c2ecf20Sopenharmony_ci return err; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void cls_cgroup_destroy(struct tcf_proto *tp, bool rtnl_held, 1338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Head can still be NULL due to cls_cgroup_init(). */ 1388c2ecf20Sopenharmony_ci if (head) { 1398c2ecf20Sopenharmony_ci if (tcf_exts_get_net(&head->exts)) 1408c2ecf20Sopenharmony_ci tcf_queue_work(&head->rwork, cls_cgroup_destroy_work); 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci __cls_cgroup_destroy(head); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last, 1478c2ecf20Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg, 1538c2ecf20Sopenharmony_ci bool rtnl_held) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (arg->count < arg->skip) 1588c2ecf20Sopenharmony_ci goto skip; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!head) 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci if (arg->fn(tp, head, arg) < 0) { 1638c2ecf20Sopenharmony_ci arg->stop = 1; 1648c2ecf20Sopenharmony_ci return; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ciskip: 1678c2ecf20Sopenharmony_ci arg->count++; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, void *fh, 1718c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 1748c2ecf20Sopenharmony_ci struct nlattr *nest; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci t->tcm_handle = head->handle; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 1798c2ecf20Sopenharmony_ci if (nest == NULL) 1808c2ecf20Sopenharmony_ci goto nla_put_failure; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (tcf_exts_dump(skb, &head->exts) < 0 || 1838c2ecf20Sopenharmony_ci tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0) 1848c2ecf20Sopenharmony_ci goto nla_put_failure; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (tcf_exts_dump_stats(skb, &head->exts) < 0) 1898c2ecf20Sopenharmony_ci goto nla_put_failure; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return skb->len; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cinla_put_failure: 1948c2ecf20Sopenharmony_ci nla_nest_cancel(skb, nest); 1958c2ecf20Sopenharmony_ci return -1; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct tcf_proto_ops cls_cgroup_ops __read_mostly = { 1998c2ecf20Sopenharmony_ci .kind = "cgroup", 2008c2ecf20Sopenharmony_ci .init = cls_cgroup_init, 2018c2ecf20Sopenharmony_ci .change = cls_cgroup_change, 2028c2ecf20Sopenharmony_ci .classify = cls_cgroup_classify, 2038c2ecf20Sopenharmony_ci .destroy = cls_cgroup_destroy, 2048c2ecf20Sopenharmony_ci .get = cls_cgroup_get, 2058c2ecf20Sopenharmony_ci .delete = cls_cgroup_delete, 2068c2ecf20Sopenharmony_ci .walk = cls_cgroup_walk, 2078c2ecf20Sopenharmony_ci .dump = cls_cgroup_dump, 2088c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int __init init_cgroup_cls(void) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return register_tcf_proto_ops(&cls_cgroup_ops); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void __exit exit_cgroup_cls(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci unregister_tcf_proto_ops(&cls_cgroup_ops); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cimodule_init(init_cgroup_cls); 2228c2ecf20Sopenharmony_cimodule_exit(exit_cgroup_cls); 2238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 224