Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * net/sched/ematch.c Extended Match API
4 : : *
5 : : * Authors: Thomas Graf <tgraf@suug.ch>
6 : : *
7 : : * ==========================================================================
8 : : *
9 : : * An extended match (ematch) is a small classification tool not worth
10 : : * writing a full classifier for. Ematches can be interconnected to form
11 : : * a logic expression and get attached to classifiers to extend their
12 : : * functionatlity.
13 : : *
14 : : * The userspace part transforms the logic expressions into an array
15 : : * consisting of multiple sequences of interconnected ematches separated
16 : : * by markers. Precedence is implemented by a special ematch kind
17 : : * referencing a sequence beyond the marker of the current sequence
18 : : * causing the current position in the sequence to be pushed onto a stack
19 : : * to allow the current position to be overwritten by the position referenced
20 : : * in the special ematch. Matching continues in the new sequence until a
21 : : * marker is reached causing the position to be restored from the stack.
22 : : *
23 : : * Example:
24 : : * A AND (B1 OR B2) AND C AND D
25 : : *
26 : : * ------->-PUSH-------
27 : : * -->-- / -->-- \ -->--
28 : : * / \ / / \ \ / \
29 : : * +-------+-------+-------+-------+-------+--------+
30 : : * | A AND | B AND | C AND | D END | B1 OR | B2 END |
31 : : * +-------+-------+-------+-------+-------+--------+
32 : : * \ /
33 : : * --------<-POP---------
34 : : *
35 : : * where B is a virtual ematch referencing to sequence starting with B1.
36 : : *
37 : : * ==========================================================================
38 : : *
39 : : * How to write an ematch in 60 seconds
40 : : * ------------------------------------
41 : : *
42 : : * 1) Provide a matcher function:
43 : : * static int my_match(struct sk_buff *skb, struct tcf_ematch *m,
44 : : * struct tcf_pkt_info *info)
45 : : * {
46 : : * struct mydata *d = (struct mydata *) m->data;
47 : : *
48 : : * if (...matching goes here...)
49 : : * return 1;
50 : : * else
51 : : * return 0;
52 : : * }
53 : : *
54 : : * 2) Fill out a struct tcf_ematch_ops:
55 : : * static struct tcf_ematch_ops my_ops = {
56 : : * .kind = unique id,
57 : : * .datalen = sizeof(struct mydata),
58 : : * .match = my_match,
59 : : * .owner = THIS_MODULE,
60 : : * };
61 : : *
62 : : * 3) Register/Unregister your ematch:
63 : : * static int __init init_my_ematch(void)
64 : : * {
65 : : * return tcf_em_register(&my_ops);
66 : : * }
67 : : *
68 : : * static void __exit exit_my_ematch(void)
69 : : * {
70 : : * tcf_em_unregister(&my_ops);
71 : : * }
72 : : *
73 : : * module_init(init_my_ematch);
74 : : * module_exit(exit_my_ematch);
75 : : *
76 : : * 4) By now you should have two more seconds left, barely enough to
77 : : * open up a beer to watch the compilation going.
78 : : */
79 : :
80 : : #include <linux/module.h>
81 : : #include <linux/slab.h>
82 : : #include <linux/types.h>
83 : : #include <linux/kernel.h>
84 : : #include <linux/errno.h>
85 : : #include <linux/rtnetlink.h>
86 : : #include <linux/skbuff.h>
87 : : #include <net/pkt_cls.h>
88 : :
89 : : static LIST_HEAD(ematch_ops);
90 : : static DEFINE_RWLOCK(ematch_mod_lock);
91 : :
92 : 0 : static struct tcf_ematch_ops *tcf_em_lookup(u16 kind)
93 : : {
94 : : struct tcf_ematch_ops *e = NULL;
95 : :
96 : 0 : read_lock(&ematch_mod_lock);
97 [ # # ]: 0 : list_for_each_entry(e, &ematch_ops, link) {
98 [ # # ]: 0 : if (kind == e->kind) {
99 [ # # ]: 0 : if (!try_module_get(e->owner))
100 : : e = NULL;
101 : : read_unlock(&ematch_mod_lock);
102 : 0 : return e;
103 : : }
104 : : }
105 : : read_unlock(&ematch_mod_lock);
106 : :
107 : 0 : return NULL;
108 : : }
109 : :
110 : : /**
111 : : * tcf_em_register - register an extended match
112 : : *
113 : : * @ops: ematch operations lookup table
114 : : *
115 : : * This function must be called by ematches to announce their presence.
116 : : * The given @ops must have kind set to a unique identifier and the
117 : : * callback match() must be implemented. All other callbacks are optional
118 : : * and a fallback implementation is used instead.
119 : : *
120 : : * Returns -EEXISTS if an ematch of the same kind has already registered.
121 : : */
122 : 0 : int tcf_em_register(struct tcf_ematch_ops *ops)
123 : : {
124 : : int err = -EEXIST;
125 : : struct tcf_ematch_ops *e;
126 : :
127 [ # # ]: 0 : if (ops->match == NULL)
128 : : return -EINVAL;
129 : :
130 : 0 : write_lock(&ematch_mod_lock);
131 [ # # ]: 0 : list_for_each_entry(e, &ematch_ops, link)
132 [ # # ]: 0 : if (ops->kind == e->kind)
133 : : goto errout;
134 : :
135 : 0 : list_add_tail(&ops->link, &ematch_ops);
136 : : err = 0;
137 : : errout:
138 : : write_unlock(&ematch_mod_lock);
139 : 0 : return err;
140 : : }
141 : : EXPORT_SYMBOL(tcf_em_register);
142 : :
143 : : /**
144 : : * tcf_em_unregister - unregster and extended match
145 : : *
146 : : * @ops: ematch operations lookup table
147 : : *
148 : : * This function must be called by ematches to announce their disappearance
149 : : * for examples when the module gets unloaded. The @ops parameter must be
150 : : * the same as the one used for registration.
151 : : *
152 : : * Returns -ENOENT if no matching ematch was found.
153 : : */
154 : 0 : void tcf_em_unregister(struct tcf_ematch_ops *ops)
155 : : {
156 : 0 : write_lock(&ematch_mod_lock);
157 : : list_del(&ops->link);
158 : : write_unlock(&ematch_mod_lock);
159 : 0 : }
160 : : EXPORT_SYMBOL(tcf_em_unregister);
161 : :
162 : : static inline struct tcf_ematch *tcf_em_get_match(struct tcf_ematch_tree *tree,
163 : : int index)
164 : : {
165 : 0 : return &tree->matches[index];
166 : : }
167 : :
168 : :
169 : 0 : static int tcf_em_validate(struct tcf_proto *tp,
170 : : struct tcf_ematch_tree_hdr *tree_hdr,
171 : : struct tcf_ematch *em, struct nlattr *nla, int idx)
172 : : {
173 : : int err = -EINVAL;
174 : : struct tcf_ematch_hdr *em_hdr = nla_data(nla);
175 : 0 : int data_len = nla_len(nla) - sizeof(*em_hdr);
176 : 0 : void *data = (void *) em_hdr + sizeof(*em_hdr);
177 : 0 : struct net *net = tp->chain->block->net;
178 : :
179 [ # # ]: 0 : if (!TCF_EM_REL_VALID(em_hdr->flags))
180 : : goto errout;
181 : :
182 [ # # ]: 0 : if (em_hdr->kind == TCF_EM_CONTAINER) {
183 : : /* Special ematch called "container", carries an index
184 : : * referencing an external ematch sequence.
185 : : */
186 : : u32 ref;
187 : :
188 [ # # ]: 0 : if (data_len < sizeof(ref))
189 : : goto errout;
190 : 0 : ref = *(u32 *) data;
191 : :
192 [ # # ]: 0 : if (ref >= tree_hdr->nmatches)
193 : : goto errout;
194 : :
195 : : /* We do not allow backward jumps to avoid loops and jumps
196 : : * to our own position are of course illegal.
197 : : */
198 [ # # ]: 0 : if (ref <= idx)
199 : : goto errout;
200 : :
201 : :
202 : 0 : em->data = ref;
203 : : } else {
204 : : /* Note: This lookup will increase the module refcnt
205 : : * of the ematch module referenced. In case of a failure,
206 : : * a destroy function is called by the underlying layer
207 : : * which automatically releases the reference again, therefore
208 : : * the module MUST not be given back under any circumstances
209 : : * here. Be aware, the destroy function assumes that the
210 : : * module is held if the ops field is non zero.
211 : : */
212 : 0 : em->ops = tcf_em_lookup(em_hdr->kind);
213 : :
214 [ # # ]: 0 : if (em->ops == NULL) {
215 : : err = -ENOENT;
216 : : #ifdef CONFIG_MODULES
217 : 0 : __rtnl_unlock();
218 : 0 : request_module("ematch-kind-%u", em_hdr->kind);
219 : 0 : rtnl_lock();
220 : 0 : em->ops = tcf_em_lookup(em_hdr->kind);
221 [ # # ]: 0 : if (em->ops) {
222 : : /* We dropped the RTNL mutex in order to
223 : : * perform the module load. Tell the caller
224 : : * to replay the request.
225 : : */
226 : 0 : module_put(em->ops->owner);
227 : 0 : em->ops = NULL;
228 : : err = -EAGAIN;
229 : : }
230 : : #endif
231 : : goto errout;
232 : : }
233 : :
234 : : /* ematch module provides expected length of data, so we
235 : : * can do a basic sanity check.
236 : : */
237 [ # # # # ]: 0 : if (em->ops->datalen && data_len < em->ops->datalen)
238 : : goto errout;
239 : :
240 [ # # ]: 0 : if (em->ops->change) {
241 : : err = -EINVAL;
242 [ # # ]: 0 : if (em_hdr->flags & TCF_EM_SIMPLE)
243 : : goto errout;
244 : 0 : err = em->ops->change(net, data, data_len, em);
245 [ # # ]: 0 : if (err < 0)
246 : : goto errout;
247 [ # # ]: 0 : } else if (data_len > 0) {
248 : : /* ematch module doesn't provide an own change
249 : : * procedure and expects us to allocate and copy
250 : : * the ematch data.
251 : : *
252 : : * TCF_EM_SIMPLE may be specified stating that the
253 : : * data only consists of a u32 integer and the module
254 : : * does not expected a memory reference but rather
255 : : * the value carried.
256 : : */
257 [ # # ]: 0 : if (em_hdr->flags & TCF_EM_SIMPLE) {
258 [ # # ]: 0 : if (data_len < sizeof(u32))
259 : : goto errout;
260 : 0 : em->data = *(u32 *) data;
261 : : } else {
262 : 0 : void *v = kmemdup(data, data_len, GFP_KERNEL);
263 [ # # ]: 0 : if (v == NULL) {
264 : : err = -ENOBUFS;
265 : : goto errout;
266 : : }
267 : 0 : em->data = (unsigned long) v;
268 : : }
269 : 0 : em->datalen = data_len;
270 : : }
271 : : }
272 : :
273 : 0 : em->matchid = em_hdr->matchid;
274 : 0 : em->flags = em_hdr->flags;
275 : 0 : em->net = net;
276 : :
277 : : err = 0;
278 : : errout:
279 : 0 : return err;
280 : : }
281 : :
282 : : static const struct nla_policy em_policy[TCA_EMATCH_TREE_MAX + 1] = {
283 : : [TCA_EMATCH_TREE_HDR] = { .len = sizeof(struct tcf_ematch_tree_hdr) },
284 : : [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
285 : : };
286 : :
287 : : /**
288 : : * tcf_em_tree_validate - validate ematch config TLV and build ematch tree
289 : : *
290 : : * @tp: classifier kind handle
291 : : * @nla: ematch tree configuration TLV
292 : : * @tree: destination ematch tree variable to store the resulting
293 : : * ematch tree.
294 : : *
295 : : * This function validates the given configuration TLV @nla and builds an
296 : : * ematch tree in @tree. The resulting tree must later be copied into
297 : : * the private classifier data using tcf_em_tree_change(). You MUST NOT
298 : : * provide the ematch tree variable of the private classifier data directly,
299 : : * the changes would not be locked properly.
300 : : *
301 : : * Returns a negative error code if the configuration TLV contains errors.
302 : : */
303 : 0 : int tcf_em_tree_validate(struct tcf_proto *tp, struct nlattr *nla,
304 : : struct tcf_ematch_tree *tree)
305 : : {
306 : : int idx, list_len, matches_len, err;
307 : : struct nlattr *tb[TCA_EMATCH_TREE_MAX + 1];
308 : : struct nlattr *rt_match, *rt_hdr, *rt_list;
309 : : struct tcf_ematch_tree_hdr *tree_hdr;
310 : : struct tcf_ematch *em;
311 : :
312 : 0 : memset(tree, 0, sizeof(*tree));
313 [ # # ]: 0 : if (!nla)
314 : : return 0;
315 : :
316 : : err = nla_parse_nested_deprecated(tb, TCA_EMATCH_TREE_MAX, nla,
317 : : em_policy, NULL);
318 [ # # ]: 0 : if (err < 0)
319 : : goto errout;
320 : :
321 : : err = -EINVAL;
322 : 0 : rt_hdr = tb[TCA_EMATCH_TREE_HDR];
323 : 0 : rt_list = tb[TCA_EMATCH_TREE_LIST];
324 : :
325 [ # # ]: 0 : if (rt_hdr == NULL || rt_list == NULL)
326 : : goto errout;
327 : :
328 : : tree_hdr = nla_data(rt_hdr);
329 : 0 : memcpy(&tree->hdr, tree_hdr, sizeof(*tree_hdr));
330 : :
331 : : rt_match = nla_data(rt_list);
332 : : list_len = nla_len(rt_list);
333 : 0 : matches_len = tree_hdr->nmatches * sizeof(*em);
334 : :
335 : 0 : tree->matches = kzalloc(matches_len, GFP_KERNEL);
336 [ # # ]: 0 : if (tree->matches == NULL)
337 : : goto errout;
338 : :
339 : : /* We do not use nla_parse_nested here because the maximum
340 : : * number of attributes is unknown. This saves us the allocation
341 : : * for a tb buffer which would serve no purpose at all.
342 : : *
343 : : * The array of rt attributes is parsed in the order as they are
344 : : * provided, their type must be incremental from 1 to n. Even
345 : : * if it does not serve any real purpose, a failure of sticking
346 : : * to this policy will result in parsing failure.
347 : : */
348 [ # # ]: 0 : for (idx = 0; nla_ok(rt_match, list_len); idx++) {
349 : : err = -EINVAL;
350 : :
351 [ # # ]: 0 : if (rt_match->nla_type != (idx + 1))
352 : : goto errout_abort;
353 : :
354 [ # # ]: 0 : if (idx >= tree_hdr->nmatches)
355 : : goto errout_abort;
356 : :
357 [ # # ]: 0 : if (nla_len(rt_match) < sizeof(struct tcf_ematch_hdr))
358 : : goto errout_abort;
359 : :
360 : : em = tcf_em_get_match(tree, idx);
361 : :
362 : 0 : err = tcf_em_validate(tp, tree_hdr, em, rt_match, idx);
363 [ # # ]: 0 : if (err < 0)
364 : : goto errout_abort;
365 : :
366 : : rt_match = nla_next(rt_match, &list_len);
367 : : }
368 : :
369 : : /* Check if the number of matches provided by userspace actually
370 : : * complies with the array of matches. The number was used for
371 : : * the validation of references and a mismatch could lead to
372 : : * undefined references during the matching process.
373 : : */
374 [ # # ]: 0 : if (idx != tree_hdr->nmatches) {
375 : : err = -EINVAL;
376 : : goto errout_abort;
377 : : }
378 : :
379 : : err = 0;
380 : : errout:
381 : 0 : return err;
382 : :
383 : : errout_abort:
384 : 0 : tcf_em_tree_destroy(tree);
385 : 0 : return err;
386 : : }
387 : : EXPORT_SYMBOL(tcf_em_tree_validate);
388 : :
389 : : /**
390 : : * tcf_em_tree_destroy - destroy an ematch tree
391 : : *
392 : : * @tp: classifier kind handle
393 : : * @tree: ematch tree to be deleted
394 : : *
395 : : * This functions destroys an ematch tree previously created by
396 : : * tcf_em_tree_validate()/tcf_em_tree_change(). You must ensure that
397 : : * the ematch tree is not in use before calling this function.
398 : : */
399 : 0 : void tcf_em_tree_destroy(struct tcf_ematch_tree *tree)
400 : : {
401 : : int i;
402 : :
403 [ # # ]: 0 : if (tree->matches == NULL)
404 : 0 : return;
405 : :
406 [ # # ]: 0 : for (i = 0; i < tree->hdr.nmatches; i++) {
407 : : struct tcf_ematch *em = tcf_em_get_match(tree, i);
408 : :
409 [ # # ]: 0 : if (em->ops) {
410 [ # # ]: 0 : if (em->ops->destroy)
411 : 0 : em->ops->destroy(em);
412 [ # # ]: 0 : else if (!tcf_em_is_simple(em))
413 : 0 : kfree((void *) em->data);
414 : 0 : module_put(em->ops->owner);
415 : : }
416 : : }
417 : :
418 : 0 : tree->hdr.nmatches = 0;
419 : 0 : kfree(tree->matches);
420 : 0 : tree->matches = NULL;
421 : : }
422 : : EXPORT_SYMBOL(tcf_em_tree_destroy);
423 : :
424 : : /**
425 : : * tcf_em_tree_dump - dump ematch tree into a rtnl message
426 : : *
427 : : * @skb: skb holding the rtnl message
428 : : * @t: ematch tree to be dumped
429 : : * @tlv: TLV type to be used to encapsulate the tree
430 : : *
431 : : * This function dumps a ematch tree into a rtnl message. It is valid to
432 : : * call this function while the ematch tree is in use.
433 : : *
434 : : * Returns -1 if the skb tailroom is insufficient.
435 : : */
436 : 0 : int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv)
437 : : {
438 : : int i;
439 : : u8 *tail;
440 : : struct nlattr *top_start;
441 : : struct nlattr *list_start;
442 : :
443 : : top_start = nla_nest_start_noflag(skb, tlv);
444 [ # # ]: 0 : if (top_start == NULL)
445 : : goto nla_put_failure;
446 : :
447 [ # # ]: 0 : if (nla_put(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr))
448 : : goto nla_put_failure;
449 : :
450 : : list_start = nla_nest_start_noflag(skb, TCA_EMATCH_TREE_LIST);
451 [ # # ]: 0 : if (list_start == NULL)
452 : : goto nla_put_failure;
453 : :
454 : : tail = skb_tail_pointer(skb);
455 [ # # ]: 0 : for (i = 0; i < tree->hdr.nmatches; i++) {
456 : : struct nlattr *match_start = (struct nlattr *)tail;
457 : : struct tcf_ematch *em = tcf_em_get_match(tree, i);
458 [ # # ]: 0 : struct tcf_ematch_hdr em_hdr = {
459 : 0 : .kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER,
460 : 0 : .matchid = em->matchid,
461 : 0 : .flags = em->flags
462 : : };
463 : :
464 [ # # ]: 0 : if (nla_put(skb, i + 1, sizeof(em_hdr), &em_hdr))
465 : : goto nla_put_failure;
466 : :
467 [ # # # # ]: 0 : if (em->ops && em->ops->dump) {
468 [ # # ]: 0 : if (em->ops->dump(skb, em) < 0)
469 : : goto nla_put_failure;
470 [ # # # # ]: 0 : } else if (tcf_em_is_container(em) || tcf_em_is_simple(em)) {
471 : 0 : u32 u = em->data;
472 : 0 : nla_put_nohdr(skb, sizeof(u), &u);
473 [ # # ]: 0 : } else if (em->datalen > 0)
474 : 0 : nla_put_nohdr(skb, em->datalen, (void *) em->data);
475 : :
476 : : tail = skb_tail_pointer(skb);
477 : 0 : match_start->nla_len = tail - (u8 *)match_start;
478 : : }
479 : :
480 : : nla_nest_end(skb, list_start);
481 : : nla_nest_end(skb, top_start);
482 : :
483 : 0 : return 0;
484 : :
485 : : nla_put_failure:
486 : : return -1;
487 : : }
488 : : EXPORT_SYMBOL(tcf_em_tree_dump);
489 : :
490 : : static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *em,
491 : : struct tcf_pkt_info *info)
492 : : {
493 : 0 : int r = em->ops->match(skb, em, info);
494 : :
495 [ # # ]: 0 : return tcf_em_is_inverted(em) ? !r : r;
496 : : }
497 : :
498 : : /* Do not use this function directly, use tcf_em_tree_match instead */
499 : 0 : int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *tree,
500 : : struct tcf_pkt_info *info)
501 : : {
502 : : int stackp = 0, match_idx = 0, res = 0;
503 : : struct tcf_ematch *cur_match;
504 : : int stack[CONFIG_NET_EMATCH_STACK];
505 : :
506 : : proceed:
507 [ # # ]: 0 : while (match_idx < tree->hdr.nmatches) {
508 : : cur_match = tcf_em_get_match(tree, match_idx);
509 : :
510 [ # # ]: 0 : if (tcf_em_is_container(cur_match)) {
511 [ # # ]: 0 : if (unlikely(stackp >= CONFIG_NET_EMATCH_STACK))
512 : : goto stack_overflow;
513 : :
514 : 0 : stack[stackp++] = match_idx;
515 : 0 : match_idx = cur_match->data;
516 : 0 : goto proceed;
517 : : }
518 : :
519 : : res = tcf_em_match(skb, cur_match, info);
520 : :
521 [ # # ]: 0 : if (tcf_em_early_end(cur_match, res))
522 : : break;
523 : :
524 : 0 : match_idx++;
525 : : }
526 : :
527 : : pop_stack:
528 [ # # ]: 0 : if (stackp > 0) {
529 : 0 : match_idx = stack[--stackp];
530 : : cur_match = tcf_em_get_match(tree, match_idx);
531 : :
532 [ # # ]: 0 : if (tcf_em_is_inverted(cur_match))
533 : 0 : res = !res;
534 : :
535 [ # # ]: 0 : if (tcf_em_early_end(cur_match, res)) {
536 : : goto pop_stack;
537 : : } else {
538 : 0 : match_idx++;
539 : 0 : goto proceed;
540 : : }
541 : : }
542 : :
543 : 0 : return res;
544 : :
545 : : stack_overflow:
546 [ # # ]: 0 : net_warn_ratelimited("tc ematch: local stack overflow, increase NET_EMATCH_STACK\n");
547 : : return -1;
548 : : }
549 : : EXPORT_SYMBOL(__tcf_em_tree_match);
|