]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - net/sched/cls_rsvp.h
Merge tag 'vfio-v4.12-rc1' of git://github.com/awilliam/linux-vfio
[mirror_ubuntu-artful-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;
100 struct rcu_head rcu;
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
1da177e4
LT
251static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
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)
260 return 0;
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
LT
266 if (f->handle == handle)
267 return (unsigned long)f;
268 }
269 }
270 return 0;
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
9e528d89 285static void rsvp_delete_filter_rcu(struct rcu_head *head)
1da177e4 286{
9e528d89
AS
287 struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu);
288
18d0264f 289 tcf_exts_destroy(&f->exts);
9e528d89
AS
290 kfree(f);
291}
292
293static 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);
1da177e4
LT
301}
302
763dbf63 303static void rsvp_destroy(struct tcf_proto *tp)
1da177e4 304{
b929d86d 305 struct rsvp_head *data = rtnl_dereference(tp->root);
1da177e4
LT
306 int h1, h2;
307
308 if (data == NULL)
763dbf63 309 return;
1da177e4 310
cc7ec456 311 for (h1 = 0; h1 < 256; h1++) {
1da177e4
LT
312 struct rsvp_session *s;
313
b929d86d
JF
314 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
315 RCU_INIT_POINTER(data->ht[h1], s->next);
1da177e4 316
cc7ec456 317 for (h2 = 0; h2 <= 16; h2++) {
1da177e4
LT
318 struct rsvp_filter *f;
319
b929d86d
JF
320 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
321 rcu_assign_pointer(s->ht[h2], f->next);
1da177e4
LT
322 rsvp_delete_filter(tp, f);
323 }
324 }
b929d86d 325 kfree_rcu(s, rcu);
1da177e4
LT
326 }
327 }
b929d86d 328 kfree_rcu(data, rcu);
1da177e4
LT
329}
330
763dbf63 331static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
1da177e4 332{
b929d86d
JF
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;
cc7ec456 336 unsigned int h = f->handle;
b929d86d
JF
337 struct rsvp_session __rcu **sp;
338 struct rsvp_session *nsp, *s = f->sess;
763dbf63 339 int i, h1;
1da177e4 340
b929d86d
JF
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);
1da177e4
LT
346 rsvp_delete_filter(tp, f);
347
348 /* Strip tree */
349
cc7ec456 350 for (i = 0; i <= 16; i++)
1da177e4 351 if (s->ht[i])
763dbf63 352 goto out;
1da177e4
LT
353
354 /* OK, session has no flows */
b929d86d
JF
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);
763dbf63 361 goto out;
1da177e4
LT
362 }
363 }
364
763dbf63
WC
365 break;
366 }
367 }
368
369out:
370 *last = true;
371 for (h1 = 0; h1 < 256; h1++) {
372 if (rcu_access_pointer(head->ht[h1])) {
373 *last = false;
374 break;
1da177e4
LT
375 }
376 }
763dbf63 377
1da177e4
LT
378 return 0;
379}
380
cc7ec456 381static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
1da177e4 382{
b929d86d 383 struct rsvp_head *data = rtnl_dereference(tp->root);
1da177e4
LT
384 int i = 0xFFFF;
385
386 while (i-- > 0) {
387 u32 h;
cc7ec456 388
1da177e4
LT
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
398static int tunnel_bts(struct rsvp_head *data)
399{
cc7ec456
ED
400 int n = data->tgenerator >> 5;
401 u32 b = 1 << (data->tgenerator & 0x1F);
10297b99 402
cc7ec456 403 if (data->tmap[n] & b)
1da177e4
LT
404 return 0;
405 data->tmap[n] |= b;
406 return 1;
407}
408
409static void tunnel_recycle(struct rsvp_head *data)
410{
b929d86d 411 struct rsvp_session __rcu **sht = data->ht;
1da177e4
LT
412 u32 tmap[256/32];
413 int h1, h2;
414
415 memset(tmap, 0, sizeof(tmap));
416
cc7ec456 417 for (h1 = 0; h1 < 256; h1++) {
1da177e4 418 struct rsvp_session *s;
b929d86d
JF
419 for (s = rtnl_dereference(sht[h1]); s;
420 s = rtnl_dereference(s->next)) {
cc7ec456 421 for (h2 = 0; h2 <= 16; h2++) {
1da177e4
LT
422 struct rsvp_filter *f;
423
b929d86d
JF
424 for (f = rtnl_dereference(s->ht[h2]); f;
425 f = rtnl_dereference(f->next)) {
1da177e4
LT
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
438static u32 gen_tunnel(struct rsvp_head *data)
439{
440 int i, k;
441
cc7ec456
ED
442 for (k = 0; k < 2; k++) {
443 for (i = 255; i > 0; i--) {
1da177e4
LT
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
6fa8c014
PM
454static 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
c1b52739 463static int rsvp_change(struct net *net, struct sk_buff *in_skb,
af4c6641 464 struct tcf_proto *tp, unsigned long base,
1da177e4 465 u32 handle,
add93b61 466 struct nlattr **tca,
2f7ef2f8 467 unsigned long *arg, bool ovr)
1da177e4 468{
b929d86d
JF
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;
1da177e4 474 struct tc_rsvp_pinfo *pinfo = NULL;
27e95a8c 475 struct nlattr *opt = tca[TCA_OPTIONS];
add93b61 476 struct nlattr *tb[TCA_RSVP_MAX + 1];
1da177e4 477 struct tcf_exts e;
cc7ec456 478 unsigned int h1, h2;
66c6f529 479 __be32 *dst;
1da177e4
LT
480 int err;
481
482 if (opt == NULL)
483 return handle ? -EINVAL : 0;
484
fceb6435 485 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
cee63723
PM
486 if (err < 0)
487 return err;
1da177e4 488
b9a24bb7 489 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
1da177e4
LT
490 if (err < 0)
491 return err;
b9a24bb7
WC
492 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
493 if (err < 0)
494 goto errout2;
1da177e4 495
cc7ec456
ED
496 f = (struct rsvp_filter *)*arg;
497 if (f) {
1da177e4 498 /* Node exists: adjust only classid */
53dfd501 499 struct rsvp_filter *n;
1da177e4
LT
500
501 if (f->handle != handle && handle)
502 goto errout2;
53dfd501
JF
503
504 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
505 if (!n) {
506 err = -ENOMEM;
507 goto errout2;
508 }
509
b9a24bb7
WC
510 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
511 if (err < 0) {
512 kfree(n);
513 goto errout2;
514 }
53dfd501 515
27e95a8c 516 if (tb[TCA_RSVP_CLASSID]) {
53dfd501
JF
517 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
518 tcf_bind_filter(tp, &n->res, base);
1da177e4
LT
519 }
520
53dfd501
JF
521 tcf_exts_change(tp, &n->exts, &e);
522 rsvp_replace(tp, n, handle);
1da177e4
LT
523 return 0;
524 }
525
526 /* Now more serious part... */
527 err = -EINVAL;
528 if (handle)
529 goto errout2;
27e95a8c 530 if (tb[TCA_RSVP_DST] == NULL)
1da177e4
LT
531 goto errout2;
532
533 err = -ENOBUFS;
0da974f4 534 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
1da177e4
LT
535 if (f == NULL)
536 goto errout2;
537
b9a24bb7
WC
538 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
539 if (err < 0)
540 goto errout;
1da177e4 541 h2 = 16;
27e95a8c
IM
542 if (tb[TCA_RSVP_SRC]) {
543 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
1da177e4
LT
544 h2 = hash_src(f->src);
545 }
27e95a8c
IM
546 if (tb[TCA_RSVP_PINFO]) {
547 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
1da177e4
LT
548 f->spi = pinfo->spi;
549 f->tunnelhdr = pinfo->tunnelhdr;
550 }
27e95a8c
IM
551 if (tb[TCA_RSVP_CLASSID])
552 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
1da177e4 553
27e95a8c 554 dst = nla_data(tb[TCA_RSVP_DST]);
1da177e4
LT
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
b929d86d
JF
572 for (sp = &data->ht[h1];
573 (s = rtnl_dereference(*sp)) != NULL;
574 sp = &s->next) {
1da177e4
LT
575 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
576 pinfo && pinfo->protocol == s->protocol &&
f64f9e71 577 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
1da177e4 578#if RSVP_DST_LEN == 4
f64f9e71
JP
579 dst[0] == s->dst[0] &&
580 dst[1] == s->dst[1] &&
581 dst[2] == s->dst[2] &&
1da177e4 582#endif
f64f9e71 583 pinfo->tunnelid == s->tunnelid) {
1da177e4
LT
584
585insert:
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
b929d86d
JF
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)
1da177e4 602 break;
b929d86d
JF
603 }
604 RCU_INIT_POINTER(f->next, nfp);
605 rcu_assign_pointer(*fp, f);
1da177e4
LT
606
607 *arg = (unsigned long)f;
608 return 0;
609 }
610 }
611
612 /* No session found. Create new one. */
613
614 err = -ENOBUFS;
0da974f4 615 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
1da177e4
LT
616 if (s == NULL)
617 goto errout;
1da177e4
LT
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 }
b929d86d
JF
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)
1da177e4
LT
629 break;
630 }
b929d86d
JF
631 RCU_INIT_POINTER(s->next, nsp);
632 rcu_assign_pointer(*sp, s);
10297b99 633
1da177e4
LT
634 goto insert;
635
636errout:
b9a24bb7 637 tcf_exts_destroy(&f->exts);
a51482bd 638 kfree(f);
1da177e4 639errout2:
18d0264f 640 tcf_exts_destroy(&e);
1da177e4
LT
641 return err;
642}
643
644static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
645{
b929d86d 646 struct rsvp_head *head = rtnl_dereference(tp->root);
cc7ec456 647 unsigned int h, h1;
1da177e4
LT
648
649 if (arg->stop)
650 return;
651
652 for (h = 0; h < 256; h++) {
653 struct rsvp_session *s;
654
b929d86d
JF
655 for (s = rtnl_dereference(head->ht[h]); s;
656 s = rtnl_dereference(s->next)) {
1da177e4
LT
657 for (h1 = 0; h1 <= 16; h1++) {
658 struct rsvp_filter *f;
659
b929d86d
JF
660 for (f = rtnl_dereference(s->ht[h1]); f;
661 f = rtnl_dereference(f->next)) {
1da177e4
LT
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
832d1d5b 677static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
1da177e4
LT
678 struct sk_buff *skb, struct tcmsg *t)
679{
cc7ec456 680 struct rsvp_filter *f = (struct rsvp_filter *)fh;
1da177e4 681 struct rsvp_session *s;
4b3550ef 682 struct nlattr *nest;
1da177e4
LT
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
4b3550ef
PM
691 nest = nla_nest_start(skb, TCA_OPTIONS);
692 if (nest == NULL)
693 goto nla_put_failure;
1da177e4 694
1b34ec43
DM
695 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
696 goto nla_put_failure;
1da177e4
LT
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;
8a47077a 702 pinfo.pad = 0;
1b34ec43
DM
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;
1da177e4 711
5da57f42 712 if (tcf_exts_dump(skb, &f->exts) < 0)
add93b61 713 goto nla_put_failure;
1da177e4 714
4b3550ef 715 nla_nest_end(skb, nest);
1da177e4 716
5da57f42 717 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
add93b61 718 goto nla_put_failure;
1da177e4
LT
719 return skb->len;
720
add93b61 721nla_put_failure:
6ea3b446 722 nla_nest_cancel(skb, nest);
1da177e4
LT
723 return -1;
724}
725
27e95a8c 726static struct tcf_proto_ops RSVP_OPS __read_mostly = {
1da177e4
LT
727 .kind = RSVP_ID,
728 .classify = rsvp_classify,
729 .init = rsvp_init,
730 .destroy = rsvp_destroy,
731 .get = rsvp_get,
1da177e4
LT
732 .change = rsvp_change,
733 .delete = rsvp_delete,
734 .walk = rsvp_walk,
735 .dump = rsvp_dump,
736 .owner = THIS_MODULE,
737};
738
739static int __init init_rsvp(void)
740{
741 return register_tcf_proto_ops(&RSVP_OPS);
742}
743
10297b99 744static void __exit exit_rsvp(void)
1da177e4
LT
745{
746 unregister_tcf_proto_ops(&RSVP_OPS);
747}
748
749module_init(init_rsvp)
750module_exit(exit_rsvp)