]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - net/sched/cls_rsvp.h
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[mirror_ubuntu-artful-kernel.git] / net / sched / cls_rsvp.h
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 restart:
156
157 #if RSVP_DST_LEN == 4
158 src = &nhptr->saddr.s6_addr32[0];
159 dst = &nhptr->daddr.s6_addr32[0];
160 protocol = nhptr->nexthdr;
161 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
162 #else
163 src = &nhptr->saddr;
164 dst = &nhptr->daddr;
165 protocol = nhptr->protocol;
166 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
167 if (ip_is_fragment(nhptr))
168 return -1;
169 #endif
170
171 h1 = hash_dst(dst, protocol, tunnelid);
172 h2 = hash_src(src);
173
174 for (s = rcu_dereference_bh(head->ht[h1]); s;
175 s = rcu_dereference_bh(s->next)) {
176 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
177 protocol == s->protocol &&
178 !(s->dpi.mask &
179 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
180 #if RSVP_DST_LEN == 4
181 dst[0] == s->dst[0] &&
182 dst[1] == s->dst[1] &&
183 dst[2] == s->dst[2] &&
184 #endif
185 tunnelid == s->tunnelid) {
186
187 for (f = rcu_dereference_bh(s->ht[h2]); f;
188 f = rcu_dereference_bh(f->next)) {
189 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
190 !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
191 #if RSVP_DST_LEN == 4
192 &&
193 src[0] == f->src[0] &&
194 src[1] == f->src[1] &&
195 src[2] == f->src[2]
196 #endif
197 ) {
198 *res = f->res;
199 RSVP_APPLY_RESULT();
200
201 matched:
202 if (f->tunnelhdr == 0)
203 return 0;
204
205 tunnelid = f->res.classid;
206 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
207 goto restart;
208 }
209 }
210
211 /* And wildcard bucket... */
212 for (f = rcu_dereference_bh(s->ht[16]); f;
213 f = rcu_dereference_bh(f->next)) {
214 *res = f->res;
215 RSVP_APPLY_RESULT();
216 goto matched;
217 }
218 return -1;
219 }
220 }
221 return -1;
222 }
223
224 static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
225 {
226 struct rsvp_head *head = rtnl_dereference(tp->root);
227 struct rsvp_session *s;
228 struct rsvp_filter __rcu **ins;
229 struct rsvp_filter *pins;
230 unsigned int h1 = h & 0xFF;
231 unsigned int h2 = (h >> 8) & 0xFF;
232
233 for (s = rtnl_dereference(head->ht[h1]); s;
234 s = rtnl_dereference(s->next)) {
235 for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
236 ins = &pins->next, pins = rtnl_dereference(*ins)) {
237 if (pins->handle == h) {
238 RCU_INIT_POINTER(n->next, pins->next);
239 rcu_assign_pointer(*ins, n);
240 return;
241 }
242 }
243 }
244
245 /* Something went wrong if we are trying to replace a non-existant
246 * node. Mind as well halt instead of silently failing.
247 */
248 BUG_ON(1);
249 }
250
251 static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
252 {
253 struct rsvp_head *head = rtnl_dereference(tp->root);
254 struct rsvp_session *s;
255 struct rsvp_filter *f;
256 unsigned int h1 = handle & 0xFF;
257 unsigned int h2 = (handle >> 8) & 0xFF;
258
259 if (h2 > 16)
260 return 0;
261
262 for (s = rtnl_dereference(head->ht[h1]); s;
263 s = rtnl_dereference(s->next)) {
264 for (f = rtnl_dereference(s->ht[h2]); f;
265 f = rtnl_dereference(f->next)) {
266 if (f->handle == handle)
267 return (unsigned long)f;
268 }
269 }
270 return 0;
271 }
272
273 static int rsvp_init(struct tcf_proto *tp)
274 {
275 struct rsvp_head *data;
276
277 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
278 if (data) {
279 rcu_assign_pointer(tp->root, data);
280 return 0;
281 }
282 return -ENOBUFS;
283 }
284
285 static void rsvp_delete_filter_rcu(struct rcu_head *head)
286 {
287 struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu);
288
289 tcf_exts_destroy(&f->exts);
290 kfree(f);
291 }
292
293 static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
294 {
295 tcf_unbind_filter(tp, &f->res);
296 /* all classifiers are required to call tcf_exts_destroy() after rcu
297 * grace period, since converted-to-rcu actions are relying on that
298 * in cleanup() callback
299 */
300 call_rcu(&f->rcu, rsvp_delete_filter_rcu);
301 }
302
303 static void rsvp_destroy(struct tcf_proto *tp)
304 {
305 struct rsvp_head *data = rtnl_dereference(tp->root);
306 int h1, h2;
307
308 if (data == NULL)
309 return;
310
311 for (h1 = 0; h1 < 256; h1++) {
312 struct rsvp_session *s;
313
314 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
315 RCU_INIT_POINTER(data->ht[h1], s->next);
316
317 for (h2 = 0; h2 <= 16; h2++) {
318 struct rsvp_filter *f;
319
320 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
321 rcu_assign_pointer(s->ht[h2], f->next);
322 rsvp_delete_filter(tp, f);
323 }
324 }
325 kfree_rcu(s, rcu);
326 }
327 }
328 kfree_rcu(data, rcu);
329 }
330
331 static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
332 {
333 struct rsvp_head *head = rtnl_dereference(tp->root);
334 struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
335 struct rsvp_filter __rcu **fp;
336 unsigned int h = f->handle;
337 struct rsvp_session __rcu **sp;
338 struct rsvp_session *nsp, *s = f->sess;
339 int i, h1;
340
341 fp = &s->ht[(h >> 8) & 0xFF];
342 for (nfp = rtnl_dereference(*fp); nfp;
343 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
344 if (nfp == f) {
345 RCU_INIT_POINTER(*fp, f->next);
346 rsvp_delete_filter(tp, f);
347
348 /* Strip tree */
349
350 for (i = 0; i <= 16; i++)
351 if (s->ht[i])
352 goto out;
353
354 /* OK, session has no flows */
355 sp = &head->ht[h & 0xFF];
356 for (nsp = rtnl_dereference(*sp); nsp;
357 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
358 if (nsp == s) {
359 RCU_INIT_POINTER(*sp, s->next);
360 kfree_rcu(s, rcu);
361 goto out;
362 }
363 }
364
365 break;
366 }
367 }
368
369 out:
370 *last = true;
371 for (h1 = 0; h1 < 256; h1++) {
372 if (rcu_access_pointer(head->ht[h1])) {
373 *last = false;
374 break;
375 }
376 }
377
378 return 0;
379 }
380
381 static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
382 {
383 struct rsvp_head *data = rtnl_dereference(tp->root);
384 int i = 0xFFFF;
385
386 while (i-- > 0) {
387 u32 h;
388
389 if ((data->hgenerator += 0x10000) == 0)
390 data->hgenerator = 0x10000;
391 h = data->hgenerator|salt;
392 if (rsvp_get(tp, h) == 0)
393 return h;
394 }
395 return 0;
396 }
397
398 static int tunnel_bts(struct rsvp_head *data)
399 {
400 int n = data->tgenerator >> 5;
401 u32 b = 1 << (data->tgenerator & 0x1F);
402
403 if (data->tmap[n] & b)
404 return 0;
405 data->tmap[n] |= b;
406 return 1;
407 }
408
409 static void tunnel_recycle(struct rsvp_head *data)
410 {
411 struct rsvp_session __rcu **sht = data->ht;
412 u32 tmap[256/32];
413 int h1, h2;
414
415 memset(tmap, 0, sizeof(tmap));
416
417 for (h1 = 0; h1 < 256; h1++) {
418 struct rsvp_session *s;
419 for (s = rtnl_dereference(sht[h1]); s;
420 s = rtnl_dereference(s->next)) {
421 for (h2 = 0; h2 <= 16; h2++) {
422 struct rsvp_filter *f;
423
424 for (f = rtnl_dereference(s->ht[h2]); f;
425 f = rtnl_dereference(f->next)) {
426 if (f->tunnelhdr == 0)
427 continue;
428 data->tgenerator = f->res.classid;
429 tunnel_bts(data);
430 }
431 }
432 }
433 }
434
435 memcpy(data->tmap, tmap, sizeof(tmap));
436 }
437
438 static u32 gen_tunnel(struct rsvp_head *data)
439 {
440 int i, k;
441
442 for (k = 0; k < 2; k++) {
443 for (i = 255; i > 0; i--) {
444 if (++data->tgenerator == 0)
445 data->tgenerator = 1;
446 if (tunnel_bts(data))
447 return data->tgenerator;
448 }
449 tunnel_recycle(data);
450 }
451 return 0;
452 }
453
454 static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
455 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
456 [TCA_RSVP_DST] = { .type = NLA_BINARY,
457 .len = RSVP_DST_LEN * sizeof(u32) },
458 [TCA_RSVP_SRC] = { .type = NLA_BINARY,
459 .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, NULL);
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)