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 31static struct ematch_util *ematch_list; 32 33/* export to bison parser */ 34int ematch_argc; 35char **ematch_argv; 36char *ematch_err = NULL; 37struct 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" \ 48 "\t%d\t%s\n", 49 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; 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; 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; 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; 124out: 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); 171 172 return NULL; 173} 174 175static int parse_tree(struct nlmsghdr *n, struct ematch *tree) 176{ 177 int index = 1; 178 struct ematch *t; 179 180 for (t = tree; t; t = t->next) { 181 struct rtattr *tail = NLMSG_TAIL(n); 182 struct tcf_ematch_hdr hdr = { 183 .flags = t->relation 184 }; 185 186 if (t->inverted) 187 hdr.flags |= TCF_EM_INVERT; 188 189 addattr_l(n, MAX_MSG, index++, NULL, 0); 190 191 if (t->child) { 192 __u32 r = t->child_ref; 193 addraw_l(n, MAX_MSG, &hdr, sizeof(hdr)); 194 addraw_l(n, MAX_MSG, &r, sizeof(r)); 195 } else { 196 int num = 0, err; 197 char buf[64]; 198 struct ematch_util *e; 199 200 if (t->args == NULL) 201 return -1; 202 203 strncpy(buf, (char*) t->args->data, sizeof(buf)-1); 204 e = get_ematch_kind(buf); 205 if (e == NULL) { 206 fprintf(stderr, "Unknown ematch \"%s\"\n", 207 buf); 208 return -1; 209 } 210 211 err = lookup_map_id(buf, &num, EMATCH_MAP); 212 if (err < 0) { 213 if (err == -ENOENT) 214 map_warning(e->kind_num, buf); 215 return err; 216 } 217 218 hdr.kind = num; 219 if (e->parse_eopt(n, &hdr, t->args->next) < 0) 220 return -1; 221 } 222 223 tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail; 224 } 225 226 return 0; 227} 228 229static int flatten_tree(struct ematch *head, struct ematch *tree) 230{ 231 int i, count = 0; 232 struct ematch *t; 233 234 for (;;) { 235 count++; 236 237 if (tree->child) { 238 for (t = head; t->next; t = t->next); 239 t->next = tree->child; 240 count += flatten_tree(head, tree->child); 241 } 242 243 if (tree->relation == 0) 244 break; 245 246 tree = tree->next; 247 } 248 249 for (i = 0, t = head; t; t = t->next, i++) 250 t->index = i; 251 252 for (t = head; t; t = t->next) 253 if (t->child) 254 t->child_ref = t->child->index; 255 256 return count; 257} 258 259int em_parse_error(int err, struct bstr *args, struct bstr *carg, 260 struct ematch_util *e, char *fmt, ...) 261{ 262 va_list a; 263 264 va_start(a, fmt); 265 vfprintf(stderr, fmt, a); 266 va_end(a); 267 268 if (ematch_err) 269 fprintf(stderr, ": %s\n... ", ematch_err); 270 else 271 fprintf(stderr, "\n... "); 272 273 while (ematch_argc < begin_argc) { 274 if (ematch_argc == (begin_argc - 1)) 275 fprintf(stderr, ">>%s<< ", *begin_argv); 276 else 277 fprintf(stderr, "%s ", *begin_argv); 278 begin_argv++; 279 begin_argc--; 280 } 281 282 fprintf(stderr, "...\n"); 283 284 if (args) { 285 fprintf(stderr, "... %s(", e->kind); 286 while (args) { 287 fprintf(stderr, "%s", args == carg ? ">>" : ""); 288 bstr_print(stderr, args, 1); 289 fprintf(stderr, "%s%s", args == carg ? "<<" : "", 290 args->next ? " " : ""); 291 args = args->next; 292 } 293 fprintf(stderr, ")...\n"); 294 295 } 296 297 if (e == NULL) { 298 fprintf(stderr, 299 "Usage: EXPR\n" \ 300 "where: EXPR := TERM [ { and | or } EXPR ]\n" \ 301 " TERM := [ not ] { MATCH | '(' EXPR ')' }\n" \ 302 " MATCH := module '(' ARGS ')'\n" \ 303 " ARGS := ARG1 ARG2 ...\n" \ 304 "\n" \ 305 "Example: a(x y) and not (b(x) or c(x y z))\n"); 306 } else 307 e->print_usage(stderr); 308 309 return -err; 310} 311 312static inline void free_ematch_err(void) 313{ 314 if (ematch_err) { 315 free(ematch_err); 316 ematch_err = NULL; 317 } 318} 319 320extern int ematch_parse(void); 321 322int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) 323{ 324 begin_argc = ematch_argc = *argc_p; 325 begin_argv = ematch_argv = *argv_p; 326 327 if (ematch_parse()) { 328 int err = em_parse_error(EINVAL, NULL, NULL, NULL, 329 "Parse error"); 330 free_ematch_err(); 331 return err; 332 } 333 334 free_ematch_err(); 335 336 /* undo look ahead by parser */ 337 ematch_argc++; 338 ematch_argv--; 339 340 if (ematch_root) { 341 struct rtattr *tail, *tail_list; 342 343 struct tcf_ematch_tree_hdr hdr = { 344 .nmatches = flatten_tree(ematch_root, ematch_root), 345 .progid = TCF_EM_PROG_TC 346 }; 347 348 tail = NLMSG_TAIL(n); 349 addattr_l(n, MAX_MSG, tca_id, NULL, 0); 350 addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr)); 351 352 tail_list = NLMSG_TAIL(n); 353 addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_LIST, NULL, 0); 354 355 if (parse_tree(n, ematch_root) < 0) 356 return -1; 357 358 tail_list->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail_list; 359 tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail; 360 } 361 362 *argc_p = ematch_argc; 363 *argv_p = ematch_argv; 364 365 return 0; 366} 367 368static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start, 369 int prefix) 370{ 371 int n, i = start; 372 struct tcf_ematch_hdr *hdr; 373 int dlen; 374 void *data; 375 376 for (;;) { 377 if (tb[i] == NULL) 378 return -1; 379 380 dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr); 381 data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr); 382 383 if (dlen < 0) 384 return -1; 385 386 hdr = RTA_DATA(tb[i]); 387 388 if (hdr->flags & TCF_EM_INVERT) 389 fprintf(fd, "NOT "); 390 391 if (hdr->kind == 0) { 392 __u32 ref; 393 394 if (dlen < sizeof(__u32)) 395 return -1; 396 397 ref = *(__u32 *) data; 398 fprintf(fd, "(\n"); 399 for (n = 0; n <= prefix; n++) 400 fprintf(fd, " "); 401 if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0) 402 return -1; 403 for (n = 0; n < prefix; n++) 404 fprintf(fd, " "); 405 fprintf(fd, ") "); 406 407 } else { 408 struct ematch_util *e; 409 410 e = get_ematch_kind_num(hdr->kind); 411 if (e == NULL) 412 fprintf(fd, "[unknown ematch %d]\n", 413 hdr->kind); 414 else { 415 fprintf(fd, "%s(", e->kind); 416 if (e->print_eopt(fd, hdr, data, dlen) < 0) 417 return -1; 418 fprintf(fd, ")\n"); 419 } 420 if (hdr->flags & TCF_EM_REL_MASK) 421 for (n = 0; n < prefix; n++) 422 fprintf(fd, " "); 423 } 424 425 switch (hdr->flags & TCF_EM_REL_MASK) { 426 case TCF_EM_REL_AND: 427 fprintf(fd, "AND "); 428 break; 429 430 case TCF_EM_REL_OR: 431 fprintf(fd, "OR "); 432 break; 433 434 default: 435 return 0; 436 } 437 438 i++; 439 } 440 441 return 0; 442} 443 444static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr, 445 struct rtattr *rta) 446{ 447 int err = -1; 448 struct rtattr **tb; 449 450 tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *)); 451 if (tb == NULL) 452 return -1; 453 454 if (hdr->nmatches > 0) { 455 if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0) 456 goto errout; 457 458 fprintf(fd, "\n "); 459 if (print_ematch_seq(fd, tb, 1, 1) < 0) 460 goto errout; 461 } 462 463 err = 0; 464errout: 465 free(tb); 466 return err; 467} 468 469int print_ematch(FILE *fd, const struct rtattr *rta) 470{ 471 struct rtattr *tb[TCA_EMATCH_TREE_MAX+1]; 472 struct tcf_ematch_tree_hdr *hdr; 473 474 if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0) 475 return -1; 476 477 if (tb[TCA_EMATCH_TREE_HDR] == NULL) { 478 fprintf(stderr, "Missing ematch tree header\n"); 479 return -1; 480 } 481 482 if (tb[TCA_EMATCH_TREE_LIST] == NULL) { 483 fprintf(stderr, "Missing ematch tree list\n"); 484 return -1; 485 } 486 487 if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) { 488 fprintf(stderr, "Ematch tree header size mismatch\n"); 489 return -1; 490 } 491 492 hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]); 493 494 return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]); 495} 496