blob: 18a94701617869d674183136b9e69449a0473b6c [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
WANG Cong763dbf62017-04-19 14:21:21 -0700305static void rsvp_destroy(struct tcf_proto *tp)
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)
WANG Cong763dbf62017-04-19 14:21:21 -0700311 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
John Fastabendb929d862014-09-12 20:09:49 -0700313 RCU_INIT_POINTER(tp->root, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000315 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 struct rsvp_session *s;
317
John Fastabendb929d862014-09-12 20:09:49 -0700318 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
319 RCU_INIT_POINTER(data->ht[h1], s->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000321 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 struct rsvp_filter *f;
323
John Fastabendb929d862014-09-12 20:09:49 -0700324 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
325 rcu_assign_pointer(s->ht[h2], f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 rsvp_delete_filter(tp, f);
327 }
328 }
John Fastabendb929d862014-09-12 20:09:49 -0700329 kfree_rcu(s, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 }
331 }
John Fastabendb929d862014-09-12 20:09:49 -0700332 kfree_rcu(data, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
WANG Cong763dbf62017-04-19 14:21:21 -0700335static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
John Fastabendb929d862014-09-12 20:09:49 -0700337 struct rsvp_head *head = rtnl_dereference(tp->root);
338 struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
339 struct rsvp_filter __rcu **fp;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000340 unsigned int h = f->handle;
John Fastabendb929d862014-09-12 20:09:49 -0700341 struct rsvp_session __rcu **sp;
342 struct rsvp_session *nsp, *s = f->sess;
WANG Cong763dbf62017-04-19 14:21:21 -0700343 int i, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
John Fastabendb929d862014-09-12 20:09:49 -0700345 fp = &s->ht[(h >> 8) & 0xFF];
346 for (nfp = rtnl_dereference(*fp); nfp;
347 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
348 if (nfp == f) {
349 RCU_INIT_POINTER(*fp, f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 rsvp_delete_filter(tp, f);
351
352 /* Strip tree */
353
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000354 for (i = 0; i <= 16; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 if (s->ht[i])
WANG Cong763dbf62017-04-19 14:21:21 -0700356 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 /* OK, session has no flows */
John Fastabendb929d862014-09-12 20:09:49 -0700359 sp = &head->ht[h & 0xFF];
360 for (nsp = rtnl_dereference(*sp); nsp;
361 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
362 if (nsp == s) {
363 RCU_INIT_POINTER(*sp, s->next);
364 kfree_rcu(s, rcu);
WANG Cong763dbf62017-04-19 14:21:21 -0700365 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367 }
368
WANG Cong763dbf62017-04-19 14:21:21 -0700369 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
371 }
WANG Cong763dbf62017-04-19 14:21:21 -0700372
373out:
374 *last = true;
375 for (h1 = 0; h1 < 256; h1++) {
376 if (rcu_access_pointer(head->ht[h1])) {
377 *last = false;
378 break;
379 }
380 }
381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return 0;
383}
384
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000385static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
John Fastabendb929d862014-09-12 20:09:49 -0700387 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 int i = 0xFFFF;
389
390 while (i-- > 0) {
391 u32 h;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000392
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if ((data->hgenerator += 0x10000) == 0)
394 data->hgenerator = 0x10000;
395 h = data->hgenerator|salt;
396 if (rsvp_get(tp, h) == 0)
397 return h;
398 }
399 return 0;
400}
401
402static int tunnel_bts(struct rsvp_head *data)
403{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000404 int n = data->tgenerator >> 5;
405 u32 b = 1 << (data->tgenerator & 0x1F);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900406
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000407 if (data->tmap[n] & b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return 0;
409 data->tmap[n] |= b;
410 return 1;
411}
412
413static void tunnel_recycle(struct rsvp_head *data)
414{
John Fastabendb929d862014-09-12 20:09:49 -0700415 struct rsvp_session __rcu **sht = data->ht;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 u32 tmap[256/32];
417 int h1, h2;
418
419 memset(tmap, 0, sizeof(tmap));
420
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000421 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 struct rsvp_session *s;
John Fastabendb929d862014-09-12 20:09:49 -0700423 for (s = rtnl_dereference(sht[h1]); s;
424 s = rtnl_dereference(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000425 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 struct rsvp_filter *f;
427
John Fastabendb929d862014-09-12 20:09:49 -0700428 for (f = rtnl_dereference(s->ht[h2]); f;
429 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 if (f->tunnelhdr == 0)
431 continue;
432 data->tgenerator = f->res.classid;
433 tunnel_bts(data);
434 }
435 }
436 }
437 }
438
439 memcpy(data->tmap, tmap, sizeof(tmap));
440}
441
442static u32 gen_tunnel(struct rsvp_head *data)
443{
444 int i, k;
445
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000446 for (k = 0; k < 2; k++) {
447 for (i = 255; i > 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (++data->tgenerator == 0)
449 data->tgenerator = 1;
450 if (tunnel_bts(data))
451 return data->tgenerator;
452 }
453 tunnel_recycle(data);
454 }
455 return 0;
456}
457
Patrick McHardy6fa8c012008-01-23 20:36:12 -0800458static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
459 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
460 [TCA_RSVP_DST] = { .type = NLA_BINARY,
461 .len = RSVP_DST_LEN * sizeof(u32) },
462 [TCA_RSVP_SRC] = { .type = NLA_BINARY,
463 .len = RSVP_DST_LEN * sizeof(u32) },
464 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
465};
466
Benjamin LaHaisec1b52732013-01-14 05:15:39 +0000467static int rsvp_change(struct net *net, struct sk_buff *in_skb,
Eric W. Biedermanaf4c6642012-05-25 13:42:45 -0600468 struct tcf_proto *tp, unsigned long base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 u32 handle,
Patrick McHardyadd93b62008-01-22 22:11:33 -0800470 struct nlattr **tca,
Cong Wang2f7ef2f2014-04-25 13:54:06 -0700471 unsigned long *arg, bool ovr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
John Fastabendb929d862014-09-12 20:09:49 -0700473 struct rsvp_head *data = rtnl_dereference(tp->root);
474 struct rsvp_filter *f, *nfp;
475 struct rsvp_filter __rcu **fp;
476 struct rsvp_session *nsp, *s;
477 struct rsvp_session __rcu **sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 struct tc_rsvp_pinfo *pinfo = NULL;
Igor Maravić27e95a82011-08-30 03:12:55 +0000479 struct nlattr *opt = tca[TCA_OPTIONS];
Patrick McHardyadd93b62008-01-22 22:11:33 -0800480 struct nlattr *tb[TCA_RSVP_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 struct tcf_exts e;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000482 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800483 __be32 *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 int err;
485
486 if (opt == NULL)
487 return handle ? -EINVAL : 0;
488
Johannes Bergfceb6432017-04-12 14:34:07 +0200489 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
Patrick McHardycee63722008-01-23 20:33:32 -0800490 if (err < 0)
491 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
WANG Congb9a24bb2016-08-19 12:36:54 -0700493 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 if (err < 0)
495 return err;
WANG Congb9a24bb2016-08-19 12:36:54 -0700496 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
497 if (err < 0)
498 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000500 f = (struct rsvp_filter *)*arg;
501 if (f) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 /* Node exists: adjust only classid */
John Fastabend53dfd502014-09-26 10:02:50 -0700503 struct rsvp_filter *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 if (f->handle != handle && handle)
506 goto errout2;
John Fastabend53dfd502014-09-26 10:02:50 -0700507
508 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
509 if (!n) {
510 err = -ENOMEM;
511 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
513
WANG Congb9a24bb2016-08-19 12:36:54 -0700514 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
515 if (err < 0) {
516 kfree(n);
517 goto errout2;
518 }
John Fastabend53dfd502014-09-26 10:02:50 -0700519
520 if (tb[TCA_RSVP_CLASSID]) {
521 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
522 tcf_bind_filter(tp, &n->res, base);
523 }
524
525 tcf_exts_change(tp, &n->exts, &e);
526 rsvp_replace(tp, n, handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 return 0;
528 }
529
530 /* Now more serious part... */
531 err = -EINVAL;
532 if (handle)
533 goto errout2;
Igor Maravić27e95a82011-08-30 03:12:55 +0000534 if (tb[TCA_RSVP_DST] == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 goto errout2;
536
537 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700538 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 if (f == NULL)
540 goto errout2;
541
WANG Congb9a24bb2016-08-19 12:36:54 -0700542 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
543 if (err < 0)
544 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 h2 = 16;
Igor Maravić27e95a82011-08-30 03:12:55 +0000546 if (tb[TCA_RSVP_SRC]) {
547 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 h2 = hash_src(f->src);
549 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000550 if (tb[TCA_RSVP_PINFO]) {
551 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 f->spi = pinfo->spi;
553 f->tunnelhdr = pinfo->tunnelhdr;
554 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000555 if (tb[TCA_RSVP_CLASSID])
556 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Igor Maravić27e95a82011-08-30 03:12:55 +0000558 dst = nla_data(tb[TCA_RSVP_DST]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
560
561 err = -ENOMEM;
562 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
563 goto errout;
564
565 if (f->tunnelhdr) {
566 err = -EINVAL;
567 if (f->res.classid > 255)
568 goto errout;
569
570 err = -ENOMEM;
571 if (f->res.classid == 0 &&
572 (f->res.classid = gen_tunnel(data)) == 0)
573 goto errout;
574 }
575
John Fastabendb929d862014-09-12 20:09:49 -0700576 for (sp = &data->ht[h1];
577 (s = rtnl_dereference(*sp)) != NULL;
578 sp = &s->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
580 pinfo && pinfo->protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800581 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800583 dst[0] == s->dst[0] &&
584 dst[1] == s->dst[1] &&
585 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800587 pinfo->tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589insert:
590 /* OK, we found appropriate session */
591
592 fp = &s->ht[h2];
593
594 f->sess = s;
595 if (f->tunnelhdr == 0)
596 tcf_bind_filter(tp, &f->res, base);
597
598 tcf_exts_change(tp, &f->exts, &e);
599
John Fastabendb929d862014-09-12 20:09:49 -0700600 fp = &s->ht[h2];
601 for (nfp = rtnl_dereference(*fp); nfp;
602 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
603 __u32 mask = nfp->spi.mask & f->spi.mask;
604
605 if (mask != f->spi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 break;
John Fastabendb929d862014-09-12 20:09:49 -0700607 }
608 RCU_INIT_POINTER(f->next, nfp);
609 rcu_assign_pointer(*fp, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 *arg = (unsigned long)f;
612 return 0;
613 }
614 }
615
616 /* No session found. Create new one. */
617
618 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700619 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 if (s == NULL)
621 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 memcpy(s->dst, dst, sizeof(s->dst));
623
624 if (pinfo) {
625 s->dpi = pinfo->dpi;
626 s->protocol = pinfo->protocol;
627 s->tunnelid = pinfo->tunnelid;
628 }
John Fastabendb929d862014-09-12 20:09:49 -0700629 sp = &data->ht[h1];
630 for (nsp = rtnl_dereference(*sp); nsp;
631 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
632 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 break;
634 }
John Fastabendb929d862014-09-12 20:09:49 -0700635 RCU_INIT_POINTER(s->next, nsp);
636 rcu_assign_pointer(*sp, s);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 goto insert;
639
640errout:
WANG Congb9a24bb2016-08-19 12:36:54 -0700641 tcf_exts_destroy(&f->exts);
Jesper Juhla51482b2005-11-08 09:41:34 -0800642 kfree(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643errout2:
WANG Cong18d02642014-09-25 10:26:37 -0700644 tcf_exts_destroy(&e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 return err;
646}
647
648static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
649{
John Fastabendb929d862014-09-12 20:09:49 -0700650 struct rsvp_head *head = rtnl_dereference(tp->root);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000651 unsigned int h, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 if (arg->stop)
654 return;
655
656 for (h = 0; h < 256; h++) {
657 struct rsvp_session *s;
658
John Fastabendb929d862014-09-12 20:09:49 -0700659 for (s = rtnl_dereference(head->ht[h]); s;
660 s = rtnl_dereference(s->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 for (h1 = 0; h1 <= 16; h1++) {
662 struct rsvp_filter *f;
663
John Fastabendb929d862014-09-12 20:09:49 -0700664 for (f = rtnl_dereference(s->ht[h1]); f;
665 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 if (arg->count < arg->skip) {
667 arg->count++;
668 continue;
669 }
670 if (arg->fn(tp, (unsigned long)f, arg) < 0) {
671 arg->stop = 1;
672 return;
673 }
674 arg->count++;
675 }
676 }
677 }
678 }
679}
680
WANG Cong832d1d52014-01-09 16:14:01 -0800681static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 struct sk_buff *skb, struct tcmsg *t)
683{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000684 struct rsvp_filter *f = (struct rsvp_filter *)fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 struct rsvp_session *s;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800686 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 struct tc_rsvp_pinfo pinfo;
688
689 if (f == NULL)
690 return skb->len;
691 s = f->sess;
692
693 t->tcm_handle = f->handle;
694
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800695 nest = nla_nest_start(skb, TCA_OPTIONS);
696 if (nest == NULL)
697 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
David S. Miller1b34ec42012-03-29 05:11:39 -0400699 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
700 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 pinfo.dpi = s->dpi;
702 pinfo.spi = f->spi;
703 pinfo.protocol = s->protocol;
704 pinfo.tunnelid = s->tunnelid;
705 pinfo.tunnelhdr = f->tunnelhdr;
Patrick McHardy8a470772005-06-28 12:56:45 -0700706 pinfo.pad = 0;
David S. Miller1b34ec42012-03-29 05:11:39 -0400707 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
708 goto nla_put_failure;
709 if (f->res.classid &&
710 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
711 goto nla_put_failure;
712 if (((f->handle >> 8) & 0xFF) != 16 &&
713 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
714 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
WANG Cong5da57f42013-12-15 20:15:07 -0800716 if (tcf_exts_dump(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800717 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800719 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
WANG Cong5da57f42013-12-15 20:15:07 -0800721 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800722 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return skb->len;
724
Patrick McHardyadd93b62008-01-22 22:11:33 -0800725nla_put_failure:
Jiri Pirko6ea3b442014-12-09 22:23:29 +0100726 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 return -1;
728}
729
Igor Maravić27e95a82011-08-30 03:12:55 +0000730static struct tcf_proto_ops RSVP_OPS __read_mostly = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 .kind = RSVP_ID,
732 .classify = rsvp_classify,
733 .init = rsvp_init,
734 .destroy = rsvp_destroy,
735 .get = rsvp_get,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 .change = rsvp_change,
737 .delete = rsvp_delete,
738 .walk = rsvp_walk,
739 .dump = rsvp_dump,
740 .owner = THIS_MODULE,
741};
742
743static int __init init_rsvp(void)
744{
745 return register_tcf_proto_ops(&RSVP_OPS);
746}
747
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900748static void __exit exit_rsvp(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
750 unregister_tcf_proto_ops(&RSVP_OPS);
751}
752
753module_init(init_rsvp)
754module_exit(exit_rsvp)