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