blob: 5cc0df690cff6b98a43089e36b62a0d1284369f5 [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;
Cong Wangd4f84a42017-10-26 18:24:38 -0700100 union {
101 struct work_struct work;
102 struct rcu_head rcu;
103 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104};
105
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000106static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000108 unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 h ^= h>>16;
111 h ^= h>>8;
112 return (h ^ protocol ^ tunnelid) & 0xFF;
113}
114
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000115static inline unsigned int hash_src(__be32 *src)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000117 unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 h ^= h>>16;
120 h ^= h>>8;
121 h ^= h>>4;
122 return h & 0xF;
123}
124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125#define RSVP_APPLY_RESULT() \
126{ \
127 int r = tcf_exts_exec(skb, &f->exts, res); \
128 if (r < 0) \
129 continue; \
130 else if (r > 0) \
131 return r; \
132}
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900133
Eric Dumazetdc7f9f62011-07-05 23:25:42 +0000134static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 struct tcf_result *res)
136{
John Fastabendb929d862014-09-12 20:09:49 -0700137 struct rsvp_head *head = rcu_dereference_bh(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 struct rsvp_session *s;
139 struct rsvp_filter *f;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000140 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800141 __be32 *dst, *src;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 u8 protocol;
143 u8 tunnelid = 0;
144 u8 *xprt;
145#if RSVP_DST_LEN == 4
Changli Gao12dc96d2010-08-04 04:55:40 +0000146 struct ipv6hdr *nhptr;
147
148 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
149 return -1;
150 nhptr = ipv6_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151#else
Changli Gao12dc96d2010-08-04 04:55:40 +0000152 struct iphdr *nhptr;
153
154 if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
155 return -1;
156 nhptr = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158restart:
159
160#if RSVP_DST_LEN == 4
161 src = &nhptr->saddr.s6_addr32[0];
162 dst = &nhptr->daddr.s6_addr32[0];
163 protocol = nhptr->nexthdr;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000164 xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165#else
166 src = &nhptr->saddr;
167 dst = &nhptr->daddr;
168 protocol = nhptr->protocol;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000169 xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
Paul Gortmaker56f8a752011-06-21 20:33:34 -0700170 if (ip_is_fragment(nhptr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 return -1;
172#endif
173
174 h1 = hash_dst(dst, protocol, tunnelid);
175 h2 = hash_src(src);
176
John Fastabendb929d862014-09-12 20:09:49 -0700177 for (s = rcu_dereference_bh(head->ht[h1]); s;
178 s = rcu_dereference_bh(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000179 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800181 !(s->dpi.mask &
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000182 (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800184 dst[0] == s->dst[0] &&
185 dst[1] == s->dst[1] &&
186 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800188 tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
John Fastabendb929d862014-09-12 20:09:49 -0700190 for (f = rcu_dereference_bh(s->ht[h2]); f;
191 f = rcu_dereference_bh(f->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000192 if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
193 !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800195 &&
196 src[0] == f->src[0] &&
197 src[1] == f->src[1] &&
198 src[2] == f->src[2]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199#endif
200 ) {
201 *res = f->res;
202 RSVP_APPLY_RESULT();
203
204matched:
205 if (f->tunnelhdr == 0)
206 return 0;
207
208 tunnelid = f->res.classid;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000209 nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 goto restart;
211 }
212 }
213
214 /* And wildcard bucket... */
John Fastabendb929d862014-09-12 20:09:49 -0700215 for (f = rcu_dereference_bh(s->ht[16]); f;
216 f = rcu_dereference_bh(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 *res = f->res;
218 RSVP_APPLY_RESULT();
219 goto matched;
220 }
221 return -1;
222 }
223 }
224 return -1;
225}
226
John Fastabend53dfd502014-09-26 10:02:50 -0700227static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
228{
229 struct rsvp_head *head = rtnl_dereference(tp->root);
230 struct rsvp_session *s;
231 struct rsvp_filter __rcu **ins;
232 struct rsvp_filter *pins;
233 unsigned int h1 = h & 0xFF;
234 unsigned int h2 = (h >> 8) & 0xFF;
235
236 for (s = rtnl_dereference(head->ht[h1]); s;
237 s = rtnl_dereference(s->next)) {
238 for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
239 ins = &pins->next, pins = rtnl_dereference(*ins)) {
240 if (pins->handle == h) {
241 RCU_INIT_POINTER(n->next, pins->next);
242 rcu_assign_pointer(*ins, n);
243 return;
244 }
245 }
246 }
247
248 /* Something went wrong if we are trying to replace a non-existant
249 * node. Mind as well halt instead of silently failing.
250 */
251 BUG_ON(1);
252}
253
WANG Cong8113c092017-08-04 21:31:43 -0700254static void *rsvp_get(struct tcf_proto *tp, u32 handle)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
John Fastabendb929d862014-09-12 20:09:49 -0700256 struct rsvp_head *head = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 struct rsvp_session *s;
258 struct rsvp_filter *f;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000259 unsigned int h1 = handle & 0xFF;
260 unsigned int h2 = (handle >> 8) & 0xFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 if (h2 > 16)
WANG Cong8113c092017-08-04 21:31:43 -0700263 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
John Fastabendb929d862014-09-12 20:09:49 -0700265 for (s = rtnl_dereference(head->ht[h1]); s;
266 s = rtnl_dereference(s->next)) {
267 for (f = rtnl_dereference(s->ht[h2]); f;
268 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 if (f->handle == handle)
WANG Cong8113c092017-08-04 21:31:43 -0700270 return f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 }
272 }
WANG Cong8113c092017-08-04 21:31:43 -0700273 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276static int rsvp_init(struct tcf_proto *tp)
277{
278 struct rsvp_head *data;
279
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700280 data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 if (data) {
John Fastabendb929d862014-09-12 20:09:49 -0700282 rcu_assign_pointer(tp->root, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 return 0;
284 }
285 return -ENOBUFS;
286}
287
Cong Wang96585062017-11-06 13:47:28 -0800288static void __rsvp_delete_filter(struct rsvp_filter *f)
289{
290 tcf_exts_destroy(&f->exts);
291 tcf_exts_put_net(&f->exts);
292 kfree(f);
293}
294
Cong Wangd4f84a42017-10-26 18:24:38 -0700295static void rsvp_delete_filter_work(struct work_struct *work)
296{
297 struct rsvp_filter *f = container_of(work, struct rsvp_filter, work);
298
299 rtnl_lock();
Cong Wang96585062017-11-06 13:47:28 -0800300 __rsvp_delete_filter(f);
Cong Wangd4f84a42017-10-26 18:24:38 -0700301 rtnl_unlock();
302}
303
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700304static void rsvp_delete_filter_rcu(struct rcu_head *head)
305{
306 struct rsvp_filter *f = container_of(head, struct rsvp_filter, rcu);
307
Cong Wangd4f84a42017-10-26 18:24:38 -0700308 INIT_WORK(&f->work, rsvp_delete_filter_work);
309 tcf_queue_work(&f->work);
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700310}
311
312static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
314 tcf_unbind_filter(tp, &f->res);
Alexei Starovoitov9e528d82015-08-25 20:06:34 -0700315 /* all classifiers are required to call tcf_exts_destroy() after rcu
316 * grace period, since converted-to-rcu actions are relying on that
317 * in cleanup() callback
318 */
Cong Wang96585062017-11-06 13:47:28 -0800319 if (tcf_exts_get_net(&f->exts))
320 call_rcu(&f->rcu, rsvp_delete_filter_rcu);
321 else
322 __rsvp_delete_filter(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323}
324
WANG Cong763dbf62017-04-19 14:21:21 -0700325static void rsvp_destroy(struct tcf_proto *tp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
John Fastabendb929d862014-09-12 20:09:49 -0700327 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 int h1, h2;
329
330 if (data == NULL)
WANG Cong763dbf62017-04-19 14:21:21 -0700331 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000333 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 struct rsvp_session *s;
335
John Fastabendb929d862014-09-12 20:09:49 -0700336 while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
337 RCU_INIT_POINTER(data->ht[h1], s->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000339 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 struct rsvp_filter *f;
341
John Fastabendb929d862014-09-12 20:09:49 -0700342 while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
343 rcu_assign_pointer(s->ht[h2], f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 rsvp_delete_filter(tp, f);
345 }
346 }
John Fastabendb929d862014-09-12 20:09:49 -0700347 kfree_rcu(s, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 }
349 }
John Fastabendb929d862014-09-12 20:09:49 -0700350 kfree_rcu(data, rcu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Alexander Aring571acf22018-01-18 11:20:53 -0500353static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
354 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
John Fastabendb929d862014-09-12 20:09:49 -0700356 struct rsvp_head *head = rtnl_dereference(tp->root);
WANG Cong8113c092017-08-04 21:31:43 -0700357 struct rsvp_filter *nfp, *f = arg;
John Fastabendb929d862014-09-12 20:09:49 -0700358 struct rsvp_filter __rcu **fp;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000359 unsigned int h = f->handle;
John Fastabendb929d862014-09-12 20:09:49 -0700360 struct rsvp_session __rcu **sp;
361 struct rsvp_session *nsp, *s = f->sess;
WANG Cong763dbf62017-04-19 14:21:21 -0700362 int i, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
John Fastabendb929d862014-09-12 20:09:49 -0700364 fp = &s->ht[(h >> 8) & 0xFF];
365 for (nfp = rtnl_dereference(*fp); nfp;
366 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
367 if (nfp == f) {
368 RCU_INIT_POINTER(*fp, f->next);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 rsvp_delete_filter(tp, f);
370
371 /* Strip tree */
372
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000373 for (i = 0; i <= 16; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 if (s->ht[i])
WANG Cong763dbf62017-04-19 14:21:21 -0700375 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 /* OK, session has no flows */
John Fastabendb929d862014-09-12 20:09:49 -0700378 sp = &head->ht[h & 0xFF];
379 for (nsp = rtnl_dereference(*sp); nsp;
380 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
381 if (nsp == s) {
382 RCU_INIT_POINTER(*sp, s->next);
383 kfree_rcu(s, rcu);
WANG Cong763dbf62017-04-19 14:21:21 -0700384 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 }
386 }
387
WANG Cong763dbf62017-04-19 14:21:21 -0700388 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 }
390 }
WANG Cong763dbf62017-04-19 14:21:21 -0700391
392out:
393 *last = true;
394 for (h1 = 0; h1 < 256; h1++) {
395 if (rcu_access_pointer(head->ht[h1])) {
396 *last = false;
397 break;
398 }
399 }
400
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 return 0;
402}
403
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000404static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
John Fastabendb929d862014-09-12 20:09:49 -0700406 struct rsvp_head *data = rtnl_dereference(tp->root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 int i = 0xFFFF;
408
409 while (i-- > 0) {
410 u32 h;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000411
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 if ((data->hgenerator += 0x10000) == 0)
413 data->hgenerator = 0x10000;
414 h = data->hgenerator|salt;
Josh Hunt230cfd22017-09-10 15:48:50 -0400415 if (!rsvp_get(tp, h))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return h;
417 }
418 return 0;
419}
420
421static int tunnel_bts(struct rsvp_head *data)
422{
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000423 int n = data->tgenerator >> 5;
424 u32 b = 1 << (data->tgenerator & 0x1F);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900425
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000426 if (data->tmap[n] & b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 return 0;
428 data->tmap[n] |= b;
429 return 1;
430}
431
432static void tunnel_recycle(struct rsvp_head *data)
433{
John Fastabendb929d862014-09-12 20:09:49 -0700434 struct rsvp_session __rcu **sht = data->ht;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 u32 tmap[256/32];
436 int h1, h2;
437
438 memset(tmap, 0, sizeof(tmap));
439
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000440 for (h1 = 0; h1 < 256; h1++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 struct rsvp_session *s;
John Fastabendb929d862014-09-12 20:09:49 -0700442 for (s = rtnl_dereference(sht[h1]); s;
443 s = rtnl_dereference(s->next)) {
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000444 for (h2 = 0; h2 <= 16; h2++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 struct rsvp_filter *f;
446
John Fastabendb929d862014-09-12 20:09:49 -0700447 for (f = rtnl_dereference(s->ht[h2]); f;
448 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (f->tunnelhdr == 0)
450 continue;
451 data->tgenerator = f->res.classid;
452 tunnel_bts(data);
453 }
454 }
455 }
456 }
457
458 memcpy(data->tmap, tmap, sizeof(tmap));
459}
460
461static u32 gen_tunnel(struct rsvp_head *data)
462{
463 int i, k;
464
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000465 for (k = 0; k < 2; k++) {
466 for (i = 255; i > 0; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 if (++data->tgenerator == 0)
468 data->tgenerator = 1;
469 if (tunnel_bts(data))
470 return data->tgenerator;
471 }
472 tunnel_recycle(data);
473 }
474 return 0;
475}
476
Patrick McHardy6fa8c012008-01-23 20:36:12 -0800477static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
478 [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
479 [TCA_RSVP_DST] = { .type = NLA_BINARY,
480 .len = RSVP_DST_LEN * sizeof(u32) },
481 [TCA_RSVP_SRC] = { .type = NLA_BINARY,
482 .len = RSVP_DST_LEN * sizeof(u32) },
483 [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
484};
485
Benjamin LaHaisec1b52732013-01-14 05:15:39 +0000486static int rsvp_change(struct net *net, struct sk_buff *in_skb,
Eric W. Biedermanaf4c6642012-05-25 13:42:45 -0600487 struct tcf_proto *tp, unsigned long base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 u32 handle,
Patrick McHardyadd93b62008-01-22 22:11:33 -0800489 struct nlattr **tca,
Alexander Aring7306db32018-01-18 11:20:51 -0500490 void **arg, bool ovr, struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491{
John Fastabendb929d862014-09-12 20:09:49 -0700492 struct rsvp_head *data = rtnl_dereference(tp->root);
493 struct rsvp_filter *f, *nfp;
494 struct rsvp_filter __rcu **fp;
495 struct rsvp_session *nsp, *s;
496 struct rsvp_session __rcu **sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 struct tc_rsvp_pinfo *pinfo = NULL;
Igor Maravić27e95a82011-08-30 03:12:55 +0000498 struct nlattr *opt = tca[TCA_OPTIONS];
Patrick McHardyadd93b62008-01-22 22:11:33 -0800499 struct nlattr *tb[TCA_RSVP_MAX + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 struct tcf_exts e;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000501 unsigned int h1, h2;
Al Viro66c6f522006-11-20 18:07:51 -0800502 __be32 *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 int err;
504
505 if (opt == NULL)
506 return handle ? -EINVAL : 0;
507
Johannes Bergfceb6432017-04-12 14:34:07 +0200508 err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
Patrick McHardycee63722008-01-23 20:33:32 -0800509 if (err < 0)
510 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
WANG Congb9a24bb2016-08-19 12:36:54 -0700512 err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if (err < 0)
514 return err;
Alexander Aring50a56192018-01-18 11:20:52 -0500515 err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, extack);
WANG Congb9a24bb2016-08-19 12:36:54 -0700516 if (err < 0)
517 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
WANG Cong8113c092017-08-04 21:31:43 -0700519 f = *arg;
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000520 if (f) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 /* Node exists: adjust only classid */
John Fastabend53dfd502014-09-26 10:02:50 -0700522 struct rsvp_filter *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
524 if (f->handle != handle && handle)
525 goto errout2;
John Fastabend53dfd502014-09-26 10:02:50 -0700526
527 n = kmemdup(f, sizeof(*f), GFP_KERNEL);
528 if (!n) {
529 err = -ENOMEM;
530 goto errout2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 }
532
WANG Congb9a24bb2016-08-19 12:36:54 -0700533 err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
534 if (err < 0) {
535 kfree(n);
536 goto errout2;
537 }
John Fastabend53dfd502014-09-26 10:02:50 -0700538
539 if (tb[TCA_RSVP_CLASSID]) {
540 n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
541 tcf_bind_filter(tp, &n->res, base);
542 }
543
Jiri Pirko9b0d4442017-08-04 14:29:15 +0200544 tcf_exts_change(&n->exts, &e);
John Fastabend53dfd502014-09-26 10:02:50 -0700545 rsvp_replace(tp, n, handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 return 0;
547 }
548
549 /* Now more serious part... */
550 err = -EINVAL;
551 if (handle)
552 goto errout2;
Igor Maravić27e95a82011-08-30 03:12:55 +0000553 if (tb[TCA_RSVP_DST] == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 goto errout2;
555
556 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700557 f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if (f == NULL)
559 goto errout2;
560
WANG Congb9a24bb2016-08-19 12:36:54 -0700561 err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
562 if (err < 0)
563 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 h2 = 16;
Igor Maravić27e95a82011-08-30 03:12:55 +0000565 if (tb[TCA_RSVP_SRC]) {
566 memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 h2 = hash_src(f->src);
568 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000569 if (tb[TCA_RSVP_PINFO]) {
570 pinfo = nla_data(tb[TCA_RSVP_PINFO]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 f->spi = pinfo->spi;
572 f->tunnelhdr = pinfo->tunnelhdr;
573 }
Igor Maravić27e95a82011-08-30 03:12:55 +0000574 if (tb[TCA_RSVP_CLASSID])
575 f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Igor Maravić27e95a82011-08-30 03:12:55 +0000577 dst = nla_data(tb[TCA_RSVP_DST]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
579
580 err = -ENOMEM;
581 if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
582 goto errout;
583
584 if (f->tunnelhdr) {
585 err = -EINVAL;
586 if (f->res.classid > 255)
587 goto errout;
588
589 err = -ENOMEM;
590 if (f->res.classid == 0 &&
591 (f->res.classid = gen_tunnel(data)) == 0)
592 goto errout;
593 }
594
John Fastabendb929d862014-09-12 20:09:49 -0700595 for (sp = &data->ht[h1];
596 (s = rtnl_dereference(*sp)) != NULL;
597 sp = &s->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
599 pinfo && pinfo->protocol == s->protocol &&
Joe Perchesf64f9e72009-11-29 16:55:45 -0800600 memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601#if RSVP_DST_LEN == 4
Joe Perchesf64f9e72009-11-29 16:55:45 -0800602 dst[0] == s->dst[0] &&
603 dst[1] == s->dst[1] &&
604 dst[2] == s->dst[2] &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605#endif
Joe Perchesf64f9e72009-11-29 16:55:45 -0800606 pinfo->tunnelid == s->tunnelid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608insert:
609 /* OK, we found appropriate session */
610
611 fp = &s->ht[h2];
612
613 f->sess = s;
614 if (f->tunnelhdr == 0)
615 tcf_bind_filter(tp, &f->res, base);
616
Jiri Pirko9b0d4442017-08-04 14:29:15 +0200617 tcf_exts_change(&f->exts, &e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
John Fastabendb929d862014-09-12 20:09:49 -0700619 fp = &s->ht[h2];
620 for (nfp = rtnl_dereference(*fp); nfp;
621 fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
622 __u32 mask = nfp->spi.mask & f->spi.mask;
623
624 if (mask != f->spi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 break;
John Fastabendb929d862014-09-12 20:09:49 -0700626 }
627 RCU_INIT_POINTER(f->next, nfp);
628 rcu_assign_pointer(*fp, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
WANG Cong8113c092017-08-04 21:31:43 -0700630 *arg = f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return 0;
632 }
633 }
634
635 /* No session found. Create new one. */
636
637 err = -ENOBUFS;
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700638 s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 if (s == NULL)
640 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 memcpy(s->dst, dst, sizeof(s->dst));
642
643 if (pinfo) {
644 s->dpi = pinfo->dpi;
645 s->protocol = pinfo->protocol;
646 s->tunnelid = pinfo->tunnelid;
647 }
John Fastabendb929d862014-09-12 20:09:49 -0700648 sp = &data->ht[h1];
649 for (nsp = rtnl_dereference(*sp); nsp;
650 sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
651 if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 break;
653 }
John Fastabendb929d862014-09-12 20:09:49 -0700654 RCU_INIT_POINTER(s->next, nsp);
655 rcu_assign_pointer(*sp, s);
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 goto insert;
658
659errout:
WANG Congb9a24bb2016-08-19 12:36:54 -0700660 tcf_exts_destroy(&f->exts);
Jesper Juhla51482b2005-11-08 09:41:34 -0800661 kfree(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662errout2:
WANG Cong18d02642014-09-25 10:26:37 -0700663 tcf_exts_destroy(&e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 return err;
665}
666
667static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
668{
John Fastabendb929d862014-09-12 20:09:49 -0700669 struct rsvp_head *head = rtnl_dereference(tp->root);
Eric Dumazetcc7ec452011-01-19 19:26:56 +0000670 unsigned int h, h1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 if (arg->stop)
673 return;
674
675 for (h = 0; h < 256; h++) {
676 struct rsvp_session *s;
677
John Fastabendb929d862014-09-12 20:09:49 -0700678 for (s = rtnl_dereference(head->ht[h]); s;
679 s = rtnl_dereference(s->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 for (h1 = 0; h1 <= 16; h1++) {
681 struct rsvp_filter *f;
682
John Fastabendb929d862014-09-12 20:09:49 -0700683 for (f = rtnl_dereference(s->ht[h1]); f;
684 f = rtnl_dereference(f->next)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 if (arg->count < arg->skip) {
686 arg->count++;
687 continue;
688 }
WANG Cong8113c092017-08-04 21:31:43 -0700689 if (arg->fn(tp, f, arg) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 arg->stop = 1;
691 return;
692 }
693 arg->count++;
694 }
695 }
696 }
697 }
698}
699
WANG Cong8113c092017-08-04 21:31:43 -0700700static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 struct sk_buff *skb, struct tcmsg *t)
702{
WANG Cong8113c092017-08-04 21:31:43 -0700703 struct rsvp_filter *f = fh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 struct rsvp_session *s;
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800705 struct nlattr *nest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 struct tc_rsvp_pinfo pinfo;
707
708 if (f == NULL)
709 return skb->len;
710 s = f->sess;
711
712 t->tcm_handle = f->handle;
713
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800714 nest = nla_nest_start(skb, TCA_OPTIONS);
715 if (nest == NULL)
716 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
David S. Miller1b34ec42012-03-29 05:11:39 -0400718 if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
719 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 pinfo.dpi = s->dpi;
721 pinfo.spi = f->spi;
722 pinfo.protocol = s->protocol;
723 pinfo.tunnelid = s->tunnelid;
724 pinfo.tunnelhdr = f->tunnelhdr;
Patrick McHardy8a470772005-06-28 12:56:45 -0700725 pinfo.pad = 0;
David S. Miller1b34ec42012-03-29 05:11:39 -0400726 if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
727 goto nla_put_failure;
728 if (f->res.classid &&
729 nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
730 goto nla_put_failure;
731 if (((f->handle >> 8) & 0xFF) != 16 &&
732 nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
733 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
WANG Cong5da57f42013-12-15 20:15:07 -0800735 if (tcf_exts_dump(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800736 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Patrick McHardy4b3550ef2008-01-23 20:34:11 -0800738 nla_nest_end(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
WANG Cong5da57f42013-12-15 20:15:07 -0800740 if (tcf_exts_dump_stats(skb, &f->exts) < 0)
Patrick McHardyadd93b62008-01-22 22:11:33 -0800741 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 return skb->len;
743
Patrick McHardyadd93b62008-01-22 22:11:33 -0800744nla_put_failure:
Jiri Pirko6ea3b442014-12-09 22:23:29 +0100745 nla_nest_cancel(skb, nest);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 return -1;
747}
748
Cong Wang07d79fc2017-08-30 14:30:36 -0700749static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl)
750{
751 struct rsvp_filter *f = fh;
752
753 if (f && f->res.classid == classid)
754 f->res.class = cl;
755}
756
Igor Maravić27e95a82011-08-30 03:12:55 +0000757static struct tcf_proto_ops RSVP_OPS __read_mostly = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 .kind = RSVP_ID,
759 .classify = rsvp_classify,
760 .init = rsvp_init,
761 .destroy = rsvp_destroy,
762 .get = rsvp_get,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 .change = rsvp_change,
764 .delete = rsvp_delete,
765 .walk = rsvp_walk,
766 .dump = rsvp_dump,
Cong Wang07d79fc2017-08-30 14:30:36 -0700767 .bind_class = rsvp_bind_class,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 .owner = THIS_MODULE,
769};
770
771static int __init init_rsvp(void)
772{
773 return register_tcf_proto_ops(&RSVP_OPS);
774}
775
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900776static void __exit exit_rsvp(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777{
778 unregister_tcf_proto_ops(&RSVP_OPS);
779}
780
781module_init(init_rsvp)
782module_exit(exit_rsvp)