]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Adds support for dynamic pair and bgp mask expressions.
authorOndrej Zajicek <santiago@crfreenet.org>
Mon, 1 Jun 2009 17:32:41 +0000 (19:32 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Mon, 1 Jun 2009 17:32:41 +0000 (19:32 +0200)
doc/bird.sgml
filter/config.Y
filter/filter.c
filter/test.conf
nest/a-path.c
nest/attrs.h

index 8d8ec35e48a72168527f3684e0b6a320a80ebeef..5027c3e4b1cdf60d7676ba3bba511ce9093ad1b0 100644 (file)
@@ -572,7 +572,8 @@ incompatible with each other (that is to prevent you from shooting in the foot).
          to +2000000000. Overflows are not checked. You can use <cf/0x1234/ syntax to write hexadecimal values.
 
        <tag/pair/ This is a pair of two short integers. Each component can have values from 0 to
-         65535. Literals of this type is written as <cf/(1234,5678)/.
+         65535. Literals of this type are written as <cf/(1234,5678)/. The same syntax can also be
+         used to construct a pair from two arbitrary integer expressions (for example <cf/(1+2,a)/).
 
        <tag/string/ This is a string of characters. There are no ways to modify strings in
          filters. You can pass them between functions, assign them to variables of type <cf/string/, print
@@ -640,6 +641,8 @@ incompatible with each other (that is to prevent you from shooting in the foot).
          For example, if <cf>bgp_path</cf> is 4 3 2 1, then:
          <tt>bgp_path &tilde; [= * 4 3 * =]</tt> is true, but 
          <tt>bgp_path &tilde; [= * 4 5 * =]</tt> is false.
+         BGP mask expressions can also contain integer expressions enclosed in parenthesis
+         and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>.
          There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *.
        <tag/clist/ 
          Community list is similar to set of pairs,
index fc2555193f019d2ab053d78d34fc7fe0227c19a9..5cff47eb17be8019916afbf0f364628e0fe51754 100644 (file)
@@ -14,6 +14,17 @@ CF_DEFINES
 
 #define P(a,b) ((a<<8) | b)
 
+static int make_pair(int i1, int i2)
+{
+  unsigned u1 = i1;
+  unsigned u2 = i2;
+
+  if ((u1 > 0xFFFF) || (u2 > 0xFFFF))
+    cf_error( "Can't operate with value out of bounds in pair constructor");
+
+  return (u1 << 16) | u2;
+}
+
 CF_DECLS
 
 CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
@@ -32,9 +43,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
 %nonassoc THEN
 %nonassoc ELSE
 
-%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call
+%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol dpair bgp_path_expr
 %type <f> filter filter_body where_filter
-%type <i> type break_command pair
+%type <i> type break_command cpair
 %type <e> set_item set_items switch_body
 %type <trie> fprefix_set
 %type <v> set_atom fprefix fprefix_s fipa
@@ -203,8 +214,8 @@ block:
 /*
  * Simple types, their bison value is int
  */
-pair:
-   '(' NUM ',' NUM ')' { $$ = $2 << 16 | $4; }
+cpair:
+   '(' NUM ',' NUM ')' { $$ = make_pair($2, $4); }
  ;
 
 /*
@@ -215,10 +226,10 @@ fipa:
  ;
 
 set_atom:
-   NUM  { $$.type = T_INT; $$.val.i = $1; }
- | pair { $$.type = T_PAIR; $$.val.i = $1; }
- | fipa { $$ = $1; }
- | ENUM {  $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; }
+   NUM   { $$.type = T_INT; $$.val.i = $1; }
+ | cpair { $$.type = T_PAIR; $$.val.i = $1; }
+ | fipa  { $$ = $1; }
+ | ENUM  {  $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; }
  ; 
 
 set_item:
@@ -277,6 +288,11 @@ switch_body: /* EMPTY */ { $$ = NULL; }
 
 /* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
 
+bgp_path_expr:
+   symbol       { $$ = $1; }   
+ | '(' term ')' { $$ = $2; }
+ ;
+
 bgp_path:
    PO  bgp_path_tail1 PC  { $$ = $2; }
  | '/' bgp_path_tail2 '/' { $$ = $2; }
@@ -286,6 +302,7 @@ bgp_path_tail1:
    NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN;      $$->val = $1; }
  | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val  = 0; }
  | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val  = 0; }
+ | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
  |                   { $$ = NULL; }
  ;
 
@@ -295,12 +312,24 @@ bgp_path_tail2:
  |                   { $$ = NULL; }
  ;
 
+dpair:
+   '(' term ',' term ')' {
+        if (($2->code == 'c') && ($4->code == 'c'))
+          { 
+            if (($2->aux != T_INT) || ($4->aux != T_INT))
+              cf_error( "Can't operate with value of non-integer type in pair constructor" );
+            $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR;  $$->a2.i = make_pair($2->a2.i, $4->a2.i);
+          }
+       else
+         { $$ = f_new_inst(); $$->code = P('m', 'p'); $$->a1.p = $2; $$->a2.p = $4; }
+    }
+ ;
+
 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; }
- | pair   { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR;  $$->a2.i = $1; }
  | fipa           { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
  | fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
  | '[' 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" ); }
@@ -309,6 +338,7 @@ constant:
  | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
  ;
 
+
 /*
  *  Maybe there are no dynamic attributes defined by protocols.
  *  For such cases, we force the dynamic_attr list to contain
@@ -342,6 +372,38 @@ function_call:
    }
  ;
 
+symbol:
+   SYM {
+     $$ = f_new_inst();
+     switch ($1->class) {
+       case SYM_NUMBER:
+       $$ = f_new_inst();
+       $$->code = 'c'; 
+       $$->aux = T_INT; 
+       $$->a2.i = $1->aux;
+       break;
+       case SYM_IPA:
+       { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); }
+       break;
+       case SYM_VARIABLE | T_BOOL:
+       case SYM_VARIABLE | T_INT:
+       case SYM_VARIABLE | T_PAIR:
+       case SYM_VARIABLE | T_STRING:
+       case SYM_VARIABLE | T_IP:
+       case SYM_VARIABLE | T_PREFIX:
+       case SYM_VARIABLE | T_PREFIX_SET:
+       case SYM_VARIABLE | T_SET:
+       case SYM_VARIABLE | T_PATH:
+       case SYM_VARIABLE | T_PATH_MASK:
+       case SYM_VARIABLE | T_CLIST:
+        $$->code = 'C';
+        $$->a1.p = $1->def;
+        break;
+       default:
+        cf_error("%s: variable expected.", $1->name );
+     }
+   }
+
 static_attr:
    FROM    { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = OFFSETOF(struct rta, from);   $$->a1.i = 1; }
 
@@ -372,37 +434,9 @@ term:
  | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
  | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e');  $$->a1.p = $3; }
 
+ | symbol   { $$ = $1; }
  | constant { $$ = $1; }
- | SYM {
-     $$ = f_new_inst();
-     switch ($1->class) {
-       case SYM_NUMBER:
-       $$ = f_new_inst();
-       $$->code = 'c'; 
-       $$->aux = T_INT; 
-       $$->a2.i = $1->aux;
-       break;
-       case SYM_IPA:
-       { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); }
-       break;
-       case SYM_VARIABLE | T_BOOL:
-       case SYM_VARIABLE | T_INT:
-       case SYM_VARIABLE | T_PAIR:
-       case SYM_VARIABLE | T_STRING:
-       case SYM_VARIABLE | T_IP:
-       case SYM_VARIABLE | T_PREFIX:
-       case SYM_VARIABLE | T_PREFIX_SET:
-       case SYM_VARIABLE | T_SET:
-       case SYM_VARIABLE | T_PATH:
-       case SYM_VARIABLE | T_PATH_MASK:
-       case SYM_VARIABLE | T_CLIST:
-        $$->code = 'C';
-        $$->a1.p = $1->def;
-        break;
-       default:
-        cf_error("%s: variable expected.", $1->name );
-     }
-   }
+ | dpair    { $$ = $1; }
 
  | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
 
index 7499843afde3d422ac0ba6820bab1378ad23fad5..9c1dfa34e4344e29d4aa01eb8197de01a657bbcc 100644 (file)
@@ -69,6 +69,8 @@ pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2)
   }
 }
 
+u32 f_eval_asn(struct f_inst *expr);
+
 static void
 pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
 {
@@ -82,10 +84,24 @@ pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
          return;
        }
 
-      if (p->kind == PM_ASN)
-       buf += bsprintf(buf, " %u", p->val);
-      else
-       buf += bsprintf(buf, (p->kind == PM_ASTERISK) ? " *" : " ?");
+      switch(p->kind)
+       {
+       case PM_ASN:
+         buf += bsprintf(buf, " %u", p->val);
+         break;
+
+       case PM_QUESTION:
+         buf += bsprintf(buf, " ?");
+         break;
+
+       case PM_ASTERISK:
+         buf += bsprintf(buf, " *");
+         break;
+
+       case PM_ASN_EXPR:
+         buf += bsprintf(buf, " %u", f_eval_asn((struct f_inst *) p->val));
+         break;
+       }
 
       p = p->next;
     }
@@ -333,6 +349,7 @@ interpret(struct f_inst *what)
 {
   struct symbol *sym;
   struct f_val v1, v2, res;
+  unsigned u1, u2;
   int i;
 
   res.type = T_VOID;
@@ -395,6 +412,18 @@ interpret(struct f_inst *what)
     res.val.i = v1.val.i || v2.val.i;
     break;
 
+  case P('m','p'):
+    TWOARGS_C;
+    if ((v1.type != T_INT) || (v2.type != T_INT))
+      runtime( "Can't operate with value of non-integer type in pair constructor" );
+    u1 = v1.val.i;
+    u2 = v2.val.i;
+    if ((u1 > 0xFFFF) || (u2 > 0xFFFF))
+      runtime( "Can't operate with value out of bounds in pair constructor" );
+    res.val.i = (u1 << 16) | u2;
+    res.type = T_PAIR;
+    break;
+
 /* Relational operators */
 
 #define COMPARE(x) \
@@ -810,6 +839,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
   case '/':
   case '|':
   case '&':
+  case P('m','p'):
   case P('!','='):
   case P('=','='):
   case '<':
@@ -933,6 +963,16 @@ f_eval_int(struct f_inst *expr)
   return res.val.i;
 }
 
+u32
+f_eval_asn(struct f_inst *expr)
+{
+  struct f_val res = interpret(expr);
+  if (res.type != T_INT)
+    cf_error("Can't operate with value of non-integer type in AS path mask constructor");
+  return res.val.i;
+}
+
 /**
  * filter_same - compare two filters
  * @new: first filter to be compared
index af88907335f43ed84ca37c448aa495b3cecd442d..7c05af052a1f7fff295103356b70c89a521306e1 100644 (file)
@@ -9,6 +9,17 @@ router id 62.168.0.1;
 
 define xyzzy = (120+10);
 
+
+function mkpair(int a)
+{
+       return (1, a);
+}
+
+function mkpath(int a; int b)
+{
+       return [= a b 3 2 1 =];
+}
+
 function callme(int arg1; int arg2)
 int local1;
 int local2;
@@ -50,6 +61,7 @@ clist l;
        print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2;
        print "Should be true: ", p2 ~  / ? 4 3 2 1 /,  " ", p2, " ",  / ? 4 3 2 1 /;
        print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =];
+       print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4);
        print "5 = ", p2.len;
        
        pm1 = [= 1 2 * 3 4 5 =];
@@ -143,11 +155,15 @@ string s;
 
        px = 1.2.0.0/18;
        print "Testing prefixes: 1.2.0.0/18 = ", px;
+       print "  must be true:  ",      192.168.0.0/16 ~ 192.168.0.0/16, " ", 192.168.0.0/17 ~ 192.168.0.0/16, " ", 192.168.254.0/24 ~ 192.168.0.0/16;
+       print "  must be false: ",      192.168.0.0/15 ~ 192.168.0.0/16, " ", 192.160.0.0/17 ~ 192.168.0.0/16;
+
        p = 127.1.2.3;
        print "Testing mask : 127.0.0.0 = ", p.mask(8);
        
        pp = (1, 2);
-       print "Testing pairs: (1,2) = ", (1,2), " = ", pp;
+       print "Testing pairs: (1,2) = ", (1,2), " = ", pp, " = ", (1,1+1), " = ", mkpair(2);
+       print "  must be true:  ", (1,2) = (1,1+1);
        print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC;
 
        s = "Hello";
index f549987743a45b23b2b67a69aa897e22b0badd0f..dba214d21bed6522e2dd29c67f1412bf20e17e9b 100644 (file)
@@ -401,6 +401,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
   struct pm_pos pos[2048 + 1];
   int plen = parse_path(path, pos);
   int l, h, i, nh, nl;
+  u32 val;
 
   /* l and h are bound of interval of positions where
      are marked states */
@@ -424,14 +425,20 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
          h = plen;
          break;
 
-       case PM_QUESTION:
        case PM_ASN:
+         val = mask->val;
+         goto step;
+       case PM_ASN_EXPR:
+         val = f_eval_asn((struct f_inst *) mask->val);
+         goto step;
+       case PM_QUESTION:
+       step:
          nh = -1;
          for (i = h; i >= l; i--)
            if (pos[i].mark)
              {
                pos[i].mark = 0;
-               if ((mask->kind == PM_QUESTION) || pm_match(pos + i, mask->val))
+               if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val))
                  pm_mark(pos, i, plen, &nl, &nh);
              }
 
index 5542be6fb5305282e268e8168b48c5db71fe3f0d..b838ce96799f67fcb86879b63d564a2d809e09c9 100644 (file)
@@ -35,11 +35,12 @@ int as_path_is_member(struct adata *path, u32 as);
 #define PM_ASN         0
 #define PM_QUESTION    1
 #define PM_ASTERISK    2
+#define PM_ASN_EXPR    3
 
 struct f_path_mask {
   struct f_path_mask *next;
   int kind;
-  u32 val;
+  uintptr_t val;
 };
 
 int as_path_match(struct adata *path, struct f_path_mask *mask);