linux/net/sched/cls_rsvp.h
<<
>>
Prefs
   1/*
   2 * net/sched/cls_rsvp.h Template file for RSVPv[46] classifiers.
   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/*
  13   Comparing to general packet classification problem,
  14   RSVP needs only sevaral relatively simple rules:
  15
  16   * (dst, protocol) are always specified,
  17     so that we are able to hash them.
  18   * src may be exact, or may be wildcard, so that
  19     we can keep a hash table plus one wildcard entry.
  20   * source port (or flow label) is important only if src is given.
  21
  22   IMPLEMENTATION.
  23
  24   We use a two level hash table: The top level is keyed by
  25   destination address and protocol ID, every bucket contains a list
  26   of "rsvp sessions", identified by destination address, protocol and
  27   DPI(="Destination Port ID"): triple (key, mask, offset).
  28
  29   Every bucket has a smaller hash table keyed by source address
  30   (cf. RSVP flowspec) and one wildcard entry for wildcard reservations.
  31   Every bucket is again a list of "RSVP flows", selected by
  32   source address and SPI(="Source Port ID" here rather than
  33   "security parameter index"): triple (key, mask, offset).
  34
  35
  36   NOTE 1. All the packets with IPv6 extension headers (but AH and ESP)
  37   and all fragmented packets go to the best-effort traffic class.
  38
  39
  40   NOTE 2. Two "port id"'s seems to be redundant, rfc2207 requires
  41   only one "Generalized Port Identifier". So that for classic
  42   ah, esp (and udp,tcp) both *pi should coincide or one of them
  43   should be wildcard.
  44
  45   At first sight, this redundancy is just a waste of CPU
  46   resources. But DPI and SPI add the possibility to assign different
  47   priorities to GPIs. Look also at note 4 about tunnels below.
  48
  49
  50   NOTE 3. One complication is the case of tunneled packets.
  51   We implement it as following: if the first lookup
  52   matches a special session with "tunnelhdr" value not zero,
  53   flowid doesn't contain the true flow ID, but the tunnel ID (1...255).
  54   In this case, we pull tunnelhdr bytes and restart lookup
  55   with tunnel ID added to the list of keys. Simple and stupid 8)8)
  56   It's enough for PIMREG and IPIP.
  57
  58
  59   NOTE 4. Two GPIs make it possible to parse even GRE packets.
  60   F.e. DPI can select ETH_P_IP (and necessary flags to make
  61   tunnelhdr correct) in GRE protocol field and SPI matches
  62   GRE key. Is it not nice? 8)8)
  63
  64
  65   Well, as result, despite its simplicity, we get a pretty
  66   powerful classification engine.  */
  67
  68
  69struct rsvp_head {
  70        u32                     tmap[256/32];
  71        u32                     hgenerator;
  72        u8                      tgenerator;
  73        struct rsvp_session __rcu *ht[256];
  74        struct rcu_head         rcu;
  75};
  76
  77struct rsvp_session {
  78        struct rsvp_session __rcu       *next;
  79        __be32                          dst[RSVP_DST_LEN];
  80        struct tc_rsvp_gpi              dpi;
  81        u8                              protocol;
  82        u8                              tunnelid;
  83        /* 16 (src,sport) hash slots, and one wildcard source slot */
  84        struct rsvp_filter __rcu        *ht[16 + 1];
  85        struct rcu_head                 rcu;
  86};
  87
  88
  89struct rsvp_filter {
  90        struct rsvp_filter __rcu        *next;
  91        __be32                          src[RSVP_DST_LEN];
  92        struct tc_rsvp_gpi              spi;
  93        u8                              tunnelhdr;
  94
  95        struct tcf_result               res;
  96        struct tcf_exts                 exts;
  97
  98        u32                             handle;
  99        struct rsvp_session             *sess;
 100        struct rcu_head                 rcu;
 101};
 102
 103static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
 104{
 105        unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
 106
 107        h ^= h>>16;
 108        h ^= h>>8;
 109        return (h ^ protocol ^ tunnelid) & 0xFF;
 110}
 111
 112static inline unsigned int hash_src(__be32 *src)
 113{
 114        unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
 115
 116        h ^= h>>16;
 117        h ^= h>>8;
 118        h ^= h>>4;
 119        return h & 0xF;
 120}
 121
 122#define RSVP_APPLY_RESULT()                             \
 123{                                                       \
 124        int r = tcf_exts_exec(skb, &f->exts, res);      \
 125        if (r < 0)                                      \
 126                continue;                               \
 127        else if (r > 0)                                 \
 128                return r;                               \
 129}
 130
 131static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 132                         struct tcf_result *res)
 133{
 134        struct rsvp_head *head = rcu_dereference_bh(tp->root);
 135        struct rsvp_session *s;
 136        struct rsvp_filter *f;
 137        unsigned int h1, h2;
 138        __be32 *dst, *src;
 139        u8 protocol;
 140        u8 tunnelid = 0;
 141        u8 *xprt;
 142#if RSVP_DST_LEN == 4
 143        struct ipv6hdr *nhptr;
 144
 145        if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
 146                return -1;
 147        nhptr = ipv6_hdr(skb);
 148#else
 149        struct iphdr *nhptr;
 150
 151        if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
 152                return -1;
 153        nhptr = ip_hdr(skb);
 154#endif
 155        if (unlikely(!head))
 156                return -1;
 157restart:
 158
 159#if RSVP_DST_LEN == 4
 160        src = &nhptr->saddr.s6_addr32[0];
 161        dst = &nhptr->daddr.s6_addr32[0];
 162        protocol = nhptr->nexthdr;
 163        xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
 164#else
 165        src = &nhptr->saddr;
 166        dst = &nhptr->daddr;
 167        protocol = nhptr->protocol;
 168        xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
 169        if (ip_is_fragment(nhptr))
 170                return -1;
 171#endif
 172
 173        h1 = hash_dst(dst, protocol, tunnelid);
 174        h2 = hash_src(src);
 175
 176        for (s = rcu_dereference_bh(head->ht[h1]); s;
 177             s = rcu_dereference_bh(s->next)) {
 178                if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
 179                    protocol == s->protocol &&
 180                    !(s->dpi.mask &
 181                      (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
 182#if RSVP_DST_LEN == 4
 183                    dst[0] == s->dst[0] &&
 184                    dst[1] == s->dst[1] &&
 185                    dst[2] == s->dst[2] &&
 186#endif
 187                    tunnelid == s->tunnelid) {
 188
 189                        for (f = rcu_dereference_bh(s->ht[h2]); f;
 190                             f = rcu_dereference_bh(f->next)) {
 191                                if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
 192                                    !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
 193#if RSVP_DST_LEN == 4
 194                                    &&
 195                                    src[0] == f->src[0] &&
 196                                    src[1] == f->src[1] &&
 197                                    src[2] == f->src[2]
 198#endif
 199                                    ) {
 200                                        *res = f->res;
 201                                        RSVP_APPLY_RESULT();
 202
 203matched:
 204                                        if (f->tunnelhdr == 0)
 205                                                return 0;
 206
 207                                        tunnelid = f->res.classid;
 208                                        nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
 209                                        goto restart;
 210                                }
 211                        }
 212
 213                        /* And wildcard bucket... */
 214                        for (f = rcu_dereference_bh(s->ht[16]); f;
 215                             f = rcu_dereference_bh(f->next)) {
 216                                *res = f->res;
 217                                RSVP_APPLY_RESULT();
 218                                goto matched;
 219                        }
 220                        return -1;
 221                }
 222        }
 223        return -1;
 224}
 225
 226static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
 227{
 228        struct rsvp_head *head = rtnl_dereference(tp->root);
 229        struct rsvp_session *s;
 230        struct rsvp_filter __rcu **ins;
 231        struct rsvp_filter *pins;
 232        unsigned int h1 = h & 0xFF;
 233        unsigned int h2 = (h >> 8) & 0xFF;
 234
 235        for (s = rtnl_dereference(head->ht[h1]); s;
 236             s = rtnl_dereference(s->next)) {
 237                for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
 238                     ins = &pins->next, pins = rtnl_dereference(*ins)) {
 239                        if (pins->handle == h) {
 240                                RCU_INIT_POINTER(n->next, pins->next);
 241                                rcu_assign_pointer(*ins, n);
 242                                return;
 243                        }
 244                }
 245        }
 246
 247        /* Something went wrong if we are trying to replace a non-existant
 248         * node. Mind as well halt instead of silently failing.
 249         */
 250        BUG_ON(1);
 251}
 252
 253static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
 254{
 255        struct rsvp_head *head = rtnl_dereference(tp->root);
 256        struct rsvp_session *s;
 257        struct rsvp_filter *f;
 258        unsigned int h1 = handle & 0xFF;
 259        unsigned int h2 = (handle >> 8) & 0xFF;
 260
 261        if (h2 > 16)
 262                return 0;
 263
 264        for (s = rtnl_dereference(head->ht[h1]); s;
 265             s = rtnl_dereference(s->next)) {
 266                for (f = rtnl_dereference(s->ht[h2]); f;
 267                     f = rtnl_dereference(f->next)) {
 268                        if (f->handle == handle)
 269                                return (unsigned long)f;
 270                }
 271        }
 272        return 0;
 273}
 274
 275static int rsvp_init(struct tcf_proto *tp)
 276{
 277        struct rsvp_head *data;
 278
 279        data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
 280        if (data) {
 281                rcu_assign_pointer(tp->root, data);
 282                return 0;
 283        }
 284        return -ENOBUFS;
 285}
 286
 287static void
 288rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
 289{
 290        tcf_unbind_filter(tp, &f->res);
 291        tcf_exts_destroy(&f->exts);
 292        kfree_rcu(f, rcu);
 293}
 294
 295static bool rsvp_destroy(struct tcf_proto *tp, bool force)
 296{
 297        struct rsvp_head *data = rtnl_dereference(tp->root);
 298        int h1, h2;
 299
 300        if (data == NULL)
 301                return true;
 302
 303        if (!force) {
 304                for (h1 = 0; h1 < 256; h1++) {
 305                        if (rcu_access_pointer(data->ht[h1]))
 306                                return false;
 307                }
 308        }
 309
 310        RCU_INIT_POINTER(tp->root, NULL);
 311
 312        for (h1 = 0; h1 < 256; h1++) {
 313                struct rsvp_session *s;
 314
 315                while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
 316                        RCU_INIT_POINTER(data->ht[h1], s->next);
 317
 318                        for (h2 = 0; h2 <= 16; h2++) {
 319                                struct rsvp_filter *f;
 320
 321                                while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
 322                                        rcu_assign_pointer(s->ht[h2], f->next);
 323                                        rsvp_delete_filter(tp, f);
 324                                }
 325                        }
 326                        kfree_rcu(s, rcu);
 327                }
 328        }
 329        kfree_rcu(data, rcu);
 330        return true;
 331}
 332
 333static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
 334{
 335        struct rsvp_head *head = rtnl_dereference(tp->root);
 336        struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
 337        struct rsvp_filter __rcu **fp;
 338        unsigned int h = f->handle;
 339        struct rsvp_session __rcu **sp;
 340        struct rsvp_session *nsp, *s = f->sess;
 341        int i;
 342
 343        fp = &s->ht[(h >> 8) & 0xFF];
 344        for (nfp = rtnl_dereference(*fp); nfp;
 345             fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
 346                if (nfp == f) {
 347                        RCU_INIT_POINTER(*fp, f->next);
 348                        rsvp_delete_filter(tp, f);
 349
 350                        /* Strip tree */
 351
 352                        for (i = 0; i <= 16; i++)
 353                                if (s->ht[i])
 354                                        return 0;
 355
 356                        /* OK, session has no flows */
 357                        sp = &head->ht[h & 0xFF];
 358                        for (nsp = rtnl_dereference(*sp); nsp;
 359                             sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
 360                                if (nsp == s) {
 361                                        RCU_INIT_POINTER(*sp, s->next);
 362                                        kfree_rcu(s, rcu);
 363                                        return 0;
 364                                }
 365                        }
 366
 367                        return 0;
 368                }
 369        }
 370        return 0;
 371}
 372
 373static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
 374{
 375        struct rsvp_head *data = rtnl_dereference(tp->root);
 376        int i = 0xFFFF;
 377
 378        while (i-- > 0) {
 379                u32 h;
 380
 381                if ((data->hgenerator += 0x10000) == 0)
 382                        data->hgenerator = 0x10000;
 383                h = data->hgenerator|salt;
 384                if (rsvp_get(tp, h) == 0)
 385                        return h;
 386        }
 387        return 0;
 388}
 389
 390static int tunnel_bts(struct rsvp_head *data)
 391{
 392        int n = data->tgenerator >> 5;
 393        u32 b = 1 << (data->tgenerator & 0x1F);
 394
 395        if (data->tmap[n] & b)
 396                return 0;
 397        data->tmap[n] |= b;
 398        return 1;
 399}
 400
 401static void tunnel_recycle(struct rsvp_head *data)
 402{
 403        struct rsvp_session __rcu **sht = data->ht;
 404        u32 tmap[256/32];
 405        int h1, h2;
 406
 407        memset(tmap, 0, sizeof(tmap));
 408
 409        for (h1 = 0; h1 < 256; h1++) {
 410                struct rsvp_session *s;
 411                for (s = rtnl_dereference(sht[h1]); s;
 412                     s = rtnl_dereference(s->next)) {
 413                        for (h2 = 0; h2 <= 16; h2++) {
 414                                struct rsvp_filter *f;
 415
 416                                for (f = rtnl_dereference(s->ht[h2]); f;
 417                                     f = rtnl_dereference(f->next)) {
 418                                        if (f->tunnelhdr == 0)
 419                                                continue;
 420                                        data->tgenerator = f->res.classid;
 421                                        tunnel_bts(data);
 422                                }
 423                        }
 424                }
 425        }
 426
 427        memcpy(data->tmap, tmap, sizeof(tmap));
 428}
 429
 430static u32 gen_tunnel(struct rsvp_head *data)
 431{
 432        int i, k;
 433
 434        for (k = 0; k < 2; k++) {
 435                for (i = 255; i > 0; i--) {
 436                        if (++data->tgenerator == 0)
 437                                data->tgenerator = 1;
 438                        if (tunnel_bts(data))
 439                                return data->tgenerator;
 440                }
 441                tunnel_recycle(data);
 442        }
 443        return 0;
 444}
 445
 446static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
 447        [TCA_RSVP_CLASSID]      = { .type = NLA_U32 },
 448        [TCA_RSVP_DST]          = { .type = NLA_BINARY,
 449                                    .len = RSVP_DST_LEN * sizeof(u32) },
 450        [TCA_RSVP_SRC]          = { .type = NLA_BINARY,
 451                                    .len = RSVP_DST_LEN * sizeof(u32) },
 452        [TCA_RSVP_PINFO]        = { .len = sizeof(struct tc_rsvp_pinfo) },
 453};
 454
 455static int rsvp_change(struct net *net, struct sk_buff *in_skb,
 456                       struct tcf_proto *tp, unsigned long base,
 457                       u32 handle,
 458                       struct nlattr **tca,
 459                       unsigned long *arg, bool ovr)
 460{
 461        struct rsvp_head *data = rtnl_dereference(tp->root);
 462        struct rsvp_filter *f, *nfp;
 463        struct rsvp_filter __rcu **fp;
 464        struct rsvp_session *nsp, *s;
 465        struct rsvp_session __rcu **sp;
 466        struct tc_rsvp_pinfo *pinfo = NULL;
 467        struct nlattr *opt = tca[TCA_OPTIONS];
 468        struct nlattr *tb[TCA_RSVP_MAX + 1];
 469        struct tcf_exts e;
 470        unsigned int h1, h2;
 471        __be32 *dst;
 472        int err;
 473
 474        if (opt == NULL)
 475                return handle ? -EINVAL : 0;
 476
 477        err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy);
 478        if (err < 0)
 479                return err;
 480
 481        tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
 482        err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
 483        if (err < 0)
 484                return err;
 485
 486        f = (struct rsvp_filter *)*arg;
 487        if (f) {
 488                /* Node exists: adjust only classid */
 489                struct rsvp_filter *n;
 490
 491                if (f->handle != handle && handle)
 492                        goto errout2;
 493
 494                n = kmemdup(f, sizeof(*f), GFP_KERNEL);
 495                if (!n) {
 496                        err = -ENOMEM;
 497                        goto errout2;
 498                }
 499
 500                tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
 501
 502                if (tb[TCA_RSVP_CLASSID]) {
 503                        n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
 504                        tcf_bind_filter(tp, &n->res, base);
 505                }
 506
 507                tcf_exts_change(tp, &n->exts, &e);
 508                rsvp_replace(tp, n, handle);
 509                return 0;
 510        }
 511
 512        /* Now more serious part... */
 513        err = -EINVAL;
 514        if (handle)
 515                goto errout2;
 516        if (tb[TCA_RSVP_DST] == NULL)
 517                goto errout2;
 518
 519        err = -ENOBUFS;
 520        f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
 521        if (f == NULL)
 522                goto errout2;
 523
 524        tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
 525        h2 = 16;
 526        if (tb[TCA_RSVP_SRC]) {
 527                memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
 528                h2 = hash_src(f->src);
 529        }
 530        if (tb[TCA_RSVP_PINFO]) {
 531                pinfo = nla_data(tb[TCA_RSVP_PINFO]);
 532                f->spi = pinfo->spi;
 533                f->tunnelhdr = pinfo->tunnelhdr;
 534        }
 535        if (tb[TCA_RSVP_CLASSID])
 536                f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
 537
 538        dst = nla_data(tb[TCA_RSVP_DST]);
 539        h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
 540
 541        err = -ENOMEM;
 542        if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
 543                goto errout;
 544
 545        if (f->tunnelhdr) {
 546                err = -EINVAL;
 547                if (f->res.classid > 255)
 548                        goto errout;
 549
 550                err = -ENOMEM;
 551                if (f->res.classid == 0 &&
 552                    (f->res.classid = gen_tunnel(data)) == 0)
 553                        goto errout;
 554        }
 555
 556        for (sp = &data->ht[h1];
 557             (s = rtnl_dereference(*sp)) != NULL;
 558             sp = &s->next) {
 559                if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
 560                    pinfo && pinfo->protocol == s->protocol &&
 561                    memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
 562#if RSVP_DST_LEN == 4
 563                    dst[0] == s->dst[0] &&
 564                    dst[1] == s->dst[1] &&
 565                    dst[2] == s->dst[2] &&
 566#endif
 567                    pinfo->tunnelid == s->tunnelid) {
 568
 569insert:
 570                        /* OK, we found appropriate session */
 571
 572                        fp = &s->ht[h2];
 573
 574                        f->sess = s;
 575                        if (f->tunnelhdr == 0)
 576                                tcf_bind_filter(tp, &f->res, base);
 577
 578                        tcf_exts_change(tp, &f->exts, &e);
 579
 580                        fp = &s->ht[h2];
 581                        for (nfp = rtnl_dereference(*fp); nfp;
 582                             fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
 583                                __u32 mask = nfp->spi.mask & f->spi.mask;
 584
 585                                if (mask != f->spi.mask)
 586                                        break;
 587                        }
 588                        RCU_INIT_POINTER(f->next, nfp);
 589                        rcu_assign_pointer(*fp, f);
 590
 591                        *arg = (unsigned long)f;
 592                        return 0;
 593                }
 594        }
 595
 596        /* No session found. Create new one. */
 597
 598        err = -ENOBUFS;
 599        s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
 600        if (s == NULL)
 601                goto errout;
 602        memcpy(s->dst, dst, sizeof(s->dst));
 603
 604        if (pinfo) {
 605                s->dpi = pinfo->dpi;
 606                s->protocol = pinfo->protocol;
 607                s->tunnelid = pinfo->tunnelid;
 608        }
 609        sp = &data->ht[h1];
 610        for (nsp = rtnl_dereference(*sp); nsp;
 611             sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
 612                if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
 613                        break;
 614        }
 615        RCU_INIT_POINTER(s->next, nsp);
 616        rcu_assign_pointer(*sp, s);
 617
 618        goto insert;
 619
 620errout:
 621        kfree(f);
 622errout2:
 623        tcf_exts_destroy(&e);
 624        return err;
 625}
 626
 627static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 628{
 629        struct rsvp_head *head = rtnl_dereference(tp->root);
 630        unsigned int h, h1;
 631
 632        if (arg->stop)
 633                return;
 634
 635        for (h = 0; h < 256; h++) {
 636                struct rsvp_session *s;
 637
 638                for (s = rtnl_dereference(head->ht[h]); s;
 639                     s = rtnl_dereference(s->next)) {
 640                        for (h1 = 0; h1 <= 16; h1++) {
 641                                struct rsvp_filter *f;
 642
 643                                for (f = rtnl_dereference(s->ht[h1]); f;
 644                                     f = rtnl_dereference(f->next)) {
 645                                        if (arg->count < arg->skip) {
 646                                                arg->count++;
 647                                                continue;
 648                                        }
 649                                        if (arg->fn(tp, (unsigned long)f, arg) < 0) {
 650                                                arg->stop = 1;
 651                                                return;
 652                                        }
 653                                        arg->count++;
 654                                }
 655                        }
 656                }
 657        }
 658}
 659
 660static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 661                     struct sk_buff *skb, struct tcmsg *t)
 662{
 663        struct rsvp_filter *f = (struct rsvp_filter *)fh;
 664        struct rsvp_session *s;
 665        struct nlattr *nest;
 666        struct tc_rsvp_pinfo pinfo;
 667
 668        if (f == NULL)
 669                return skb->len;
 670        s = f->sess;
 671
 672        t->tcm_handle = f->handle;
 673
 674        nest = nla_nest_start(skb, TCA_OPTIONS);
 675        if (nest == NULL)
 676                goto nla_put_failure;
 677
 678        if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
 679                goto nla_put_failure;
 680        pinfo.dpi = s->dpi;
 681        pinfo.spi = f->spi;
 682        pinfo.protocol = s->protocol;
 683        pinfo.tunnelid = s->tunnelid;
 684        pinfo.tunnelhdr = f->tunnelhdr;
 685        pinfo.pad = 0;
 686        if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
 687                goto nla_put_failure;
 688        if (f->res.classid &&
 689            nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
 690                goto nla_put_failure;
 691        if (((f->handle >> 8) & 0xFF) != 16 &&
 692            nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
 693                goto nla_put_failure;
 694
 695        if (tcf_exts_dump(skb, &f->exts) < 0)
 696                goto nla_put_failure;
 697
 698        nla_nest_end(skb, nest);
 699
 700        if (tcf_exts_dump_stats(skb, &f->exts) < 0)
 701                goto nla_put_failure;
 702        return skb->len;
 703
 704nla_put_failure:
 705        nla_nest_cancel(skb, nest);
 706        return -1;
 707}
 708
 709static struct tcf_proto_ops RSVP_OPS __read_mostly = {
 710        .kind           =       RSVP_ID,
 711        .classify       =       rsvp_classify,
 712        .init           =       rsvp_init,
 713        .destroy        =       rsvp_destroy,
 714        .get            =       rsvp_get,
 715        .change         =       rsvp_change,
 716        .delete         =       rsvp_delete,
 717        .walk           =       rsvp_walk,
 718        .dump           =       rsvp_dump,
 719        .owner          =       THIS_MODULE,
 720};
 721
 722static int __init init_rsvp(void)
 723{
 724        return register_tcf_proto_ops(&RSVP_OPS);
 725}
 726
 727static void __exit exit_rsvp(void)
 728{
 729        unregister_tcf_proto_ops(&RSVP_OPS);
 730}
 731
 732module_init(init_rsvp)
 733module_exit(exit_rsvp)
 734