linux/net/sched/cls_basic.c
<<
>>
Prefs
   1/*
   2 * net/sched/cls_basic.c        Basic Packet Classifier.
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Thomas Graf <tgraf@suug.ch>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/types.h>
  15#include <linux/kernel.h>
  16#include <linux/string.h>
  17#include <linux/errno.h>
  18#include <linux/rtnetlink.h>
  19#include <linux/skbuff.h>
  20#include <net/netlink.h>
  21#include <net/act_api.h>
  22#include <net/pkt_cls.h>
  23
  24struct basic_head {
  25        u32                     hgenerator;
  26        struct list_head        flist;
  27        struct rcu_head         rcu;
  28};
  29
  30struct basic_filter {
  31        u32                     handle;
  32        struct tcf_exts         exts;
  33        struct tcf_ematch_tree  ematches;
  34        struct tcf_result       res;
  35        struct tcf_proto        *tp;
  36        struct list_head        link;
  37        struct rcu_head         rcu;
  38};
  39
  40static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
  41                          struct tcf_result *res)
  42{
  43        int r;
  44        struct basic_head *head = rcu_dereference_bh(tp->root);
  45        struct basic_filter *f;
  46
  47        list_for_each_entry_rcu(f, &head->flist, link) {
  48                if (!tcf_em_tree_match(skb, &f->ematches, NULL))
  49                        continue;
  50                *res = f->res;
  51                r = tcf_exts_exec(skb, &f->exts, res);
  52                if (r < 0)
  53                        continue;
  54                return r;
  55        }
  56        return -1;
  57}
  58
  59static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
  60{
  61        unsigned long l = 0UL;
  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                        l = (unsigned long) f;
  68                        break;
  69                }
  70        }
  71
  72        return l;
  73}
  74
  75static int basic_init(struct tcf_proto *tp)
  76{
  77        struct basic_head *head;
  78
  79        head = kzalloc(sizeof(*head), GFP_KERNEL);
  80        if (head == NULL)
  81                return -ENOBUFS;
  82        INIT_LIST_HEAD(&head->flist);
  83        rcu_assign_pointer(tp->root, head);
  84        return 0;
  85}
  86
  87static void basic_delete_filter(struct rcu_head *head)
  88{
  89        struct basic_filter *f = container_of(head, struct basic_filter, rcu);
  90
  91        tcf_exts_destroy(&f->exts);
  92        tcf_em_tree_destroy(&f->ematches);
  93        kfree(f);
  94}
  95
  96static bool basic_destroy(struct tcf_proto *tp, bool force)
  97{
  98        struct basic_head *head = rtnl_dereference(tp->root);
  99        struct basic_filter *f, *n;
 100
 101        if (!force && !list_empty(&head->flist))
 102                return false;
 103
 104        list_for_each_entry_safe(f, n, &head->flist, link) {
 105                list_del_rcu(&f->link);
 106                tcf_unbind_filter(tp, &f->res);
 107                call_rcu(&f->rcu, basic_delete_filter);
 108        }
 109        kfree_rcu(head, rcu);
 110        return true;
 111}
 112
 113static int basic_delete(struct tcf_proto *tp, unsigned long arg)
 114{
 115        struct basic_filter *f = (struct basic_filter *) arg;
 116
 117        list_del_rcu(&f->link);
 118        tcf_unbind_filter(tp, &f->res);
 119        call_rcu(&f->rcu, basic_delete_filter);
 120        return 0;
 121}
 122
 123static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
 124        [TCA_BASIC_CLASSID]     = { .type = NLA_U32 },
 125        [TCA_BASIC_EMATCHES]    = { .type = NLA_NESTED },
 126};
 127
 128static int basic_set_parms(struct net *net, struct tcf_proto *tp,
 129                           struct basic_filter *f, unsigned long base,
 130                           struct nlattr **tb,
 131                           struct nlattr *est, bool ovr)
 132{
 133        int err;
 134        struct tcf_exts e;
 135        struct tcf_ematch_tree t;
 136
 137        err = tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
 138        if (err < 0)
 139                return err;
 140        err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
 141        if (err < 0)
 142                goto errout;
 143
 144        err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &t);
 145        if (err < 0)
 146                goto errout;
 147
 148        if (tb[TCA_BASIC_CLASSID]) {
 149                f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
 150                tcf_bind_filter(tp, &f->res, base);
 151        }
 152
 153        tcf_exts_change(tp, &f->exts, &e);
 154        tcf_em_tree_change(tp, &f->ematches, &t);
 155        f->tp = tp;
 156
 157        return 0;
 158errout:
 159        tcf_exts_destroy(&e);
 160        return err;
 161}
 162
 163static int basic_change(struct net *net, struct sk_buff *in_skb,
 164                        struct tcf_proto *tp, unsigned long base, u32 handle,
 165                        struct nlattr **tca, unsigned long *arg, bool ovr)
 166{
 167        int err;
 168        struct basic_head *head = rtnl_dereference(tp->root);
 169        struct nlattr *tb[TCA_BASIC_MAX + 1];
 170        struct basic_filter *fold = (struct basic_filter *) *arg;
 171        struct basic_filter *fnew;
 172
 173        if (tca[TCA_OPTIONS] == NULL)
 174                return -EINVAL;
 175
 176        err = nla_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS],
 177                               basic_policy);
 178        if (err < 0)
 179                return err;
 180
 181        if (fold != NULL) {
 182                if (handle && fold->handle != handle)
 183                        return -EINVAL;
 184        }
 185
 186        fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
 187        if (!fnew)
 188                return -ENOBUFS;
 189
 190        err = tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
 191        if (err < 0)
 192                goto errout;
 193
 194        err = -EINVAL;
 195        if (handle) {
 196                fnew->handle = handle;
 197        } else if (fold) {
 198                fnew->handle = fold->handle;
 199        } else {
 200                unsigned int i = 0x80000000;
 201                do {
 202                        if (++head->hgenerator == 0x7FFFFFFF)
 203                                head->hgenerator = 1;
 204                } while (--i > 0 && basic_get(tp, head->hgenerator));
 205
 206                if (i <= 0) {
 207                        pr_err("Insufficient number of handles\n");
 208                        goto errout;
 209                }
 210
 211                fnew->handle = head->hgenerator;
 212        }
 213
 214        err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr);
 215        if (err < 0)
 216                goto errout;
 217
 218        *arg = (unsigned long)fnew;
 219
 220        if (fold) {
 221                list_replace_rcu(&fold->link, &fnew->link);
 222                tcf_unbind_filter(tp, &fold->res);
 223                call_rcu(&fold->rcu, basic_delete_filter);
 224        } else {
 225                list_add_rcu(&fnew->link, &head->flist);
 226        }
 227
 228        return 0;
 229errout:
 230        tcf_exts_destroy(&fnew->exts);
 231        kfree(fnew);
 232        return err;
 233}
 234
 235static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 236{
 237        struct basic_head *head = rtnl_dereference(tp->root);
 238        struct basic_filter *f;
 239
 240        list_for_each_entry(f, &head->flist, link) {
 241                if (arg->count < arg->skip)
 242                        goto skip;
 243
 244                if (arg->fn(tp, (unsigned long) f, arg) < 0) {
 245                        arg->stop = 1;
 246                        break;
 247                }
 248skip:
 249                arg->count++;
 250        }
 251}
 252
 253static int basic_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 254                      struct sk_buff *skb, struct tcmsg *t)
 255{
 256        struct basic_filter *f = (struct basic_filter *) fh;
 257        struct nlattr *nest;
 258
 259        if (f == NULL)
 260                return skb->len;
 261
 262        t->tcm_handle = f->handle;
 263
 264        nest = nla_nest_start(skb, TCA_OPTIONS);
 265        if (nest == NULL)
 266                goto nla_put_failure;
 267
 268        if (f->res.classid &&
 269            nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
 270                goto nla_put_failure;
 271
 272        if (tcf_exts_dump(skb, &f->exts) < 0 ||
 273            tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
 274                goto nla_put_failure;
 275
 276        nla_nest_end(skb, nest);
 277
 278        if (tcf_exts_dump_stats(skb, &f->exts) < 0)
 279                goto nla_put_failure;
 280
 281        return skb->len;
 282
 283nla_put_failure:
 284        nla_nest_cancel(skb, nest);
 285        return -1;
 286}
 287
 288static struct tcf_proto_ops cls_basic_ops __read_mostly = {
 289        .kind           =       "basic",
 290        .classify       =       basic_classify,
 291        .init           =       basic_init,
 292        .destroy        =       basic_destroy,
 293        .get            =       basic_get,
 294        .change         =       basic_change,
 295        .delete         =       basic_delete,
 296        .walk           =       basic_walk,
 297        .dump           =       basic_dump,
 298        .owner          =       THIS_MODULE,
 299};
 300
 301static int __init init_basic(void)
 302{
 303        return register_tcf_proto_ops(&cls_basic_ops);
 304}
 305
 306static void __exit exit_basic(void)
 307{
 308        unregister_tcf_proto_ops(&cls_basic_ops);
 309}
 310
 311module_init(init_basic)
 312module_exit(exit_basic)
 313MODULE_LICENSE("GPL");
 314
 315