iproute2/misc/ssfilter.y
Dmitry Yakunin 7bd9188581 ss: add checks for bc filter support
As noted by David Ahern, now if some bytecode filter is not supported
by running kernel printed error message is not clear. This patch is attempt to
detect such case and print correct message. This is done by providing checking
function for new filter types. As example check function for cgroup filter
is implemented. It sends correct lightweight request (idiag_states = 0)
with zero cgroup condition to the kernel and checks returned errno. If filter
is not supported EINVAL is returned. Result of checking is cached to
avoid extra checks if several same filters are specified.

Signed-off-by: Dmitry Yakunin <zeil@yandex-team.ru>
Signed-off-by: David Ahern <dsahern@gmail.com>
2020-05-13 14:28:38 +00:00

369 lines
7.5 KiB
Plaintext

%{
#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;
}