1 /*
2 * lib/route/cls/ematch.c Extended Matches
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation version 2.1
7 * of the License.
8 *
9 * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
10 */
11
12 /**
13 * @ingroup cls
14 * @defgroup ematch Extended Match
15 *
16 * @{
17 */
18
19 #include <netlink-private/netlink.h>
20 #include <netlink-private/tc.h>
21 #include <netlink/netlink.h>
22 #include <netlink/route/classifier.h>
23 #include <netlink/route/cls/ematch.h>
24 #include <netlink/route/cls/ematch/cmp.h>
25
26 #include "ematch_syntax.h"
27 #include "ematch_grammar.h"
28
29 /**
30 * @name Module API
31 * @{
32 */
33
34 static NL_LIST_HEAD(ematch_ops_list);
35
36 /**
37 * Register ematch module
38 * @arg ops Module operations.
39 *
40 * This function must be called by each ematch module at initialization
41 * time. It registers the calling module as available module.
42 *
43 * @return 0 on success or a negative error code.
44 */
rtnl_ematch_register(struct rtnl_ematch_ops * ops)45 int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
46 {
47 if (rtnl_ematch_lookup_ops(ops->eo_kind))
48 return -NLE_EXIST;
49
50 NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name);
51
52 nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
53
54 return 0;
55 }
56
57 /**
58 * Lookup ematch module by identification number.
59 * @arg kind Module kind.
60 *
61 * Searches the list of registered ematch modules for match and returns it.
62 *
63 * @return Module operations or NULL if not found.
64 */
rtnl_ematch_lookup_ops(int kind)65 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
66 {
67 struct rtnl_ematch_ops *ops;
68
69 nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
70 if (ops->eo_kind == kind)
71 return ops;
72
73 return NULL;
74 }
75
76 /**
77 * Lookup ematch module by name
78 * @arg name Name of ematch module.
79 *
80 * Searches the list of registered ematch modules for a match and returns it.
81 *
82 * @return Module operations or NULL if not fuond.
83 */
rtnl_ematch_lookup_ops_by_name(const char * name)84 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name)
85 {
86 struct rtnl_ematch_ops *ops;
87
88 nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
89 if (!strcasecmp(ops->eo_name, name))
90 return ops;
91
92 return NULL;
93 }
94
95 /** @} */
96
97 /**
98 * @name Match
99 */
100
101 /**
102 * Allocate ematch object.
103 *
104 * Allocates and initializes an ematch object.
105 *
106 * @return New ematch object or NULL.
107 */
rtnl_ematch_alloc(void)108 struct rtnl_ematch *rtnl_ematch_alloc(void)
109 {
110 struct rtnl_ematch *e;
111
112 if (!(e = calloc(1, sizeof(*e))))
113 return NULL;
114
115 NL_DBG(2, "allocated ematch %p\n", e);
116
117 NL_INIT_LIST_HEAD(&e->e_list);
118 NL_INIT_LIST_HEAD(&e->e_childs);
119
120 return e;
121 }
122
123 /**
124 * Add ematch to the end of the parent's list of children.
125 * @arg parent parent ematch object
126 * @arg child ematch object to be added to parent
127 *
128 * The parent must be a container ematch.
129 */
rtnl_ematch_add_child(struct rtnl_ematch * parent,struct rtnl_ematch * child)130 int rtnl_ematch_add_child(struct rtnl_ematch *parent,
131 struct rtnl_ematch *child)
132 {
133 if (parent->e_kind != TCF_EM_CONTAINER)
134 return -NLE_OPNOTSUPP;
135
136 NL_DBG(2, "added ematch %p \"%s\" to container %p\n",
137 child, child->e_ops->eo_name, parent);
138
139 nl_list_add_tail(&child->e_list, &parent->e_childs);
140
141 return 0;
142 }
143
144 /**
145 * Remove ematch from the list of ematches it is linked to.
146 * @arg ematch ematch object
147 */
rtnl_ematch_unlink(struct rtnl_ematch * ematch)148 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
149 {
150 NL_DBG(2, "unlinked ematch %p from any lists\n", ematch);
151
152 if (!nl_list_empty(&ematch->e_childs))
153 NL_DBG(1, "warning: ematch %p with childs was unlinked\n",
154 ematch);
155
156 nl_list_del(&ematch->e_list);
157 nl_init_list_head(&ematch->e_list);
158 }
159
rtnl_ematch_free(struct rtnl_ematch * ematch)160 void rtnl_ematch_free(struct rtnl_ematch *ematch)
161 {
162 NL_DBG(2, "freed ematch %p\n", ematch);
163 rtnl_ematch_unlink(ematch);
164 free(ematch->e_data);
165 free(ematch);
166 }
167
rtnl_ematch_set_ops(struct rtnl_ematch * ematch,struct rtnl_ematch_ops * ops)168 int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops)
169 {
170 if (ematch->e_ops)
171 return -NLE_EXIST;
172
173 ematch->e_ops = ops;
174 ematch->e_kind = ops->eo_kind;
175
176 if (ops->eo_datalen) {
177 ematch->e_data = calloc(1, ops->eo_datalen);
178 if (!ematch->e_data)
179 return -NLE_NOMEM;
180
181 ematch->e_datalen = ops->eo_datalen;
182 }
183
184 return 0;
185 }
186
rtnl_ematch_set_kind(struct rtnl_ematch * ematch,uint16_t kind)187 int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind)
188 {
189 struct rtnl_ematch_ops *ops;
190
191 if (ematch->e_kind)
192 return -NLE_EXIST;
193
194 ematch->e_kind = kind;
195
196 if ((ops = rtnl_ematch_lookup_ops(kind)))
197 rtnl_ematch_set_ops(ematch, ops);
198
199 return 0;
200 }
201
rtnl_ematch_set_name(struct rtnl_ematch * ematch,const char * name)202 int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name)
203 {
204 struct rtnl_ematch_ops *ops;
205
206 if (ematch->e_kind)
207 return -NLE_EXIST;
208
209 if (!(ops = rtnl_ematch_lookup_ops_by_name(name)))
210 return -NLE_OPNOTSUPP;
211
212 rtnl_ematch_set_ops(ematch, ops);
213
214 return 0;
215 }
216
rtnl_ematch_set_flags(struct rtnl_ematch * ematch,uint16_t flags)217 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
218 {
219 ematch->e_flags |= flags;
220 }
221
rtnl_ematch_unset_flags(struct rtnl_ematch * ematch,uint16_t flags)222 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
223 {
224 ematch->e_flags &= ~flags;
225 }
226
rtnl_ematch_get_flags(struct rtnl_ematch * ematch)227 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
228 {
229 return ematch->e_flags;
230 }
231
rtnl_ematch_data(struct rtnl_ematch * ematch)232 void *rtnl_ematch_data(struct rtnl_ematch *ematch)
233 {
234 return ematch->e_data;
235 }
236
237 /** @} */
238
239 /**
240 * @name Tree
241 */
242
243 /**
244 * Allocate ematch tree object
245 * @arg progid program id
246 */
rtnl_ematch_tree_alloc(uint16_t progid)247 struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
248 {
249 struct rtnl_ematch_tree *tree;
250
251 if (!(tree = calloc(1, sizeof(*tree))))
252 return NULL;
253
254 NL_INIT_LIST_HEAD(&tree->et_list);
255 tree->et_progid = progid;
256
257 NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid);
258
259 return tree;
260 }
261
free_ematch_list(struct nl_list_head * head)262 static void free_ematch_list(struct nl_list_head *head)
263 {
264 struct rtnl_ematch *pos, *next;
265
266 nl_list_for_each_entry_safe(pos, next, head, e_list) {
267 if (!nl_list_empty(&pos->e_childs))
268 free_ematch_list(&pos->e_childs);
269 rtnl_ematch_free(pos);
270 }
271 }
272
273 /**
274 * Free ematch tree object
275 * @arg tree ematch tree object
276 *
277 * This function frees the ematch tree and all ematches attached to it.
278 */
rtnl_ematch_tree_free(struct rtnl_ematch_tree * tree)279 void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
280 {
281 if (!tree)
282 return;
283
284 free_ematch_list(&tree->et_list);
285
286 NL_DBG(2, "Freed ematch tree %p\n", tree);
287
288 free(tree);
289 }
290
291 /**
292 * Add ematch object to the end of the ematch tree
293 * @arg tree ematch tree object
294 * @arg ematch ematch object to add
295 */
rtnl_ematch_tree_add(struct rtnl_ematch_tree * tree,struct rtnl_ematch * ematch)296 void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree,
297 struct rtnl_ematch *ematch)
298 {
299 nl_list_add_tail(&ematch->e_list, &tree->et_list);
300 }
301
container_ref(struct rtnl_ematch * ematch)302 static inline uint32_t container_ref(struct rtnl_ematch *ematch)
303 {
304 return *((uint32_t *) rtnl_ematch_data(ematch));
305 }
306
link_tree(struct rtnl_ematch * index[],int nmatches,int pos,struct nl_list_head * root)307 static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
308 struct nl_list_head *root)
309 {
310 struct rtnl_ematch *ematch;
311 int i;
312
313 for (i = pos; i < nmatches; i++) {
314 ematch = index[i];
315
316 nl_list_add_tail(&ematch->e_list, root);
317
318 if (ematch->e_kind == TCF_EM_CONTAINER)
319 link_tree(index, nmatches, container_ref(ematch),
320 &ematch->e_childs);
321
322 if (!(ematch->e_flags & TCF_EM_REL_MASK))
323 return 0;
324 }
325
326 /* Last entry in chain can't possibly have no relation */
327 return -NLE_INVAL;
328 }
329
330 static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
331 [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
332 [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
333 };
334
335 /**
336 * Parse ematch netlink attributes
337 *
338 * @return 0 on success or a negative error code.
339 */
rtnl_ematch_parse_attr(struct nlattr * attr,struct rtnl_ematch_tree ** result)340 int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result)
341 {
342 struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
343 struct tcf_ematch_tree_hdr *thdr;
344 struct rtnl_ematch_tree *tree;
345 struct rtnl_ematch **index;
346 int nmatches = 0, err, remaining;
347
348 NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr);
349
350 err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
351 if (err < 0)
352 return err;
353
354 if (!tb[TCA_EMATCH_TREE_HDR])
355 return -NLE_MISSING_ATTR;
356
357 thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
358
359 /* Ignore empty trees */
360 if (thdr->nmatches == 0) {
361 NL_DBG(2, "Ignoring empty ematch configuration\n");
362 return 0;
363 }
364
365 if (!tb[TCA_EMATCH_TREE_LIST])
366 return -NLE_MISSING_ATTR;
367
368 NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n",
369 thdr->nmatches, thdr->progid);
370
371 /*
372 * Do some basic sanity checking since we will allocate
373 * index[thdr->nmatches]. Calculate how many ematch headers fit into
374 * the provided data and make sure nmatches does not exceed it.
375 */
376 if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
377 nla_total_size(sizeof(struct tcf_ematch_hdr))))
378 return -NLE_INVAL;
379
380 if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
381 return -NLE_NOMEM;
382
383 if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
384 err = -NLE_NOMEM;
385 goto errout;
386 }
387
388 nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
389 struct rtnl_ematch_ops *ops;
390 struct tcf_ematch_hdr *hdr;
391 struct rtnl_ematch *ematch;
392 void *data;
393 size_t len;
394
395 NL_DBG(3, "parsing ematch attribute %d, len=%u\n",
396 nmatches+1, nla_len(a));
397
398 if (nla_len(a) < sizeof(*hdr)) {
399 err = -NLE_INVAL;
400 goto errout;
401 }
402
403 /* Quit as soon as we've parsed more matches than expected */
404 if (nmatches >= thdr->nmatches) {
405 err = -NLE_RANGE;
406 goto errout;
407 }
408
409 hdr = nla_data(a);
410 data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
411 len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
412
413 NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n",
414 hdr->matchid, hdr->kind, hdr->flags);
415
416 /*
417 * Container matches contain a reference to another sequence
418 * of matches. Ensure that the reference is within boundries.
419 */
420 if (hdr->kind == TCF_EM_CONTAINER &&
421 *((uint32_t *) data) >= thdr->nmatches) {
422 err = -NLE_INVAL;
423 goto errout;
424 }
425
426 if (!(ematch = rtnl_ematch_alloc())) {
427 err = -NLE_NOMEM;
428 goto errout;
429 }
430
431 ematch->e_id = hdr->matchid;
432 ematch->e_kind = hdr->kind;
433 ematch->e_flags = hdr->flags;
434
435 if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) {
436 if (ops->eo_minlen && len < ops->eo_minlen) {
437 rtnl_ematch_free(ematch);
438 err = -NLE_INVAL;
439 goto errout;
440 }
441
442 rtnl_ematch_set_ops(ematch, ops);
443
444 if (ops->eo_parse &&
445 (err = ops->eo_parse(ematch, data, len)) < 0) {
446 rtnl_ematch_free(ematch);
447 goto errout;
448 }
449 }
450
451 NL_DBG(3, "index[%d] = %p\n", nmatches, ematch);
452 index[nmatches++] = ematch;
453 }
454
455 if (nmatches != thdr->nmatches) {
456 err = -NLE_INVAL;
457 goto errout;
458 }
459
460 err = link_tree(index, nmatches, 0, &tree->et_list);
461 if (err < 0)
462 goto errout;
463
464 free(index);
465 *result = tree;
466
467 return 0;
468
469 errout:
470 rtnl_ematch_tree_free(tree);
471 free(index);
472 return err;
473 }
474
dump_ematch_sequence(struct nl_list_head * head,struct nl_dump_params * p)475 static void dump_ematch_sequence(struct nl_list_head *head,
476 struct nl_dump_params *p)
477 {
478 struct rtnl_ematch *match;
479
480 nl_list_for_each_entry(match, head, e_list) {
481 if (match->e_flags & TCF_EM_INVERT)
482 nl_dump(p, "!");
483
484 if (match->e_kind == TCF_EM_CONTAINER) {
485 nl_dump(p, "(");
486 dump_ematch_sequence(&match->e_childs, p);
487 nl_dump(p, ")");
488 } else if (!match->e_ops) {
489 nl_dump(p, "[unknown ematch %d]", match->e_kind);
490 } else {
491 if (match->e_ops->eo_dump)
492 match->e_ops->eo_dump(match, p);
493 else
494 nl_dump(p, "[data]");
495 }
496
497 switch (match->e_flags & TCF_EM_REL_MASK) {
498 case TCF_EM_REL_AND:
499 nl_dump(p, " AND ");
500 break;
501 case TCF_EM_REL_OR:
502 nl_dump(p, " OR ");
503 break;
504 default:
505 /* end of first level ematch sequence */
506 return;
507 }
508 }
509 }
510
rtnl_ematch_tree_dump(struct rtnl_ematch_tree * tree,struct nl_dump_params * p)511 void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
512 struct nl_dump_params *p)
513 {
514 if (!tree)
515 BUG();
516
517 dump_ematch_sequence(&tree->et_list, p);
518 nl_dump(p, "\n");
519 }
520
update_container_index(struct nl_list_head * list,int * index)521 static int update_container_index(struct nl_list_head *list, int *index)
522 {
523 struct rtnl_ematch *e;
524
525 nl_list_for_each_entry(e, list, e_list)
526 e->e_index = (*index)++;
527
528 nl_list_for_each_entry(e, list, e_list) {
529 if (e->e_kind == TCF_EM_CONTAINER) {
530 int err;
531
532 if (nl_list_empty(&e->e_childs))
533 return -NLE_OBJ_NOTFOUND;
534
535 *((uint32_t *) e->e_data) = *index;
536
537 err = update_container_index(&e->e_childs, index);
538 if (err < 0)
539 return err;
540 }
541 }
542
543 return 0;
544 }
545
fill_ematch_sequence(struct nl_msg * msg,struct nl_list_head * list)546 static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list)
547 {
548 struct rtnl_ematch *e;
549
550 nl_list_for_each_entry(e, list, e_list) {
551 struct tcf_ematch_hdr match = {
552 .matchid = e->e_id,
553 .kind = e->e_kind,
554 .flags = e->e_flags,
555 };
556 struct nlattr *attr;
557 int err = 0;
558
559 if (!(attr = nla_nest_start(msg, e->e_index + 1)))
560 return -NLE_NOMEM;
561
562 if (nlmsg_append(msg, &match, sizeof(match), 0) < 0)
563 return -NLE_NOMEM;
564
565 if (e->e_ops->eo_fill)
566 err = e->e_ops->eo_fill(e, msg);
567 else if (e->e_flags & TCF_EM_SIMPLE)
568 err = nlmsg_append(msg, e->e_data, 4, 0);
569 else if (e->e_datalen > 0)
570 err = nlmsg_append(msg, e->e_data, e->e_datalen, 0);
571
572 NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n",
573 msg, e->e_index, match.matchid, match.kind, match.flags);
574
575 if (err < 0)
576 return -NLE_NOMEM;
577
578 nla_nest_end(msg, attr);
579 }
580
581 nl_list_for_each_entry(e, list, e_list) {
582 if (e->e_kind == TCF_EM_CONTAINER &&
583 fill_ematch_sequence(msg, &e->e_childs) < 0)
584 return -NLE_NOMEM;
585 }
586
587 return 0;
588 }
589
rtnl_ematch_fill_attr(struct nl_msg * msg,int attrid,struct rtnl_ematch_tree * tree)590 int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid,
591 struct rtnl_ematch_tree *tree)
592 {
593 struct tcf_ematch_tree_hdr thdr = {
594 .progid = tree->et_progid,
595 };
596 struct nlattr *list, *topattr;
597 int err, index = 0;
598
599 /* Assign index number to each ematch to allow for references
600 * to be made while constructing the sequence of matches. */
601 err = update_container_index(&tree->et_list, &index);
602 if (err < 0)
603 return err;
604
605 if (!(topattr = nla_nest_start(msg, attrid)))
606 goto nla_put_failure;
607
608 thdr.nmatches = index;
609 NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr);
610
611 if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST)))
612 goto nla_put_failure;
613
614 if (fill_ematch_sequence(msg, &tree->et_list) < 0)
615 goto nla_put_failure;
616
617 nla_nest_end(msg, list);
618
619 nla_nest_end(msg, topattr);
620
621 return 0;
622
623 nla_put_failure:
624 return -NLE_NOMEM;
625 }
626
627 /** @} */
628
629 extern int ematch_parse(void *, char **, struct nl_list_head *);
630
rtnl_ematch_parse_expr(const char * expr,char ** errp,struct rtnl_ematch_tree ** result)631 int rtnl_ematch_parse_expr(const char *expr, char **errp,
632 struct rtnl_ematch_tree **result)
633 {
634 struct rtnl_ematch_tree *tree;
635 YY_BUFFER_STATE buf = NULL;
636 yyscan_t scanner = NULL;
637 int err;
638
639 NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr);
640
641 if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID)))
642 return -NLE_FAILURE;
643
644 if ((err = ematch_lex_init(&scanner)) < 0) {
645 err = -NLE_FAILURE;
646 goto errout;
647 }
648
649 buf = ematch__scan_string(expr, scanner);
650
651 if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) {
652 ematch__delete_buffer(buf, scanner);
653 err = -NLE_PARSE_ERR;
654 goto errout;
655 }
656
657 ematch_lex_destroy(scanner);
658 *result = tree;
659
660 return 0;
661
662 errout:
663 if (scanner)
664 ematch_lex_destroy(scanner);
665
666 rtnl_ematch_tree_free(tree);
667
668 return err;
669 }
670
671 static const char *layer_txt[] = {
672 [TCF_LAYER_LINK] = "eth",
673 [TCF_LAYER_NETWORK] = "ip",
674 [TCF_LAYER_TRANSPORT] = "tcp",
675 };
676
rtnl_ematch_offset2txt(uint8_t layer,uint16_t offset,char * buf,size_t len)677 char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len)
678 {
679 snprintf(buf, len, "%s+%u",
680 (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?",
681 offset);
682
683 return buf;
684 }
685
686 static const char *operand_txt[] = {
687 [TCF_EM_OPND_EQ] = "=",
688 [TCF_EM_OPND_LT] = "<",
689 [TCF_EM_OPND_GT] = ">",
690 };
691
rtnl_ematch_opnd2txt(uint8_t opnd,char * buf,size_t len)692 char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len)
693 {
694 snprintf(buf, len, "%s",
695 opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?");
696
697 return buf;
698 }
699
700 /** @} */
701