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