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