blob: 0d9d077986992926a79af6236b8840181517679d [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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155restart:
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;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000161 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162#else
163 src = &nhptr->saddr;
164 dst = &nhptr->daddr;
165 protocol = nhptr->protocol;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000166 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
Paul Gortmaker56f8a752011-06-21 20:33:34 -0700167 if (ip_is_fragment(nhptr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return -1;
169#endif
170
171 h1 = hash_dst(dst, protocol, tunnelid);
172 h2 = hash_src(src);
173
John Fastabendb929d862014-09-12 20:09:49 -0700174 for (s = rcu_dereference_bh(head->ht[h1]); s;
175 s = rcu_dereference_bh(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000176 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800178 !(s->dpi.mask &
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000179 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800181 dst[0] == s->dst[0] &&
182 dst[1] == s->dst[1] &&
183 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800185 tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
John Fastabendb929d862014-09-12 20:09:49 -0700187 for (f = rcu_dereference_bh(s->ht[h2]); f;
188 f = rcu_dereference_bh(f->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000189 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
190 !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800192 &&
193 src[0] == f->src[0] &&
194 src[1] == f->src[1] &&
195 src[2] == f->src[2]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196#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;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000206 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 goto restart;
208 }
209 }
210
211 /* And wildcard bucket... */
John Fastabendb929d862014-09-12 20:09:49 -0700212 for (f = rcu_dereference_bh(s->ht[16]); f;
213 f = rcu_dereference_bh(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 *res = f->res;
215 RSVP_APPLY_RESULT();
216 goto matched;
217 }
218 return -1;
219 }
220 }
221 return -1;
222}
223
John Fastabend53dfd502014-09-26 10:02:50 -0700224static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
252{
John Fastabendb929d862014-09-12 20:09:49 -0700253 struct rsvp_head *head = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 struct rsvp_session *s;
255 struct rsvp_filter *f;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000256 unsigned int h1 = handle & 0xFF;
257 unsigned int h2 = (handle >> 8) & 0xFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
259 if (h2 > 16)
260 return 0;
261
John Fastabendb929d862014-09-12 20:09:49 -0700262 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)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 if (f->handle == handle)
267 return (unsigned long)f;
268 }
269 }
270 return 0;
271}
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273static int rsvp_init(struct tcf_proto *tp)
274{
275 struct rsvp_head *data;
276
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700277 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 if (data) {
John Fastabendb929d862014-09-12 20:09:49 -0700279 rcu_assign_pointer(tp->root, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 return 0;
281 }
282 return -ENOBUFS;
283}
284
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700285static void rsvp_delete_filter_rcu(struct rcu_head *head)
286{
287 struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu);
288
289 tcf_exts_destroy(&f->exts);
290 kfree(f);
291}
292
293static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
295 tcf_unbind_filter(tp, &f->res);
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700296 /* 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}
302
WANG Cong763dbf62017-04-19 14:21:21 -0700303static void rsvp_destroy(struct tcf_proto *tp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
John Fastabendb929d862014-09-12 20:09:49 -0700305 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 int h1, h2;
307
308 if (data == NULL)
WANG Cong763dbf62017-04-19 14:21:21 -0700309 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000311 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 struct rsvp_session *s;
313
John Fastabendb929d862014-09-12 20:09:49 -0700314 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
315 RCU_INIT_POINTER(data->ht[h1], s->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000317 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 struct rsvp_filter *f;
319
John Fastabendb929d862014-09-12 20:09:49 -0700320 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
321 rcu_assign_pointer(s->ht[h2], f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 rsvp_delete_filter(tp, f);
323 }
324 }
John Fastabendb929d862014-09-12 20:09:49 -0700325 kfree_rcu(s, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327 }
John Fastabendb929d862014-09-12 20:09:49 -0700328 kfree_rcu(data, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
WANG Cong763dbf62017-04-19 14:21:21 -0700331static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
John Fastabendb929d862014-09-12 20:09:49 -0700333 struct rsvp_head *head = rtnl_dereference(tp->root);
334 struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
335 struct rsvp_filter __rcu **fp;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000336 unsigned int h = f->handle;
John Fastabendb929d862014-09-12 20:09:49 -0700337 struct rsvp_session __rcu **sp;
338 struct rsvp_session *nsp, *s = f->sess;
WANG Cong763dbf62017-04-19 14:21:21 -0700339 int i, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
John Fastabendb929d862014-09-12 20:09:49 -0700341 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 rsvp_delete_filter(tp, f);
347
348 /* Strip tree */
349
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000350 for (i = 0; i <= 16; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 if (s->ht[i])
WANG Cong763dbf62017-04-19 14:21:21 -0700352 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 /* OK, session has no flows */
John Fastabendb929d862014-09-12 20:09:49 -0700355 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);
WANG Cong763dbf62017-04-19 14:21:21 -0700361 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363 }
364
WANG Cong763dbf62017-04-19 14:21:21 -0700365 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367 }
WANG Cong763dbf62017-04-19 14:21:21 -0700368
369out:
370 *last = true;
371 for (h1 = 0; h1 < 256; h1++) {
372 if (rcu_access_pointer(head->ht[h1])) {
373 *last = false;
374 break;
375 }
376 }
377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 return 0;
379}
380
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000381static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
John Fastabendb929d862014-09-12 20:09:49 -0700383 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 int i = 0xFFFF;
385
386 while (i-- > 0) {
387 u32 h;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000388
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 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{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000400 int n = data->tgenerator >> 5;
401 u32 b = 1 << (data->tgenerator & 0x1F);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900402
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000403 if (data->tmap[n] & b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 return 0;
405 data->tmap[n] |= b;
406 return 1;
407}
408
409static void tunnel_recycle(struct rsvp_head *data)
410{
John Fastabendb929d862014-09-12 20:09:49 -0700411 struct rsvp_session __rcu **sht = data->ht;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 u32 tmap[256/32];
413 int h1, h2;
414
415 memset(tmap, 0, sizeof(tmap));
416
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000417 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 struct rsvp_session *s;
John Fastabendb929d862014-09-12 20:09:49 -0700419 for (s = rtnl_dereference(sht[h1]); s;
420 s = rtnl_dereference(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000421 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 struct rsvp_filter *f;
423
John Fastabendb929d862014-09-12 20:09:49 -0700424 for (f = rtnl_dereference(s->ht[h2]); f;
425 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 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
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000442 for (k = 0; k < 2; k++) {
443 for (i = 255; i > 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 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
Patrick McHardy6fa8c012008-01-23 20:36:12 -0800454static 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
Benjamin LaHaisec1b52732013-01-14 05:15:39 +0000463static int rsvp_change(struct net *net, struct sk_buff *in_skb,
Eric W. Biedermanaf4c6642012-05-25 13:42:45 -0600464 struct tcf_proto *tp, unsigned long base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 u32 handle,
Patrick McHardyadd93b62008-01-22 22:11:33 -0800466 struct nlattr **tca,
Cong Wang2f7ef2f2014-04-25 13:54:06 -0700467 unsigned long *arg, bool ovr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
John Fastabendb929d862014-09-12 20:09:49 -0700469 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 struct tc_rsvp_pinfo *pinfo = NULL;
Igor Maravić27e95a82011-08-30 03:12:55 +0000475 struct nlattr *opt = tca[TCA_OPTIONS];
Patrick McHardyadd93b62008-01-22 22:11:33 -0800476 struct nlattr *tb[TCA_RSVP_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 struct tcf_exts e;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000478 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800479 __be32 *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 int err;
481
482 if (opt == NULL)
483 return handle ? -EINVAL : 0;
484
Johannes Bergfceb6432017-04-12 14:34:07 +0200485 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
Patrick McHardycee63722008-01-23 20:33:32 -0800486 if (err < 0)
487 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
WANG Congb9a24bb2016-08-19 12:36:54 -0700489 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 if (err < 0)
491 return err;
WANG Congb9a24bb2016-08-19 12:36:54 -0700492 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
493 if (err < 0)
494 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000496 f = (struct rsvp_filter *)*arg;
497 if (f) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 /* Node exists: adjust only classid */
John Fastabend53dfd502014-09-26 10:02:50 -0700499 struct rsvp_filter *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
501 if (f->handle != handle && handle)
502 goto errout2;
John Fastabend53dfd502014-09-26 10:02:50 -0700503
504 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
505 if (!n) {
506 err = -ENOMEM;
507 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 }
509
WANG Congb9a24bb2016-08-19 12:36:54 -0700510 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
511 if (err < 0) {
512 kfree(n);
513 goto errout2;
514 }
John Fastabend53dfd502014-09-26 10:02:50 -0700515
516 if (tb[TCA_RSVP_CLASSID]) {
517 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
518 tcf_bind_filter(tp, &n->res, base);
519 }
520
521 tcf_exts_change(tp, &n->exts, &e);
522 rsvp_replace(tp, n, handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 return 0;
524 }
525
526 /* Now more serious part... */
527 err = -EINVAL;
528 if (handle)
529 goto errout2;
Igor Maravić27e95a82011-08-30 03:12:55 +0000530 if (tb[TCA_RSVP_DST] == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 goto errout2;
532
533 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700534 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (f == NULL)
536 goto errout2;
537
WANG Congb9a24bb2016-08-19 12:36:54 -0700538 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
539 if (err < 0)
540 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 h2 = 16;
Igor Maravić27e95a82011-08-30 03:12:55 +0000542 if (tb[TCA_RSVP_SRC]) {
543 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 h2 = hash_src(f->src);
545 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000546 if (tb[TCA_RSVP_PINFO]) {
547 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 f->spi = pinfo->spi;
549 f->tunnelhdr = pinfo->tunnelhdr;
550 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000551 if (tb[TCA_RSVP_CLASSID])
552 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Igor Maravić27e95a82011-08-30 03:12:55 +0000554 dst = nla_data(tb[TCA_RSVP_DST]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 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
John Fastabendb929d862014-09-12 20:09:49 -0700572 for (sp = &data->ht[h1];
573 (s = rtnl_dereference(*sp)) != NULL;
574 sp = &s->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
576 pinfo && pinfo->protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800577 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800579 dst[0] == s->dst[0] &&
580 dst[1] == s->dst[1] &&
581 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800583 pinfo->tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
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
John Fastabendb929d862014-09-12 20:09:49 -0700596 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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 break;
John Fastabendb929d862014-09-12 20:09:49 -0700603 }
604 RCU_INIT_POINTER(f->next, nfp);
605 rcu_assign_pointer(*fp, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 *arg = (unsigned long)f;
608 return 0;
609 }
610 }
611
612 /* No session found. Create new one. */
613
614 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700615 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 if (s == NULL)
617 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 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 }
John Fastabendb929d862014-09-12 20:09:49 -0700625 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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 break;
630 }
John Fastabendb929d862014-09-12 20:09:49 -0700631 RCU_INIT_POINTER(s->next, nsp);
632 rcu_assign_pointer(*sp, s);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 goto insert;
635
636errout:
WANG Congb9a24bb2016-08-19 12:36:54 -0700637 tcf_exts_destroy(&f->exts);
Jesper Juhla51482b2005-11-08 09:41:34 -0800638 kfree(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639errout2:
WANG Cong18d02642014-09-25 10:26:37 -0700640 tcf_exts_destroy(&e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 return err;
642}
643
644static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
645{
John Fastabendb929d862014-09-12 20:09:49 -0700646 struct rsvp_head *head = rtnl_dereference(tp->root);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000647 unsigned int h, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649 if (arg->stop)
650 return;
651
652 for (h = 0; h < 256; h++) {
653 struct rsvp_session *s;
654
John Fastabendb929d862014-09-12 20:09:49 -0700655 for (s = rtnl_dereference(head->ht[h]); s;
656 s = rtnl_dereference(s->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 for (h1 = 0; h1 <= 16; h1++) {
658 struct rsvp_filter *f;
659
John Fastabendb929d862014-09-12 20:09:49 -0700660 for (f = rtnl_dereference(s->ht[h1]); f;
661 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 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
WANG Cong832d1d52014-01-09 16:14:01 -0800677static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 struct sk_buff *skb, struct tcmsg *t)
679{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000680 struct rsvp_filter *f = (struct rsvp_filter *)fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 struct rsvp_session *s;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800682 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 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
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800691 nest = nla_nest_start(skb, TCA_OPTIONS);
692 if (nest == NULL)
693 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
David S. Miller1b34ec42012-03-29 05:11:39 -0400695 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
696 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 pinfo.dpi = s->dpi;
698 pinfo.spi = f->spi;
699 pinfo.protocol = s->protocol;
700 pinfo.tunnelid = s->tunnelid;
701 pinfo.tunnelhdr = f->tunnelhdr;
Patrick McHardy8a470772005-06-28 12:56:45 -0700702 pinfo.pad = 0;
David S. Miller1b34ec42012-03-29 05:11:39 -0400703 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
WANG Cong5da57f42013-12-15 20:15:07 -0800712 if (tcf_exts_dump(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800713 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800715 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
WANG Cong5da57f42013-12-15 20:15:07 -0800717 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800718 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 return skb->len;
720
Patrick McHardyadd93b62008-01-22 22:11:33 -0800721nla_put_failure:
Jiri Pirko6ea3b442014-12-09 22:23:29 +0100722 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return -1;
724}
725
Igor Maravić27e95a82011-08-30 03:12:55 +0000726static struct tcf_proto_ops RSVP_OPS __read_mostly = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 .kind = RSVP_ID,
728 .classify = rsvp_classify,
729 .init = rsvp_init,
730 .destroy = rsvp_destroy,
731 .get = rsvp_get,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 .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
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900744static void __exit exit_rsvp(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
746 unregister_tcf_proto_ops(&RSVP_OPS);
747}
748
749module_init(init_rsvp)
750module_exit(exit_rsvp)