]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Extends set operations in filters.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 15 Mar 2012 19:42:29 +0000 (20:42 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 15 Mar 2012 20:07:58 +0000 (21:07 +0100)
Allows add/filter/delete clist on clist (set algebra on clists).

Allows number ~ bgppath match.

doc/bird.sgml
filter/filter.c
filter/test.conf
nest/a-set.c
nest/attrs.h

index f06e21c916a29feb0841036133866daee000cacd..4abe706c2a211da5f2099f30943700995217a38e 100644 (file)
@@ -875,19 +875,22 @@ incompatible with each other (that is to prevent you from shooting in the foot).
 
           <cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist
          <m/C/ and returns the result.  If item <m/P/ is already in
-         clist <m/C/, it does nothing.
+         clist <m/C/, it does nothing. <m/P/ may also be a clist,
+         in that case all its members are added; i.e., it works as clist union.
 
           <cf>delete(<m/C/,<m/P/)</cf> deletes pair (or quad)
          <m/P/ from clist <m/C/ and returns the result.  If clist
          <m/C/ does not contain item <m/P/, it does nothing.
          <m/P/ may also be a pair (or quad) set, in that case the
          operator deletes all items from clist <m/C/ that are also
-         members of set <m/P/.
+         members of set <m/P/. Moreover, <m/P/ may also be a clist,
+         which works analogously; i.e., it works as clist difference.
 
           <cf>filter(<m/C/,<m/P/)</cf> deletes all items from clist
          <m/C/ that are not members of pair (or quad) set <m/P/.
          I.e., <cf/filter/ do the same as <cf/delete/ with inverted
-         set <m/P/.
+         set <m/P/. <m/P/ may also be a clist, which works analogously;
+         i.e., it works as clist intersection.
 
           Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to
           <cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route
@@ -910,7 +913,7 @@ incompatible with each other (that is to prevent you from shooting in the foot).
 Special operators include <cf/&tilde;/ for "is element of a set" operation - it can be
 used on element and set of elements of the same type (returning true if element is contained in the given set), or
 on two strings (returning true if first string matches a shell-like pattern stored in second string) or on IP and prefix (returning true if IP is within the range defined by that prefix), or on
-prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set).
+prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on number and bgppath (returning true if the number is in the path) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set).
 
 
 <sect>Control structures
index d6d338bf91b5d3f463d91758969ec0b490aced54..a087c50b0ba13e68dcc02b88d8c2aa1e361f746c 100644 (file)
@@ -228,6 +228,9 @@ val_simple_in_range(struct f_val v1, struct f_val v2)
 {
   if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK))
     return as_path_match(v1.val.ad, v2.val.path_mask);
+  if ((v1.type == T_INT) && (v2.type == T_PATH))
+    return as_path_is_member(v2.val.ad, v1.val.i);
+
   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
@@ -320,28 +323,34 @@ eclist_match_set(struct adata *list, struct f_tree *set)
 }
 
 static struct adata *
-clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int pos)
+clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
 {
-  if (!clist)
+  if (!list)
     return NULL;
 
+  int tree = (set.type == T_SET);      /* 1 -> set is T_SET, 0 -> set is T_CLIST */
   struct f_val v;
-  clist_set_type(set, &v);
+  if (tree)
+    clist_set_type(set.val.t, &v);
+  else
+    v.type = T_PAIR;
 
-  u32 tmp[clist->length/4];
-  u32 *l = (u32 *) clist->data;
+  int len = int_set_get_size(list);
+  u32 *l = int_set_get_data(list);
+  u32 tmp[len];
   u32 *k = tmp;
-  u32 *end = l + clist->length/4;
+  u32 *end = l + len;
 
   while (l < end) {
     v.val.i = *l++;
-    if (pos == !!find_tree(set, v))    /* pos && find_tree || !pos && !find_tree */
+    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
+    if ((tree ? !!find_tree(set.val.t, v) : int_set_contains(set.val.ad, v.val.i)) == pos)
       *k++ = v.val.i;
   }
 
   int nl = (k - tmp) * 4;
-  if (nl == clist->length)
-    return clist;
+  if (nl == list->length)
+    return list;
 
   struct adata *res = adata_empty(pool, nl);
   memcpy(res->data, tmp, nl);
@@ -349,11 +358,12 @@ clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int
 }
 
 static struct adata *
-eclist_filter(struct linpool *pool, struct adata *list, struct f_tree *set, int pos)
+eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
 {
   if (!list)
     return NULL;
 
+  int tree = (set.type == T_SET);      /* 1 -> set is T_SET, 0 -> set is T_CLIST */
   struct f_val v;
 
   int len = int_set_get_size(list);
@@ -365,7 +375,8 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_tree *set, int
   v.type = T_EC;
   for (i = 0; i < len; i += 2) {
     v.val.ec = ec_get(l, i);
-    if (pos == !!find_tree(set, v)) {  /* pos && find_tree || !pos && !find_tree */
+    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
+    if ((tree ? !!find_tree(set.val.t, v) : ec_set_contains(set.val.ad, v.val.ec)) == pos) {
       *k++ = l[i];
       *k++ = l[i+1];
     }
@@ -1116,6 +1127,8 @@ interpret(struct f_inst *what)
 #endif
       else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
        arg_set = 1;
+      else if (v2.type == T_CLIST)
+       arg_set = 2;
       else
        runtime("Can't add/delete non-pair");
 
@@ -1123,22 +1136,25 @@ interpret(struct f_inst *what)
       switch (what->aux)
       {
       case 'a':
-       if (arg_set)
+       if (arg_set == 1)
          runtime("Can't add set");
-       res.val.ad = int_set_add(f_pool, v1.val.ad, i);
+       else if (!arg_set)
+         res.val.ad = int_set_add(f_pool, v1.val.ad, i);
+       else 
+         res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad);
        break;
       
       case 'd':
        if (!arg_set)
          res.val.ad = int_set_del(f_pool, v1.val.ad, i);
        else
-         res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 0);
+         res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 0);
        break;
 
       case 'f':
        if (!arg_set)
          runtime("Can't filter pair");
-       res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 1);
+       res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 1);
        break;
 
       default:
@@ -1153,6 +1169,8 @@ interpret(struct f_inst *what)
       /* v2.val is either EC or EC-set */
       if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
        arg_set = 1;
+      else if (v2.type == T_ECLIST)
+       arg_set = 2;
       else if (v2.type != T_EC)
        runtime("Can't add/delete non-pair");
 
@@ -1160,22 +1178,25 @@ interpret(struct f_inst *what)
       switch (what->aux)
       {
       case 'a':
-       if (arg_set)
+       if (arg_set == 1)
          runtime("Can't add set");
-       res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
+       else if (!arg_set)
+         res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
+       else 
+         res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad);
        break;
       
       case 'd':
        if (!arg_set)
          res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec);
        else
-         res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 0);
+         res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 0);
        break;
 
       case 'f':
        if (!arg_set)
          runtime("Can't filter ec");
-       res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 1);
+       res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 1);
        break;
 
       default:
index 4f09637c12d4dea39577e6ab0b7230dd923eb540..dbb05de8a27a66cbac88c08b540124a4160dda9d 100644 (file)
@@ -57,7 +57,9 @@ bgpmask pm1;
 bgpmask pm2;
 bgppath p2;
 clist l;
+clist l2;
 eclist el;
+eclist el2;
 {
        pm1 =  / 4 3 2 1 /;
        pm2 = [= 4 3 2 1 =];
@@ -67,10 +69,10 @@ eclist el;
        p2 = prepend( p2, 3 );
        p2 = prepend( p2, 4 );
        print "Testing paths: ", p2;
-       print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2;
+       print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 3 ~ p2;
        print "4 = ", p2.len;
        p2 = prepend( p2, 5 );
-       print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2;
+       print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2;
        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);
@@ -108,6 +110,7 @@ eclist el;
        l = add( l, (3,3) );
        l = add( l, (3,4) );
        l = add( l, (3,5) );
+       l2 = filter( l, [(3,*)] );
        l = delete( l, [(3,2..4)] );
        print "Community list (1,2) (3,1) (3,5) ", l;
        l = add( l, (3,2) );
@@ -120,6 +123,14 @@ eclist el;
        print "Community list (3,1) ", l;
        l = delete( l, [(*,(onef(5)))] );
        print "Community list empty ", l;
+       l2 = add( l2, (3,6) );
+       l = filter( l2, [(3,1..4)] );
+       l2 = filter( l2, [(3,3..6)] );
+       print "clist A (1..4): ", l;
+       print "clist B (3..6): ", l2;
+       print "clist A union B: ", add( l2, l );
+       print "clist A isect B: ", filter( l, l2 );
+       print "clist A \  B: ", delete( l, l2 );
 
        el = -- empty --;
        el = add(el, (rt, 10, 20));
@@ -143,6 +154,16 @@ eclist el;
        print "EC list (rt, 10, 1) (rt, 10, 30): ", el;
        print "Testing EC list, true: ", (rt, 10, 1) ~ el, " ", el ~ [(rt, 10, ten..40)];
        print "Testing EC list, false: ", (rt, 10, 20) ~ el, " ", (ro, 10.20.30.40, 100) ~ el, " ", el ~ [(rt, 10, 35..40)], " ", el ~ [(ro, 10, *)];
+       el = add(el, (rt, 10, 40));
+       el2 = filter(el, [(rt, 10, 20..40)] );
+       el2 = add(el2, (rt, 10, 50));
+       print "eclist A (1,30,40): ", el;
+       print "eclist B (30,40,50): ", el2;
+       print "eclist A union B: ", add( el2, el );
+       print "eclist A isect B: ", filter( el, el2 );
+       print "eclist A \  B: ", delete( el, el2 );
+
+
 }
 
 function bla()
index 020d09785c4685c55b7278e0766b8635a2d2a367..42ef9b06352c675f5e66a095d31bc82db06caefa 100644 (file)
@@ -264,3 +264,67 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val)
 
   return res;
 }
+
+
+struct adata *
+int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
+{
+  if (!l1)
+    return l2;
+  if (!l2)
+    return l1;
+
+  struct adata *res;
+  int len = int_set_get_size(l2);
+  u32 *l = int_set_get_data(l2);
+  u32 tmp[len];
+  u32 *k = tmp;
+  int i;
+
+  for (i = 0; i < len; i++)
+    if (!int_set_contains(l1, l[i]))
+      *k++ = l[i];
+
+  if (k == tmp)
+    return l1;
+
+  len = (k - tmp) * 4;
+  res = lp_alloc(pool, sizeof(struct adata) + l1->length + len);
+  res->length = l1->length + len;
+  memcpy(res->data, l1->data, l1->length);
+  memcpy(res->data + l1->length, tmp, len);
+  return res;
+}
+
+struct adata *
+ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
+{
+  if (!l1)
+    return l2;
+  if (!l2)
+    return l1;
+
+  struct adata *res;
+  int len = int_set_get_size(l2);
+  u32 *l = int_set_get_data(l2);
+  u32 tmp[len];
+  u32 *k = tmp;
+  int i;
+
+  for (i = 0; i < len; i += 2)
+    if (!ec_set_contains(l1, ec_get(l, i)))
+      {
+       *k++ = l[i];
+       *k++ = l[i+1];
+      }
+
+  if (k == tmp)
+    return l1;
+
+  len = (k - tmp) * 4;
+  res = lp_alloc(pool, sizeof(struct adata) + l1->length + len);
+  res->length = l1->length + len;
+  memcpy(res->data, l1->data, l1->length);
+  memcpy(res->data + l1->length, tmp, len);
+  return res;
+}
index 85e4e59a6d9169712b71003a810e7155660fca3f..42f81a103d9ad518579659f9027385ce39b7dc6b 100644 (file)
@@ -96,6 +96,8 @@ struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
 struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val);
 struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
 struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val);
+struct adata *int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
+struct adata *ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
 
 
 #endif