blob: 322438fb3ffcb426194be6c1dd05893b27cdf51c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Eric Dumazetcc7ec452011-01-19 19:26:56 +000069struct rsvp_head {
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 u32 tmap[256/32];
71 u32 hgenerator;
72 u8 tgenerator;
John Fastabendb929d862014-09-12 20:09:49 -070073 struct rsvp_session __rcu *ht[256];
74 struct rcu_head rcu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070075};
76
Eric Dumazetcc7ec452011-01-19 19:26:56 +000077struct rsvp_session {
John Fastabendb929d862014-09-12 20:09:49 -070078 struct rsvp_session __rcu *next;
79 __be32 dst[RSVP_DST_LEN];
80 struct tc_rsvp_gpi dpi;
81 u8 protocol;
82 u8 tunnelid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 /* 16 (src,sport) hash slots, and one wildcard source slot */
John Fastabendb929d862014-09-12 20:09:49 -070084 struct rsvp_filter __rcu *ht[16 + 1];
85 struct rcu_head rcu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
88
Eric Dumazetcc7ec452011-01-19 19:26:56 +000089struct rsvp_filter {
John Fastabendb929d862014-09-12 20:09:49 -070090 struct rsvp_filter __rcu *next;
91 __be32 src[RSVP_DST_LEN];
92 struct tc_rsvp_gpi spi;
93 u8 tunnelhdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
John Fastabendb929d862014-09-12 20:09:49 -070095 struct tcf_result res;
96 struct tcf_exts exts;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
John Fastabendb929d862014-09-12 20:09:49 -070098 u32 handle;
99 struct rsvp_session *sess;
100 struct rcu_head rcu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101};
102
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000103static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000105 unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 h ^= h>>16;
108 h ^= h>>8;
109 return (h ^ protocol ^ tunnelid) & 0xFF;
110}
111
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000112static inline unsigned int hash_src(__be32 *src)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000114 unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 h ^= h>>16;
117 h ^= h>>8;
118 h ^= h>>4;
119 return h & 0xF;
120}
121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122#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}
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900130
Eric Dumazetdc7f9f62011-07-05 23:25:42 +0000131static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 struct tcf_result *res)
133{
John Fastabendb929d862014-09-12 20:09:49 -0700134 struct rsvp_head *head = rcu_dereference_bh(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 struct rsvp_session *s;
136 struct rsvp_filter *f;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000137 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800138 __be32 *dst, *src;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 u8 protocol;
140 u8 tunnelid = 0;
141 u8 *xprt;
142#if RSVP_DST_LEN == 4
Changli Gao12dc96d2010-08-04 04:55:40 +0000143 struct ipv6hdr *nhptr;
144
145 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
146 return -1;
147 nhptr = ipv6_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148#else
Changli Gao12dc96d2010-08-04 04:55:40 +0000149 struct iphdr *nhptr;
150
151 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
152 return -1;
153 nhptr = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154#endif
Daniel Borkmannd9363772016-11-27 01:18:01 +0100155 if (unlikely(!head))
156 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157restart:
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;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000163 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164#else
165 src = &nhptr->saddr;
166 dst = &nhptr->daddr;
167 protocol = nhptr->protocol;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000168 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
Paul Gortmaker56f8a752011-06-21 20:33:34 -0700169 if (ip_is_fragment(nhptr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 return -1;
171#endif
172
173 h1 = hash_dst(dst, protocol, tunnelid);
174 h2 = hash_src(src);
175
John Fastabendb929d862014-09-12 20:09:49 -0700176 for (s = rcu_dereference_bh(head->ht[h1]); s;
177 s = rcu_dereference_bh(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000178 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800180 !(s->dpi.mask &
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000181 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800183 dst[0] == s->dst[0] &&
184 dst[1] == s->dst[1] &&
185 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800187 tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
John Fastabendb929d862014-09-12 20:09:49 -0700189 for (f = rcu_dereference_bh(s->ht[h2]); f;
190 f = rcu_dereference_bh(f->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000191 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
192 !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800194 &&
195 src[0] == f->src[0] &&
196 src[1] == f->src[1] &&
197 src[2] == f->src[2]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198#endif
199 ) {
200 *res = f->res;
201 RSVP_APPLY_RESULT();
202
203matched:
204 if (f->tunnelhdr == 0)
205 return 0;
206
207 tunnelid = f->res.classid;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000208 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 goto restart;
210 }
211 }
212
213 /* And wildcard bucket... */
John Fastabendb929d862014-09-12 20:09:49 -0700214 for (f = rcu_dereference_bh(s->ht[16]); f;
215 f = rcu_dereference_bh(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 *res = f->res;
217 RSVP_APPLY_RESULT();
218 goto matched;
219 }
220 return -1;
221 }
222 }
223 return -1;
224}
225
John Fastabend53dfd502014-09-26 10:02:50 -0700226static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
254{
John Fastabendb929d862014-09-12 20:09:49 -0700255 struct rsvp_head *head = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 struct rsvp_session *s;
257 struct rsvp_filter *f;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000258 unsigned int h1 = handle & 0xFF;
259 unsigned int h2 = (handle >> 8) & 0xFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261 if (h2 > 16)
262 return 0;
263
John Fastabendb929d862014-09-12 20:09:49 -0700264 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)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 if (f->handle == handle)
269 return (unsigned long)f;
270 }
271 }
272 return 0;
273}
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275static int rsvp_init(struct tcf_proto *tp)
276{
277 struct rsvp_head *data;
278
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700279 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 if (data) {
John Fastabendb929d862014-09-12 20:09:49 -0700281 rcu_assign_pointer(tp->root, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 return 0;
283 }
284 return -ENOBUFS;
285}
286
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700287static 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
295static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 tcf_unbind_filter(tp, &f->res);
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700298 /* 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303}
304
Cong Wang1e052be2015-03-06 11:47:59 -0800305static bool rsvp_destroy(struct tcf_proto *tp, bool force)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{
John Fastabendb929d862014-09-12 20:09:49 -0700307 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 int h1, h2;
309
310 if (data == NULL)
Cong Wang1e052be2015-03-06 11:47:59 -0800311 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
John Fastabendb929d862014-09-12 20:09:49 -0700320 RCU_INIT_POINTER(tp->root, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000322 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 struct rsvp_session *s;
324
John Fastabendb929d862014-09-12 20:09:49 -0700325 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
326 RCU_INIT_POINTER(data->ht[h1], s->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000328 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 struct rsvp_filter *f;
330
John Fastabendb929d862014-09-12 20:09:49 -0700331 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
332 rcu_assign_pointer(s->ht[h2], f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 rsvp_delete_filter(tp, f);
334 }
335 }
John Fastabendb929d862014-09-12 20:09:49 -0700336 kfree_rcu(s, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 }
338 }
John Fastabendb929d862014-09-12 20:09:49 -0700339 kfree_rcu(data, rcu);
Cong Wang1e052be2015-03-06 11:47:59 -0800340 return true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
343static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
344{
John Fastabendb929d862014-09-12 20:09:49 -0700345 struct rsvp_head *head = rtnl_dereference(tp->root);
346 struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
347 struct rsvp_filter __rcu **fp;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000348 unsigned int h = f->handle;
John Fastabendb929d862014-09-12 20:09:49 -0700349 struct rsvp_session __rcu **sp;
350 struct rsvp_session *nsp, *s = f->sess;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 int i;
352
John Fastabendb929d862014-09-12 20:09:49 -0700353 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 rsvp_delete_filter(tp, f);
359
360 /* Strip tree */
361
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000362 for (i = 0; i <= 16; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 if (s->ht[i])
364 return 0;
365
366 /* OK, session has no flows */
John Fastabendb929d862014-09-12 20:09:49 -0700367 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 return 0;
374 }
375 }
376
377 return 0;
378 }
379 }
380 return 0;
381}
382
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000383static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384{
John Fastabendb929d862014-09-12 20:09:49 -0700385 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 int i = 0xFFFF;
387
388 while (i-- > 0) {
389 u32 h;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000390
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 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
400static int tunnel_bts(struct rsvp_head *data)
401{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000402 int n = data->tgenerator >> 5;
403 u32 b = 1 << (data->tgenerator & 0x1F);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900404
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000405 if (data->tmap[n] & b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 return 0;
407 data->tmap[n] |= b;
408 return 1;
409}
410
411static void tunnel_recycle(struct rsvp_head *data)
412{
John Fastabendb929d862014-09-12 20:09:49 -0700413 struct rsvp_session __rcu **sht = data->ht;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 u32 tmap[256/32];
415 int h1, h2;
416
417 memset(tmap, 0, sizeof(tmap));
418
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000419 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 struct rsvp_session *s;
John Fastabendb929d862014-09-12 20:09:49 -0700421 for (s = rtnl_dereference(sht[h1]); s;
422 s = rtnl_dereference(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000423 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 struct rsvp_filter *f;
425
John Fastabendb929d862014-09-12 20:09:49 -0700426 for (f = rtnl_dereference(s->ht[h2]); f;
427 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 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
440static u32 gen_tunnel(struct rsvp_head *data)
441{
442 int i, k;
443
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000444 for (k = 0; k < 2; k++) {
445 for (i = 255; i > 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 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
Patrick McHardy6fa8c012008-01-23 20:36:12 -0800456static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
457 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
458 [TCA_RSVP_DST] = { .type = NLA_BINARY,
459 .len = RSVP_DST_LEN * sizeof(u32) },
460 [TCA_RSVP_SRC] = { .type = NLA_BINARY,
461 .len = RSVP_DST_LEN * sizeof(u32) },
462 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
463};
464
Benjamin LaHaisec1b52732013-01-14 05:15:39 +0000465static int rsvp_change(struct net *net, struct sk_buff *in_skb,
Eric W. Biedermanaf4c6642012-05-25 13:42:45 -0600466 struct tcf_proto *tp, unsigned long base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 u32 handle,
Patrick McHardyadd93b62008-01-22 22:11:33 -0800468 struct nlattr **tca,
Cong Wang2f7ef2f2014-04-25 13:54:06 -0700469 unsigned long *arg, bool ovr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
John Fastabendb929d862014-09-12 20:09:49 -0700471 struct rsvp_head *data = rtnl_dereference(tp->root);
472 struct rsvp_filter *f, *nfp;
473 struct rsvp_filter __rcu **fp;
474 struct rsvp_session *nsp, *s;
475 struct rsvp_session __rcu **sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 struct tc_rsvp_pinfo *pinfo = NULL;
Igor Maravić27e95a82011-08-30 03:12:55 +0000477 struct nlattr *opt = tca[TCA_OPTIONS];
Patrick McHardyadd93b62008-01-22 22:11:33 -0800478 struct nlattr *tb[TCA_RSVP_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 struct tcf_exts e;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000480 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800481 __be32 *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 int err;
483
484 if (opt == NULL)
485 return handle ? -EINVAL : 0;
486
Patrick McHardy6fa8c012008-01-23 20:36:12 -0800487 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy);
Patrick McHardycee63722008-01-23 20:33:32 -0800488 if (err < 0)
489 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
WANG Congb9a24bb2016-08-19 12:36:54 -0700491 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 if (err < 0)
493 return err;
WANG Congb9a24bb2016-08-19 12:36:54 -0700494 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
495 if (err < 0)
496 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000498 f = (struct rsvp_filter *)*arg;
499 if (f) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 /* Node exists: adjust only classid */
John Fastabend53dfd502014-09-26 10:02:50 -0700501 struct rsvp_filter *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 if (f->handle != handle && handle)
504 goto errout2;
John Fastabend53dfd502014-09-26 10:02:50 -0700505
506 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
507 if (!n) {
508 err = -ENOMEM;
509 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
511
WANG Congb9a24bb2016-08-19 12:36:54 -0700512 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
513 if (err < 0) {
514 kfree(n);
515 goto errout2;
516 }
John Fastabend53dfd502014-09-26 10:02:50 -0700517
518 if (tb[TCA_RSVP_CLASSID]) {
519 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
520 tcf_bind_filter(tp, &n->res, base);
521 }
522
523 tcf_exts_change(tp, &n->exts, &e);
524 rsvp_replace(tp, n, handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 return 0;
526 }
527
528 /* Now more serious part... */
529 err = -EINVAL;
530 if (handle)
531 goto errout2;
Igor Maravić27e95a82011-08-30 03:12:55 +0000532 if (tb[TCA_RSVP_DST] == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 goto errout2;
534
535 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700536 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 if (f == NULL)
538 goto errout2;
539
WANG Congb9a24bb2016-08-19 12:36:54 -0700540 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
541 if (err < 0)
542 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 h2 = 16;
Igor Maravić27e95a82011-08-30 03:12:55 +0000544 if (tb[TCA_RSVP_SRC]) {
545 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 h2 = hash_src(f->src);
547 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000548 if (tb[TCA_RSVP_PINFO]) {
549 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 f->spi = pinfo->spi;
551 f->tunnelhdr = pinfo->tunnelhdr;
552 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000553 if (tb[TCA_RSVP_CLASSID])
554 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Igor Maravić27e95a82011-08-30 03:12:55 +0000556 dst = nla_data(tb[TCA_RSVP_DST]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
558
559 err = -ENOMEM;
560 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
561 goto errout;
562
563 if (f->tunnelhdr) {
564 err = -EINVAL;
565 if (f->res.classid > 255)
566 goto errout;
567
568 err = -ENOMEM;
569 if (f->res.classid == 0 &&
570 (f->res.classid = gen_tunnel(data)) == 0)
571 goto errout;
572 }
573
John Fastabendb929d862014-09-12 20:09:49 -0700574 for (sp = &data->ht[h1];
575 (s = rtnl_dereference(*sp)) != NULL;
576 sp = &s->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
578 pinfo && pinfo->protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800579 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800581 dst[0] == s->dst[0] &&
582 dst[1] == s->dst[1] &&
583 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800585 pinfo->tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
587insert:
588 /* OK, we found appropriate session */
589
590 fp = &s->ht[h2];
591
592 f->sess = s;
593 if (f->tunnelhdr == 0)
594 tcf_bind_filter(tp, &f->res, base);
595
596 tcf_exts_change(tp, &f->exts, &e);
597
John Fastabendb929d862014-09-12 20:09:49 -0700598 fp = &s->ht[h2];
599 for (nfp = rtnl_dereference(*fp); nfp;
600 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
601 __u32 mask = nfp->spi.mask & f->spi.mask;
602
603 if (mask != f->spi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 break;
John Fastabendb929d862014-09-12 20:09:49 -0700605 }
606 RCU_INIT_POINTER(f->next, nfp);
607 rcu_assign_pointer(*fp, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 *arg = (unsigned long)f;
610 return 0;
611 }
612 }
613
614 /* No session found. Create new one. */
615
616 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700617 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 if (s == NULL)
619 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 memcpy(s->dst, dst, sizeof(s->dst));
621
622 if (pinfo) {
623 s->dpi = pinfo->dpi;
624 s->protocol = pinfo->protocol;
625 s->tunnelid = pinfo->tunnelid;
626 }
John Fastabendb929d862014-09-12 20:09:49 -0700627 sp = &data->ht[h1];
628 for (nsp = rtnl_dereference(*sp); nsp;
629 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
630 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 break;
632 }
John Fastabendb929d862014-09-12 20:09:49 -0700633 RCU_INIT_POINTER(s->next, nsp);
634 rcu_assign_pointer(*sp, s);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 goto insert;
637
638errout:
WANG Congb9a24bb2016-08-19 12:36:54 -0700639 tcf_exts_destroy(&f->exts);
Jesper Juhla51482b2005-11-08 09:41:34 -0800640 kfree(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641errout2:
WANG Cong18d02642014-09-25 10:26:37 -0700642 tcf_exts_destroy(&e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return err;
644}
645
646static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
647{
John Fastabendb929d862014-09-12 20:09:49 -0700648 struct rsvp_head *head = rtnl_dereference(tp->root);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000649 unsigned int h, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651 if (arg->stop)
652 return;
653
654 for (h = 0; h < 256; h++) {
655 struct rsvp_session *s;
656
John Fastabendb929d862014-09-12 20:09:49 -0700657 for (s = rtnl_dereference(head->ht[h]); s;
658 s = rtnl_dereference(s->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 for (h1 = 0; h1 <= 16; h1++) {
660 struct rsvp_filter *f;
661
John Fastabendb929d862014-09-12 20:09:49 -0700662 for (f = rtnl_dereference(s->ht[h1]); f;
663 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (arg->count < arg->skip) {
665 arg->count++;
666 continue;
667 }
668 if (arg->fn(tp, (unsigned long)f, arg) < 0) {
669 arg->stop = 1;
670 return;
671 }
672 arg->count++;
673 }
674 }
675 }
676 }
677}
678
WANG Cong832d1d52014-01-09 16:14:01 -0800679static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 struct sk_buff *skb, struct tcmsg *t)
681{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000682 struct rsvp_filter *f = (struct rsvp_filter *)fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 struct rsvp_session *s;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800684 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 struct tc_rsvp_pinfo pinfo;
686
687 if (f == NULL)
688 return skb->len;
689 s = f->sess;
690
691 t->tcm_handle = f->handle;
692
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800693 nest = nla_nest_start(skb, TCA_OPTIONS);
694 if (nest == NULL)
695 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
David S. Miller1b34ec42012-03-29 05:11:39 -0400697 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
698 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 pinfo.dpi = s->dpi;
700 pinfo.spi = f->spi;
701 pinfo.protocol = s->protocol;
702 pinfo.tunnelid = s->tunnelid;
703 pinfo.tunnelhdr = f->tunnelhdr;
Patrick McHardy8a470772005-06-28 12:56:45 -0700704 pinfo.pad = 0;
David S. Miller1b34ec42012-03-29 05:11:39 -0400705 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
706 goto nla_put_failure;
707 if (f->res.classid &&
708 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
709 goto nla_put_failure;
710 if (((f->handle >> 8) & 0xFF) != 16 &&
711 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
712 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
WANG Cong5da57f42013-12-15 20:15:07 -0800714 if (tcf_exts_dump(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800715 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800717 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
WANG Cong5da57f42013-12-15 20:15:07 -0800719 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800720 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 return skb->len;
722
Patrick McHardyadd93b62008-01-22 22:11:33 -0800723nla_put_failure:
Jiri Pirko6ea3b442014-12-09 22:23:29 +0100724 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return -1;
726}
727
Igor Maravić27e95a82011-08-30 03:12:55 +0000728static struct tcf_proto_ops RSVP_OPS __read_mostly = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 .kind = RSVP_ID,
730 .classify = rsvp_classify,
731 .init = rsvp_init,
732 .destroy = rsvp_destroy,
733 .get = rsvp_get,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 .change = rsvp_change,
735 .delete = rsvp_delete,
736 .walk = rsvp_walk,
737 .dump = rsvp_dump,
738 .owner = THIS_MODULE,
739};
740
741static int __init init_rsvp(void)
742{
743 return register_tcf_proto_ops(&RSVP_OPS);
744}
745
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900746static void __exit exit_rsvp(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747{
748 unregister_tcf_proto_ops(&RSVP_OPS);
749}
750
751module_init(init_rsvp)
752module_exit(exit_rsvp)