linux/net/sched/cls_route.c
<<
>>
Prefs
   1/*
   2 * net/sched/cls_route.c        ROUTE4 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:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  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/skbuff.h>
  19#include <net/dst.h>
  20#include <net/route.h>
  21#include <net/netlink.h>
  22#include <net/act_api.h>
  23#include <net/pkt_cls.h>
  24
  25/*
  26 * 1. For now we assume that route tags < 256.
  27 *    It allows to use direct table lookups, instead of hash tables.
  28 * 2. For now we assume that "from TAG" and "fromdev DEV" statements
  29 *    are mutually  exclusive.
  30 * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX"
  31 */
  32struct route4_fastmap {
  33        struct route4_filter            *filter;
  34        u32                             id;
  35        int                             iif;
  36};
  37
  38struct route4_head {
  39        struct route4_fastmap           fastmap[16];
  40        struct route4_bucket __rcu      *table[256 + 1];
  41        struct rcu_head                 rcu;
  42};
  43
  44struct route4_bucket {
  45        /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
  46        struct route4_filter __rcu      *ht[16 + 16 + 1];
  47        struct rcu_head                 rcu;
  48};
  49
  50struct route4_filter {
  51        struct route4_filter __rcu      *next;
  52        u32                     id;
  53        int                     iif;
  54
  55        struct tcf_result       res;
  56        struct tcf_exts         exts;
  57        u32                     handle;
  58        struct route4_bucket    *bkt;
  59        struct tcf_proto        *tp;
  60        union {
  61                struct work_struct      work;
  62                struct rcu_head         rcu;
  63        };
  64};
  65
  66#define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
  67
  68static inline int route4_fastmap_hash(u32 id, int iif)
  69{
  70        return id & 0xF;
  71}
  72
  73static DEFINE_SPINLOCK(fastmap_lock);
  74static void
  75route4_reset_fastmap(struct route4_head *head)
  76{
  77        spin_lock_bh(&fastmap_lock);
  78        memset(head->fastmap, 0, sizeof(head->fastmap));
  79        spin_unlock_bh(&fastmap_lock);
  80}
  81
  82static void
  83route4_set_fastmap(struct route4_head *head, u32 id, int iif,
  84                   struct route4_filter *f)
  85{
  86        int h = route4_fastmap_hash(id, iif);
  87
  88        /* fastmap updates must look atomic to aling id, iff, filter */
  89        spin_lock_bh(&fastmap_lock);
  90        head->fastmap[h].id = id;
  91        head->fastmap[h].iif = iif;
  92        head->fastmap[h].filter = f;
  93        spin_unlock_bh(&fastmap_lock);
  94}
  95
  96static inline int route4_hash_to(u32 id)
  97{
  98        return id & 0xFF;
  99}
 100
 101static inline int route4_hash_from(u32 id)
 102{
 103        return (id >> 16) & 0xF;
 104}
 105
 106static inline int route4_hash_iif(int iif)
 107{
 108        return 16 + ((iif >> 16) & 0xF);
 109}
 110
 111static inline int route4_hash_wild(void)
 112{
 113        return 32;
 114}
 115
 116#define ROUTE4_APPLY_RESULT()                                   \
 117{                                                               \
 118        *res = f->res;                                          \
 119        if (tcf_exts_has_actions(&f->exts)) {                   \
 120                int r = tcf_exts_exec(skb, &f->exts, res);      \
 121                if (r < 0) {                                    \
 122                        dont_cache = 1;                         \
 123                        continue;                               \
 124                }                                               \
 125                return r;                                       \
 126        } else if (!dont_cache)                                 \
 127                route4_set_fastmap(head, id, iif, f);           \
 128        return 0;                                               \
 129}
 130
 131static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 132                           struct tcf_result *res)
 133{
 134        struct route4_head *head = rcu_dereference_bh(tp->root);
 135        struct dst_entry *dst;
 136        struct route4_bucket *b;
 137        struct route4_filter *f;
 138        u32 id, h;
 139        int iif, dont_cache = 0;
 140
 141        dst = skb_dst(skb);
 142        if (!dst)
 143                goto failure;
 144
 145        id = dst->tclassid;
 146
 147        iif = inet_iif(skb);
 148
 149        h = route4_fastmap_hash(id, iif);
 150
 151        spin_lock(&fastmap_lock);
 152        if (id == head->fastmap[h].id &&
 153            iif == head->fastmap[h].iif &&
 154            (f = head->fastmap[h].filter) != NULL) {
 155                if (f == ROUTE4_FAILURE) {
 156                        spin_unlock(&fastmap_lock);
 157                        goto failure;
 158                }
 159
 160                *res = f->res;
 161                spin_unlock(&fastmap_lock);
 162                return 0;
 163        }
 164        spin_unlock(&fastmap_lock);
 165
 166        h = route4_hash_to(id);
 167
 168restart:
 169        b = rcu_dereference_bh(head->table[h]);
 170        if (b) {
 171                for (f = rcu_dereference_bh(b->ht[route4_hash_from(id)]);
 172                     f;
 173                     f = rcu_dereference_bh(f->next))
 174                        if (f->id == id)
 175                                ROUTE4_APPLY_RESULT();
 176
 177                for (f = rcu_dereference_bh(b->ht[route4_hash_iif(iif)]);
 178                     f;
 179                     f = rcu_dereference_bh(f->next))
 180                        if (f->iif == iif)
 181                                ROUTE4_APPLY_RESULT();
 182
 183                for (f = rcu_dereference_bh(b->ht[route4_hash_wild()]);
 184                     f;
 185                     f = rcu_dereference_bh(f->next))
 186                        ROUTE4_APPLY_RESULT();
 187        }
 188        if (h < 256) {
 189                h = 256;
 190                id &= ~0xFFFF;
 191                goto restart;
 192        }
 193
 194        if (!dont_cache)
 195                route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
 196failure:
 197        return -1;
 198}
 199
 200static inline u32 to_hash(u32 id)
 201{
 202        u32 h = id & 0xFF;
 203
 204        if (id & 0x8000)
 205                h += 256;
 206        return h;
 207}
 208
 209static inline u32 from_hash(u32 id)
 210{
 211        id &= 0xFFFF;
 212        if (id == 0xFFFF)
 213                return 32;
 214        if (!(id & 0x8000)) {
 215                if (id > 255)
 216                        return 256;
 217                return id & 0xF;
 218        }
 219        return 16 + (id & 0xF);
 220}
 221
 222static void *route4_get(struct tcf_proto *tp, u32 handle)
 223{
 224        struct route4_head *head = rtnl_dereference(tp->root);
 225        struct route4_bucket *b;
 226        struct route4_filter *f;
 227        unsigned int h1, h2;
 228
 229        h1 = to_hash(handle);
 230        if (h1 > 256)
 231                return NULL;
 232
 233        h2 = from_hash(handle >> 16);
 234        if (h2 > 32)
 235                return NULL;
 236
 237        b = rtnl_dereference(head->table[h1]);
 238        if (b) {
 239                for (f = rtnl_dereference(b->ht[h2]);
 240                     f;
 241                     f = rtnl_dereference(f->next))
 242                        if (f->handle == handle)
 243                                return f;
 244        }
 245        return NULL;
 246}
 247
 248static int route4_init(struct tcf_proto *tp)
 249{
 250        struct route4_head *head;
 251
 252        head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
 253        if (head == NULL)
 254                return -ENOBUFS;
 255
 256        rcu_assign_pointer(tp->root, head);
 257        return 0;
 258}
 259
 260static void __route4_delete_filter(struct route4_filter *f)
 261{
 262        tcf_exts_destroy(&f->exts);
 263        tcf_exts_put_net(&f->exts);
 264        kfree(f);
 265}
 266
 267static void route4_delete_filter_work(struct work_struct *work)
 268{
 269        struct route4_filter *f = container_of(work, struct route4_filter, work);
 270
 271        rtnl_lock();
 272        __route4_delete_filter(f);
 273        rtnl_unlock();
 274}
 275
 276static void route4_delete_filter(struct rcu_head *head)
 277{
 278        struct route4_filter *f = container_of(head, struct route4_filter, rcu);
 279
 280        INIT_WORK(&f->work, route4_delete_filter_work);
 281        tcf_queue_work(&f->work);
 282}
 283
 284static void route4_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 285{
 286        struct route4_head *head = rtnl_dereference(tp->root);
 287        int h1, h2;
 288
 289        if (head == NULL)
 290                return;
 291
 292        for (h1 = 0; h1 <= 256; h1++) {
 293                struct route4_bucket *b;
 294
 295                b = rtnl_dereference(head->table[h1]);
 296                if (b) {
 297                        for (h2 = 0; h2 <= 32; h2++) {
 298                                struct route4_filter *f;
 299
 300                                while ((f = rtnl_dereference(b->ht[h2])) != NULL) {
 301                                        struct route4_filter *next;
 302
 303                                        next = rtnl_dereference(f->next);
 304                                        RCU_INIT_POINTER(b->ht[h2], next);
 305                                        tcf_unbind_filter(tp, &f->res);
 306                                        if (tcf_exts_get_net(&f->exts))
 307                                                call_rcu(&f->rcu, route4_delete_filter);
 308                                        else
 309                                                __route4_delete_filter(f);
 310                                }
 311                        }
 312                        RCU_INIT_POINTER(head->table[h1], NULL);
 313                        kfree_rcu(b, rcu);
 314                }
 315        }
 316        kfree_rcu(head, rcu);
 317}
 318
 319static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
 320                         struct netlink_ext_ack *extack)
 321{
 322        struct route4_head *head = rtnl_dereference(tp->root);
 323        struct route4_filter *f = arg;
 324        struct route4_filter __rcu **fp;
 325        struct route4_filter *nf;
 326        struct route4_bucket *b;
 327        unsigned int h = 0;
 328        int i, h1;
 329
 330        if (!head || !f)
 331                return -EINVAL;
 332
 333        h = f->handle;
 334        b = f->bkt;
 335
 336        fp = &b->ht[from_hash(h >> 16)];
 337        for (nf = rtnl_dereference(*fp); nf;
 338             fp = &nf->next, nf = rtnl_dereference(*fp)) {
 339                if (nf == f) {
 340                        /* unlink it */
 341                        RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
 342
 343                        /* Remove any fastmap lookups that might ref filter
 344                         * notice we unlink'd the filter so we can't get it
 345                         * back in the fastmap.
 346                         */
 347                        route4_reset_fastmap(head);
 348
 349                        /* Delete it */
 350                        tcf_unbind_filter(tp, &f->res);
 351                        tcf_exts_get_net(&f->exts);
 352                        call_rcu(&f->rcu, route4_delete_filter);
 353
 354                        /* Strip RTNL protected tree */
 355                        for (i = 0; i <= 32; i++) {
 356                                struct route4_filter *rt;
 357
 358                                rt = rtnl_dereference(b->ht[i]);
 359                                if (rt)
 360                                        goto out;
 361                        }
 362
 363                        /* OK, session has no flows */
 364                        RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
 365                        kfree_rcu(b, rcu);
 366                        break;
 367                }
 368        }
 369
 370out:
 371        *last = true;
 372        for (h1 = 0; h1 <= 256; h1++) {
 373                if (rcu_access_pointer(head->table[h1])) {
 374                        *last = false;
 375                        break;
 376                }
 377        }
 378
 379        return 0;
 380}
 381
 382static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
 383        [TCA_ROUTE4_CLASSID]    = { .type = NLA_U32 },
 384        [TCA_ROUTE4_TO]         = { .type = NLA_U32 },
 385        [TCA_ROUTE4_FROM]       = { .type = NLA_U32 },
 386        [TCA_ROUTE4_IIF]        = { .type = NLA_U32 },
 387};
 388
 389static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 390                            unsigned long base, struct route4_filter *f,
 391                            u32 handle, struct route4_head *head,
 392                            struct nlattr **tb, struct nlattr *est, int new,
 393                            bool ovr, struct netlink_ext_ack *extack)
 394{
 395        u32 id = 0, to = 0, nhandle = 0x8000;
 396        struct route4_filter *fp;
 397        unsigned int h1;
 398        struct route4_bucket *b;
 399        int err;
 400
 401        err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, extack);
 402        if (err < 0)
 403                return err;
 404
 405        if (tb[TCA_ROUTE4_TO]) {
 406                if (new && handle & 0x8000)
 407                        return -EINVAL;
 408                to = nla_get_u32(tb[TCA_ROUTE4_TO]);
 409                if (to > 0xFF)
 410                        return -EINVAL;
 411                nhandle = to;
 412        }
 413
 414        if (tb[TCA_ROUTE4_FROM]) {
 415                if (tb[TCA_ROUTE4_IIF])
 416                        return -EINVAL;
 417                id = nla_get_u32(tb[TCA_ROUTE4_FROM]);
 418                if (id > 0xFF)
 419                        return -EINVAL;
 420                nhandle |= id << 16;
 421        } else if (tb[TCA_ROUTE4_IIF]) {
 422                id = nla_get_u32(tb[TCA_ROUTE4_IIF]);
 423                if (id > 0x7FFF)
 424                        return -EINVAL;
 425                nhandle |= (id | 0x8000) << 16;
 426        } else
 427                nhandle |= 0xFFFF << 16;
 428
 429        if (handle && new) {
 430                nhandle |= handle & 0x7F00;
 431                if (nhandle != handle)
 432                        return -EINVAL;
 433        }
 434
 435        h1 = to_hash(nhandle);
 436        b = rtnl_dereference(head->table[h1]);
 437        if (!b) {
 438                b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL);
 439                if (b == NULL)
 440                        return -ENOBUFS;
 441
 442                rcu_assign_pointer(head->table[h1], b);
 443        } else {
 444                unsigned int h2 = from_hash(nhandle >> 16);
 445
 446                for (fp = rtnl_dereference(b->ht[h2]);
 447                     fp;
 448                     fp = rtnl_dereference(fp->next))
 449                        if (fp->handle == f->handle)
 450                                return -EEXIST;
 451        }
 452
 453        if (tb[TCA_ROUTE4_TO])
 454                f->id = to;
 455
 456        if (tb[TCA_ROUTE4_FROM])
 457                f->id = to | id<<16;
 458        else if (tb[TCA_ROUTE4_IIF])
 459                f->iif = id;
 460
 461        f->handle = nhandle;
 462        f->bkt = b;
 463        f->tp = tp;
 464
 465        if (tb[TCA_ROUTE4_CLASSID]) {
 466                f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]);
 467                tcf_bind_filter(tp, &f->res, base);
 468        }
 469
 470        return 0;
 471}
 472
 473static int route4_change(struct net *net, struct sk_buff *in_skb,
 474                         struct tcf_proto *tp, unsigned long base, u32 handle,
 475                         struct nlattr **tca, void **arg, bool ovr,
 476                         struct netlink_ext_ack *extack)
 477{
 478        struct route4_head *head = rtnl_dereference(tp->root);
 479        struct route4_filter __rcu **fp;
 480        struct route4_filter *fold, *f1, *pfp, *f = NULL;
 481        struct route4_bucket *b;
 482        struct nlattr *opt = tca[TCA_OPTIONS];
 483        struct nlattr *tb[TCA_ROUTE4_MAX + 1];
 484        unsigned int h, th;
 485        int err;
 486        bool new = true;
 487
 488        if (opt == NULL)
 489                return handle ? -EINVAL : 0;
 490
 491        err = nla_parse_nested(tb, TCA_ROUTE4_MAX, opt, route4_policy, NULL);
 492        if (err < 0)
 493                return err;
 494
 495        fold = *arg;
 496        if (fold && handle && fold->handle != handle)
 497                        return -EINVAL;
 498
 499        err = -ENOBUFS;
 500        f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
 501        if (!f)
 502                goto errout;
 503
 504        err = tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
 505        if (err < 0)
 506                goto errout;
 507
 508        if (fold) {
 509                f->id = fold->id;
 510                f->iif = fold->iif;
 511                f->res = fold->res;
 512                f->handle = fold->handle;
 513
 514                f->tp = fold->tp;
 515                f->bkt = fold->bkt;
 516                new = false;
 517        }
 518
 519        err = route4_set_parms(net, tp, base, f, handle, head, tb,
 520                               tca[TCA_RATE], new, ovr, extack);
 521        if (err < 0)
 522                goto errout;
 523
 524        h = from_hash(f->handle >> 16);
 525        fp = &f->bkt->ht[h];
 526        for (pfp = rtnl_dereference(*fp);
 527             (f1 = rtnl_dereference(*fp)) != NULL;
 528             fp = &f1->next)
 529                if (f->handle < f1->handle)
 530                        break;
 531
 532        tcf_block_netif_keep_dst(tp->chain->block);
 533        rcu_assign_pointer(f->next, f1);
 534        rcu_assign_pointer(*fp, f);
 535
 536        if (fold && fold->handle && f->handle != fold->handle) {
 537                th = to_hash(fold->handle);
 538                h = from_hash(fold->handle >> 16);
 539                b = rtnl_dereference(head->table[th]);
 540                if (b) {
 541                        fp = &b->ht[h];
 542                        for (pfp = rtnl_dereference(*fp); pfp;
 543                             fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
 544                                if (pfp == f) {
 545                                        *fp = f->next;
 546                                        break;
 547                                }
 548                        }
 549                }
 550        }
 551
 552        route4_reset_fastmap(head);
 553        *arg = f;
 554        if (fold) {
 555                tcf_unbind_filter(tp, &fold->res);
 556                tcf_exts_get_net(&fold->exts);
 557                call_rcu(&fold->rcu, route4_delete_filter);
 558        }
 559        return 0;
 560
 561errout:
 562        if (f)
 563                tcf_exts_destroy(&f->exts);
 564        kfree(f);
 565        return err;
 566}
 567
 568static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 569{
 570        struct route4_head *head = rtnl_dereference(tp->root);
 571        unsigned int h, h1;
 572
 573        if (head == NULL)
 574                arg->stop = 1;
 575
 576        if (arg->stop)
 577                return;
 578
 579        for (h = 0; h <= 256; h++) {
 580                struct route4_bucket *b = rtnl_dereference(head->table[h]);
 581
 582                if (b) {
 583                        for (h1 = 0; h1 <= 32; h1++) {
 584                                struct route4_filter *f;
 585
 586                                for (f = rtnl_dereference(b->ht[h1]);
 587                                     f;
 588                                     f = rtnl_dereference(f->next)) {
 589                                        if (arg->count < arg->skip) {
 590                                                arg->count++;
 591                                                continue;
 592                                        }
 593                                        if (arg->fn(tp, f, arg) < 0) {
 594                                                arg->stop = 1;
 595                                                return;
 596                                        }
 597                                        arg->count++;
 598                                }
 599                        }
 600                }
 601        }
 602}
 603
 604static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh,
 605                       struct sk_buff *skb, struct tcmsg *t)
 606{
 607        struct route4_filter *f = fh;
 608        struct nlattr *nest;
 609        u32 id;
 610
 611        if (f == NULL)
 612                return skb->len;
 613
 614        t->tcm_handle = f->handle;
 615
 616        nest = nla_nest_start(skb, TCA_OPTIONS);
 617        if (nest == NULL)
 618                goto nla_put_failure;
 619
 620        if (!(f->handle & 0x8000)) {
 621                id = f->id & 0xFF;
 622                if (nla_put_u32(skb, TCA_ROUTE4_TO, id))
 623                        goto nla_put_failure;
 624        }
 625        if (f->handle & 0x80000000) {
 626                if ((f->handle >> 16) != 0xFFFF &&
 627                    nla_put_u32(skb, TCA_ROUTE4_IIF, f->iif))
 628                        goto nla_put_failure;
 629        } else {
 630                id = f->id >> 16;
 631                if (nla_put_u32(skb, TCA_ROUTE4_FROM, id))
 632                        goto nla_put_failure;
 633        }
 634        if (f->res.classid &&
 635            nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid))
 636                goto nla_put_failure;
 637
 638        if (tcf_exts_dump(skb, &f->exts) < 0)
 639                goto nla_put_failure;
 640
 641        nla_nest_end(skb, nest);
 642
 643        if (tcf_exts_dump_stats(skb, &f->exts) < 0)
 644                goto nla_put_failure;
 645
 646        return skb->len;
 647
 648nla_put_failure:
 649        nla_nest_cancel(skb, nest);
 650        return -1;
 651}
 652
 653static void route4_bind_class(void *fh, u32 classid, unsigned long cl)
 654{
 655        struct route4_filter *f = fh;
 656
 657        if (f && f->res.classid == classid)
 658                f->res.class = cl;
 659}
 660
 661static struct tcf_proto_ops cls_route4_ops __read_mostly = {
 662        .kind           =       "route",
 663        .classify       =       route4_classify,
 664        .init           =       route4_init,
 665        .destroy        =       route4_destroy,
 666        .get            =       route4_get,
 667        .change         =       route4_change,
 668        .delete         =       route4_delete,
 669        .walk           =       route4_walk,
 670        .dump           =       route4_dump,
 671        .bind_class     =       route4_bind_class,
 672        .owner          =       THIS_MODULE,
 673};
 674
 675static int __init init_route4(void)
 676{
 677        return register_tcf_proto_ops(&cls_route4_ops);
 678}
 679
 680static void __exit exit_route4(void)
 681{
 682        unregister_tcf_proto_ops(&cls_route4_ops);
 683}
 684
 685module_init(init_route4)
 686module_exit(exit_route4)
 687MODULE_LICENSE("GPL");
 688