]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/sched/cls_rsvp.h
net: sched: gred: pass the right attribute to gred_change_table_def()
[mirror_ubuntu-bionic-kernel.git] / net / sched / cls_rsvp.h
CommitLineData
1da177e4
LT
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
1da177e4 68
cc7ec456 69struct rsvp_head {
1da177e4
LT
70 u32 tmap[256/32];
71 u32 hgenerator;
72 u8 tgenerator;
b929d86d
JF
73 struct rsvp_session __rcu *ht[256];
74 struct rcu_head rcu;
1da177e4
LT
75};
76
cc7ec456 77struct rsvp_session {
b929d86d
JF
78 struct rsvp_session __rcu *next;
79 __be32 dst[RSVP_DST_LEN];
80 struct tc_rsvp_gpi dpi;
81 u8 protocol;
82 u8 tunnelid;
1da177e4 83 /* 16 (src,sport) hash slots, and one wildcard source slot */
b929d86d
JF
84 struct rsvp_filter __rcu *ht[16 + 1];
85 struct rcu_head rcu;
1da177e4
LT
86};
87
88
cc7ec456 89struct rsvp_filter {
b929d86d
JF
90 struct rsvp_filter __rcu *next;
91 __be32 src[RSVP_DST_LEN];
92 struct tc_rsvp_gpi spi;
93 u8 tunnelhdr;
1da177e4 94
b929d86d
JF
95 struct tcf_result res;
96 struct tcf_exts exts;
1da177e4 97
b929d86d
JF
98 u32 handle;
99 struct rsvp_session *sess;
7afe8e4f 100 struct rcu_work rwork;
1da177e4
LT
101};
102
cc7ec456 103static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
1da177e4 104{
cc7ec456
ED
105 unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
106
1da177e4
LT
107 h ^= h>>16;
108 h ^= h>>8;
109 return (h ^ protocol ^ tunnelid) & 0xFF;
110}
111
cc7ec456 112static inline unsigned int hash_src(__be32 *src)
1da177e4 113{
cc7ec456
ED
114 unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
115
1da177e4
LT
116 h ^= h>>16;
117 h ^= h>>8;
118 h ^= h>>4;
119 return h & 0xF;
120}
121
1da177e4
LT
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}
10297b99 130
dc7f9f6e 131static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1da177e4
LT
132 struct tcf_result *res)
133{
b929d86d 134 struct rsvp_head *head = rcu_dereference_bh(tp->root);
1da177e4
LT
135 struct rsvp_session *s;
136 struct rsvp_filter *f;
cc7ec456 137 unsigned int h1, h2;
66c6f529 138 __be32 *dst, *src;
1da177e4
LT
139 u8 protocol;
140 u8 tunnelid = 0;
141 u8 *xprt;
142#if RSVP_DST_LEN == 4
12dc96d1
CG
143 struct ipv6hdr *nhptr;
144
145 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
146 return -1;
147 nhptr = ipv6_hdr(skb);
1da177e4 148#else
12dc96d1
CG
149 struct iphdr *nhptr;
150
151 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
152 return -1;
153 nhptr = ip_hdr(skb);
1da177e4 154#endif
1da177e4
LT
155restart:
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;
cc7ec456 161 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
1da177e4
LT
162#else
163 src = &nhptr->saddr;
164 dst = &nhptr->daddr;
165 protocol = nhptr->protocol;
cc7ec456 166 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
56f8a75c 167 if (ip_is_fragment(nhptr))
1da177e4
LT
168 return -1;
169#endif
170
171 h1 = hash_dst(dst, protocol, tunnelid);
172 h2 = hash_src(src);
173
b929d86d
JF
174 for (s = rcu_dereference_bh(head->ht[h1]); s;
175 s = rcu_dereference_bh(s->next)) {
cc7ec456 176 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
1da177e4 177 protocol == s->protocol &&
f64f9e71 178 !(s->dpi.mask &
cc7ec456 179 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
1da177e4 180#if RSVP_DST_LEN == 4
f64f9e71
JP
181 dst[0] == s->dst[0] &&
182 dst[1] == s->dst[1] &&
183 dst[2] == s->dst[2] &&
1da177e4 184#endif
f64f9e71 185 tunnelid == s->tunnelid) {
1da177e4 186
b929d86d
JF
187 for (f = rcu_dereference_bh(s->ht[h2]); f;
188 f = rcu_dereference_bh(f->next)) {
cc7ec456
ED
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))
1da177e4 191#if RSVP_DST_LEN == 4
f64f9e71
JP
192 &&
193 src[0] == f->src[0] &&
194 src[1] == f->src[1] &&
195 src[2] == f->src[2]
1da177e4
LT
196#endif
197 ) {
198 *res = f->res;
199 RSVP_APPLY_RESULT();
200
201matched:
202 if (f->tunnelhdr == 0)
203 return 0;
204
205 tunnelid = f->res.classid;
cc7ec456 206 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
1da177e4
LT
207 goto restart;
208 }
209 }
210
211 /* And wildcard bucket... */
b929d86d
JF
212 for (f = rcu_dereference_bh(s->ht[16]); f;
213 f = rcu_dereference_bh(f->next)) {
1da177e4
LT
214 *res = f->res;
215 RSVP_APPLY_RESULT();
216 goto matched;
217 }
218 return -1;
219 }
220 }
221 return -1;
222}
223
53dfd501
JF
224static 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
8113c095 251static void *rsvp_get(struct tcf_proto *tp, u32 handle)
1da177e4 252{
b929d86d 253 struct rsvp_head *head = rtnl_dereference(tp->root);
1da177e4
LT
254 struct rsvp_session *s;
255 struct rsvp_filter *f;
cc7ec456
ED
256 unsigned int h1 = handle & 0xFF;
257 unsigned int h2 = (handle >> 8) & 0xFF;
1da177e4
LT
258
259 if (h2 > 16)
8113c095 260 return NULL;
1da177e4 261
b929d86d
JF
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)) {
1da177e4 266 if (f->handle == handle)
8113c095 267 return f;
1da177e4
LT
268 }
269 }
8113c095 270 return NULL;
1da177e4
LT
271}
272
1da177e4
LT
273static int rsvp_init(struct tcf_proto *tp)
274{
275 struct rsvp_head *data;
276
0da974f4 277 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
1da177e4 278 if (data) {
b929d86d 279 rcu_assign_pointer(tp->root, data);
1da177e4
LT
280 return 0;
281 }
282 return -ENOBUFS;
283}
284
96585063
CW
285static void __rsvp_delete_filter(struct rsvp_filter *f)
286{
287 tcf_exts_destroy(&f->exts);
288 tcf_exts_put_net(&f->exts);
289 kfree(f);
290}
291
d4f84a41 292static void rsvp_delete_filter_work(struct work_struct *work)
1da177e4 293{
7afe8e4f
CW
294 struct rsvp_filter *f = container_of(to_rcu_work(work),
295 struct rsvp_filter,
296 rwork);
d4f84a41 297 rtnl_lock();
96585063 298 __rsvp_delete_filter(f);
d4f84a41
CW
299 rtnl_unlock();
300}
301
9e528d89
AS
302static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
303{
304 tcf_unbind_filter(tp, &f->res);
305 /* all classifiers are required to call tcf_exts_destroy() after rcu
306 * grace period, since converted-to-rcu actions are relying on that
307 * in cleanup() callback
308 */
96585063 309 if (tcf_exts_get_net(&f->exts))
7afe8e4f 310 tcf_queue_work(&f->rwork, rsvp_delete_filter_work);
96585063
CW
311 else
312 __rsvp_delete_filter(f);
1da177e4
LT
313}
314
763dbf63 315static void rsvp_destroy(struct tcf_proto *tp)
1da177e4 316{
b929d86d 317 struct rsvp_head *data = rtnl_dereference(tp->root);
1da177e4
LT
318 int h1, h2;
319
320 if (data == NULL)
763dbf63 321 return;
1da177e4 322
cc7ec456 323 for (h1 = 0; h1 < 256; h1++) {
1da177e4
LT
324 struct rsvp_session *s;
325
b929d86d
JF
326 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
327 RCU_INIT_POINTER(data->ht[h1], s->next);
1da177e4 328
cc7ec456 329 for (h2 = 0; h2 <= 16; h2++) {
1da177e4
LT
330 struct rsvp_filter *f;
331
b929d86d
JF
332 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
333 rcu_assign_pointer(s->ht[h2], f->next);
1da177e4
LT
334 rsvp_delete_filter(tp, f);
335 }
336 }
b929d86d 337 kfree_rcu(s, rcu);
1da177e4
LT
338 }
339 }
b929d86d 340 kfree_rcu(data, rcu);
1da177e4
LT
341}
342
8113c095 343static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last)
1da177e4 344{
b929d86d 345 struct rsvp_head *head = rtnl_dereference(tp->root);
8113c095 346 struct rsvp_filter *nfp, *f = arg;
b929d86d 347 struct rsvp_filter __rcu **fp;
cc7ec456 348 unsigned int h = f->handle;
b929d86d
JF
349 struct rsvp_session __rcu **sp;
350 struct rsvp_session *nsp, *s = f->sess;
763dbf63 351 int i, h1;
1da177e4 352
b929d86d
JF
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);
1da177e4
LT
358 rsvp_delete_filter(tp, f);
359
360 /* Strip tree */
361
cc7ec456 362 for (i = 0; i <= 16; i++)
1da177e4 363 if (s->ht[i])
763dbf63 364 goto out;
1da177e4
LT
365
366 /* OK, session has no flows */
b929d86d
JF
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);
763dbf63 373 goto out;
1da177e4
LT
374 }
375 }
376
763dbf63
WC
377 break;
378 }
379 }
380
381out:
382 *last = true;
383 for (h1 = 0; h1 < 256; h1++) {
384 if (rcu_access_pointer(head->ht[h1])) {
385 *last = false;
386 break;
1da177e4
LT
387 }
388 }
763dbf63 389
1da177e4
LT
390 return 0;
391}
392
cc7ec456 393static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
1da177e4 394{
b929d86d 395 struct rsvp_head *data = rtnl_dereference(tp->root);
1da177e4
LT
396 int i = 0xFFFF;
397
398 while (i-- > 0) {
399 u32 h;
cc7ec456 400
1da177e4
LT
401 if ((data->hgenerator += 0x10000) == 0)
402 data->hgenerator = 0x10000;
403 h = data->hgenerator|salt;
230cfd2d 404 if (!rsvp_get(tp, h))
1da177e4
LT
405 return h;
406 }
407 return 0;
408}
409
410static int tunnel_bts(struct rsvp_head *data)
411{
cc7ec456
ED
412 int n = data->tgenerator >> 5;
413 u32 b = 1 << (data->tgenerator & 0x1F);
10297b99 414
cc7ec456 415 if (data->tmap[n] & b)
1da177e4
LT
416 return 0;
417 data->tmap[n] |= b;
418 return 1;
419}
420
421static void tunnel_recycle(struct rsvp_head *data)
422{
b929d86d 423 struct rsvp_session __rcu **sht = data->ht;
1da177e4
LT
424 u32 tmap[256/32];
425 int h1, h2;
426
427 memset(tmap, 0, sizeof(tmap));
428
cc7ec456 429 for (h1 = 0; h1 < 256; h1++) {
1da177e4 430 struct rsvp_session *s;
b929d86d
JF
431 for (s = rtnl_dereference(sht[h1]); s;
432 s = rtnl_dereference(s->next)) {
cc7ec456 433 for (h2 = 0; h2 <= 16; h2++) {
1da177e4
LT
434 struct rsvp_filter *f;
435
b929d86d
JF
436 for (f = rtnl_dereference(s->ht[h2]); f;
437 f = rtnl_dereference(f->next)) {
1da177e4
LT
438 if (f->tunnelhdr == 0)
439 continue;
440 data->tgenerator = f->res.classid;
441 tunnel_bts(data);
442 }
443 }
444 }
445 }
446
447 memcpy(data->tmap, tmap, sizeof(tmap));
448}
449
450static u32 gen_tunnel(struct rsvp_head *data)
451{
452 int i, k;
453
cc7ec456
ED
454 for (k = 0; k < 2; k++) {
455 for (i = 255; i > 0; i--) {
1da177e4
LT
456 if (++data->tgenerator == 0)
457 data->tgenerator = 1;
458 if (tunnel_bts(data))
459 return data->tgenerator;
460 }
461 tunnel_recycle(data);
462 }
463 return 0;
464}
465
6fa8c014
PM
466static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
467 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
468 [TCA_RSVP_DST] = { .type = NLA_BINARY,
469 .len = RSVP_DST_LEN * sizeof(u32) },
470 [TCA_RSVP_SRC] = { .type = NLA_BINARY,
471 .len = RSVP_DST_LEN * sizeof(u32) },
472 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
473};
474
c1b52739 475static int rsvp_change(struct net *net, struct sk_buff *in_skb,
af4c6641 476 struct tcf_proto *tp, unsigned long base,
1da177e4 477 u32 handle,
add93b61 478 struct nlattr **tca,
8113c095 479 void **arg, bool ovr)
1da177e4 480{
b929d86d
JF
481 struct rsvp_head *data = rtnl_dereference(tp->root);
482 struct rsvp_filter *f, *nfp;
483 struct rsvp_filter __rcu **fp;
484 struct rsvp_session *nsp, *s;
485 struct rsvp_session __rcu **sp;
1da177e4 486 struct tc_rsvp_pinfo *pinfo = NULL;
27e95a8c 487 struct nlattr *opt = tca[TCA_OPTIONS];
add93b61 488 struct nlattr *tb[TCA_RSVP_MAX + 1];
1da177e4 489 struct tcf_exts e;
cc7ec456 490 unsigned int h1, h2;
66c6f529 491 __be32 *dst;
1da177e4
LT
492 int err;
493
494 if (opt == NULL)
495 return handle ? -EINVAL : 0;
496
fceb6435 497 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
cee63723
PM
498 if (err < 0)
499 return err;
1da177e4 500
fb6ad63b 501 err = tcf_exts_init(&e, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
1da177e4
LT
502 if (err < 0)
503 return err;
b9a24bb7
WC
504 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
505 if (err < 0)
506 goto errout2;
1da177e4 507
8113c095 508 f = *arg;
cc7ec456 509 if (f) {
1da177e4 510 /* Node exists: adjust only classid */
53dfd501 511 struct rsvp_filter *n;
1da177e4
LT
512
513 if (f->handle != handle && handle)
514 goto errout2;
53dfd501
JF
515
516 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
517 if (!n) {
518 err = -ENOMEM;
519 goto errout2;
520 }
521
fb6ad63b
CW
522 err = tcf_exts_init(&n->exts, net, TCA_RSVP_ACT,
523 TCA_RSVP_POLICE);
b9a24bb7
WC
524 if (err < 0) {
525 kfree(n);
526 goto errout2;
527 }
53dfd501 528
27e95a8c 529 if (tb[TCA_RSVP_CLASSID]) {
53dfd501
JF
530 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
531 tcf_bind_filter(tp, &n->res, base);
1da177e4
LT
532 }
533
9b0d4446 534 tcf_exts_change(&n->exts, &e);
53dfd501 535 rsvp_replace(tp, n, handle);
1da177e4
LT
536 return 0;
537 }
538
539 /* Now more serious part... */
540 err = -EINVAL;
541 if (handle)
542 goto errout2;
27e95a8c 543 if (tb[TCA_RSVP_DST] == NULL)
1da177e4
LT
544 goto errout2;
545
546 err = -ENOBUFS;
0da974f4 547 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
1da177e4
LT
548 if (f == NULL)
549 goto errout2;
550
fb6ad63b 551 err = tcf_exts_init(&f->exts, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
b9a24bb7
WC
552 if (err < 0)
553 goto errout;
1da177e4 554 h2 = 16;
27e95a8c
IM
555 if (tb[TCA_RSVP_SRC]) {
556 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
1da177e4
LT
557 h2 = hash_src(f->src);
558 }
27e95a8c
IM
559 if (tb[TCA_RSVP_PINFO]) {
560 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
1da177e4
LT
561 f->spi = pinfo->spi;
562 f->tunnelhdr = pinfo->tunnelhdr;
563 }
27e95a8c
IM
564 if (tb[TCA_RSVP_CLASSID])
565 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
1da177e4 566
27e95a8c 567 dst = nla_data(tb[TCA_RSVP_DST]);
1da177e4
LT
568 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
569
570 err = -ENOMEM;
571 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
572 goto errout;
573
574 if (f->tunnelhdr) {
575 err = -EINVAL;
576 if (f->res.classid > 255)
577 goto errout;
578
579 err = -ENOMEM;
580 if (f->res.classid == 0 &&
581 (f->res.classid = gen_tunnel(data)) == 0)
582 goto errout;
583 }
584
b929d86d
JF
585 for (sp = &data->ht[h1];
586 (s = rtnl_dereference(*sp)) != NULL;
587 sp = &s->next) {
1da177e4
LT
588 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
589 pinfo && pinfo->protocol == s->protocol &&
f64f9e71 590 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
1da177e4 591#if RSVP_DST_LEN == 4
f64f9e71
JP
592 dst[0] == s->dst[0] &&
593 dst[1] == s->dst[1] &&
594 dst[2] == s->dst[2] &&
1da177e4 595#endif
f64f9e71 596 pinfo->tunnelid == s->tunnelid) {
1da177e4
LT
597
598insert:
599 /* OK, we found appropriate session */
600
601 fp = &s->ht[h2];
602
603 f->sess = s;
604 if (f->tunnelhdr == 0)
605 tcf_bind_filter(tp, &f->res, base);
606
9b0d4446 607 tcf_exts_change(&f->exts, &e);
1da177e4 608
b929d86d
JF
609 fp = &s->ht[h2];
610 for (nfp = rtnl_dereference(*fp); nfp;
611 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
612 __u32 mask = nfp->spi.mask & f->spi.mask;
613
614 if (mask != f->spi.mask)
1da177e4 615 break;
b929d86d
JF
616 }
617 RCU_INIT_POINTER(f->next, nfp);
618 rcu_assign_pointer(*fp, f);
1da177e4 619
8113c095 620 *arg = f;
1da177e4
LT
621 return 0;
622 }
623 }
624
625 /* No session found. Create new one. */
626
627 err = -ENOBUFS;
0da974f4 628 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
1da177e4
LT
629 if (s == NULL)
630 goto errout;
1da177e4
LT
631 memcpy(s->dst, dst, sizeof(s->dst));
632
633 if (pinfo) {
634 s->dpi = pinfo->dpi;
635 s->protocol = pinfo->protocol;
636 s->tunnelid = pinfo->tunnelid;
637 }
b929d86d
JF
638 sp = &data->ht[h1];
639 for (nsp = rtnl_dereference(*sp); nsp;
640 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
641 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
1da177e4
LT
642 break;
643 }
b929d86d
JF
644 RCU_INIT_POINTER(s->next, nsp);
645 rcu_assign_pointer(*sp, s);
10297b99 646
1da177e4
LT
647 goto insert;
648
649errout:
b9a24bb7 650 tcf_exts_destroy(&f->exts);
a51482bd 651 kfree(f);
1da177e4 652errout2:
18d0264f 653 tcf_exts_destroy(&e);
1da177e4
LT
654 return err;
655}
656
657static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
658{
b929d86d 659 struct rsvp_head *head = rtnl_dereference(tp->root);
cc7ec456 660 unsigned int h, h1;
1da177e4
LT
661
662 if (arg->stop)
663 return;
664
665 for (h = 0; h < 256; h++) {
666 struct rsvp_session *s;
667
b929d86d
JF
668 for (s = rtnl_dereference(head->ht[h]); s;
669 s = rtnl_dereference(s->next)) {
1da177e4
LT
670 for (h1 = 0; h1 <= 16; h1++) {
671 struct rsvp_filter *f;
672
b929d86d
JF
673 for (f = rtnl_dereference(s->ht[h1]); f;
674 f = rtnl_dereference(f->next)) {
1da177e4
LT
675 if (arg->count < arg->skip) {
676 arg->count++;
677 continue;
678 }
8113c095 679 if (arg->fn(tp, f, arg) < 0) {
1da177e4
LT
680 arg->stop = 1;
681 return;
682 }
683 arg->count++;
684 }
685 }
686 }
687 }
688}
689
8113c095 690static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
1da177e4
LT
691 struct sk_buff *skb, struct tcmsg *t)
692{
8113c095 693 struct rsvp_filter *f = fh;
1da177e4 694 struct rsvp_session *s;
4b3550ef 695 struct nlattr *nest;
1da177e4
LT
696 struct tc_rsvp_pinfo pinfo;
697
698 if (f == NULL)
699 return skb->len;
700 s = f->sess;
701
702 t->tcm_handle = f->handle;
703
4b3550ef
PM
704 nest = nla_nest_start(skb, TCA_OPTIONS);
705 if (nest == NULL)
706 goto nla_put_failure;
1da177e4 707
1b34ec43
DM
708 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
709 goto nla_put_failure;
1da177e4
LT
710 pinfo.dpi = s->dpi;
711 pinfo.spi = f->spi;
712 pinfo.protocol = s->protocol;
713 pinfo.tunnelid = s->tunnelid;
714 pinfo.tunnelhdr = f->tunnelhdr;
8a47077a 715 pinfo.pad = 0;
1b34ec43
DM
716 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
717 goto nla_put_failure;
718 if (f->res.classid &&
719 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
720 goto nla_put_failure;
721 if (((f->handle >> 8) & 0xFF) != 16 &&
722 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
723 goto nla_put_failure;
1da177e4 724
5da57f42 725 if (tcf_exts_dump(skb, &f->exts) < 0)
add93b61 726 goto nla_put_failure;
1da177e4 727
4b3550ef 728 nla_nest_end(skb, nest);
1da177e4 729
5da57f42 730 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
add93b61 731 goto nla_put_failure;
1da177e4
LT
732 return skb->len;
733
add93b61 734nla_put_failure:
6ea3b446 735 nla_nest_cancel(skb, nest);
1da177e4
LT
736 return -1;
737}
738
07d79fc7
CW
739static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl)
740{
741 struct rsvp_filter *f = fh;
742
743 if (f && f->res.classid == classid)
744 f->res.class = cl;
745}
746
27e95a8c 747static struct tcf_proto_ops RSVP_OPS __read_mostly = {
1da177e4
LT
748 .kind = RSVP_ID,
749 .classify = rsvp_classify,
750 .init = rsvp_init,
751 .destroy = rsvp_destroy,
752 .get = rsvp_get,
1da177e4
LT
753 .change = rsvp_change,
754 .delete = rsvp_delete,
755 .walk = rsvp_walk,
756 .dump = rsvp_dump,
07d79fc7 757 .bind_class = rsvp_bind_class,
1da177e4
LT
758 .owner = THIS_MODULE,
759};
760
761static int __init init_rsvp(void)
762{
763 return register_tcf_proto_ops(&RSVP_OPS);
764}
765
10297b99 766static void __exit exit_rsvp(void)
1da177e4
LT
767{
768 unregister_tcf_proto_ops(&RSVP_OPS);
769}
770
771module_init(init_rsvp)
772module_exit(exit_rsvp)