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