Home | History | Annotate | Download | only in tc
      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 (at) 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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 (hdr->nmatches > 0) {
    453 		if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
    454 			goto errout;
    455 
    456 		fprintf(fd, "\n  ");
    457 		if (print_ematch_seq(fd, tb, 1, 1) < 0)
    458 			goto errout;
    459 	}
    460 
    461 	err = 0;
    462 errout:
    463 	free(tb);
    464 	return err;
    465 }
    466 
    467 int print_ematch(FILE *fd, const struct rtattr *rta)
    468 {
    469 	struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
    470 	struct tcf_ematch_tree_hdr *hdr;
    471 
    472 	if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
    473 		return -1;
    474 
    475 	if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
    476 		fprintf(stderr, "Missing ematch tree header\n");
    477 		return -1;
    478 	}
    479 
    480 	if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
    481 		fprintf(stderr, "Missing ematch tree list\n");
    482 		return -1;
    483 	}
    484 
    485 	if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
    486 		fprintf(stderr, "Ematch tree header size mismatch\n");
    487 		return -1;
    488 	}
    489 
    490 	hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
    491 
    492 	return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
    493 }
    494 
    495 struct bstr * bstr_alloc(const char *text)
    496 {
    497 	struct bstr *b = calloc(1, sizeof(*b));
    498 
    499 	if (b == NULL)
    500 		return NULL;
    501 
    502 	b->data = strdup(text);
    503 	if (b->data == NULL) {
    504 		free(b);
    505 		return NULL;
    506 	}
    507 
    508 	b->len = strlen(text);
    509 
    510 	return b;
    511 }
    512 
    513 unsigned long bstrtoul(const struct bstr *b)
    514 {
    515 	char *inv = NULL;
    516 	unsigned long l;
    517 	char buf[b->len+1];
    518 
    519 	memcpy(buf, b->data, b->len);
    520 	buf[b->len] = '\0';
    521 
    522 	l = strtoul(buf, &inv, 0);
    523 	if (l == ULONG_MAX || inv == buf)
    524 		return ULONG_MAX;
    525 
    526 	return l;
    527 }
    528 
    529 void bstr_print(FILE *fd, const struct bstr *b, int ascii)
    530 {
    531 	int i;
    532 	char *s = b->data;
    533 
    534 	if (ascii)
    535 		for (i = 0; i < b->len; i++)
    536 		    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
    537 	else {
    538 		for (i = 0; i < b->len; i++)
    539 		    fprintf(fd, "%02x", s[i]);
    540 		fprintf(fd, "\"");
    541 		for (i = 0; i < b->len; i++)
    542 		    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
    543 		fprintf(fd, "\"");
    544 	}
    545 }
    546 
    547 void print_ematch_tree(const struct ematch *tree)
    548 {
    549 	const struct ematch *t;
    550 
    551 	for (t = tree; t; t = t->next) {
    552 		if (t->inverted)
    553 			printf("NOT ");
    554 
    555 		if (t->child) {
    556 			printf("(");
    557 			print_ematch_tree(t->child);
    558 			printf(")");
    559 		} else {
    560 			struct bstr *b;
    561 			for (b = t->args; b; b = b->next)
    562 				printf("%s%s", b->data, b->next ? " " : "");
    563 		}
    564 
    565 		if (t->relation == TCF_EM_REL_AND)
    566 			printf(" AND ");
    567 		else if (t->relation == TCF_EM_REL_OR)
    568 			printf(" OR ");
    569 	}
    570 }
    571