linux/net/sched/cls_basic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * net/sched/cls_basic.c        Basic Packet Classifier.
   4 *
   5 * Authors:     Thomas Graf <tgraf@suug.ch>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/types.h>
  11#include <linux/kernel.h>
  12#include <linux/string.h>
  13#include <linux/errno.h>
  14#include <linux/rtnetlink.h>
  15#include <linux/skbuff.h>
  16#include <linux/idr.h>
  17#include <linux/percpu.h>
  18#include <net/netlink.h>
  19#include <net/act_api.h>
  20#include <net/pkt_cls.h>
  21
  22struct basic_head {
  23        struct list_head        flist;
  24        struct idr              handle_idr;
  25        struct rcu_head         rcu;
  26};
  27
  28struct basic_filter {
  29        u32                     handle;
  30        struct tcf_exts         exts;
  31        struct tcf_ematch_tree  ematches;
  32        struct tcf_result       res;
  33        struct tcf_proto        *tp;
  34        struct list_head        link;
  35        struct tc_basic_pcnt __percpu *pf;
  36        struct rcu_work         rwork;
  37};
  38
  39static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
  40                          struct tcf_result *res)
  41{
  42        int r;
  43        struct basic_head *head = rcu_dereference_bh(tp->root);
  44        struct basic_filter *f;
  45
  46        list_for_each_entry_rcu(f, &head->flist, link) {
  47                __this_cpu_inc(f->pf->rcnt);
  48                if (!tcf_em_tree_match(skb, &f->ematches, NULL))
  49                        continue;
  50                __this_cpu_inc(f->pf->rhit);
  51                *res = f->res;
  52                r = tcf_exts_exec(skb, &f->exts, res);
  53                if (r < 0)
  54                        continue;
  55                return r;
  56        }
  57        return -1;
  58}
  59
  60static void *basic_get(struct tcf_proto *tp, u32 handle)
  61{
  62        struct basic_head *head = rtnl_dereference(tp->root);
  63        struct basic_filter *f;
  64
  65        list_for_each_entry(f, &head->flist, link) {
  66                if (f->handle == handle) {
  67                        return f;
  68                }
  69        }
  70
  71        return NULL;
  72}
  73
  74static int basic_init(struct tcf_proto *tp)
  75{
  76        struct basic_head *head;
  77
  78        head = kzalloc(sizeof(*head), GFP_KERNEL);
  79        if (head == NULL)
  80                return -ENOBUFS;
  81        INIT_LIST_HEAD(&head->flist);
  82        idr_init(&head->handle_idr);
  83        rcu_assign_pointer(tp->root, head);
  84        return 0;
  85}
  86
  87static void __basic_delete_filter(struct basic_filter *f)
  88{
  89        tcf_exts_destroy(&f->exts);
  90        tcf_em_tree_destroy(&f->ematches);
  91        tcf_exts_put_net(&f->exts);
  92        free_percpu(f->pf);
  93        kfree(f);
  94}
  95
  96static void basic_delete_filter_work(struct work_struct *work)
  97{
  98        struct basic_filter *f = container_of(to_rcu_work(work),
  99                                              struct basic_filter,
 100                                              rwork);
 101        rtnl_lock();
 102        __basic_delete_filter(f);
 103        rtnl_unlock();
 104}
 105
 106static void basic_destroy(struct tcf_proto *tp, bool rtnl_held,
 107                          struct netlink_ext_ack *extack)
 108{
 109        struct basic_head *head = rtnl_dereference(tp->root);
 110        struct basic_filter *f, *n;
 111
 112        list_for_each_entry_safe(f, n, &head->flist, link) {
 113                list_del_rcu(&f->link);
 114                tcf_unbind_filter(tp, &f->res);
 115                idr_remove(&head->handle_idr, f->handle);
 116                if (tcf_exts_get_net(&f->exts))
 117                        tcf_queue_work(&f->rwork, basic_delete_filter_work);
 118                else
 119                        __basic_delete_filter(f);
 120        }
 121        idr_destroy(&head->handle_idr);
 122        kfree_rcu(head, rcu);
 123}
 124
 125static int basic_delete(struct tcf_proto *tp, void *arg, bool *last,
 126                        bool rtnl_held, struct netlink_ext_ack *extack)
 127{
 128        struct basic_head *head = rtnl_dereference(tp->root);
 129        struct basic_filter *f = arg;
 130
 131        list_del_rcu(&f->link);
 132        tcf_unbind_filter(tp, &f->res);
 133        idr_remove(&head->handle_idr, f->handle);
 134        tcf_exts_get_net(&f->exts);
 135        tcf_queue_work(&f->rwork, basic_delete_filter_work);
 136        *last = list_empty(&head->flist);
 137        return 0;
 138}
 139
 140static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
 141        [TCA_BASIC_CLASSID]     = { .type = NLA_U32 },
 142        [TCA_BASIC_EMATCHES]    = { .type = NLA_NESTED },
 143};
 144
 145static int basic_set_parms(struct net *net, struct tcf_proto *tp,
 146                           struct basic_filter *f, unsigned long base,
 147                           struct nlattr **tb,
 148                           struct nlattr *est, u32 flags,
 149                           struct netlink_ext_ack *extack)
 150{
 151        int err;
 152
 153        err = tcf_exts_validate(net, tp, tb, est, &f->exts, flags, extack);
 154        if (err < 0)
 155                return err;
 156
 157        err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches);
 158        if (err < 0)
 159                return err;
 160
 161        if (tb[TCA_BASIC_CLASSID]) {
 162                f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
 163                tcf_bind_filter(tp, &f->res, base);
 164        }
 165
 166        f->tp = tp;
 167        return 0;
 168}
 169
 170static int basic_change(struct net *net, struct sk_buff *in_skb,
 171                        struct tcf_proto *tp, unsigned long base, u32 handle,
 172                        struct nlattr **tca, void **arg,
 173                        u32 flags, struct netlink_ext_ack *extack)
 174{
 175        int err;
 176        struct basic_head *head = rtnl_dereference(tp->root);
 177        struct nlattr *tb[TCA_BASIC_MAX + 1];
 178        struct basic_filter *fold = (struct basic_filter *) *arg;
 179        struct basic_filter *fnew;
 180
 181        if (tca[TCA_OPTIONS] == NULL)
 182                return -EINVAL;
 183
 184        err = nla_parse_nested_deprecated(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS],
 185                                          basic_policy, NULL);
 186        if (err < 0)
 187                return err;
 188
 189        if (fold != NULL) {
 190                if (handle && fold->handle != handle)
 191                        return -EINVAL;
 192        }
 193
 194        fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
 195        if (!fnew)
 196                return -ENOBUFS;
 197
 198        err = tcf_exts_init(&fnew->exts, net, TCA_BASIC_ACT, TCA_BASIC_POLICE);
 199        if (err < 0)
 200                goto errout;
 201
 202        if (!handle) {
 203                handle = 1;
 204                err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
 205                                    INT_MAX, GFP_KERNEL);
 206        } else if (!fold) {
 207                err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
 208                                    handle, GFP_KERNEL);
 209        }
 210        if (err)
 211                goto errout;
 212        fnew->handle = handle;
 213        fnew->pf = alloc_percpu(struct tc_basic_pcnt);
 214        if (!fnew->pf) {
 215                err = -ENOMEM;
 216                goto errout;
 217        }
 218
 219        err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], flags,
 220                              extack);
 221        if (err < 0) {
 222                if (!fold)
 223                        idr_remove(&head->handle_idr, fnew->handle);
 224                goto errout;
 225        }
 226
 227        *arg = fnew;
 228
 229        if (fold) {
 230                idr_replace(&head->handle_idr, fnew, fnew->handle);
 231                list_replace_rcu(&fold->link, &fnew->link);
 232                tcf_unbind_filter(tp, &fold->res);
 233                tcf_exts_get_net(&fold->exts);
 234                tcf_queue_work(&fold->rwork, basic_delete_filter_work);
 235        } else {
 236                list_add_rcu(&fnew->link, &head->flist);
 237        }
 238
 239        return 0;
 240errout:
 241        free_percpu(fnew->pf);
 242        tcf_exts_destroy(&fnew->exts);
 243        kfree(fnew);
 244        return err;
 245}
 246
 247static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg,
 248                       bool rtnl_held)
 249{
 250        struct basic_head *head = rtnl_dereference(tp->root);
 251        struct basic_filter *f;
 252
 253        list_for_each_entry(f, &head->flist, link) {
 254                if (arg->count < arg->skip)
 255                        goto skip;
 256
 257                if (arg->fn(tp, f, arg) < 0) {
 258                        arg->stop = 1;
 259                        break;
 260                }
 261skip:
 262                arg->count++;
 263        }
 264}
 265
 266static void basic_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
 267                             unsigned long base)
 268{
 269        struct basic_filter *f = fh;
 270
 271        if (f && f->res.classid == classid) {
 272                if (cl)
 273                        __tcf_bind_filter(q, &f->res, base);
 274                else
 275                        __tcf_unbind_filter(q, &f->res);
 276        }
 277}
 278
 279static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh,
 280                      struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 281{
 282        struct tc_basic_pcnt gpf = {};
 283        struct basic_filter *f = fh;
 284        struct nlattr *nest;
 285        int cpu;
 286
 287        if (f == NULL)
 288                return skb->len;
 289
 290        t->tcm_handle = f->handle;
 291
 292        nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
 293        if (nest == NULL)
 294                goto nla_put_failure;
 295
 296        if (f->res.classid &&
 297            nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
 298                goto nla_put_failure;
 299
 300        for_each_possible_cpu(cpu) {
 301                struct tc_basic_pcnt *pf = per_cpu_ptr(f->pf, cpu);
 302
 303                gpf.rcnt += pf->rcnt;
 304                gpf.rhit += pf->rhit;
 305        }
 306
 307        if (nla_put_64bit(skb, TCA_BASIC_PCNT,
 308                          sizeof(struct tc_basic_pcnt),
 309                          &gpf, TCA_BASIC_PAD))
 310                goto nla_put_failure;
 311
 312        if (tcf_exts_dump(skb, &f->exts) < 0 ||
 313            tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
 314                goto nla_put_failure;
 315
 316        nla_nest_end(skb, nest);
 317
 318        if (tcf_exts_dump_stats(skb, &f->exts) < 0)
 319                goto nla_put_failure;
 320
 321        return skb->len;
 322
 323nla_put_failure:
 324        nla_nest_cancel(skb, nest);
 325        return -1;
 326}
 327
 328static struct tcf_proto_ops cls_basic_ops __read_mostly = {
 329        .kind           =       "basic",
 330        .classify       =       basic_classify,
 331        .init           =       basic_init,
 332        .destroy        =       basic_destroy,
 333        .get            =       basic_get,
 334        .change         =       basic_change,
 335        .delete         =       basic_delete,
 336        .walk           =       basic_walk,
 337        .dump           =       basic_dump,
 338        .bind_class     =       basic_bind_class,
 339        .owner          =       THIS_MODULE,
 340};
 341
 342static int __init init_basic(void)
 343{
 344        return register_tcf_proto_ops(&cls_basic_ops);
 345}
 346
 347static void __exit exit_basic(void)
 348{
 349        unregister_tcf_proto_ops(&cls_basic_ops);
 350}
 351
 352module_init(init_basic)
 353module_exit(exit_basic)
 354MODULE_LICENSE("GPL");
 355