]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge branch 'master' into int-new
authorJan Maria Matejka <mq@ucw.cz>
Tue, 13 Mar 2018 15:51:04 +0000 (16:51 +0100)
committerJan Maria Matejka <mq@ucw.cz>
Tue, 13 Mar 2018 15:51:04 +0000 (16:51 +0100)
1  2 
conf/confbase.Y
filter/config.Y
filter/f-util.c
filter/filter.c
filter/filter.h

diff --cc conf/confbase.Y
index 55615528f137b5592dab28cb3bc8c089cbd64800,b8deed54e810681d5615dcd0c79da0c954d33eb2..72f56f1e9872fec2bc324b8376bf06c5b5202fe2
@@@ -47,8 -42,9 +47,10 @@@ CF_DECL
    struct symbol *s;
    char *t;
    struct rtable_config *r;
 +  struct channel_config *cc;
    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;
diff --cc filter/config.Y
index cd5a5b33dedc60ad80a17660770729d5ba132976,1ef5a3a8b039ff2342716cb7b62307812ef5b54f..6b7bedaf3b9a68245581ceb1da5ba10ceb1dadf7
@@@ -232,8 -227,9 +227,8 @@@ f_generate_ec(u16 kind, struct f_inst *
        cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
    }
  
 -#ifndef IPV6
    /* IP->Quad implicit conversion */
-   else if (tk->code == 'C') {
+   else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
      c1 = 1;
      struct f_val *val = tk->a1.p;
  
      else
        cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
    }
 -#endif
  
-   if (tv->code == 'c') {
+   if (tv->fi_code == FI_CONSTANT) {
      if (tv->aux != T_INT)
        cf_error("Can't operate with value of non-integer type in EC constructor");
      c2 = 1;
@@@ -323,71 -317,7 +315,70 @@@ f_generate_lc(struct f_inst *t1, struc
    return rv;
  }
  
 +/*
 + * Remove all new lines and doubled whitespaces
 + * and convert all tabulators to spaces
 + * and return a copy of string
 + */
 +char *
 +assert_copy_expr(const char *start, size_t len)
 +{
 +  /* XXX: Allocates maybe a little more memory than we really finally need */
 +  char *str = cfg_alloc(len + 1);
 +
 +  char *dst = str;
 +  const char *src = start - 1;
 +  const char *end = start + len;
 +  while (++src < end)
 +  {
 +    if (*src == '\n')
 +      continue;
 +
 +    /* Skip doubled whitespaces */
 +    if (src != start)
 +    {
 +      const char *prev = src - 1;
 +      if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
 +      continue;
 +    }
 +
 +    if (*src == '\t')
 +      *dst = ' ';
 +    else
 +      *dst = *src;
 +
 +    dst++;
 +  }
 +  *dst = '\0';
  
-   i = f_new_inst();
-   i->code = P('a','s');
 +  return str;
 +}
 +
 +/*
 + * assert_done - create f_instruction of bt_assert
 + * @expr: expression in bt_assert()
 + * @start: pointer to first char of test expression
 + * @end: pointer to the last char of test expression
 + */
 +static struct f_inst *
 +assert_done(struct f_inst *expr, const char *start, const char *end)
 +{
 +  struct f_inst *i;
++  i = f_new_inst(FI_ASSERT);
 +  i->a1.p = expr;
 +
 +  if (end >= start)
 +  {
 +    i->a2.p = assert_copy_expr(start, end - start + 1);
 +  }
 +  else
 +  {
 +    /* this is a break of lexer buffer */
 +    i->a2.p = "???";
 +  }
 +
 +  return i;
 +}
  
  CF_DECLS
  
@@@ -775,18 -691,24 +764,18 @@@ bgp_path_tail
   |                            { $$ = NULL; }
   ;
  
 -bgp_path_tail2:
 -   NUM bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
 - | '?' bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
 - |                  { $$ = NULL; }
 - ;
 -
  constant:
-    NUM    { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT;  $$->a2.i = $1; }
-  | TRUE   { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1;  }
-  | FALSE  { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0;  }
-  | TEXT   { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
-  | fipa         { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
-  | VPN_RD { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
-  | net_   { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
-  | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
-  | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET;  $$->a2.p = $2; }
-  | ENUM         { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
-  | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
+    NUM    { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT;  $$->a2.i = $1; }
+  | TRUE   { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1;  }
+  | FALSE  { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0;  }
+  | TEXT   { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
 - | fipa          { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
 - | fprefix_s {NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
 - | RTRID  { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_QUAD;  $$->a2.i = $1; }
++ | fipa         { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
++ | VPN_RD { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
++ | net_   { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
+  | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
+  | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET;  $$->a2.p = $2; }
+  | ENUM         { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
+  | bgp_path { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
   ;
  
  constructor:
@@@ -844,15 -763,16 +830,15 @@@ symbol
     }
  
  static_attr:
-    FROM    { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = SA_FROM;   $$->a1.i = 1; }
-  | GW      { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = SA_GW;     $$->a1.i = 1; }
-  | NET     { $$ = f_new_inst(); $$->aux = T_NET;        $$->a2.i = SA_NET; }
-  | PROTO   { $$ = f_new_inst(); $$->aux = T_STRING;     $$->a2.i = SA_PROTO; }
-  | SOURCE  { $$ = f_new_inst(); $$->aux = T_ENUM_RTS;   $$->a2.i = SA_SOURCE; }
-  | SCOPE   { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE;  $$->a1.i = 1; }
-  | DEST    { $$ = f_new_inst(); $$->aux = T_ENUM_RTD;   $$->a2.i = SA_DEST;   $$->a1.i = 1; }
-  | IFNAME  { $$ = f_new_inst(); $$->aux = T_STRING;     $$->a2.i = SA_IFNAME; }
-  | IFINDEX { $$ = f_new_inst(); $$->aux = T_INT;        $$->a2.i = SA_IFINDEX; }
+    FROM    { $$ = f_new_static_attr(T_IP,         SA_FROM,    1); }
+  | GW      { $$ = f_new_static_attr(T_IP,         SA_GW,      1); }
 - | NET     { $$ = f_new_static_attr(T_PREFIX,     SA_NET,     0); }
++ | NET     { $$ = f_new_static_attr(T_NET,      SA_NET,       0); }
+  | PROTO   { $$ = f_new_static_attr(T_STRING,     SA_PROTO,   0); }
+  | SOURCE  { $$ = f_new_static_attr(T_ENUM_RTS,   SA_SOURCE,  0); }
+  | SCOPE   { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE,   1); }
 - | CAST    { $$ = f_new_static_attr(T_ENUM_RTC,   SA_CAST,    0); }
+  | DEST    { $$ = f_new_static_attr(T_ENUM_RTD,   SA_DEST,    1); }
+  | IFNAME  { $$ = f_new_static_attr(T_STRING,     SA_IFNAME,  0); }
+  | IFINDEX { $$ = f_new_static_attr(T_INT,        SA_IFINDEX, 0); }
   ;
  
  term:
   | constant { $$ = $1; }
   | constructor { $$ = $1; }
  
-  | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
+  | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
  
-  | rtadot static_attr { $$ = $2; $$->code = 'a'; }
+  | rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); }
  
-  | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
+  | rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); }
  
-  | term '.' IS_V4 { $$ = f_new_inst(); $$->code = P('I','i'); $$->a1.p = $1; }
-  | term '.' TYPE { $$ = f_new_inst(); $$->code = 'T'; $$->a1.p = $1; }
-  | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
-  | term '.' RD { $$ = f_new_inst(); $$->code = P('R','D'); $$->a1.p = $1; $$->aux = T_RD; }
-  | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
-  | term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
-  | term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
-  | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
-  | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
-  | term '.' LAST  { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
-  | term '.' LAST_NONAGGREGATED  { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
++ | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
++ | term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
+  | term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
++ | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
+  | term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
++ | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
++ | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
+  | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
+  | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
+  | term '.' LAST  { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; }
+  | term '.' LAST_NONAGGREGATED  { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; }
  
  /* Communities */
  /* This causes one shift/reduce conflict
   | rtadot dynamic_attr '.' RESET{ }
  */
  
-  | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
-  | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
-  | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
-  | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; }
-  | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
-  | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
-  | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
-  | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
+  | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
+  | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
+  | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
+  | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
+  | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
+  | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
+  | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
+  | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
  
 - | ROA_CHECK '(' SYM ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
 - | ROA_CHECK '(' SYM ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
 + | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
 + | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
 +
-  | FORMAT '(' term ')' {  $$ = f_new_inst(); $$->code = P('f','m'); $$->a1.p = $3; }
++ | FORMAT '(' term ')' {  $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; }
 +
 +/* | term '.' LEN { $$->code = P('P','l'); } */
  
  /* function_call is inlined here */
   | SYM '(' var_list ')' {
        $$->a2.p = build_tree( $4 );
     }
  
 -
   | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
-  | rtadot dynamic_attr '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
-  | rtadot dynamic_attr '.' ADD '(' term ')' ';'       { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
-  | rtadot dynamic_attr '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
-  | rtadot dynamic_attr '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
+  | rtadot dynamic_attr '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
+  | rtadot dynamic_attr '.' ADD '(' term ')' ';'       { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); }
+  | rtadot dynamic_attr '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); }
+  | rtadot dynamic_attr '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); }
 + | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
   ;
  
 +get_cf_position:
 +{
 +  $$ = cf_text;
 +};
 +
 +
  CF_END
diff --cc filter/f-util.c
index 52c132231a76725210950107926d57ce7b2de50d,42b08868b3971d5f294710805b9680255535d886..68aecd738f6f35ed3f1fc1210b9432886cad9fef
@@@ -24,13 -24,22 +24,22 @@@ f_new_inst(enum f_instruction_code fi_c
  }
  
  struct f_inst *
- f_new_dynamic_attr(int type, int f_type, 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 = (f_type << 8) | type;
-   f->a2.i = code;
-   return f;
+   struct f_inst *ret = f_new_inst(fi_code);
 -  ret->aux = da.type;
++  ret->aux = (da.f_type << 8) | 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;
  }
  
  /*
@@@ -54,11 -60,12 +60,11 @@@ f_generate_complex(int operation, int o
    return set_dyn;
  }
  
 -
  struct f_inst *
 -f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn)
 +f_generate_roa_check(struct rtable_config *table, 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;
diff --cc filter/filter.c
index 8cf90b5356fb74d07cfc55cf8aa4cf769ee78b49,85721dbdc011254a9419ff96316d10a655b19ed0..f81f5cfec4b5d9685ecfd4f61f92e68edc05ec63
  #include "conf/conf.h"
  #include "filter/filter.h"
  
- #define P(a,b) ((a<<8) | b)
  #define CMP_ERROR 999
  
 +void (*bt_assert_hook)(int result, struct f_inst *assert);
 +
 +static struct adata undef_adata;      /* adata of length 0 used for undefined */
 +
 +/* Special undef value for paths and clists */
 +static inline int
 +undef_value(struct f_val v)
 +{
 +  return ((v.type == T_PATH) || (v.type == T_CLIST) ||
 +        (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
 +    (v.val.ad == &undef_adata);
 +}
 +
  static struct adata *
  adata_empty(struct linpool *pool, int l)
  {
@@@ -462,10 -471,11 +460,9 @@@ val_in_range(struct f_val v1, struct f_
  
    if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST))
      return int_set_contains(v2.val.ad, v1.val.i);
 -#ifndef IPV6
    /* IP->Quad implicit conversion */
 -  if ((v1.type == T_IP) && (v2.type == T_CLIST))
 -    return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.px.ip));
 -#endif
 +  if (val_is_ip4(v1) && (v2.type == T_CLIST))
 +    return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip));
  
    if ((v1.type == T_EC) && (v2.type == T_ECLIST))
      return ec_set_contains(v2.val.ad, v1.val.ec);
@@@ -824,29 -825,10 +819,29 @@@ 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);
 +    res.val.i = (v1.type != T_VOID) && !undef_value(v1);
 +    break;
-   case 'T':
++  case FI_TYPE:
 +    ONEARG;
 +    switch (v1.type)
 +    {
 +      case T_NET:
 +      res.type = T_ENUM_NETTYPE;
 +      res.val.i = v1.val.net->type;
 +      break;
 +      default:
 +      runtime( "Can't determine type of this item" );
 +    }
 +    break;
-   case P('I','i'):
++  case FI_IS_V4:
 +    ONEARG;
 +    if (v1.type != T_IP)
 +      runtime( "IP version check needs an IP address" );
 +    res.type = T_BOOL;
 +    res.val.i = ipa_is_ip4(v1.val.ip);
      break;
  
    /* Set to indirect value, a1 = variable, a2 = value */
      default: runtime( "Prefix, path, clist or eclist expected" );
      }
      break;
-   case P('R','m'):    /* Get ROA max prefix length */
++  case FI_ROA_MAXLEN:         /* Get ROA max prefix length */
 +    ONEARG;
 +    if (v1.type != T_NET || !net_is_roa(v1.val.net))
 +      runtime( "ROA expected" );
 +
 +    res.type = T_INT;
 +    res.val.i = (v1.val.net->type == NET_ROA4) ?
 +      ((net_addr_roa4 *) v1.val.net)->max_pxlen :
 +      ((net_addr_roa6 *) v1.val.net)->max_pxlen;
 +    break;
-   case P('R','a'):    /* Get ROA ASN */
++  case FI_ROA_ASN:    /* Get ROA ASN */
 +    ONEARG;
 +    if (v1.type != T_NET || !net_is_roa(v1.val.net))
 +      runtime( "ROA expected" );
 +
 +    res.type = T_INT;
 +    res.val.i = (v1.val.net->type == NET_ROA4) ?
 +      ((net_addr_roa4 *) v1.val.net)->asn :
 +      ((net_addr_roa6 *) v1.val.net)->asn;
 +    break;
-   case P('c','p'):    /* Convert prefix to ... */
+   case FI_IP: /* Convert prefix to ... */
      ONEARG;
 -    if (v1.type != T_PREFIX)
 +    if (v1.type != T_NET)
        runtime( "Prefix expected" );
 -    res.type = what->aux;
 -    switch(res.type) {
 -      /*    case T_INT:       res.val.i = v1.val.px.len; break; Not needed any more */
 -    case T_IP: res.val.px.ip = v1.val.px.ip; break;
 -    default: bug( "Unknown prefix to conversion" );
 -    }
 +    res.type = T_IP;
 +    res.val.ip = net_prefix(v1.val.net);
 +    break;
-   case P('R','D'):
++  case FI_ROUTE_DISTINGUISHER:
 +    ONEARG;
 +    if (v1.type != T_NET)
 +      runtime( "Prefix expected" );
 +    if (!net_is_vpn(v1.val.net))
 +      runtime( "VPN address expected" );
 +    res.type = T_RD;
 +    res.val.ec = net_rd(v1.val.net);
      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" );
        runtime( "Integer expected");
      if (v1.type != T_IP)
        runtime( "You can mask only IP addresses" );
 -    {
 -      ip_addr mask = ipa_mkmask(v2.val.i);
 -      res.type = T_IP;
 -      res.val.px.ip = ipa_and(mask, v1.val.px.ip);
 -    }
 +
 +    res.type = T_IP;
 +    res.val.ip = ipa_is_ip4(v1.val.ip) ?
 +      ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) :
 +      ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i)));
      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;
        as_path_get_last(e->u.ptr, &as);
      }
  
 -    struct roa_table_config *rtc = ((struct f_inst_roa_check *) what)->rtc;
 -    if (!rtc->table)
 +    struct rtable *table = ((struct f_inst_roa_check *) what)->rtc->table;
 +    if (!table)
        runtime("Missing ROA table");
  
 +    if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
 +      runtime("Table type must be either ROA4 or ROA6");
 +
      res.type = T_ENUM_ROA;
 -    res.val.i = roa_check(rtc->table, v1.val.px.ip, v1.val.px.len, as);
 +
 +    if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
 +      res.val.i = ROA_UNKNOWN; /* Prefix and table type mismatch */
 +    else
 +      res.val.i = net_roa_check(table, v1.val.net, as);
 +
 +    break;
 +
-   case P('f','m'):    /* Format */
++  case FI_FORMAT:     /* Format */
 +    ONEARG;
 +
 +    res.type = T_STRING;
 +    res.val.s = val_format_str(v1);
 +    break;
 +
-   case P('a','s'):    /* Birdtest Assert */
++  case FI_ASSERT:     /* Birdtest Assert */
 +    ONEARG;
 +
 +    if (v1.type != T_BOOL)
 +      runtime("Should be boolean value");
 +
 +    res.type = v1.type;
 +    res.val = v1.val;
 +
 +    CALL(bt_assert_hook, res.val.i, what);
      break;
  
    default:
@@@ -1614,29 -1540,27 +1606,28 @@@ i_same(struct f_inst *f1, struct f_ins
    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 'T': ONEARG; break;
-   case P('n','T'): 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_TYPE: ONEARG; break;
+   case FI_LC_CONSTRUCT:
      TWOARGS;
      if (!i_same(INST3(f1).p, INST3(f2).p))
        return 0;
      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('R','D'): 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_ROUTE_DISTINGUISHER: 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 */
 +    /* Does not really make sense - ROA check results may change anyway */
      if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
               ((struct f_inst_roa_check *) f2)->rtc->name))
        return 0;
diff --cc filter/filter.h
index 49004c331652c1283a8eb725df9b7b4c1f3797e9,09f96084ea2be04e465c7c297a8a32e75170a102..b1fc6c2907da239ef07fa63077a73490b85f6bc6
  #include "nest/route.h"
  #include "nest/attrs.h"
  
 -  F(FI_ROA_CHECK,             'R', 'C')
+ /* 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_TYPE,                    0, 'T') \
++  F(FI_IS_V4,                 'I', 'i') \
+   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_ROA_MAXLEN,            'R', 'M') \
++  F(FI_ROA_ASN,                       'R', 'A') \
+   F(FI_IP,                    'c', 'p') \
++  F(FI_ROUTE_DISTINGUISHER,   'R', 'D') \
+   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') \
++  F(FI_FORMAT,                          0, 'F') \
++  F(FI_ASSERT,                        'a', 's')
+ enum f_instruction_code {
+ #define F(c,a,b) \
+   c = FI__TWOCHAR(a,b),
+ FI__LIST
+ #undef F
+ } PACKED;
  struct f_inst {               /* Instruction */
    struct f_inst *next;        /* Structure is 16 bytes, anyway */
-   u16 code;           /* Instruction code, see the interpret() function and P() macro */
+   enum f_instruction_code fi_code;
 -  u16 aux;
 +  u16 aux;            /* Extension to instruction code, T_*, EA_*, EAF_*  */
    union {
 -    int i;
 +    uint i;
      void *p;
 -  } a1;
 +  } a1;                       /* The first argument */
    union {
 -    int i;
 +    uint i;
      void *p;
 -  } a2;
 +  } a2;                       /* The second argument */
    int lineno;
  };
  
@@@ -75,11 -150,16 +152,16 @@@ struct filter 
    struct f_inst *root;
  };
  
- struct f_inst *f_new_inst(void);
- struct f_inst *f_new_dynamic_attr(int type, int f_type, int code);    /* Type as core knows it, type as filters know it, and code of dynamic attribute */
+ struct f_inst *f_new_inst(enum f_instruction_code fi_code);
+ struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da);
+ struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa);
+ static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
+ { return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; }   /* f_type currently unused; will be handy for static type checking */
+ static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
+ { return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
  struct f_tree *f_new_tree(void);
- struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument);
+ struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument);
 -struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn);
 +struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
  
  
  struct f_tree *build_tree(struct f_tree *);