linux/net/sched/sch_dsmark.c
<<
>>
Prefs
   1/* net/sched/sch_dsmark.c - Differentiated Services field marker */
   2
   3/* Written 1998-2000 by Werner Almesberger, EPFL ICA */
   4
   5
   6#include <linux/module.h>
   7#include <linux/init.h>
   8#include <linux/slab.h>
   9#include <linux/types.h>
  10#include <linux/string.h>
  11#include <linux/errno.h>
  12#include <linux/skbuff.h>
  13#include <linux/rtnetlink.h>
  14#include <linux/bitops.h>
  15#include <net/pkt_sched.h>
  16#include <net/dsfield.h>
  17#include <net/inet_ecn.h>
  18#include <asm/byteorder.h>
  19
  20/*
  21 * classid      class           marking
  22 * -------      -----           -------
  23 *   n/a          0             n/a
  24 *   x:0          1             use entry [0]
  25 *   ...         ...            ...
  26 *   x:y y>0     y+1            use entry [y]
  27 *   ...         ...            ...
  28 * x:indices-1  indices         use entry [indices-1]
  29 *   ...         ...            ...
  30 *   x:y         y+1            use entry [y & (indices-1)]
  31 *   ...         ...            ...
  32 * 0xffff       0x10000         use entry [indices-1]
  33 */
  34
  35
  36#define NO_DEFAULT_INDEX        (1 << 16)
  37
  38struct dsmark_qdisc_data {
  39        struct Qdisc            *q;
  40        struct tcf_proto        *filter_list;
  41        u8                      *mask;  /* "owns" the array */
  42        u8                      *value;
  43        u16                     indices;
  44        u32                     default_index;  /* index range is 0...0xffff */
  45        int                     set_tc_index;
  46};
  47
  48static inline int dsmark_valid_index(struct dsmark_qdisc_data *p, u16 index)
  49{
  50        return (index <= p->indices && index > 0);
  51}
  52
  53/* ------------------------- Class/flow operations ------------------------- */
  54
  55static int dsmark_graft(struct Qdisc *sch, unsigned long arg,
  56                        struct Qdisc *new, struct Qdisc **old)
  57{
  58        struct dsmark_qdisc_data *p = qdisc_priv(sch);
  59
  60        pr_debug("dsmark_graft(sch %p,[qdisc %p],new %p,old %p)\n",
  61                sch, p, new, old);
  62
  63        if (new == NULL) {
  64                new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
  65                                        &pfifo_qdisc_ops,
  66                                        sch->handle);
  67                if (new == NULL)
  68                        new = &noop_qdisc;
  69        }
  70
  71        sch_tree_lock(sch);
  72        *old = p->q;
  73        p->q = new;
  74        qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
  75        qdisc_reset(*old);
  76        sch_tree_unlock(sch);
  77
  78        return 0;
  79}
  80
  81static struct Qdisc *dsmark_leaf(struct Qdisc *sch, unsigned long arg)
  82{
  83        struct dsmark_qdisc_data *p = qdisc_priv(sch);
  84        return p->q;
  85}
  86
  87static unsigned long dsmark_get(struct Qdisc *sch, u32 classid)
  88{
  89        pr_debug("dsmark_get(sch %p,[qdisc %p],classid %x)\n",
  90                sch, qdisc_priv(sch), classid);
  91
  92        return TC_H_MIN(classid) + 1;
  93}
  94
  95static unsigned long dsmark_bind_filter(struct Qdisc *sch,
  96                                        unsigned long parent, u32 classid)
  97{
  98        return dsmark_get(sch, classid);
  99}
 100
 101static void dsmark_put(struct Qdisc *sch, unsigned long cl)
 102{
 103}
 104
 105static const struct nla_policy dsmark_policy[TCA_DSMARK_MAX + 1] = {
 106        [TCA_DSMARK_INDICES]            = { .type = NLA_U16 },
 107        [TCA_DSMARK_DEFAULT_INDEX]      = { .type = NLA_U16 },
 108        [TCA_DSMARK_SET_TC_INDEX]       = { .type = NLA_FLAG },
 109        [TCA_DSMARK_MASK]               = { .type = NLA_U8 },
 110        [TCA_DSMARK_VALUE]              = { .type = NLA_U8 },
 111};
 112
 113static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent,
 114                         struct nlattr **tca, unsigned long *arg)
 115{
 116        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 117        struct nlattr *opt = tca[TCA_OPTIONS];
 118        struct nlattr *tb[TCA_DSMARK_MAX + 1];
 119        int err = -EINVAL;
 120        u8 mask = 0;
 121
 122        pr_debug("dsmark_change(sch %p,[qdisc %p],classid %x,parent %x),"
 123                "arg 0x%lx\n", sch, p, classid, parent, *arg);
 124
 125        if (!dsmark_valid_index(p, *arg)) {
 126                err = -ENOENT;
 127                goto errout;
 128        }
 129
 130        if (!opt)
 131                goto errout;
 132
 133        err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy);
 134        if (err < 0)
 135                goto errout;
 136
 137        if (tb[TCA_DSMARK_MASK])
 138                mask = nla_get_u8(tb[TCA_DSMARK_MASK]);
 139
 140        if (tb[TCA_DSMARK_VALUE])
 141                p->value[*arg-1] = nla_get_u8(tb[TCA_DSMARK_VALUE]);
 142
 143        if (tb[TCA_DSMARK_MASK])
 144                p->mask[*arg-1] = mask;
 145
 146        err = 0;
 147
 148errout:
 149        return err;
 150}
 151
 152static int dsmark_delete(struct Qdisc *sch, unsigned long arg)
 153{
 154        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 155
 156        if (!dsmark_valid_index(p, arg))
 157                return -EINVAL;
 158
 159        p->mask[arg-1] = 0xff;
 160        p->value[arg-1] = 0;
 161
 162        return 0;
 163}
 164
 165static void dsmark_walk(struct Qdisc *sch, struct qdisc_walker *walker)
 166{
 167        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 168        int i;
 169
 170        pr_debug("dsmark_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
 171
 172        if (walker->stop)
 173                return;
 174
 175        for (i = 0; i < p->indices; i++) {
 176                if (p->mask[i] == 0xff && !p->value[i])
 177                        goto ignore;
 178                if (walker->count >= walker->skip) {
 179                        if (walker->fn(sch, i+1, walker) < 0) {
 180                                walker->stop = 1;
 181                                break;
 182                        }
 183                }
 184ignore:
 185                walker->count++;
 186        }
 187}
 188
 189static inline struct tcf_proto **dsmark_find_tcf(struct Qdisc *sch,
 190                                                 unsigned long cl)
 191{
 192        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 193        return &p->filter_list;
 194}
 195
 196/* --------------------------- Qdisc operations ---------------------------- */
 197
 198static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 199{
 200        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 201        int err;
 202
 203        pr_debug("dsmark_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
 204
 205        if (p->set_tc_index) {
 206                switch (skb->protocol) {
 207                case htons(ETH_P_IP):
 208                        if (skb_cow_head(skb, sizeof(struct iphdr)))
 209                                goto drop;
 210
 211                        skb->tc_index = ipv4_get_dsfield(ip_hdr(skb))
 212                                & ~INET_ECN_MASK;
 213                        break;
 214
 215                case htons(ETH_P_IPV6):
 216                        if (skb_cow_head(skb, sizeof(struct ipv6hdr)))
 217                                goto drop;
 218
 219                        skb->tc_index = ipv6_get_dsfield(ipv6_hdr(skb))
 220                                & ~INET_ECN_MASK;
 221                        break;
 222                default:
 223                        skb->tc_index = 0;
 224                        break;
 225                }
 226        }
 227
 228        if (TC_H_MAJ(skb->priority) == sch->handle)
 229                skb->tc_index = TC_H_MIN(skb->priority);
 230        else {
 231                struct tcf_result res;
 232                int result = tc_classify(skb, p->filter_list, &res);
 233
 234                pr_debug("result %d class 0x%04x\n", result, res.classid);
 235
 236                switch (result) {
 237#ifdef CONFIG_NET_CLS_ACT
 238                case TC_ACT_QUEUED:
 239                case TC_ACT_STOLEN:
 240                        kfree_skb(skb);
 241                        return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
 242
 243                case TC_ACT_SHOT:
 244                        goto drop;
 245#endif
 246                case TC_ACT_OK:
 247                        skb->tc_index = TC_H_MIN(res.classid);
 248                        break;
 249
 250                default:
 251                        if (p->default_index != NO_DEFAULT_INDEX)
 252                                skb->tc_index = p->default_index;
 253                        break;
 254                }
 255        }
 256
 257        err = qdisc_enqueue(skb, p->q);
 258        if (err != NET_XMIT_SUCCESS) {
 259                if (net_xmit_drop_count(err))
 260                        sch->qstats.drops++;
 261                return err;
 262        }
 263
 264        sch->bstats.bytes += qdisc_pkt_len(skb);
 265        sch->bstats.packets++;
 266        sch->q.qlen++;
 267
 268        return NET_XMIT_SUCCESS;
 269
 270drop:
 271        kfree_skb(skb);
 272        sch->qstats.drops++;
 273        return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 274}
 275
 276static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
 277{
 278        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 279        struct sk_buff *skb;
 280        u32 index;
 281
 282        pr_debug("dsmark_dequeue(sch %p,[qdisc %p])\n", sch, p);
 283
 284        skb = p->q->ops->dequeue(p->q);
 285        if (skb == NULL)
 286                return NULL;
 287
 288        sch->q.qlen--;
 289
 290        index = skb->tc_index & (p->indices - 1);
 291        pr_debug("index %d->%d\n", skb->tc_index, index);
 292
 293        switch (skb->protocol) {
 294        case htons(ETH_P_IP):
 295                ipv4_change_dsfield(ip_hdr(skb), p->mask[index],
 296                                    p->value[index]);
 297                        break;
 298        case htons(ETH_P_IPV6):
 299                ipv6_change_dsfield(ipv6_hdr(skb), p->mask[index],
 300                                    p->value[index]);
 301                        break;
 302        default:
 303                /*
 304                 * Only complain if a change was actually attempted.
 305                 * This way, we can send non-IP traffic through dsmark
 306                 * and don't need yet another qdisc as a bypass.
 307                 */
 308                if (p->mask[index] != 0xff || p->value[index])
 309                        printk(KERN_WARNING
 310                               "dsmark_dequeue: unsupported protocol %d\n",
 311                               ntohs(skb->protocol));
 312                break;
 313        }
 314
 315        return skb;
 316}
 317
 318static struct sk_buff *dsmark_peek(struct Qdisc *sch)
 319{
 320        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 321
 322        pr_debug("dsmark_peek(sch %p,[qdisc %p])\n", sch, p);
 323
 324        return p->q->ops->peek(p->q);
 325}
 326
 327static unsigned int dsmark_drop(struct Qdisc *sch)
 328{
 329        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 330        unsigned int len;
 331
 332        pr_debug("dsmark_reset(sch %p,[qdisc %p])\n", sch, p);
 333
 334        if (p->q->ops->drop == NULL)
 335                return 0;
 336
 337        len = p->q->ops->drop(p->q);
 338        if (len)
 339                sch->q.qlen--;
 340
 341        return len;
 342}
 343
 344static int dsmark_init(struct Qdisc *sch, struct nlattr *opt)
 345{
 346        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 347        struct nlattr *tb[TCA_DSMARK_MAX + 1];
 348        int err = -EINVAL;
 349        u32 default_index = NO_DEFAULT_INDEX;
 350        u16 indices;
 351        u8 *mask;
 352
 353        pr_debug("dsmark_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
 354
 355        if (!opt)
 356                goto errout;
 357
 358        err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy);
 359        if (err < 0)
 360                goto errout;
 361
 362        err = -EINVAL;
 363        indices = nla_get_u16(tb[TCA_DSMARK_INDICES]);
 364
 365        if (hweight32(indices) != 1)
 366                goto errout;
 367
 368        if (tb[TCA_DSMARK_DEFAULT_INDEX])
 369                default_index = nla_get_u16(tb[TCA_DSMARK_DEFAULT_INDEX]);
 370
 371        mask = kmalloc(indices * 2, GFP_KERNEL);
 372        if (mask == NULL) {
 373                err = -ENOMEM;
 374                goto errout;
 375        }
 376
 377        p->mask = mask;
 378        memset(p->mask, 0xff, indices);
 379
 380        p->value = p->mask + indices;
 381        memset(p->value, 0, indices);
 382
 383        p->indices = indices;
 384        p->default_index = default_index;
 385        p->set_tc_index = nla_get_flag(tb[TCA_DSMARK_SET_TC_INDEX]);
 386
 387        p->q = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
 388                                 &pfifo_qdisc_ops, sch->handle);
 389        if (p->q == NULL)
 390                p->q = &noop_qdisc;
 391
 392        pr_debug("dsmark_init: qdisc %p\n", p->q);
 393
 394        err = 0;
 395errout:
 396        return err;
 397}
 398
 399static void dsmark_reset(struct Qdisc *sch)
 400{
 401        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 402
 403        pr_debug("dsmark_reset(sch %p,[qdisc %p])\n", sch, p);
 404        qdisc_reset(p->q);
 405        sch->q.qlen = 0;
 406}
 407
 408static void dsmark_destroy(struct Qdisc *sch)
 409{
 410        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 411
 412        pr_debug("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p);
 413
 414        tcf_destroy_chain(&p->filter_list);
 415        qdisc_destroy(p->q);
 416        kfree(p->mask);
 417}
 418
 419static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl,
 420                             struct sk_buff *skb, struct tcmsg *tcm)
 421{
 422        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 423        struct nlattr *opts = NULL;
 424
 425        pr_debug("dsmark_dump_class(sch %p,[qdisc %p],class %ld\n", sch, p, cl);
 426
 427        if (!dsmark_valid_index(p, cl))
 428                return -EINVAL;
 429
 430        tcm->tcm_handle = TC_H_MAKE(TC_H_MAJ(sch->handle), cl-1);
 431        tcm->tcm_info = p->q->handle;
 432
 433        opts = nla_nest_start(skb, TCA_OPTIONS);
 434        if (opts == NULL)
 435                goto nla_put_failure;
 436        NLA_PUT_U8(skb, TCA_DSMARK_MASK, p->mask[cl-1]);
 437        NLA_PUT_U8(skb, TCA_DSMARK_VALUE, p->value[cl-1]);
 438
 439        return nla_nest_end(skb, opts);
 440
 441nla_put_failure:
 442        nla_nest_cancel(skb, opts);
 443        return -EMSGSIZE;
 444}
 445
 446static int dsmark_dump(struct Qdisc *sch, struct sk_buff *skb)
 447{
 448        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 449        struct nlattr *opts = NULL;
 450
 451        opts = nla_nest_start(skb, TCA_OPTIONS);
 452        if (opts == NULL)
 453                goto nla_put_failure;
 454        NLA_PUT_U16(skb, TCA_DSMARK_INDICES, p->indices);
 455
 456        if (p->default_index != NO_DEFAULT_INDEX)
 457                NLA_PUT_U16(skb, TCA_DSMARK_DEFAULT_INDEX, p->default_index);
 458
 459        if (p->set_tc_index)
 460                NLA_PUT_FLAG(skb, TCA_DSMARK_SET_TC_INDEX);
 461
 462        return nla_nest_end(skb, opts);
 463
 464nla_put_failure:
 465        nla_nest_cancel(skb, opts);
 466        return -EMSGSIZE;
 467}
 468
 469static const struct Qdisc_class_ops dsmark_class_ops = {
 470        .graft          =       dsmark_graft,
 471        .leaf           =       dsmark_leaf,
 472        .get            =       dsmark_get,
 473        .put            =       dsmark_put,
 474        .change         =       dsmark_change,
 475        .delete         =       dsmark_delete,
 476        .walk           =       dsmark_walk,
 477        .tcf_chain      =       dsmark_find_tcf,
 478        .bind_tcf       =       dsmark_bind_filter,
 479        .unbind_tcf     =       dsmark_put,
 480        .dump           =       dsmark_dump_class,
 481};
 482
 483static struct Qdisc_ops dsmark_qdisc_ops __read_mostly = {
 484        .next           =       NULL,
 485        .cl_ops         =       &dsmark_class_ops,
 486        .id             =       "dsmark",
 487        .priv_size      =       sizeof(struct dsmark_qdisc_data),
 488        .enqueue        =       dsmark_enqueue,
 489        .dequeue        =       dsmark_dequeue,
 490        .peek           =       dsmark_peek,
 491        .drop           =       dsmark_drop,
 492        .init           =       dsmark_init,
 493        .reset          =       dsmark_reset,
 494        .destroy        =       dsmark_destroy,
 495        .change         =       NULL,
 496        .dump           =       dsmark_dump,
 497        .owner          =       THIS_MODULE,
 498};
 499
 500static int __init dsmark_module_init(void)
 501{
 502        return register_qdisc(&dsmark_qdisc_ops);
 503}
 504
 505static void __exit dsmark_module_exit(void)
 506{
 507        unregister_qdisc(&dsmark_qdisc_ops);
 508}
 509
 510module_init(dsmark_module_init)
 511module_exit(dsmark_module_exit)
 512
 513MODULE_LICENSE("GPL");
 514