Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/sched/cls_basic.c    Basic Packet Classifier.
0004  *
0005  * Authors: Thomas Graf <tgraf@suug.ch>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/slab.h>
0010 #include <linux/types.h>
0011 #include <linux/kernel.h>
0012 #include <linux/string.h>
0013 #include <linux/errno.h>
0014 #include <linux/rtnetlink.h>
0015 #include <linux/skbuff.h>
0016 #include <linux/idr.h>
0017 #include <linux/percpu.h>
0018 #include <net/netlink.h>
0019 #include <net/act_api.h>
0020 #include <net/pkt_cls.h>
0021 
0022 struct basic_head {
0023     struct list_head    flist;
0024     struct idr      handle_idr;
0025     struct rcu_head     rcu;
0026 };
0027 
0028 struct basic_filter {
0029     u32         handle;
0030     struct tcf_exts     exts;
0031     struct tcf_ematch_tree  ematches;
0032     struct tcf_result   res;
0033     struct tcf_proto    *tp;
0034     struct list_head    link;
0035     struct tc_basic_pcnt __percpu *pf;
0036     struct rcu_work     rwork;
0037 };
0038 
0039 static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
0040               struct tcf_result *res)
0041 {
0042     int r;
0043     struct basic_head *head = rcu_dereference_bh(tp->root);
0044     struct basic_filter *f;
0045 
0046     list_for_each_entry_rcu(f, &head->flist, link) {
0047         __this_cpu_inc(f->pf->rcnt);
0048         if (!tcf_em_tree_match(skb, &f->ematches, NULL))
0049             continue;
0050         __this_cpu_inc(f->pf->rhit);
0051         *res = f->res;
0052         r = tcf_exts_exec(skb, &f->exts, res);
0053         if (r < 0)
0054             continue;
0055         return r;
0056     }
0057     return -1;
0058 }
0059 
0060 static void *basic_get(struct tcf_proto *tp, u32 handle)
0061 {
0062     struct basic_head *head = rtnl_dereference(tp->root);
0063     struct basic_filter *f;
0064 
0065     list_for_each_entry(f, &head->flist, link) {
0066         if (f->handle == handle) {
0067             return f;
0068         }
0069     }
0070 
0071     return NULL;
0072 }
0073 
0074 static int basic_init(struct tcf_proto *tp)
0075 {
0076     struct basic_head *head;
0077 
0078     head = kzalloc(sizeof(*head), GFP_KERNEL);
0079     if (head == NULL)
0080         return -ENOBUFS;
0081     INIT_LIST_HEAD(&head->flist);
0082     idr_init(&head->handle_idr);
0083     rcu_assign_pointer(tp->root, head);
0084     return 0;
0085 }
0086 
0087 static void __basic_delete_filter(struct basic_filter *f)
0088 {
0089     tcf_exts_destroy(&f->exts);
0090     tcf_em_tree_destroy(&f->ematches);
0091     tcf_exts_put_net(&f->exts);
0092     free_percpu(f->pf);
0093     kfree(f);
0094 }
0095 
0096 static void basic_delete_filter_work(struct work_struct *work)
0097 {
0098     struct basic_filter *f = container_of(to_rcu_work(work),
0099                           struct basic_filter,
0100                           rwork);
0101     rtnl_lock();
0102     __basic_delete_filter(f);
0103     rtnl_unlock();
0104 }
0105 
0106 static void basic_destroy(struct tcf_proto *tp, bool rtnl_held,
0107               struct netlink_ext_ack *extack)
0108 {
0109     struct basic_head *head = rtnl_dereference(tp->root);
0110     struct basic_filter *f, *n;
0111 
0112     list_for_each_entry_safe(f, n, &head->flist, link) {
0113         list_del_rcu(&f->link);
0114         tcf_unbind_filter(tp, &f->res);
0115         idr_remove(&head->handle_idr, f->handle);
0116         if (tcf_exts_get_net(&f->exts))
0117             tcf_queue_work(&f->rwork, basic_delete_filter_work);
0118         else
0119             __basic_delete_filter(f);
0120     }
0121     idr_destroy(&head->handle_idr);
0122     kfree_rcu(head, rcu);
0123 }
0124 
0125 static int basic_delete(struct tcf_proto *tp, void *arg, bool *last,
0126             bool rtnl_held, struct netlink_ext_ack *extack)
0127 {
0128     struct basic_head *head = rtnl_dereference(tp->root);
0129     struct basic_filter *f = arg;
0130 
0131     list_del_rcu(&f->link);
0132     tcf_unbind_filter(tp, &f->res);
0133     idr_remove(&head->handle_idr, f->handle);
0134     tcf_exts_get_net(&f->exts);
0135     tcf_queue_work(&f->rwork, basic_delete_filter_work);
0136     *last = list_empty(&head->flist);
0137     return 0;
0138 }
0139 
0140 static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
0141     [TCA_BASIC_CLASSID] = { .type = NLA_U32 },
0142     [TCA_BASIC_EMATCHES]    = { .type = NLA_NESTED },
0143 };
0144 
0145 static int basic_set_parms(struct net *net, struct tcf_proto *tp,
0146                struct basic_filter *f, unsigned long base,
0147                struct nlattr **tb,
0148                struct nlattr *est, u32 flags,
0149                struct netlink_ext_ack *extack)
0150 {
0151     int err;
0152 
0153     err = tcf_exts_validate(net, tp, tb, est, &f->exts, flags, extack);
0154     if (err < 0)
0155         return err;
0156 
0157     err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches);
0158     if (err < 0)
0159         return err;
0160 
0161     if (tb[TCA_BASIC_CLASSID]) {
0162         f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
0163         tcf_bind_filter(tp, &f->res, base);
0164     }
0165 
0166     f->tp = tp;
0167     return 0;
0168 }
0169 
0170 static int basic_change(struct net *net, struct sk_buff *in_skb,
0171             struct tcf_proto *tp, unsigned long base, u32 handle,
0172             struct nlattr **tca, void **arg,
0173             u32 flags, struct netlink_ext_ack *extack)
0174 {
0175     int err;
0176     struct basic_head *head = rtnl_dereference(tp->root);
0177     struct nlattr *tb[TCA_BASIC_MAX + 1];
0178     struct basic_filter *fold = (struct basic_filter *) *arg;
0179     struct basic_filter *fnew;
0180 
0181     if (tca[TCA_OPTIONS] == NULL)
0182         return -EINVAL;
0183 
0184     err = nla_parse_nested_deprecated(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS],
0185                       basic_policy, NULL);
0186     if (err < 0)
0187         return err;
0188 
0189     if (fold != NULL) {
0190         if (handle && fold->handle != handle)
0191             return -EINVAL;
0192     }
0193 
0194     fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
0195     if (!fnew)
0196         return -ENOBUFS;
0197 
0198     err = tcf_exts_init(&fnew->exts, net, TCA_BASIC_ACT, TCA_BASIC_POLICE);
0199     if (err < 0)
0200         goto errout;
0201 
0202     if (!handle) {
0203         handle = 1;
0204         err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
0205                     INT_MAX, GFP_KERNEL);
0206     } else if (!fold) {
0207         err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
0208                     handle, GFP_KERNEL);
0209     }
0210     if (err)
0211         goto errout;
0212     fnew->handle = handle;
0213     fnew->pf = alloc_percpu(struct tc_basic_pcnt);
0214     if (!fnew->pf) {
0215         err = -ENOMEM;
0216         goto errout;
0217     }
0218 
0219     err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], flags,
0220                   extack);
0221     if (err < 0) {
0222         if (!fold)
0223             idr_remove(&head->handle_idr, fnew->handle);
0224         goto errout;
0225     }
0226 
0227     *arg = fnew;
0228 
0229     if (fold) {
0230         idr_replace(&head->handle_idr, fnew, fnew->handle);
0231         list_replace_rcu(&fold->link, &fnew->link);
0232         tcf_unbind_filter(tp, &fold->res);
0233         tcf_exts_get_net(&fold->exts);
0234         tcf_queue_work(&fold->rwork, basic_delete_filter_work);
0235     } else {
0236         list_add_rcu(&fnew->link, &head->flist);
0237     }
0238 
0239     return 0;
0240 errout:
0241     free_percpu(fnew->pf);
0242     tcf_exts_destroy(&fnew->exts);
0243     kfree(fnew);
0244     return err;
0245 }
0246 
0247 static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg,
0248                bool rtnl_held)
0249 {
0250     struct basic_head *head = rtnl_dereference(tp->root);
0251     struct basic_filter *f;
0252 
0253     list_for_each_entry(f, &head->flist, link) {
0254         if (arg->count < arg->skip)
0255             goto skip;
0256 
0257         if (arg->fn(tp, f, arg) < 0) {
0258             arg->stop = 1;
0259             break;
0260         }
0261 skip:
0262         arg->count++;
0263     }
0264 }
0265 
0266 static void basic_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
0267                  unsigned long base)
0268 {
0269     struct basic_filter *f = fh;
0270 
0271     if (f && f->res.classid == classid) {
0272         if (cl)
0273             __tcf_bind_filter(q, &f->res, base);
0274         else
0275             __tcf_unbind_filter(q, &f->res);
0276     }
0277 }
0278 
0279 static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh,
0280               struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
0281 {
0282     struct tc_basic_pcnt gpf = {};
0283     struct basic_filter *f = fh;
0284     struct nlattr *nest;
0285     int cpu;
0286 
0287     if (f == NULL)
0288         return skb->len;
0289 
0290     t->tcm_handle = f->handle;
0291 
0292     nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
0293     if (nest == NULL)
0294         goto nla_put_failure;
0295 
0296     if (f->res.classid &&
0297         nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
0298         goto nla_put_failure;
0299 
0300     for_each_possible_cpu(cpu) {
0301         struct tc_basic_pcnt *pf = per_cpu_ptr(f->pf, cpu);
0302 
0303         gpf.rcnt += pf->rcnt;
0304         gpf.rhit += pf->rhit;
0305     }
0306 
0307     if (nla_put_64bit(skb, TCA_BASIC_PCNT,
0308               sizeof(struct tc_basic_pcnt),
0309               &gpf, TCA_BASIC_PAD))
0310         goto nla_put_failure;
0311 
0312     if (tcf_exts_dump(skb, &f->exts) < 0 ||
0313         tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
0314         goto nla_put_failure;
0315 
0316     nla_nest_end(skb, nest);
0317 
0318     if (tcf_exts_dump_stats(skb, &f->exts) < 0)
0319         goto nla_put_failure;
0320 
0321     return skb->len;
0322 
0323 nla_put_failure:
0324     nla_nest_cancel(skb, nest);
0325     return -1;
0326 }
0327 
0328 static struct tcf_proto_ops cls_basic_ops __read_mostly = {
0329     .kind       =   "basic",
0330     .classify   =   basic_classify,
0331     .init       =   basic_init,
0332     .destroy    =   basic_destroy,
0333     .get        =   basic_get,
0334     .change     =   basic_change,
0335     .delete     =   basic_delete,
0336     .walk       =   basic_walk,
0337     .dump       =   basic_dump,
0338     .bind_class =   basic_bind_class,
0339     .owner      =   THIS_MODULE,
0340 };
0341 
0342 static int __init init_basic(void)
0343 {
0344     return register_tcf_proto_ops(&cls_basic_ops);
0345 }
0346 
0347 static void __exit exit_basic(void)
0348 {
0349     unregister_tcf_proto_ops(&cls_basic_ops);
0350 }
0351 
0352 module_init(init_basic)
0353 module_exit(exit_basic)
0354 MODULE_LICENSE("GPL");