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