blob: 251f5aa19f62709ea4e027419f2fe671e5cd53d3 [file] [log] [blame]
shemminger7a473c72005-06-23 20:26:01 +00001/*
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
31static struct ematch_util *ematch_list;
32
33/* export to bison parser */
34int ematch_argc;
35char **ematch_argv;
Stephen Hemminger32a121c2016-03-21 11:48:36 -070036char *ematch_err;
shemminger7a473c72005-06-23 20:26:01 +000037struct ematch *ematch_root;
38
39static int begin_argc;
40static char **begin_argv;
41
42static 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" \
Stephen Hemmingerae665a52006-12-05 10:10:22 -080048 "\t%d\t%s\n",
shemminger7a473c72005-06-23 20:26:01 +000049 kind, EMATCH_MAP, num, kind);
50}
51
52static 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;
Stephen Hemmingerae665a52006-12-05 10:10:22 -080069
shemminger7a473c72005-06-23 20:26:01 +000070 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;
85out:
86 fclose(fd);
87 return err;
88}
89
90static 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;
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800107
shemminger7a473c72005-06-23 20:26:01 +0000108 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;
shemmingerf332d162005-07-05 22:37:15 +0000123 *dst = 0;
shemminger7a473c72005-06-23 20:26:01 +0000124out:
125 fclose(fd);
126 return err;
127}
128
129static 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
163static 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);
shemminger7a473c72005-06-23 20:26:01 +0000171}
172
173static 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;
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800186
shemminger7a473c72005-06-23 20:26:01 +0000187 addattr_l(n, MAX_MSG, index++, NULL, 0);
188
189 if (t->child) {
190 __u32 r = t->child_ref;
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700191
shemminger7a473c72005-06-23 20:26:01 +0000192 addraw_l(n, MAX_MSG, &hdr, sizeof(hdr));
193 addraw_l(n, MAX_MSG, &r, sizeof(r));
194 } else {
shemminger3fcdebb2005-07-14 16:49:27 +0000195 int num = 0, err;
shemminger7a473c72005-06-23 20:26:01 +0000196 char buf[64];
197 struct ematch_util *e;
198
199 if (t->args == NULL)
200 return -1;
201
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700202 strncpy(buf, (char *) t->args->data, sizeof(buf)-1);
shemminger7a473c72005-06-23 20:26:01 +0000203 e = get_ematch_kind(buf);
204 if (e == NULL) {
205 fprintf(stderr, "Unknown ematch \"%s\"\n",
206 buf);
207 return -1;
208 }
209
210 err = lookup_map_id(buf, &num, EMATCH_MAP);
211 if (err < 0) {
212 if (err == -ENOENT)
213 map_warning(e->kind_num, buf);
214 return err;
215 }
216
217 hdr.kind = num;
218 if (e->parse_eopt(n, &hdr, t->args->next) < 0)
219 return -1;
220 }
221
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700222 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
shemminger7a473c72005-06-23 20:26:01 +0000223 }
224
225 return 0;
226}
227
228static int flatten_tree(struct ematch *head, struct ematch *tree)
229{
230 int i, count = 0;
231 struct ematch *t;
232
233 for (;;) {
234 count++;
235
236 if (tree->child) {
237 for (t = head; t->next; t = t->next);
238 t->next = tree->child;
239 count += flatten_tree(head, tree->child);
240 }
241
242 if (tree->relation == 0)
243 break;
244
245 tree = tree->next;
246 }
247
248 for (i = 0, t = head; t; t = t->next, i++)
249 t->index = i;
250
251 for (t = head; t; t = t->next)
252 if (t->child)
253 t->child_ref = t->child->index;
254
255 return count;
256}
257
258int em_parse_error(int err, struct bstr *args, struct bstr *carg,
259 struct ematch_util *e, char *fmt, ...)
260{
261 va_list a;
262
263 va_start(a, fmt);
264 vfprintf(stderr, fmt, a);
265 va_end(a);
266
267 if (ematch_err)
268 fprintf(stderr, ": %s\n... ", ematch_err);
269 else
270 fprintf(stderr, "\n... ");
271
272 while (ematch_argc < begin_argc) {
273 if (ematch_argc == (begin_argc - 1))
274 fprintf(stderr, ">>%s<< ", *begin_argv);
275 else
276 fprintf(stderr, "%s ", *begin_argv);
277 begin_argv++;
278 begin_argc--;
279 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800280
shemminger7a473c72005-06-23 20:26:01 +0000281 fprintf(stderr, "...\n");
282
283 if (args) {
284 fprintf(stderr, "... %s(", e->kind);
285 while (args) {
286 fprintf(stderr, "%s", args == carg ? ">>" : "");
287 bstr_print(stderr, args, 1);
288 fprintf(stderr, "%s%s", args == carg ? "<<" : "",
289 args->next ? " " : "");
290 args = args->next;
291 }
292 fprintf(stderr, ")...\n");
293
294 }
295
296 if (e == NULL) {
297 fprintf(stderr,
298 "Usage: EXPR\n" \
299 "where: EXPR := TERM [ { and | or } EXPR ]\n" \
300 " TERM := [ not ] { MATCH | '(' EXPR ')' }\n" \
301 " MATCH := module '(' ARGS ')'\n" \
302 " ARGS := ARG1 ARG2 ...\n" \
303 "\n" \
304 "Example: a(x y) and not (b(x) or c(x y z))\n");
305 } else
306 e->print_usage(stderr);
307
308 return -err;
309}
310
311static inline void free_ematch_err(void)
312{
313 if (ematch_err) {
314 free(ematch_err);
315 ematch_err = NULL;
316 }
317}
318
319extern int ematch_parse(void);
320
321int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
322{
323 begin_argc = ematch_argc = *argc_p;
324 begin_argv = ematch_argv = *argv_p;
325
326 if (ematch_parse()) {
327 int err = em_parse_error(EINVAL, NULL, NULL, NULL,
328 "Parse error");
329 free_ematch_err();
330 return err;
331 }
332
333 free_ematch_err();
334
335 /* undo look ahead by parser */
336 ematch_argc++;
337 ematch_argv--;
338
339 if (ematch_root) {
340 struct rtattr *tail, *tail_list;
341
342 struct tcf_ematch_tree_hdr hdr = {
343 .nmatches = flatten_tree(ematch_root, ematch_root),
344 .progid = TCF_EM_PROG_TC
345 };
346
347 tail = NLMSG_TAIL(n);
348 addattr_l(n, MAX_MSG, tca_id, NULL, 0);
349 addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr));
350
351 tail_list = NLMSG_TAIL(n);
352 addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_LIST, NULL, 0);
353
354 if (parse_tree(n, ematch_root) < 0)
355 return -1;
356
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700357 tail_list->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail_list;
358 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
shemminger7a473c72005-06-23 20:26:01 +0000359 }
360
361 *argc_p = ematch_argc;
362 *argv_p = ematch_argv;
363
364 return 0;
365}
366
367static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
368 int prefix)
369{
370 int n, i = start;
371 struct tcf_ematch_hdr *hdr;
372 int dlen;
373 void *data;
374
375 for (;;) {
376 if (tb[i] == NULL)
377 return -1;
378
379 dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr);
380 data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr);
381
382 if (dlen < 0)
383 return -1;
384
385 hdr = RTA_DATA(tb[i]);
386
387 if (hdr->flags & TCF_EM_INVERT)
388 fprintf(fd, "NOT ");
389
390 if (hdr->kind == 0) {
391 __u32 ref;
392
393 if (dlen < sizeof(__u32))
394 return -1;
395
396 ref = *(__u32 *) data;
397 fprintf(fd, "(\n");
398 for (n = 0; n <= prefix; n++)
399 fprintf(fd, " ");
400 if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
401 return -1;
402 for (n = 0; n < prefix; n++)
403 fprintf(fd, " ");
404 fprintf(fd, ") ");
405
406 } else {
407 struct ematch_util *e;
408
409 e = get_ematch_kind_num(hdr->kind);
410 if (e == NULL)
411 fprintf(fd, "[unknown ematch %d]\n",
412 hdr->kind);
413 else {
414 fprintf(fd, "%s(", e->kind);
415 if (e->print_eopt(fd, hdr, data, dlen) < 0)
416 return -1;
417 fprintf(fd, ")\n");
418 }
419 if (hdr->flags & TCF_EM_REL_MASK)
420 for (n = 0; n < prefix; n++)
421 fprintf(fd, " ");
422 }
423
424 switch (hdr->flags & TCF_EM_REL_MASK) {
425 case TCF_EM_REL_AND:
426 fprintf(fd, "AND ");
427 break;
428
429 case TCF_EM_REL_OR:
430 fprintf(fd, "OR ");
431 break;
432
433 default:
434 return 0;
435 }
436
437 i++;
438 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800439
shemminger7a473c72005-06-23 20:26:01 +0000440 return 0;
441}
442
443static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
444 struct rtattr *rta)
445{
446 int err = -1;
447 struct rtattr **tb;
448
449 tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *));
450 if (tb == NULL)
451 return -1;
452
Stephen Hemminger4b45abd2010-07-29 18:03:35 -0700453 if (hdr->nmatches > 0) {
454 if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
455 goto errout;
shemminger7a473c72005-06-23 20:26:01 +0000456
Stephen Hemminger4b45abd2010-07-29 18:03:35 -0700457 fprintf(fd, "\n ");
458 if (print_ematch_seq(fd, tb, 1, 1) < 0)
459 goto errout;
460 }
shemminger7a473c72005-06-23 20:26:01 +0000461
462 err = 0;
463errout:
464 free(tb);
465 return err;
466}
467
468int print_ematch(FILE *fd, const struct rtattr *rta)
469{
470 struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
471 struct tcf_ematch_tree_hdr *hdr;
472
473 if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
474 return -1;
475
476 if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
477 fprintf(stderr, "Missing ematch tree header\n");
478 return -1;
479 }
480
481 if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
482 fprintf(stderr, "Missing ematch tree list\n");
483 return -1;
484 }
485
486 if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
487 fprintf(stderr, "Ematch tree header size mismatch\n");
488 return -1;
489 }
490
491 hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
492
493 return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
494}
Stephen Hemmingerb6da1af2008-05-29 11:54:19 -0700495
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700496struct bstr *bstr_alloc(const char *text)
Stephen Hemmingerb6da1af2008-05-29 11:54:19 -0700497{
498 struct bstr *b = calloc(1, sizeof(*b));
499
500 if (b == NULL)
501 return NULL;
502
503 b->data = strdup(text);
504 if (b->data == NULL) {
505 free(b);
506 return NULL;
507 }
508
509 b->len = strlen(text);
510
511 return b;
512}
513
514unsigned long bstrtoul(const struct bstr *b)
515{
516 char *inv = NULL;
517 unsigned long l;
518 char buf[b->len+1];
519
520 memcpy(buf, b->data, b->len);
521 buf[b->len] = '\0';
522
523 l = strtoul(buf, &inv, 0);
524 if (l == ULONG_MAX || inv == buf)
525 return ULONG_MAX;
526
527 return l;
528}
529
530void bstr_print(FILE *fd, const struct bstr *b, int ascii)
531{
532 int i;
533 char *s = b->data;
534
535 if (ascii)
536 for (i = 0; i < b->len; i++)
537 fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
538 else {
539 for (i = 0; i < b->len; i++)
540 fprintf(fd, "%02x", s[i]);
541 fprintf(fd, "\"");
542 for (i = 0; i < b->len; i++)
543 fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
544 fprintf(fd, "\"");
545 }
546}
547
548void print_ematch_tree(const struct ematch *tree)
549{
550 const struct ematch *t;
551
552 for (t = tree; t; t = t->next) {
553 if (t->inverted)
554 printf("NOT ");
555
556 if (t->child) {
557 printf("(");
558 print_ematch_tree(t->child);
559 printf(")");
560 } else {
561 struct bstr *b;
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700562
Stephen Hemmingerb6da1af2008-05-29 11:54:19 -0700563 for (b = t->args; b; b = b->next)
564 printf("%s%s", b->data, b->next ? " " : "");
565 }
566
567 if (t->relation == TCF_EM_REL_AND)
568 printf(" AND ");
569 else if (t->relation == TCF_EM_REL_OR)
570 printf(" OR ");
571 }
572}