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