iproute2/tc/m_ematch.c
Eyal Birger 526862038e tc: ematch: add parse_eopt_argv() method for providing ematches with argv parameters
ematche uses YACC to parse ematch arguments and places them in struct bstr
linked lists.

It is useful to be able to receive parameters as argc,argv in order to use
getopt (and alike) argument parsers.

Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2018-02-27 09:43:06 -08:00

593 lines
11 KiB
C

/*
* m_ematch.c Extended Matches
*
* This program is free software; you can distribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Thomas Graf <tgraf@suug.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <errno.h>
#include "utils.h"
#include "tc_util.h"
#include "m_ematch.h"
#define EMATCH_MAP "/etc/iproute2/ematch_map"
static struct ematch_util *ematch_list;
/* export to bison parser */
int ematch_argc;
char **ematch_argv;
char *ematch_err;
struct ematch *ematch_root;
static int begin_argc;
static char **begin_argv;
static inline void map_warning(int num, char *kind)
{
fprintf(stderr,
"Error: Unable to find ematch \"%s\" in %s\n" \
"Please assign a unique ID to the ematch kind the suggested " \
"entry is:\n" \
"\t%d\t%s\n",
kind, EMATCH_MAP, num, kind);
}
static int lookup_map(__u16 num, char *dst, int len, const char *file)
{
int err = -EINVAL;
char buf[512];
FILE *fd = fopen(file, "r");
if (fd == NULL)
return -errno;
while (fgets(buf, sizeof(buf), fd)) {
char namebuf[512], *p = buf;
int id;
while (*p == ' ' || *p == '\t')
p++;
if (*p == '#' || *p == '\n' || *p == 0)
continue;
if (sscanf(p, "%d %s", &id, namebuf) != 2) {
fprintf(stderr, "ematch map %s corrupted at %s\n",
file, p);
goto out;
}
if (id == num) {
if (dst)
strncpy(dst, namebuf, len - 1);
err = 0;
goto out;
}
}
err = -ENOENT;
out:
fclose(fd);
return err;
}
static int lookup_map_id(char *kind, int *dst, const char *file)
{
int err = -EINVAL;
char buf[512];
FILE *fd = fopen(file, "r");
if (fd == NULL)
return -errno;
while (fgets(buf, sizeof(buf), fd)) {
char namebuf[512], *p = buf;
int id;
while (*p == ' ' || *p == '\t')
p++;
if (*p == '#' || *p == '\n' || *p == 0)
continue;
if (sscanf(p, "%d %s", &id, namebuf) != 2) {
fprintf(stderr, "ematch map %s corrupted at %s\n",
file, p);
goto out;
}
if (!strcasecmp(namebuf, kind)) {
if (dst)
*dst = id;
err = 0;
goto out;
}
}
err = -ENOENT;
*dst = 0;
out:
fclose(fd);
return err;
}
static struct ematch_util *get_ematch_kind(char *kind)
{
static void *body;
void *dlh;
char buf[256];
struct ematch_util *e;
for (e = ematch_list; e; e = e->next) {
if (strcmp(e->kind, kind) == 0)
return e;
}
snprintf(buf, sizeof(buf), "em_%s.so", kind);
dlh = dlopen(buf, RTLD_LAZY);
if (dlh == NULL) {
dlh = body;
if (dlh == NULL) {
dlh = body = dlopen(NULL, RTLD_LAZY);
if (dlh == NULL)
return NULL;
}
}
snprintf(buf, sizeof(buf), "%s_ematch_util", kind);
e = dlsym(dlh, buf);
if (e == NULL)
return NULL;
e->next = ematch_list;
ematch_list = e;
return e;
}
static struct ematch_util *get_ematch_kind_num(__u16 kind)
{
char name[32];
if (lookup_map(kind, name, sizeof(name), EMATCH_MAP) < 0)
return NULL;
return get_ematch_kind(name);
}
static int em_parse_call(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
struct ematch_util *e, struct ematch *t)
{
if (e->parse_eopt_argv) {
int argc = 0, i = 0, ret;
struct bstr *args;
char **argv;
for (args = t->args; args; args = bstr_next(args))
argc++;
argv = calloc(argc, sizeof(char *));
if (!argv)
return -1;
for (args = t->args; args; args = bstr_next(args))
argv[i++] = args->data;
ret = e->parse_eopt_argv(n, hdr, argc, argv);
free(argv);
return ret;
}
return e->parse_eopt(n, hdr, t->args->next);
}
static int parse_tree(struct nlmsghdr *n, struct ematch *tree)
{
int index = 1;
struct ematch *t;
for (t = tree; t; t = t->next) {
struct rtattr *tail;
struct tcf_ematch_hdr hdr = { .flags = t->relation };
if (t->inverted)
hdr.flags |= TCF_EM_INVERT;
tail = addattr_nest(n, MAX_MSG, index++);
if (t->child) {
__u32 r = t->child_ref;
addraw_l(n, MAX_MSG, &hdr, sizeof(hdr));
addraw_l(n, MAX_MSG, &r, sizeof(r));
} else {
int num = 0, err;
char buf[64];
struct ematch_util *e;
if (t->args == NULL)
return -1;
strncpy(buf, (char *) t->args->data, sizeof(buf)-1);
e = get_ematch_kind(buf);
if (e == NULL) {
fprintf(stderr, "Unknown ematch \"%s\"\n",
buf);
return -1;
}
err = lookup_map_id(buf, &num, EMATCH_MAP);
if (err < 0) {
if (err == -ENOENT)
map_warning(e->kind_num, buf);
return err;
}
hdr.kind = num;
if (em_parse_call(n, &hdr, e, t) < 0)
return -1;
}
addattr_nest_end(n, tail);
}
return 0;
}
static int flatten_tree(struct ematch *head, struct ematch *tree)
{
int i, count = 0;
struct ematch *t;
for (;;) {
count++;
if (tree->child) {
for (t = head; t->next; t = t->next);
t->next = tree->child;
count += flatten_tree(head, tree->child);
}
if (tree->relation == 0)
break;
tree = tree->next;
}
for (i = 0, t = head; t; t = t->next, i++)
t->index = i;
for (t = head; t; t = t->next)
if (t->child)
t->child_ref = t->child->index;
return count;
}
int em_parse_error(int err, struct bstr *args, struct bstr *carg,
struct ematch_util *e, char *fmt, ...)
{
va_list a;
va_start(a, fmt);
vfprintf(stderr, fmt, a);
va_end(a);
if (ematch_err)
fprintf(stderr, ": %s\n... ", ematch_err);
else
fprintf(stderr, "\n... ");
while (ematch_argc < begin_argc) {
if (ematch_argc == (begin_argc - 1))
fprintf(stderr, ">>%s<< ", *begin_argv);
else
fprintf(stderr, "%s ", *begin_argv);
begin_argv++;
begin_argc--;
}
fprintf(stderr, "...\n");
if (args) {
fprintf(stderr, "... %s(", e->kind);
while (args) {
fprintf(stderr, "%s", args == carg ? ">>" : "");
bstr_print(stderr, args, 1);
fprintf(stderr, "%s%s", args == carg ? "<<" : "",
args->next ? " " : "");
args = args->next;
}
fprintf(stderr, ")...\n");
}
if (e == NULL) {
fprintf(stderr,
"Usage: EXPR\n" \
"where: EXPR := TERM [ { and | or } EXPR ]\n" \
" TERM := [ not ] { MATCH | '(' EXPR ')' }\n" \
" MATCH := module '(' ARGS ')'\n" \
" ARGS := ARG1 ARG2 ...\n" \
"\n" \
"Example: a(x y) and not (b(x) or c(x y z))\n");
} else
e->print_usage(stderr);
return -err;
}
static inline void free_ematch_err(void)
{
if (ematch_err) {
free(ematch_err);
ematch_err = NULL;
}
}
extern int ematch_parse(void);
int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
{
begin_argc = ematch_argc = *argc_p;
begin_argv = ematch_argv = *argv_p;
if (ematch_parse()) {
int err = em_parse_error(EINVAL, NULL, NULL, NULL,
"Parse error");
free_ematch_err();
return err;
}
free_ematch_err();
/* undo look ahead by parser */
ematch_argc++;
ematch_argv--;
if (ematch_root) {
struct rtattr *tail, *tail_list;
struct tcf_ematch_tree_hdr hdr = {
.nmatches = flatten_tree(ematch_root, ematch_root),
.progid = TCF_EM_PROG_TC
};
tail = addattr_nest(n, MAX_MSG, tca_id);
addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr));
tail_list = addattr_nest(n, MAX_MSG, TCA_EMATCH_TREE_LIST);
if (parse_tree(n, ematch_root) < 0)
return -1;
addattr_nest_end(n, tail_list);
addattr_nest_end(n, tail);
}
*argc_p = ematch_argc;
*argv_p = ematch_argv;
return 0;
}
static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
int prefix)
{
int n, i = start;
struct tcf_ematch_hdr *hdr;
int dlen;
void *data;
for (;;) {
if (tb[i] == NULL)
return -1;
dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr);
data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr);
if (dlen < 0)
return -1;
hdr = RTA_DATA(tb[i]);
if (hdr->flags & TCF_EM_INVERT)
fprintf(fd, "NOT ");
if (hdr->kind == 0) {
__u32 ref;
if (dlen < sizeof(__u32))
return -1;
ref = *(__u32 *) data;
fprintf(fd, "(\n");
for (n = 0; n <= prefix; n++)
fprintf(fd, " ");
if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
return -1;
for (n = 0; n < prefix; n++)
fprintf(fd, " ");
fprintf(fd, ") ");
} else {
struct ematch_util *e;
e = get_ematch_kind_num(hdr->kind);
if (e == NULL)
fprintf(fd, "[unknown ematch %d]\n",
hdr->kind);
else {
fprintf(fd, "%s(", e->kind);
if (e->print_eopt(fd, hdr, data, dlen) < 0)
return -1;
fprintf(fd, ")\n");
}
if (hdr->flags & TCF_EM_REL_MASK)
for (n = 0; n < prefix; n++)
fprintf(fd, " ");
}
switch (hdr->flags & TCF_EM_REL_MASK) {
case TCF_EM_REL_AND:
fprintf(fd, "AND ");
break;
case TCF_EM_REL_OR:
fprintf(fd, "OR ");
break;
default:
return 0;
}
i++;
}
return 0;
}
static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
struct rtattr *rta)
{
int err = -1;
struct rtattr **tb;
tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *));
if (tb == NULL)
return -1;
if (hdr->nmatches > 0) {
if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
goto errout;
fprintf(fd, "\n ");
if (print_ematch_seq(fd, tb, 1, 1) < 0)
goto errout;
}
err = 0;
errout:
free(tb);
return err;
}
int print_ematch(FILE *fd, const struct rtattr *rta)
{
struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
struct tcf_ematch_tree_hdr *hdr;
if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
return -1;
if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
fprintf(stderr, "Missing ematch tree header\n");
return -1;
}
if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
fprintf(stderr, "Missing ematch tree list\n");
return -1;
}
if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
fprintf(stderr, "Ematch tree header size mismatch\n");
return -1;
}
hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
}
struct bstr *bstr_alloc(const char *text)
{
struct bstr *b = calloc(1, sizeof(*b));
if (b == NULL)
return NULL;
b->data = strdup(text);
if (b->data == NULL) {
free(b);
return NULL;
}
b->len = strlen(text);
return b;
}
unsigned long bstrtoul(const struct bstr *b)
{
char *inv = NULL;
unsigned long l;
char buf[b->len+1];
memcpy(buf, b->data, b->len);
buf[b->len] = '\0';
l = strtoul(buf, &inv, 0);
if (l == ULONG_MAX || inv == buf)
return ULONG_MAX;
return l;
}
void bstr_print(FILE *fd, const struct bstr *b, int ascii)
{
int i;
char *s = b->data;
if (ascii)
for (i = 0; i < b->len; i++)
fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
else {
for (i = 0; i < b->len; i++)
fprintf(fd, "%02x", s[i]);
fprintf(fd, "\"");
for (i = 0; i < b->len; i++)
fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
fprintf(fd, "\"");
}
}
void print_ematch_tree(const struct ematch *tree)
{
const struct ematch *t;
for (t = tree; t; t = t->next) {
if (t->inverted)
printf("NOT ");
if (t->child) {
printf("(");
print_ematch_tree(t->child);
printf(")");
} else {
struct bstr *b;
for (b = t->args; b; b = b->next)
printf("%s%s", b->data, b->next ? " " : "");
}
if (t->relation == TCF_EM_REL_AND)
printf(" AND ");
else if (t->relation == TCF_EM_REL_OR)
printf(" OR ");
}
}