• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * m_ematch.c		Extended Matches
3  *
4  *		This program is free software; you can distribute it and/or
5  *		modify it under the terms of the GNU General Public License
6  *		as published by the Free Software Foundation; either version
7  *		2 of the License, or (at your option) any later version.
8  *
9  * Authors:	Thomas Graf <tgraf@suug.ch>
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <syslog.h>
16 #include <fcntl.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <string.h>
21 #include <dlfcn.h>
22 #include <stdarg.h>
23 #include <errno.h>
24 
25 #include "utils.h"
26 #include "tc_util.h"
27 #include "m_ematch.h"
28 
29 #define EMATCH_MAP "/etc/iproute2/ematch_map"
30 
31 static struct ematch_util *ematch_list;
32 
33 /* export to bison parser */
34 int ematch_argc;
35 char **ematch_argv;
36 char *ematch_err = NULL;
37 struct ematch *ematch_root;
38 
39 static int begin_argc;
40 static char **begin_argv;
41 
map_warning(int num,char * kind)42 static inline void map_warning(int num, char *kind)
43 {
44 	fprintf(stderr,
45 	    "Error: Unable to find ematch \"%s\" in %s\n" \
46 	    "Please assign a unique ID to the ematch kind the suggested " \
47 	    "entry is:\n" \
48 	    "\t%d\t%s\n",
49 	    kind, EMATCH_MAP, num, kind);
50 }
51 
lookup_map(__u16 num,char * dst,int len,const char * file)52 static int lookup_map(__u16 num, char *dst, int len, const char *file)
53 {
54 	int err = -EINVAL;
55 	char buf[512];
56 	FILE *fd = fopen(file, "r");
57 
58 	if (fd == NULL)
59 		return -errno;
60 
61 	while (fgets(buf, sizeof(buf), fd)) {
62 		char namebuf[512], *p = buf;
63 		int id;
64 
65 		while (*p == ' ' || *p == '\t')
66 			p++;
67 		if (*p == '#' || *p == '\n' || *p == 0)
68 			continue;
69 
70 		if (sscanf(p, "%d %s", &id, namebuf) != 2) {
71 			fprintf(stderr, "ematch map %s corrupted at %s\n",
72 			    file, p);
73 			goto out;
74 		}
75 
76 		if (id == num) {
77 			if (dst)
78 				strncpy(dst, namebuf, len - 1);
79 			err = 0;
80 			goto out;
81 		}
82 	}
83 
84 	err = -ENOENT;
85 out:
86 	fclose(fd);
87 	return err;
88 }
89 
lookup_map_id(char * kind,int * dst,const char * file)90 static int lookup_map_id(char *kind, int *dst, const char *file)
91 {
92 	int err = -EINVAL;
93 	char buf[512];
94 	FILE *fd = fopen(file, "r");
95 
96 	if (fd == NULL)
97 		return -errno;
98 
99 	while (fgets(buf, sizeof(buf), fd)) {
100 		char namebuf[512], *p = buf;
101 		int id;
102 
103 		while (*p == ' ' || *p == '\t')
104 			p++;
105 		if (*p == '#' || *p == '\n' || *p == 0)
106 			continue;
107 
108 		if (sscanf(p, "%d %s", &id, namebuf) != 2) {
109 			fprintf(stderr, "ematch map %s corrupted at %s\n",
110 			    file, p);
111 			goto out;
112 		}
113 
114 		if (!strcasecmp(namebuf, kind)) {
115 			if (dst)
116 				*dst = id;
117 			err = 0;
118 			goto out;
119 		}
120 	}
121 
122 	err = -ENOENT;
123 	*dst = 0;
124 out:
125 	fclose(fd);
126 	return err;
127 }
128 
get_ematch_kind(char * kind)129 static struct ematch_util *get_ematch_kind(char *kind)
130 {
131 	static void *body;
132 	void *dlh;
133 	char buf[256];
134 	struct ematch_util *e;
135 
136 	for (e = ematch_list; e; e = e->next) {
137 		if (strcmp(e->kind, kind) == 0)
138 			return e;
139 	}
140 
141 	snprintf(buf, sizeof(buf), "em_%s.so", kind);
142 	dlh = dlopen(buf, RTLD_LAZY);
143 	if (dlh == NULL) {
144 		dlh = body;
145 		if (dlh == NULL) {
146 			dlh = body = dlopen(NULL, RTLD_LAZY);
147 			if (dlh == NULL)
148 				return NULL;
149 		}
150 	}
151 
152 	snprintf(buf, sizeof(buf), "%s_ematch_util", kind);
153 	e = dlsym(dlh, buf);
154 	if (e == NULL)
155 		return NULL;
156 
157 	e->next = ematch_list;
158 	ematch_list = e;
159 
160 	return e;
161 }
162 
get_ematch_kind_num(__u16 kind)163 static struct ematch_util *get_ematch_kind_num(__u16 kind)
164 {
165 	char name[32];
166 
167 	if (lookup_map(kind, name, sizeof(name), EMATCH_MAP) < 0)
168 		return NULL;
169 
170 	return get_ematch_kind(name);
171 }
172 
parse_tree(struct nlmsghdr * n,struct ematch * tree)173 static int parse_tree(struct nlmsghdr *n, struct ematch *tree)
174 {
175 	int index = 1;
176 	struct ematch *t;
177 
178 	for (t = tree; t; t = t->next) {
179 		struct rtattr *tail = NLMSG_TAIL(n);
180 		struct tcf_ematch_hdr hdr = {
181 			.flags = t->relation
182 		};
183 
184 		if (t->inverted)
185 			hdr.flags |= TCF_EM_INVERT;
186 
187 		addattr_l(n, MAX_MSG, index++, NULL, 0);
188 
189 		if (t->child) {
190 			__u32 r = t->child_ref;
191 			addraw_l(n, MAX_MSG, &hdr, sizeof(hdr));
192 			addraw_l(n, MAX_MSG, &r, sizeof(r));
193 		} else {
194 			int num = 0, err;
195 			char buf[64];
196 			struct ematch_util *e;
197 
198 			if (t->args == NULL)
199 				return -1;
200 
201 			strncpy(buf, (char*) t->args->data, sizeof(buf)-1);
202 			e = get_ematch_kind(buf);
203 			if (e == NULL) {
204 				fprintf(stderr, "Unknown ematch \"%s\"\n",
205 				    buf);
206 				return -1;
207 			}
208 
209 			err = lookup_map_id(buf, &num, EMATCH_MAP);
210 			if (err < 0) {
211 				if (err == -ENOENT)
212 					map_warning(e->kind_num, buf);
213 				return err;
214 			}
215 
216 			hdr.kind = num;
217 			if (e->parse_eopt(n, &hdr, t->args->next) < 0)
218 				return -1;
219 		}
220 
221 		tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail;
222 	}
223 
224 	return 0;
225 }
226 
flatten_tree(struct ematch * head,struct ematch * tree)227 static int flatten_tree(struct ematch *head, struct ematch *tree)
228 {
229 	int i, count = 0;
230 	struct ematch *t;
231 
232 	for (;;) {
233 		count++;
234 
235 		if (tree->child) {
236 			for (t = head; t->next; t = t->next);
237 			t->next = tree->child;
238 			count += flatten_tree(head, tree->child);
239 		}
240 
241 		if (tree->relation == 0)
242 			break;
243 
244 		tree = tree->next;
245 	}
246 
247 	for (i = 0, t = head; t; t = t->next, i++)
248 		t->index = i;
249 
250 	for (t = head; t; t = t->next)
251 		if (t->child)
252 			t->child_ref = t->child->index;
253 
254 	return count;
255 }
256 
em_parse_error(int err,struct bstr * args,struct bstr * carg,struct ematch_util * e,char * fmt,...)257 int em_parse_error(int err, struct bstr *args, struct bstr *carg,
258 		   struct ematch_util *e, char *fmt, ...)
259 {
260 	va_list a;
261 
262 	va_start(a, fmt);
263 	vfprintf(stderr, fmt, a);
264 	va_end(a);
265 
266 	if (ematch_err)
267 		fprintf(stderr, ": %s\n... ", ematch_err);
268 	else
269 		fprintf(stderr, "\n... ");
270 
271 	while (ematch_argc < begin_argc) {
272 		if (ematch_argc == (begin_argc - 1))
273 			fprintf(stderr, ">>%s<< ", *begin_argv);
274 		else
275 			fprintf(stderr, "%s ", *begin_argv);
276 		begin_argv++;
277 		begin_argc--;
278 	}
279 
280 	fprintf(stderr, "...\n");
281 
282 	if (args) {
283 		fprintf(stderr, "... %s(", e->kind);
284 		while (args) {
285 			fprintf(stderr, "%s", args == carg ? ">>" : "");
286 			bstr_print(stderr, args, 1);
287 			fprintf(stderr, "%s%s", args == carg ? "<<" : "",
288 			    args->next ? " " : "");
289 			args = args->next;
290 		}
291 		fprintf(stderr, ")...\n");
292 
293 	}
294 
295 	if (e == NULL) {
296 		fprintf(stderr,
297 		    "Usage: EXPR\n" \
298 		    "where: EXPR  := TERM [ { and | or } EXPR ]\n" \
299 		    "       TERM  := [ not ] { MATCH | '(' EXPR ')' }\n" \
300 		    "       MATCH := module '(' ARGS ')'\n" \
301 		    "       ARGS := ARG1 ARG2 ...\n" \
302 		    "\n" \
303 		    "Example: a(x y) and not (b(x) or c(x y z))\n");
304 	} else
305 		e->print_usage(stderr);
306 
307 	return -err;
308 }
309 
free_ematch_err(void)310 static inline void free_ematch_err(void)
311 {
312 	if (ematch_err) {
313 		free(ematch_err);
314 		ematch_err = NULL;
315 	}
316 }
317 
318 extern int ematch_parse(void);
319 
parse_ematch(int * argc_p,char *** argv_p,int tca_id,struct nlmsghdr * n)320 int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
321 {
322 	begin_argc = ematch_argc = *argc_p;
323 	begin_argv = ematch_argv = *argv_p;
324 
325 	if (ematch_parse()) {
326 		int err = em_parse_error(EINVAL, NULL, NULL, NULL,
327 		    "Parse error");
328 		free_ematch_err();
329 		return err;
330 	}
331 
332 	free_ematch_err();
333 
334 	/* undo look ahead by parser */
335 	ematch_argc++;
336 	ematch_argv--;
337 
338 	if (ematch_root) {
339 		struct rtattr *tail, *tail_list;
340 
341 		struct tcf_ematch_tree_hdr hdr = {
342 			.nmatches = flatten_tree(ematch_root, ematch_root),
343 			.progid = TCF_EM_PROG_TC
344 		};
345 
346 		tail = NLMSG_TAIL(n);
347 		addattr_l(n, MAX_MSG, tca_id, NULL, 0);
348 		addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr));
349 
350 		tail_list = NLMSG_TAIL(n);
351 		addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_LIST, NULL, 0);
352 
353 		if (parse_tree(n, ematch_root) < 0)
354 			return -1;
355 
356 		tail_list->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail_list;
357 		tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail;
358 	}
359 
360 	*argc_p = ematch_argc;
361 	*argv_p = ematch_argv;
362 
363 	return 0;
364 }
365 
print_ematch_seq(FILE * fd,struct rtattr ** tb,int start,int prefix)366 static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
367 			    int prefix)
368 {
369 	int n, i = start;
370 	struct tcf_ematch_hdr *hdr;
371 	int dlen;
372 	void *data;
373 
374 	for (;;) {
375 		if (tb[i] == NULL)
376 			return -1;
377 
378 		dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr);
379 		data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr);
380 
381 		if (dlen < 0)
382 			return -1;
383 
384 		hdr = RTA_DATA(tb[i]);
385 
386 		if (hdr->flags & TCF_EM_INVERT)
387 			fprintf(fd, "NOT ");
388 
389 		if (hdr->kind == 0) {
390 			__u32 ref;
391 
392 			if (dlen < sizeof(__u32))
393 				return -1;
394 
395 			ref = *(__u32 *) data;
396 			fprintf(fd, "(\n");
397 			for (n = 0; n <= prefix; n++)
398 				fprintf(fd, "  ");
399 			if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
400 				return -1;
401 			for (n = 0; n < prefix; n++)
402 				fprintf(fd, "  ");
403 			fprintf(fd, ") ");
404 
405 		} else {
406 			struct ematch_util *e;
407 
408 			e = get_ematch_kind_num(hdr->kind);
409 			if (e == NULL)
410 				fprintf(fd, "[unknown ematch %d]\n",
411 				    hdr->kind);
412 			else {
413 				fprintf(fd, "%s(", e->kind);
414 				if (e->print_eopt(fd, hdr, data, dlen) < 0)
415 					return -1;
416 				fprintf(fd, ")\n");
417 			}
418 			if (hdr->flags & TCF_EM_REL_MASK)
419 				for (n = 0; n < prefix; n++)
420 					fprintf(fd, "  ");
421 		}
422 
423 		switch (hdr->flags & TCF_EM_REL_MASK) {
424 			case TCF_EM_REL_AND:
425 				fprintf(fd, "AND ");
426 				break;
427 
428 			case TCF_EM_REL_OR:
429 				fprintf(fd, "OR ");
430 				break;
431 
432 			default:
433 				return 0;
434 		}
435 
436 		i++;
437 	}
438 
439 	return 0;
440 }
441 
print_ematch_list(FILE * fd,struct tcf_ematch_tree_hdr * hdr,struct rtattr * rta)442 static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
443 			     struct rtattr *rta)
444 {
445 	int err = -1;
446 	struct rtattr **tb;
447 
448 	tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *));
449 	if (tb == NULL)
450 		return -1;
451 
452 	if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
453 		goto errout;
454 
455 	fprintf(fd, "\n  ");
456 	if (print_ematch_seq(fd, tb, 1, 1) < 0)
457 		goto errout;
458 
459 	err = 0;
460 errout:
461 	free(tb);
462 	return err;
463 }
464 
print_ematch(FILE * fd,const struct rtattr * rta)465 int print_ematch(FILE *fd, const struct rtattr *rta)
466 {
467 	struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
468 	struct tcf_ematch_tree_hdr *hdr;
469 
470 	if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
471 		return -1;
472 
473 	if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
474 		fprintf(stderr, "Missing ematch tree header\n");
475 		return -1;
476 	}
477 
478 	if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
479 		fprintf(stderr, "Missing ematch tree list\n");
480 		return -1;
481 	}
482 
483 	if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
484 		fprintf(stderr, "Ematch tree header size mismatch\n");
485 		return -1;
486 	}
487 
488 	hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
489 
490 	return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
491 }
492 
bstr_alloc(const char * text)493 struct bstr * bstr_alloc(const char *text)
494 {
495 	struct bstr *b = calloc(1, sizeof(*b));
496 
497 	if (b == NULL)
498 		return NULL;
499 
500 	b->data = strdup(text);
501 	if (b->data == NULL) {
502 		free(b);
503 		return NULL;
504 	}
505 
506 	b->len = strlen(text);
507 
508 	return b;
509 }
510 
bstrtoul(const struct bstr * b)511 unsigned long bstrtoul(const struct bstr *b)
512 {
513 	char *inv = NULL;
514 	unsigned long l;
515 	char buf[b->len+1];
516 
517 	memcpy(buf, b->data, b->len);
518 	buf[b->len] = '\0';
519 
520 	l = strtoul(buf, &inv, 0);
521 	if (l == ULONG_MAX || inv == buf)
522 		return ULONG_MAX;
523 
524 	return l;
525 }
526 
bstr_print(FILE * fd,const struct bstr * b,int ascii)527 void bstr_print(FILE *fd, const struct bstr *b, int ascii)
528 {
529 	int i;
530 	char *s = b->data;
531 
532 	if (ascii)
533 		for (i = 0; i < b->len; i++)
534 		    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
535 	else {
536 		for (i = 0; i < b->len; i++)
537 		    fprintf(fd, "%02x", s[i]);
538 		fprintf(fd, "\"");
539 		for (i = 0; i < b->len; i++)
540 		    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
541 		fprintf(fd, "\"");
542 	}
543 }
544 
print_ematch_tree(const struct ematch * tree)545 void print_ematch_tree(const struct ematch *tree)
546 {
547 	const struct ematch *t;
548 
549 	for (t = tree; t; t = t->next) {
550 		if (t->inverted)
551 			printf("NOT ");
552 
553 		if (t->child) {
554 			printf("(");
555 			print_ematch_tree(t->child);
556 			printf(")");
557 		} else {
558 			struct bstr *b;
559 			for (b = t->args; b; b = b->next)
560 				printf("%s%s", b->data, b->next ? " " : "");
561 		}
562 
563 		if (t->relation == TCF_EM_REL_AND)
564 			printf(" AND ");
565 		else if (t->relation == TCF_EM_REL_OR)
566 			printf(" OR ");
567 	}
568 }
569