162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/cls_cgroup.c Control Group Classifier 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/skbuff.h> 1162306a36Sopenharmony_ci#include <linux/rcupdate.h> 1262306a36Sopenharmony_ci#include <net/rtnetlink.h> 1362306a36Sopenharmony_ci#include <net/pkt_cls.h> 1462306a36Sopenharmony_ci#include <net/sock.h> 1562306a36Sopenharmony_ci#include <net/cls_cgroup.h> 1662306a36Sopenharmony_ci#include <net/tc_wrapper.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct cls_cgroup_head { 1962306a36Sopenharmony_ci u32 handle; 2062306a36Sopenharmony_ci struct tcf_exts exts; 2162306a36Sopenharmony_ci struct tcf_ematch_tree ematches; 2262306a36Sopenharmony_ci struct tcf_proto *tp; 2362306a36Sopenharmony_ci struct rcu_work rwork; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciTC_INDIRECT_SCOPE int cls_cgroup_classify(struct sk_buff *skb, 2762306a36Sopenharmony_ci const struct tcf_proto *tp, 2862306a36Sopenharmony_ci struct tcf_result *res) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct cls_cgroup_head *head = rcu_dereference_bh(tp->root); 3162306a36Sopenharmony_ci u32 classid = task_get_classid(skb); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (unlikely(!head)) 3462306a36Sopenharmony_ci return -1; 3562306a36Sopenharmony_ci if (!classid) 3662306a36Sopenharmony_ci return -1; 3762306a36Sopenharmony_ci if (!tcf_em_tree_match(skb, &head->ematches, NULL)) 3862306a36Sopenharmony_ci return -1; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci res->classid = classid; 4162306a36Sopenharmony_ci res->class = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return tcf_exts_exec(skb, &head->exts, res); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void *cls_cgroup_get(struct tcf_proto *tp, u32 handle) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci return NULL; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int cls_cgroup_init(struct tcf_proto *tp) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { 5762306a36Sopenharmony_ci [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void __cls_cgroup_destroy(struct cls_cgroup_head *head) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci tcf_exts_destroy(&head->exts); 6362306a36Sopenharmony_ci tcf_em_tree_destroy(&head->ematches); 6462306a36Sopenharmony_ci tcf_exts_put_net(&head->exts); 6562306a36Sopenharmony_ci kfree(head); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void cls_cgroup_destroy_work(struct work_struct *work) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct cls_cgroup_head *head = container_of(to_rcu_work(work), 7162306a36Sopenharmony_ci struct cls_cgroup_head, 7262306a36Sopenharmony_ci rwork); 7362306a36Sopenharmony_ci rtnl_lock(); 7462306a36Sopenharmony_ci __cls_cgroup_destroy(head); 7562306a36Sopenharmony_ci rtnl_unlock(); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, 7962306a36Sopenharmony_ci struct tcf_proto *tp, unsigned long base, 8062306a36Sopenharmony_ci u32 handle, struct nlattr **tca, 8162306a36Sopenharmony_ci void **arg, u32 flags, 8262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct nlattr *tb[TCA_CGROUP_MAX + 1]; 8562306a36Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 8662306a36Sopenharmony_ci struct cls_cgroup_head *new; 8762306a36Sopenharmony_ci int err; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!tca[TCA_OPTIONS]) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!head && !handle) 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (head && handle != head->handle) 9662306a36Sopenharmony_ci return -ENOENT; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci new = kzalloc(sizeof(*head), GFP_KERNEL); 9962306a36Sopenharmony_ci if (!new) 10062306a36Sopenharmony_ci return -ENOBUFS; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = tcf_exts_init(&new->exts, net, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); 10362306a36Sopenharmony_ci if (err < 0) 10462306a36Sopenharmony_ci goto errout; 10562306a36Sopenharmony_ci new->handle = handle; 10662306a36Sopenharmony_ci new->tp = tp; 10762306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_CGROUP_MAX, 10862306a36Sopenharmony_ci tca[TCA_OPTIONS], cgroup_policy, 10962306a36Sopenharmony_ci NULL); 11062306a36Sopenharmony_ci if (err < 0) 11162306a36Sopenharmony_ci goto errout; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &new->exts, flags, 11462306a36Sopenharmony_ci extack); 11562306a36Sopenharmony_ci if (err < 0) 11662306a36Sopenharmony_ci goto errout; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &new->ematches); 11962306a36Sopenharmony_ci if (err < 0) 12062306a36Sopenharmony_ci goto errout; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci rcu_assign_pointer(tp->root, new); 12362306a36Sopenharmony_ci if (head) { 12462306a36Sopenharmony_ci tcf_exts_get_net(&head->exts); 12562306a36Sopenharmony_ci tcf_queue_work(&head->rwork, cls_cgroup_destroy_work); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_cierrout: 12962306a36Sopenharmony_ci tcf_exts_destroy(&new->exts); 13062306a36Sopenharmony_ci kfree(new); 13162306a36Sopenharmony_ci return err; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void cls_cgroup_destroy(struct tcf_proto *tp, bool rtnl_held, 13562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Head can still be NULL due to cls_cgroup_init(). */ 14062306a36Sopenharmony_ci if (head) { 14162306a36Sopenharmony_ci if (tcf_exts_get_net(&head->exts)) 14262306a36Sopenharmony_ci tcf_queue_work(&head->rwork, cls_cgroup_destroy_work); 14362306a36Sopenharmony_ci else 14462306a36Sopenharmony_ci __cls_cgroup_destroy(head); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last, 14962306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci return -EOPNOTSUPP; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg, 15562306a36Sopenharmony_ci bool rtnl_held) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (arg->count < arg->skip) 16062306a36Sopenharmony_ci goto skip; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!head) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci if (arg->fn(tp, head, arg) < 0) { 16562306a36Sopenharmony_ci arg->stop = 1; 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ciskip: 16962306a36Sopenharmony_ci arg->count++; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, void *fh, 17362306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct cls_cgroup_head *head = rtnl_dereference(tp->root); 17662306a36Sopenharmony_ci struct nlattr *nest; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci t->tcm_handle = head->handle; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 18162306a36Sopenharmony_ci if (nest == NULL) 18262306a36Sopenharmony_ci goto nla_put_failure; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (tcf_exts_dump(skb, &head->exts) < 0 || 18562306a36Sopenharmony_ci tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0) 18662306a36Sopenharmony_ci goto nla_put_failure; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci nla_nest_end(skb, nest); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (tcf_exts_dump_stats(skb, &head->exts) < 0) 19162306a36Sopenharmony_ci goto nla_put_failure; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return skb->len; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cinla_put_failure: 19662306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 19762306a36Sopenharmony_ci return -1; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic struct tcf_proto_ops cls_cgroup_ops __read_mostly = { 20162306a36Sopenharmony_ci .kind = "cgroup", 20262306a36Sopenharmony_ci .init = cls_cgroup_init, 20362306a36Sopenharmony_ci .change = cls_cgroup_change, 20462306a36Sopenharmony_ci .classify = cls_cgroup_classify, 20562306a36Sopenharmony_ci .destroy = cls_cgroup_destroy, 20662306a36Sopenharmony_ci .get = cls_cgroup_get, 20762306a36Sopenharmony_ci .delete = cls_cgroup_delete, 20862306a36Sopenharmony_ci .walk = cls_cgroup_walk, 20962306a36Sopenharmony_ci .dump = cls_cgroup_dump, 21062306a36Sopenharmony_ci .owner = THIS_MODULE, 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int __init init_cgroup_cls(void) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci return register_tcf_proto_ops(&cls_cgroup_ops); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void __exit exit_cgroup_cls(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci unregister_tcf_proto_ops(&cls_cgroup_ops); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cimodule_init(init_cgroup_cls); 22462306a36Sopenharmony_cimodule_exit(exit_cgroup_cls); 22562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 226