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 69 struct 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 77 struct 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 89 struct 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 103 static 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 112 static 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 131 static 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; 157 restart: 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 203 matched: 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 226 static 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 253 static 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 275 static 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 287 static void rsvp_delete_filter_rcu(struct rcu_head *head) 288 { 289 struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu); 290 291 tcf_exts_destroy(&f->exts); 292 kfree(f); 293 } 294 295 static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) 296 { 297 tcf_unbind_filter(tp, &f->res); 298 /* all classifiers are required to call tcf_exts_destroy() after rcu 299 * grace period, since converted-to-rcu actions are relying on that 300 * in cleanup() callback 301 */ 302 call_rcu(&f->rcu, rsvp_delete_filter_rcu); 303 } 304 305 static bool rsvp_destroy(struct tcf_proto *tp, bool force) 306 { 307 struct rsvp_head *data = rtnl_dereference(tp->root); 308 int h1, h2; 309 310 if (data == NULL) 311 return true; 312 313 if (!force) { 314 for (h1 = 0; h1 < 256; h1++) { 315 if (rcu_access_pointer(data->ht[h1])) 316 return false; 317 } 318 } 319 320 RCU_INIT_POINTER(tp->root, NULL); 321 322 for (h1 = 0; h1 < 256; h1++) { 323 struct rsvp_session *s; 324 325 while ((s = rtnl_dereference(data->ht[h1])) != NULL) { 326 RCU_INIT_POINTER(data->ht[h1], s->next); 327 328 for (h2 = 0; h2 <= 16; h2++) { 329 struct rsvp_filter *f; 330 331 while ((f = rtnl_dereference(s->ht[h2])) != NULL) { 332 rcu_assign_pointer(s->ht[h2], f->next); 333 rsvp_delete_filter(tp, f); 334 } 335 } 336 kfree_rcu(s, rcu); 337 } 338 } 339 kfree_rcu(data, rcu); 340 return true; 341 } 342 343 static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) 344 { 345 struct rsvp_head *head = rtnl_dereference(tp->root); 346 struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg; 347 struct rsvp_filter __rcu **fp; 348 unsigned int h = f->handle; 349 struct rsvp_session __rcu **sp; 350 struct rsvp_session *nsp, *s = f->sess; 351 int i; 352 353 fp = &s->ht[(h >> 8) & 0xFF]; 354 for (nfp = rtnl_dereference(*fp); nfp; 355 fp = &nfp->next, nfp = rtnl_dereference(*fp)) { 356 if (nfp == f) { 357 RCU_INIT_POINTER(*fp, f->next); 358 rsvp_delete_filter(tp, f); 359 360 /* Strip tree */ 361 362 for (i = 0; i <= 16; i++) 363 if (s->ht[i]) 364 return 0; 365 366 /* OK, session has no flows */ 367 sp = &head->ht[h & 0xFF]; 368 for (nsp = rtnl_dereference(*sp); nsp; 369 sp = &nsp->next, nsp = rtnl_dereference(*sp)) { 370 if (nsp == s) { 371 RCU_INIT_POINTER(*sp, s->next); 372 kfree_rcu(s, rcu); 373 return 0; 374 } 375 } 376 377 return 0; 378 } 379 } 380 return 0; 381 } 382 383 static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt) 384 { 385 struct rsvp_head *data = rtnl_dereference(tp->root); 386 int i = 0xFFFF; 387 388 while (i-- > 0) { 389 u32 h; 390 391 if ((data->hgenerator += 0x10000) == 0) 392 data->hgenerator = 0x10000; 393 h = data->hgenerator|salt; 394 if (rsvp_get(tp, h) == 0) 395 return h; 396 } 397 return 0; 398 } 399 400 static int tunnel_bts(struct rsvp_head *data) 401 { 402 int n = data->tgenerator >> 5; 403 u32 b = 1 << (data->tgenerator & 0x1F); 404 405 if (data->tmap[n] & b) 406 return 0; 407 data->tmap[n] |= b; 408 return 1; 409 } 410 411 static void tunnel_recycle(struct rsvp_head *data) 412 { 413 struct rsvp_session __rcu **sht = data->ht; 414 u32 tmap[256/32]; 415 int h1, h2; 416 417 memset(tmap, 0, sizeof(tmap)); 418 419 for (h1 = 0; h1 < 256; h1++) { 420 struct rsvp_session *s; 421 for (s = rtnl_dereference(sht[h1]); s; 422 s = rtnl_dereference(s->next)) { 423 for (h2 = 0; h2 <= 16; h2++) { 424 struct rsvp_filter *f; 425 426 for (f = rtnl_dereference(s->ht[h2]); f; 427 f = rtnl_dereference(f->next)) { 428 if (f->tunnelhdr == 0) 429 continue; 430 data->tgenerator = f->res.classid; 431 tunnel_bts(data); 432 } 433 } 434 } 435 } 436 437 memcpy(data->tmap, tmap, sizeof(tmap)); 438 } 439 440 static u32 gen_tunnel(struct rsvp_head *data) 441 { 442 int i, k; 443 444 for (k = 0; k < 2; k++) { 445 for (i = 255; i > 0; i--) { 446 if (++data->tgenerator == 0) 447 data->tgenerator = 1; 448 if (tunnel_bts(data)) 449 return data->tgenerator; 450 } 451 tunnel_recycle(data); 452 } 453 return 0; 454 } 455 456 static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = { 457 [TCA_RSVP_CLASSID] = { .type = NLA_U32 }, 458 [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) }, 459 [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) }, 460 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) }, 461 }; 462 463 static int rsvp_change(struct net *net, struct sk_buff *in_skb, 464 struct tcf_proto *tp, unsigned long base, 465 u32 handle, 466 struct nlattr **tca, 467 unsigned long *arg, bool ovr) 468 { 469 struct rsvp_head *data = rtnl_dereference(tp->root); 470 struct rsvp_filter *f, *nfp; 471 struct rsvp_filter __rcu **fp; 472 struct rsvp_session *nsp, *s; 473 struct rsvp_session __rcu **sp; 474 struct tc_rsvp_pinfo *pinfo = NULL; 475 struct nlattr *opt = tca[TCA_OPTIONS]; 476 struct nlattr *tb[TCA_RSVP_MAX + 1]; 477 struct tcf_exts e; 478 unsigned int h1, h2; 479 __be32 *dst; 480 int err; 481 482 if (opt == NULL) 483 return handle ? -EINVAL : 0; 484 485 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy); 486 if (err < 0) 487 return err; 488 489 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE); 490 if (err < 0) 491 return err; 492 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); 493 if (err < 0) 494 goto errout2; 495 496 f = (struct rsvp_filter *)*arg; 497 if (f) { 498 /* Node exists: adjust only classid */ 499 struct rsvp_filter *n; 500 501 if (f->handle != handle && handle) 502 goto errout2; 503 504 n = kmemdup(f, sizeof(*f), GFP_KERNEL); 505 if (!n) { 506 err = -ENOMEM; 507 goto errout2; 508 } 509 510 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE); 511 if (err < 0) { 512 kfree(n); 513 goto errout2; 514 } 515 516 if (tb[TCA_RSVP_CLASSID]) { 517 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]); 518 tcf_bind_filter(tp, &n->res, base); 519 } 520 521 tcf_exts_change(tp, &n->exts, &e); 522 rsvp_replace(tp, n, handle); 523 return 0; 524 } 525 526 /* Now more serious part... */ 527 err = -EINVAL; 528 if (handle) 529 goto errout2; 530 if (tb[TCA_RSVP_DST] == NULL) 531 goto errout2; 532 533 err = -ENOBUFS; 534 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL); 535 if (f == NULL) 536 goto errout2; 537 538 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE); 539 if (err < 0) 540 goto errout; 541 h2 = 16; 542 if (tb[TCA_RSVP_SRC]) { 543 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src)); 544 h2 = hash_src(f->src); 545 } 546 if (tb[TCA_RSVP_PINFO]) { 547 pinfo = nla_data(tb[TCA_RSVP_PINFO]); 548 f->spi = pinfo->spi; 549 f->tunnelhdr = pinfo->tunnelhdr; 550 } 551 if (tb[TCA_RSVP_CLASSID]) 552 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]); 553 554 dst = nla_data(tb[TCA_RSVP_DST]); 555 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0); 556 557 err = -ENOMEM; 558 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0) 559 goto errout; 560 561 if (f->tunnelhdr) { 562 err = -EINVAL; 563 if (f->res.classid > 255) 564 goto errout; 565 566 err = -ENOMEM; 567 if (f->res.classid == 0 && 568 (f->res.classid = gen_tunnel(data)) == 0) 569 goto errout; 570 } 571 572 for (sp = &data->ht[h1]; 573 (s = rtnl_dereference(*sp)) != NULL; 574 sp = &s->next) { 575 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] && 576 pinfo && pinfo->protocol == s->protocol && 577 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 && 578 #if RSVP_DST_LEN == 4 579 dst[0] == s->dst[0] && 580 dst[1] == s->dst[1] && 581 dst[2] == s->dst[2] && 582 #endif 583 pinfo->tunnelid == s->tunnelid) { 584 585 insert: 586 /* OK, we found appropriate session */ 587 588 fp = &s->ht[h2]; 589 590 f->sess = s; 591 if (f->tunnelhdr == 0) 592 tcf_bind_filter(tp, &f->res, base); 593 594 tcf_exts_change(tp, &f->exts, &e); 595 596 fp = &s->ht[h2]; 597 for (nfp = rtnl_dereference(*fp); nfp; 598 fp = &nfp->next, nfp = rtnl_dereference(*fp)) { 599 __u32 mask = nfp->spi.mask & f->spi.mask; 600 601 if (mask != f->spi.mask) 602 break; 603 } 604 RCU_INIT_POINTER(f->next, nfp); 605 rcu_assign_pointer(*fp, f); 606 607 *arg = (unsigned long)f; 608 return 0; 609 } 610 } 611 612 /* No session found. Create new one. */ 613 614 err = -ENOBUFS; 615 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL); 616 if (s == NULL) 617 goto errout; 618 memcpy(s->dst, dst, sizeof(s->dst)); 619 620 if (pinfo) { 621 s->dpi = pinfo->dpi; 622 s->protocol = pinfo->protocol; 623 s->tunnelid = pinfo->tunnelid; 624 } 625 sp = &data->ht[h1]; 626 for (nsp = rtnl_dereference(*sp); nsp; 627 sp = &nsp->next, nsp = rtnl_dereference(*sp)) { 628 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask) 629 break; 630 } 631 RCU_INIT_POINTER(s->next, nsp); 632 rcu_assign_pointer(*sp, s); 633 634 goto insert; 635 636 errout: 637 tcf_exts_destroy(&f->exts); 638 kfree(f); 639 errout2: 640 tcf_exts_destroy(&e); 641 return err; 642 } 643 644 static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg) 645 { 646 struct rsvp_head *head = rtnl_dereference(tp->root); 647 unsigned int h, h1; 648 649 if (arg->stop) 650 return; 651 652 for (h = 0; h < 256; h++) { 653 struct rsvp_session *s; 654 655 for (s = rtnl_dereference(head->ht[h]); s; 656 s = rtnl_dereference(s->next)) { 657 for (h1 = 0; h1 <= 16; h1++) { 658 struct rsvp_filter *f; 659 660 for (f = rtnl_dereference(s->ht[h1]); f; 661 f = rtnl_dereference(f->next)) { 662 if (arg->count < arg->skip) { 663 arg->count++; 664 continue; 665 } 666 if (arg->fn(tp, (unsigned long)f, arg) < 0) { 667 arg->stop = 1; 668 return; 669 } 670 arg->count++; 671 } 672 } 673 } 674 } 675 } 676 677 static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, 678 struct sk_buff *skb, struct tcmsg *t) 679 { 680 struct rsvp_filter *f = (struct rsvp_filter *)fh; 681 struct rsvp_session *s; 682 struct nlattr *nest; 683 struct tc_rsvp_pinfo pinfo; 684 685 if (f == NULL) 686 return skb->len; 687 s = f->sess; 688 689 t->tcm_handle = f->handle; 690 691 nest = nla_nest_start(skb, TCA_OPTIONS); 692 if (nest == NULL) 693 goto nla_put_failure; 694 695 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst)) 696 goto nla_put_failure; 697 pinfo.dpi = s->dpi; 698 pinfo.spi = f->spi; 699 pinfo.protocol = s->protocol; 700 pinfo.tunnelid = s->tunnelid; 701 pinfo.tunnelhdr = f->tunnelhdr; 702 pinfo.pad = 0; 703 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo)) 704 goto nla_put_failure; 705 if (f->res.classid && 706 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid)) 707 goto nla_put_failure; 708 if (((f->handle >> 8) & 0xFF) != 16 && 709 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src)) 710 goto nla_put_failure; 711 712 if (tcf_exts_dump(skb, &f->exts) < 0) 713 goto nla_put_failure; 714 715 nla_nest_end(skb, nest); 716 717 if (tcf_exts_dump_stats(skb, &f->exts) < 0) 718 goto nla_put_failure; 719 return skb->len; 720 721 nla_put_failure: 722 nla_nest_cancel(skb, nest); 723 return -1; 724 } 725 726 static struct tcf_proto_ops RSVP_OPS __read_mostly = { 727 .kind = RSVP_ID, 728 .classify = rsvp_classify, 729 .init = rsvp_init, 730 .destroy = rsvp_destroy, 731 .get = rsvp_get, 732 .change = rsvp_change, 733 .delete = rsvp_delete, 734 .walk = rsvp_walk, 735 .dump = rsvp_dump, 736 .owner = THIS_MODULE, 737 }; 738 739 static int __init init_rsvp(void) 740 { 741 return register_tcf_proto_ops(&RSVP_OPS); 742 } 743 744 static void __exit exit_rsvp(void) 745 { 746 unregister_tcf_proto_ops(&RSVP_OPS); 747 } 748 749 module_init(init_rsvp) 750 module_exit(exit_rsvp) 751
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.