Commit 5a14df39 authored by Maria Jan Matejka's avatar Maria Jan Matejka Committed by Jan Maria Matejka

Filter: Instruction codes named as enum

The two-letter instructions were quite messy but they could be easily
read from memory dumps. Now GDB (since 2012) supports pretty printing
enum values and GCC checks the switch construction for missing enum
values so we are converting the nice two-byte values to enums.

Anyway, the enum still keeps the old two-byte values to be able to read
the instruction codes even without GDB from plain memory dump.
parent 2d6d4b80
......@@ -43,6 +43,8 @@ CF_DECLS
char *t;
struct rtable_config *r;
struct f_inst *x;
struct f_dynamic_attr fda;
struct f_static_attr fsa;
struct filter *f;
struct f_tree *e;
struct f_trie *trie;
......
This diff is collapsed.
......@@ -2,6 +2,7 @@
* Filters: utility functions
*
* Copyright 1998 Pavel Machek <pavel@ucw.cz>
* 2017 Jan Maria Matejka <mq@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
......@@ -13,43 +14,48 @@
#define P(a,b) ((a<<8) | b)
struct f_inst *
f_new_inst(void)
f_new_inst(enum f_instruction_code fi_code)
{
struct f_inst * ret;
ret = cfg_alloc(sizeof(struct f_inst));
ret->code = ret->aux = 0;
ret->arg1 = ret->arg2 = ret->next = NULL;
ret = cfg_allocz(sizeof(struct f_inst));
ret->fi_code = fi_code;
ret->lineno = ifs->lino;
return ret;
}
struct f_inst *
f_new_dynamic_attr(int type, int f_type UNUSED, int code)
f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da)
{
/* FIXME: Remove the f_type parameter? */
struct f_inst *f = f_new_inst();
f->aux = type;
f->a2.i = code;
return f;
struct f_inst *ret = f_new_inst(fi_code);
ret->aux = da.type;
ret->a2.i = da.ea_code;
return ret;
}
struct f_inst *
f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa)
{
struct f_inst *ret = f_new_inst(fi_code);
ret->aux = sa.f_type;
ret->a2.i = sa.sa_code;
ret->a1.i = sa.readonly;
return ret;
}
/*
* Generate set_dynamic( operation( get_dynamic(), argument ) )
*/
struct f_inst *
f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument)
f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument)
{
struct f_inst *set_dyn = f_new_inst(),
*oper = f_new_inst(),
*get_dyn = dyn;
struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
*oper = f_new_inst(operation),
*get_dyn = f_new_inst_da(FI_EA_GET, da);
*set_dyn = *get_dyn;
get_dyn->code = P('e','a');
oper->code = operation;
oper->aux = operation_aux;
oper->a1.p = get_dyn;
oper->a2.p = argument;
set_dyn->code = P('e','S');
set_dyn->a1.p = oper;
return set_dyn;
}
......@@ -59,7 +65,7 @@ struct f_inst *
f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn)
{
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
ret->i.code = P('R','C');
ret->i.fi_code = FI_ROA_CHECK;
ret->i.lineno = ifs->lino;
ret->i.arg1 = prefix;
ret->i.arg2 = asn;
......
......@@ -46,8 +46,6 @@
#include "conf/conf.h"
#include "filter/filter.h"
#define P(a,b) ((a<<8) | b)
#define CMP_ERROR 999
static struct adata *
......@@ -644,13 +642,13 @@ interpret(struct f_inst *what)
if (!what)
return res;
switch(what->code) {
case ',':
switch(what->fi_code) {
case FI_COMMA:
TWOARGS;
break;
/* Binary operators */
case '+':
case FI_ADD:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
......@@ -658,7 +656,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '-':
case FI_SUBTRACT:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
......@@ -666,7 +664,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '*':
case FI_MULTIPLY:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
......@@ -674,7 +672,7 @@ interpret(struct f_inst *what)
default: runtime( "Usage of unknown type" );
}
break;
case '/':
case FI_DIVIDE:
TWOARGS_C;
switch (res.type = v1.type) {
case T_VOID: runtime( "Can't operate with values of type void" );
......@@ -684,12 +682,12 @@ interpret(struct f_inst *what)
}
break;
case '&':
case '|':
case FI_AND:
case FI_OR:
ARG(v1, a1.p);
if (v1.type != T_BOOL)
runtime( "Can't do boolean operation on non-booleans" );
if (v1.val.i == (what->code == '|')) {
if (v1.val.i == (what->fi_code == FI_OR)) {
res.type = T_BOOL;
res.val.i = v1.val.i;
break;
......@@ -702,7 +700,7 @@ interpret(struct f_inst *what)
res.val.i = v2.val.i;
break;
case P('m','p'):
case FI_PAIR_CONSTRUCT:
TWOARGS;
if ((v1.type != T_INT) || (v2.type != T_INT))
runtime( "Can't operate with value of non-integer type in pair constructor" );
......@@ -714,7 +712,7 @@ interpret(struct f_inst *what)
res.type = T_PAIR;
break;
case P('m','c'):
case FI_EC_CONSTRUCT:
{
TWOARGS;
......@@ -762,7 +760,7 @@ interpret(struct f_inst *what)
break;
}
case P('m','l'):
case FI_LC_CONSTRUCT:
{
TWOARGS;
......@@ -798,12 +796,12 @@ interpret(struct f_inst *what)
res.val.i = (x); \
break;
case P('!','='): SAME(!i);
case P('=','='): SAME(i);
case '<': COMPARE(i==-1);
case P('<','='): COMPARE(i!=1);
case FI_NEQ: SAME(!i);
case FI_EQ: SAME(i);
case FI_LT: COMPARE(i==-1);
case FI_LTE: COMPARE(i!=1);
case '!':
case FI_NOT:
ONEARG;
if (v1.type != T_BOOL)
runtime( "Not applied to non-boolean" );
......@@ -811,7 +809,7 @@ interpret(struct f_inst *what)
res.val.i = !res.val.i;
break;
case '~':
case FI_MATCH:
TWOARGS;
res.type = T_BOOL;
res.val.i = val_in_range(v1, v2);
......@@ -820,7 +818,7 @@ interpret(struct f_inst *what)
res.val.i = !!res.val.i;
break;
case P('!','~'):
case FI_NOT_MATCH:
TWOARGS;
res.type = T_BOOL;
res.val.i = val_in_range(v1, v2);
......@@ -829,14 +827,14 @@ interpret(struct f_inst *what)
res.val.i = !res.val.i;
break;
case P('d','e'):
case FI_DEFINED:
ONEARG;
res.type = T_BOOL;
res.val.i = (v1.type != T_VOID);
break;
/* Set to indirect value, a1 = variable, a2 = value */
case 's':
case FI_SET:
ARG(v2, a2.p);
sym = what->a1.p;
vp = sym->def;
......@@ -855,7 +853,7 @@ interpret(struct f_inst *what)
break;
/* some constants have value in a2, some in *a1.p, strange. */
case 'c': /* integer (or simple type) constant, string, set, or prefix_set */
case FI_CONSTANT: /* integer (or simple type) constant, string, set, or prefix_set */
res.type = what->aux;
if (res.type == T_PREFIX_SET)
......@@ -867,15 +865,15 @@ interpret(struct f_inst *what)
else
res.val.i = what->a2.i;
break;
case 'V':
case 'C':
case FI_VARIABLE:
case FI_CONSTANT_INDIRECT:
res = * ((struct f_val *) what->a1.p);
break;
case 'p':
case FI_PRINT:
ONEARG;
val_format(v1, &f_buf);
break;
case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */
case FI_CONDITION: /* ? has really strange error value, so we can implement if ... else nicely :-) */
ONEARG;
if (v1.type != T_BOOL)
runtime( "If requires boolean expression" );
......@@ -885,10 +883,10 @@ interpret(struct f_inst *what)
} else res.val.i = 1;
res.type = T_BOOL;
break;
case '0':
case FI_NOP:
debug( "No operation\n" );
break;
case P('p',','):
case FI_PRINT_AND_DIE:
ONEARG;
if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
!(f_flags & FF_SILENT))
......@@ -911,7 +909,7 @@ interpret(struct f_inst *what)
bug( "unknown return type: Can't happen");
}
break;
case 'a': /* rta access */
case FI_RTA_GET: /* rta access */
{
ACCESS_RTE;
struct rta *rta = (*f_rte)->attrs;
......@@ -936,7 +934,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('a','S'):
case FI_RTA_SET:
ACCESS_RTE;
ONEARG;
if (what->aux != v1.type)
......@@ -988,7 +986,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('e','a'): /* Access to extended attributes */
case FI_EA_GET: /* Access to extended attributes */
ACCESS_RTE;
{
eattr *e = NULL;
......@@ -1074,7 +1072,7 @@ interpret(struct f_inst *what)
}
}
break;
case P('e','S'):
case FI_EA_SET:
ACCESS_RTE;
ONEARG;
{
......@@ -1180,12 +1178,12 @@ interpret(struct f_inst *what)
}
}
break;
case 'P':
case FI_PREF_GET:
ACCESS_RTE;
res.type = T_INT;
res.val.i = (*f_rte)->pref;
break;
case P('P','S'):
case FI_PREF_SET:
ACCESS_RTE;
ONEARG;
if (v1.type != T_INT)
......@@ -1195,7 +1193,7 @@ interpret(struct f_inst *what)
f_rte_cow();
(*f_rte)->pref = v1.val.i;
break;
case 'L': /* Get length of */
case FI_LENGTH: /* Get length of */
ONEARG;
res.type = T_INT;
switch(v1.type) {
......@@ -1207,7 +1205,7 @@ interpret(struct f_inst *what)
default: runtime( "Prefix, path, clist or eclist expected" );
}
break;
case P('c','p'): /* Convert prefix to ... */
case FI_IP: /* Convert prefix to ... */
ONEARG;
if (v1.type != T_PREFIX)
runtime( "Prefix expected" );
......@@ -1218,7 +1216,7 @@ interpret(struct f_inst *what)
default: bug( "Unknown prefix to conversion" );
}
break;
case P('a','f'): /* Get first ASN from AS PATH */
case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
......@@ -1228,7 +1226,7 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as;
break;
case P('a','l'): /* Get last ASN from AS PATH */
case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
......@@ -1238,7 +1236,7 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as;
break;
case P('a','L'): /* Get last ASN from non-aggregated part of AS PATH */
case FI_AS_PATH_LAST_NAG: /* Get last ASN from non-aggregated part of AS PATH */
ONEARG;
if (v1.type != T_PATH)
runtime( "AS path expected" );
......@@ -1246,23 +1244,23 @@ interpret(struct f_inst *what)
res.type = T_INT;
res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
break;
case 'r':
case FI_RETURN:
ONEARG;
res = v1;
res.type |= T_RETURN;
return res;
case P('c','a'): /* CALL: this is special: if T_RETURN and returning some value, mask it out */
case FI_CALL: /* CALL: this is special: if T_RETURN and returning some value, mask it out */
ONEARG;
res = interpret(what->a2.p);
if (res.type == T_RETURN)
return res;
res.type &= ~T_RETURN;
break;
case P('c','v'): /* Clear local variables */
case FI_CLEAR_LOCAL_VARS: /* Clear local variables */
for (sym = what->a1.p; sym != NULL; sym = sym->aux2)
((struct f_val *) sym->def)->type = T_VOID;
break;
case P('S','W'):
case FI_SWITCH:
ONEARG;
{
struct f_tree *t = find_tree(what->a2.p, v1);
......@@ -1281,7 +1279,7 @@ interpret(struct f_inst *what)
return res;
}
break;
case P('i','M'): /* IP.MASK(val) */
case FI_IP_MASK: /* IP.MASK(val) */
TWOARGS;
if (v2.type != T_INT)
runtime( "Integer expected");
......@@ -1294,11 +1292,11 @@ interpret(struct f_inst *what)
}
break;
case 'E': /* Create empty attribute */
case FI_EMPTY: /* Create empty attribute */
res.type = what->aux;
res.val.ad = adata_empty(f_pool, 0);
break;
case P('A','p'): /* Path prepend */
case FI_PATH_PREPEND: /* Path prepend */
TWOARGS;
if (v1.type != T_PATH)
runtime("Can't prepend to non-path");
......@@ -1309,7 +1307,7 @@ interpret(struct f_inst *what)
res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
break;
case P('C','a'): /* (Extended) Community list add or delete */
case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */
TWOARGS;
if (v1.type == T_PATH)
{
......@@ -1477,7 +1475,7 @@ interpret(struct f_inst *what)
break;
case P('R','C'): /* ROA Check */
case FI_ROA_CHECK: /* ROA Check */
if (what->arg1)
{
TWOARGS;
......@@ -1511,7 +1509,7 @@ interpret(struct f_inst *what)
break;
default:
bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff);
bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
}
if (what->next)
return interpret(what->next);
......@@ -1541,38 +1539,38 @@ i_same(struct f_inst *f1, struct f_inst *f2)
return 1;
if (f1->aux != f2->aux)
return 0;
if (f1->code != f2->code)
if (f1->fi_code != f2->fi_code)
return 0;
if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */
return 1;
switch(f1->code) {
case ',': /* fall through */
case '+':
case '-':
case '*':
case '/':
case '|':
case '&':
case P('m','p'):
case P('m','c'):
case P('!','='):
case P('=','='):
case '<':
case P('<','='): TWOARGS; break;
case '!': ONEARG; break;
case P('!', '~'):
case '~': TWOARGS; break;
case P('d','e'): ONEARG; break;
case P('m','l'):
switch(f1->fi_code) {
case FI_COMMA: /* fall through */
case FI_ADD:
case FI_SUBTRACT:
case FI_MULTIPLY:
case FI_DIVIDE:
case FI_OR:
case FI_AND:
case FI_PAIR_CONSTRUCT:
case FI_EC_CONSTRUCT:
case FI_NEQ:
case FI_EQ:
case FI_LT:
case FI_LTE: TWOARGS; break;
case FI_NOT: ONEARG; break;
case FI_NOT_MATCH:
case FI_MATCH: TWOARGS; break;
case FI_DEFINED: ONEARG; break;
case FI_LC_CONSTRUCT:
TWOARGS;
if (!i_same(INST3(f1).p, INST3(f2).p))
return 0;
break;
case 's':
case FI_SET:
ARG(v2, a2.p);
{
struct symbol *s1, *s2;
......@@ -1585,7 +1583,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
}
break;
case 'c':
case FI_CONSTANT:
switch (f1->aux) {
case T_PREFIX_SET:
......@@ -1608,43 +1606,43 @@ i_same(struct f_inst *f1, struct f_inst *f2)
}
break;
case 'C':
case FI_CONSTANT_INDIRECT:
if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p))
return 0;
break;
case 'V':
case FI_VARIABLE:
if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
return 0;
break;
case 'p': case 'L': ONEARG; break;
case '?': TWOARGS; break;
case '0': case 'E': break;
case P('p',','): ONEARG; A2_SAME; break;
case 'P':
case 'a': A2_SAME; break;
case P('e','a'): A2_SAME; break;
case P('P','S'):
case P('a','S'):
case P('e','S'): ONEARG; A2_SAME; break;
case 'r': ONEARG; break;
case P('c','p'): ONEARG; break;
case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
case FI_PRINT: case FI_LENGTH: ONEARG; break;
case FI_CONDITION: TWOARGS; break;
case FI_NOP: case FI_EMPTY: break;
case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break;
case FI_PREF_GET:
case FI_RTA_GET: A2_SAME; break;
case FI_EA_GET: A2_SAME; break;
case FI_PREF_SET:
case FI_RTA_SET:
case FI_EA_SET: ONEARG; A2_SAME; break;
case FI_RETURN: ONEARG; break;
case FI_IP: ONEARG; break;
case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */
ONEARG;
if (!i_same(f1->a2.p, f2->a2.p))
return 0;
f2->a2.p = f1->a2.p;
break;
case P('c','v'): break; /* internal instruction */
case P('S','W'): ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
case P('i','M'): TWOARGS; break;
case P('A','p'): TWOARGS; break;
case P('C','a'): TWOARGS; break;
case P('a','f'):
case P('a','l'):
case P('a','L'): ONEARG; break;
case P('R','C'):
case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */
case FI_SWITCH: ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
case FI_IP_MASK: TWOARGS; break;
case FI_PATH_PREPEND: TWOARGS; break;
case FI_CLIST_ADD_DEL: TWOARGS; break;
case FI_AS_PATH_FIRST:
case FI_AS_PATH_LAST:
case FI_AS_PATH_LAST_NAG: ONEARG; break;
case FI_ROA_CHECK:
TWOARGS;
/* Does not really make sense - ROA check resuls may change anyway */
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
......@@ -1652,7 +1650,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
return 0;
break;
default:
bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff);
bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff);
}
return i_same(f1->next, f2->next);
}
......
......@@ -14,9 +14,67 @@
#include "nest/route.h"
#include "nest/attrs.h"
/* Filter instruction types */
#define FI__TWOCHAR(a,b) ((a<<8) | b)
#define FI__LIST \
F(FI_COMMA, 0, ',') \
F(FI_ADD, 0, '+') \
F(FI_SUBTRACT, 0, '-') \
F(FI_MULTIPLY, 0, '*') \
F(FI_DIVIDE, 0, '/') \
F(FI_AND, 0, '&') \
F(FI_OR, 0, '|') \
F(FI_PAIR_CONSTRUCT, 'm', 'p') \
F(FI_EC_CONSTRUCT, 'm', 'c') \
F(FI_LC_CONSTRUCT, 'm', 'l') \
F(FI_NEQ, '!', '=') \
F(FI_EQ, '=', '=') \
F(FI_LT, 0, '<') \
F(FI_LTE, '<', '=') \
F(FI_NOT, 0, '!') \
F(FI_MATCH, 0, '~') \
F(FI_NOT_MATCH, '!', '~') \
F(FI_DEFINED, 'd', 'e') \
F(FI_SET, 0, 's') \
F(FI_CONSTANT, 0, 'c') \
F(FI_VARIABLE, 0, 'V') \
F(FI_CONSTANT_INDIRECT, 0, 'C') \
F(FI_PRINT, 0, 'p') \
F(FI_CONDITION, 0, '?') \
F(FI_NOP, 0, '0') \
F(FI_PRINT_AND_DIE, 'p', ',') \
F(FI_RTA_GET, 0, 'a') \
F(FI_RTA_SET, 'a', 'S') \
F(FI_EA_GET, 'e', 'a') \
F(FI_EA_SET, 'e', 'S') \
F(FI_PREF_GET, 0, 'P') \
F(FI_PREF_SET, 'P', 'S') \
F(FI_LENGTH, 0, 'L') \
F(FI_IP, 'c', 'p') \
F(FI_AS_PATH_FIRST, 'a', 'f') \
F(FI_AS_PATH_LAST, 'a', 'l') \
F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
F(FI_RETURN, 0, 'r') \
F(FI_CALL, 'c', 'a') \
F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
F(FI_SWITCH, 'S', 'W') \
F(FI_IP_MASK, 'i', 'M') \
F(FI_EMPTY, 0, 'E') \
F(FI_PATH_PREPEND, 'A', 'p') \
F(FI_CLIST_ADD_DEL, 'C', 'a') \
F(FI_ROA_CHECK, 'R', 'C')
enum f_instruction_code {
#define F(c,a,b) \
c = FI__TWOCHAR(a,b),
FI__LIST