diff options
Diffstat (limited to 'misc/ssfilter.y')
-rw-r--r-- | misc/ssfilter.y | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/misc/ssfilter.y b/misc/ssfilter.y new file mode 100644 index 0000000..3195723 --- /dev/null +++ b/misc/ssfilter.y @@ -0,0 +1,369 @@ +%{ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include "ssfilter.h" + +typedef struct ssfilter * ssfilter_t; + +#define YYSTYPE ssfilter_t + +static struct ssfilter * alloc_node(int type, void *pred) +{ + struct ssfilter *n; + + if (!ssfilter_is_supported(type)) { + fprintf(stderr, "It looks like such filter is not supported! Too old kernel?\n"); + exit(-1); + } + + n = malloc(sizeof(*n)); + if (n == NULL) + abort(); + n->type = type; + n->pred = pred; + n->post = NULL; + return n; +} + +static char **yy_argv; +static int yy_argc; +static FILE *yy_fp; +static ssfilter_t *yy_ret; +static int tok_type = -1; + +static int yylex(void); + +static void yyerror(char *s) +{ + fprintf(stderr, "ss: bison bellows (while parsing filter): \"%s!\"", s); +} + +%} + +%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK CGROUPCOND CGROUPPATH +%left '|' +%left '&' +%nonassoc '!' + +%% +applet: exprlist + { + *yy_ret = $1; + $$ = $1; + } + | null + ; + +null: /* NOTHING */ { $$ = NULL; } + ; + +exprlist: expr + | exprlist '|' expr + { + $$ = alloc_node(SSF_OR, $1); + $$->post = $3; + } + | exprlist '&' expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $3; + } + | exprlist expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $2; + } + ; + +eq: '=' + | /* nothing */ + ; + +expr: '(' exprlist ')' + { + $$ = $2; + } + | '!' expr + { + $$ = alloc_node(SSF_NOT, $2); + } + | DCOND eq HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $3); + } + | SCOND eq HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $3); + } + | DPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_D_GE, $3); + } + | DPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_D_LE, $3); + } + | DPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_LE, $3)); + } + | DPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_GE, $3)); + } + | DPORT eq HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $3); + } + | DPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_DCOND, $3)); + } + + | SPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_S_GE, $3); + } + | SPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_S_LE, $3); + } + | SPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_LE, $3)); + } + | SPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_GE, $3)); + } + | SPORT eq HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $3); + } + | SPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, $3)); + } + | DEVNAME eq DEVCOND + { + $$ = alloc_node(SSF_DEVCOND, $3); + } + | DEVNAME NEQ DEVCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_DEVCOND, $3)); + } + | FWMARK eq MARKMASK + { + $$ = alloc_node(SSF_MARKMASK, $3); + } + | FWMARK NEQ MARKMASK + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3)); + } + | CGROUPPATH eq CGROUPCOND + { + $$ = alloc_node(SSF_CGROUPCOND, $3); + } + | CGROUPPATH NEQ CGROUPCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_CGROUPCOND, $3)); + } + | AUTOBOUND + { + $$ = alloc_node(SSF_S_AUTO, NULL); + } +; +%% + +static char *get_token_from_line(char **ptr) +{ + char *tok, *cp = *ptr; + + while (*cp == ' ' || *cp == '\t') cp++; + + if (*cp == 0) { + *ptr = cp; + return NULL; + } + + tok = cp; + + while (*cp != 0 && *cp != ' ' && *cp != '\t') { + /* Backslash escapes everything. */ + if (*cp == '\\') { + char *tp; + for (tp = cp; tp != tok; tp--) + *tp = *(tp-1); + cp++; + tok++; + if (*cp == 0) + break; + } + cp++; + } + if (*cp) + *cp++ = 0; + *ptr = cp; + return tok; +} + +int yylex(void) +{ + static char argbuf[1024]; + static char *tokptr = argbuf; + static int argc; + char *curtok; + + do { + while (*tokptr == 0) { + tokptr = NULL; + if (argc < yy_argc) { + tokptr = yy_argv[argc]; + argc++; + } else if (yy_fp) { + while (tokptr == NULL) { + size_t len; + + if (fgets(argbuf, sizeof(argbuf), yy_fp) == NULL) + return 0; + + len = strnlen(argbuf, sizeof(argbuf)); + if (len == 0) { + fprintf(stderr, "Invalid line\n"); + exit(-1); + } + + if (len >= sizeof(argbuf) - 1) { + fprintf(stderr, "Too long line in filter\n"); + exit(-1); + } + if (argbuf[len - 1] == '\n') + argbuf[len-1] = 0; + if (argbuf[0] == '#' || argbuf[0] == '0') + continue; + tokptr = argbuf; + } + } else { + return 0; + } + } + } while ((curtok = get_token_from_line(&tokptr)) == NULL); + + if (strcmp(curtok, "!") == 0 || + strcmp(curtok, "not") == 0) + return '!'; + if (strcmp(curtok, "&") == 0 || + strcmp(curtok, "&&") == 0 || + strcmp(curtok, "and") == 0) + return '&'; + if (strcmp(curtok, "|") == 0 || + strcmp(curtok, "||") == 0 || + strcmp(curtok, "or") == 0) + return '|'; + if (strcmp(curtok, "(") == 0) + return '('; + if (strcmp(curtok, ")") == 0) + return ')'; + if (strcmp(curtok, "dst") == 0) { + tok_type = DCOND; + return DCOND; + } + if (strcmp(curtok, "src") == 0) { + tok_type = SCOND; + return SCOND; + } + if (strcmp(curtok, "dport") == 0) { + tok_type = DPORT; + return DPORT; + } + if (strcmp(curtok, "sport") == 0) { + tok_type = SPORT; + return SPORT; + } + if (strcmp(curtok, "dev") == 0) { + tok_type = DEVNAME; + return DEVNAME; + } + if (strcmp(curtok, "fwmark") == 0) { + tok_type = FWMARK; + return FWMARK; + } + if (strcmp(curtok, "cgroup") == 0) { + tok_type = CGROUPPATH; + return CGROUPPATH; + } + if (strcmp(curtok, ">=") == 0 || + strcmp(curtok, "ge") == 0 || + strcmp(curtok, "geq") == 0) + return GEQ; + if (strcmp(curtok, "<=") == 0 || + strcmp(curtok, "le") == 0 || + strcmp(curtok, "leq") == 0) + return LEQ; + if (strcmp(curtok, "!=") == 0 || + strcmp(curtok, "ne") == 0 || + strcmp(curtok, "neq") == 0) + return NEQ; + if (strcmp(curtok, "=") == 0 || + strcmp(curtok, "==") == 0 || + strcmp(curtok, "eq") == 0) + return '='; + if (strcmp(curtok, ">") == 0 || + strcmp(curtok, "gt") == 0) + return '>'; + if (strcmp(curtok, "<") == 0 || + strcmp(curtok, "lt") == 0) + return '<'; + if (strcmp(curtok, "autobound") == 0) { + tok_type = AUTOBOUND; + return AUTOBOUND; + } + if (tok_type == DEVNAME) { + yylval = (void*)parse_devcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse device.\n"); + exit(1); + } + return DEVCOND; + } + if (tok_type == FWMARK) { + yylval = (void*)parse_markmask(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse mark %s.\n", curtok); + exit(1); + } + return MARKMASK; + } + if (tok_type == CGROUPPATH) { + yylval = (void*)parse_cgroupcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse cgroup %s.\n", curtok); + exit(1); + } + return CGROUPCOND; + } + yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse dst/src address.\n"); + exit(1); + } + return HOSTCOND; +} + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp) +{ + yy_argc = argc; + yy_argv = argv; + yy_fp = fp; + yy_ret = f; + + if (yyparse()) { + fprintf(stderr, " Sorry.\n"); + return -1; + } + return 0; +} |