В письме от 21 августа 2013 22:58:48 Вы написали:
> Среда, 21 августа 2013, 20:46 +02:00 от Ondrej Zajicek 
<santi...@crfreenet.org>:

> >> Can some one explain to me this behavior of BIRD? Does this mean
> >> that usage of constats with type prefix set is not recommended?
> >
> >Hello
> >
> >This is just a stupid mistake. Sets were not properly compared.
> >Use attached patch.
> 
> Huge thanks! Tested and found working!
> 

There are at least one other caveat related to comparison, not covered by this 
patch:

    How to determine if clist, eclist are empty? Statements like
      bgp_community_list = -empty- or
      bgp_ext_community_list = --empty--
    gives filter runtime error. Same reason for comparing bgppath type
    with +empty+ (i.e. bgp_path = +empty+), however for this we could
    use something like bgp_path.len = 0)?
 
    Interesting thing about comparison I found in filter/test.conf2:
      clist ~ [(*,*)]
    but this is not documented.

So I consider to submit my attempt to address this caveat and add some other 
useful things related to comparison:

  * Move same_tree() for SET comparison and trie_same() for PREFIX_SET 
    comparison into val_compare(). Now statements like
    bgp_community_list = -empty- and bgp_ext_community = --empty-- works.
    I think this could be done as there at least one function, that violates
    return code of val_compare() (-1,0,1): pm_path_compare().

  * Intdodoce list_compare() to compare clist and eclist as arrays of u32, not 
    much useful, but I think it does not broke something. This is done to 
    extend val_compare() to handle all known filter types (probably).

  * Introduce path_compare() to compare bgppath type. Compares path length,
    and element by element, considering AS_PATH_SET more longer. Reason
    for adding same as with list_compare().

  * Add minor optimisations on speed when comparing with int_cmp(), 
    uint_cmp(), u64_cmp(), by removing branching.

  * Split comparison to T_VOID in val_compare() in two parts, when types are 
    equal (add case to switch) and when are not. Do similar in val_in_range().

  * Document -empty-, --empty-- and +empty+ special purpose constants.
    Also document S.empty statement for use with dynamic attributes.

Another patch, just adds some other possibilities to check length of clist, 
eclist and ip address. Last could be used to determine AFI for which bird is 
build (see example function in documentation section on patch).

Patches tested, and found working, at least for me.

-- 
SP5474-RIPE
Sergey Popovich
diff -purN a/doc/bird.sgml b/doc/bird.sgml
--- a/doc/bird.sgml	2013-09-25 12:09:39.000000000 +0300
+++ b/doc/bird.sgml	2013-09-25 12:21:33.997680313 +0300
@@ -1038,6 +1038,11 @@ incompatible with each other (that is to
           <cf><m/P/.prepend(<m/A/);</cf> if <m/P/ is appropriate route attribute
           (for example <cf/bgp_path/).
 
+	  Additionaly operator <cf><m/P/.empty</cf> empties list of autonomous systems if
+	  <m/P/ is appropriate route attribute (for example bgp_path). This is equivalent to
+	  statement <cf><m/P/ = +empty+;</cf> where <cf/+empty+/ represents empty bgppath and
+	  could be used in conditional expressions to match empty list of autonomous systems.
+
 	<tag/bgpmask/
 	  BGP masks are patterns used for BGP path matching
 	  (using <cf>path &tilde; [= 2 3 5 * =]</cf> syntax). The masks
@@ -1082,13 +1087,22 @@ incompatible with each other (that is to
           attribute (for example <cf/bgp_community/). Similarly for
           <cf/delete/ and <cf/filter/.
 
+	  Additionaly operator <cf><m/C/.empty</cf> empties appropriate route
+	  attribute <m/C/ (for example <cf/bgp_community/), which is
+	  equivalent to statement <cf><m/C/ = -empty-;</cf> where
+	  <cf/-empty-/ represents empty clist and could be used in
+	  conditional expressions to determine if clist variable is empty,
+	  as <cf>defined()</cf> always succeed on clist even if variable is
+	  not defined.
+
 	<tag/eclist/
 	  Eclist is a data type used for BGP extended community lists.
 	  Eclists are very similar to clists, but they are sets of ECs
 	  instead of pairs. The same operations (like <cf/add/,
 	  <cf/delete/, or <cf/&tilde;/ membership operator) can be
 	  used to modify or test eclists, with ECs instead of pairs as
-	  arguments.
+	  arguments. For extended communities <cf/--empty--/ represents
+	  empty eclist and could be used similarly to <cf/-empty-/ in clist.
 </descrip>
 
 <sect>Operators
diff -purN a/filter/filter.c b/filter/filter.c
--- a/filter/filter.c	2013-09-25 12:10:45.000000000 +0300
+++ b/filter/filter.c	2013-09-25 12:12:34.215026255 +0300
@@ -112,25 +112,107 @@ pm_format(struct f_path_mask *p, byte *b
   *buf = 0;
 }
 
-static inline int int_cmp(int i1, int i2)
+static inline int
+int_cmp(int i1, int i2)
+{
+  return (i1 > i2) - (i1 < i2);
+}
+
+static inline int
+uint_cmp(unsigned int i1, unsigned int i2)
 {
-  if (i1 == i2) return 0;
-  if (i1 < i2) return -1;
-  else return 1;
+  return (i1 > i2) - (i1 < i2);
 }
 
-static inline int uint_cmp(unsigned int i1, unsigned int i2)
+static inline int
+u64_cmp(u64 i1, u64 i2)
 {
-  if (i1 == i2) return 0;
-  if (i1 < i2) return -1;
-  else return 1;
+  return (i1 > i2) - (i1 < i2);
+}
+
+static int
+list_compare(struct adata *a1, struct adata *a2)
+{
+  u32 *l1, *l2;
+  int l, rc;
+
+  rc = (a1 > a2) - (a1 < a2);
+  if (!rc)
+    return rc;
+  if ((!!a1) != (!!a2))
+    return rc;
+
+  rc = uint_cmp(a1->length, a2->length);
+  if (rc)
+    return rc;
+
+  l1 = int_set_get_data(a1);
+  l2 = int_set_get_data(a2);
+  l = int_set_get_size(a1);
+
+  while (l--) {
+    rc = uint_cmp(*l1++, *l2++);
+    if (rc)
+      return rc;
+  }
+
+  return rc;
 }
 
-static inline int u64_cmp(u64 i1, u64 i2)
+static int
+path_compare(struct adata *a1, struct adata *a2)
 {
-  if (i1 == i2) return 0;
-  if (i1 < i2) return -1;
-  else return 1;
+  u8 *p1, *q1;
+  u8 *p2, *q2;
+  int l, rc;
+
+  rc = (a1 > a2) - (a1 < a2);
+  if (!rc)
+    return rc;
+  if ((!!a1) != (!!a2))
+    return rc;
+
+  rc = int_cmp(as_path_getlen(a1), as_path_getlen(a2));
+  if (rc)
+    return rc;
+
+  p1 = a1->data;
+  q1 = p1 + a1->length;
+  p2 = a2->data;
+  q2 = p2 + a2->length;
+
+  while ((p1 < q1) && (p2 < q2)) {
+    rc = uint_cmp(*p1++, *p2++);
+    if (rc)
+      return -rc; /* AS_PATH_SET longer */
+
+    l = *p1++;
+    rc = int_cmp(l, *p2++);
+    if (rc)
+      return rc;
+
+    while (l--) {
+      rc = uint_cmp(get_as(p1++), get_as(p2++));
+      if (rc)
+        return rc;
+    }
+  }
+
+  return int_cmp((p1 < q1), (p2 < q2));
+}
+
+static inline int
+set_compare(struct f_tree *t1, struct f_tree *t2)
+{
+  /* FIXME: buggy, should return -1, 0, 1; but it doesn't matter */
+  return !same_tree(t1, t2);
+}
+
+static inline int
+prefix_set_compare(struct f_trie *ti1, struct f_trie *ti2)
+{
+  /* FIXME: buggy, should return -1, 0, 1; but it doesn't matter */
+  return !trie_same(ti1, ti2);
 }
 
 /**
@@ -147,14 +229,11 @@ val_compare(struct f_val v1, struct f_va
 {
   int rc;
 
-  if ((v1.type == T_VOID) && (v2.type == T_VOID))
-    return 0;
-  if (v1.type == T_VOID)	/* Hack for else */
-    return -1;
-  if (v2.type == T_VOID)
-    return 1;
-
   if (v1.type != v2.type) {
+    if (v1.type == T_VOID)	/* Hack for else */
+      return -1;
+    if (v2.type == T_VOID)
+      return 1;
 #ifndef IPV6
     /* IP->Quad implicit conversion */
     if ((v1.type == T_QUAD) && (v2.type == T_IP))
@@ -181,15 +260,22 @@ val_compare(struct f_val v1, struct f_va
   case T_PREFIX:
     if (rc = ipa_compare(v1.val.px.ip, v2.val.px.ip))
       return rc;
-    if (v1.val.px.len < v2.val.px.len)
-      return -1;
-    if (v1.val.px.len > v2.val.px.len)
-      return 1;
-    return 0;
+    return int_cmp(v1.val.px.len, v2.val.px.len);
   case T_PATH_MASK:
     return pm_path_compare(v1.val.path_mask, v2.val.path_mask);
   case T_STRING:
     return strcmp(v1.val.s, v2.val.s);
+  case T_CLIST:
+  case T_ECLIST:
+    return list_compare(v1.val.ad, v2.val.ad);
+  case T_PATH:
+    return path_compare(v1.val.ad, v2.val.ad);
+  case T_SET:
+    return set_compare(v1.val.t, v2.val.t);
+  case T_PREFIX_SET:
+    return prefix_set_compare(v1.val.ti, v2.val.ti);
+  case T_VOID:
+    return 0;
   default:
     debug( "Compare of unknown entities: %x\n", v1.type );
     return CMP_ERROR;
@@ -408,18 +494,6 @@ val_in_range(struct f_val v1, struct f_v
 
   if (res != CMP_ERROR)
     return res;
-  
-  if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX_SET))
-    return trie_match_fprefix(v2.val.ti, &v1.val.px);
-
-  if ((v1.type == T_CLIST) && (v2.type == T_SET))
-    return clist_match_set(v1.val.ad, v2.val.t);
-
-  if ((v1.type == T_ECLIST) && (v2.type == T_SET))
-    return eclist_match_set(v1.val.ad, v2.val.t);
-
-  if ((v1.type == T_PATH) && (v2.type == T_SET))
-    return as_path_match_set(v1.val.ad, v2.val.t);
 
   if (v2.type == T_SET)
     switch (v1.type) {
@@ -436,7 +510,19 @@ val_in_range(struct f_val v1, struct f_v
 	  return 0;
 	return !! (val_simple_in_range(v1, n->from));	/* We turn CMP_ERROR into compared ok, and that's fine */
       }
+    case T_PATH:
+      return as_path_match_set(v1.val.ad, v2.val.t);
+    case T_CLIST:
+      return clist_match_set(v1.val.ad, v2.val.t);
+    case T_ECLIST:
+      return eclist_match_set(v1.val.ad, v2.val.t);
     }
+  else if (v2.type == T_PREFIX_SET)
+    switch (v1.type) {
+    case T_PREFIX:
+      return trie_match_fprefix(v2.val.ti, &v1.val.px);
+    }
+
   return CMP_ERROR;
 }
 
diff -purN a/nest/a-path.c b/nest/a-path.c
--- a/nest/a-path.c	2013-09-25 12:09:39.000000000 +0300
+++ b/nest/a-path.c	2013-09-25 12:12:34.215026255 +0300
@@ -15,13 +15,6 @@
 #include "lib/string.h"
 #include "filter/filter.h"
 
-// static inline void put_as(byte *data, u32 as) { put_u32(data, as); }
-// static inline u32 get_as(byte *data) { return get_u32(data); }
-
-#define put_as put_u32
-#define get_as get_u32
-#define BS  4
-
 struct adata *
 as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
 {
diff -purN a/nest/attrs.h b/nest/attrs.h
--- a/nest/attrs.h	2013-09-25 12:09:39.000000000 +0300
+++ b/nest/attrs.h	2013-09-25 12:12:34.215026255 +0300
@@ -25,6 +25,10 @@
  * to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
  */
 
+#define put_as put_u32
+#define get_as get_u32
+#define BS  4
+
 struct f_tree;
 
 struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as);
diff -purN a/doc/bird.sgml b/doc/bird.sgml
--- a/doc/bird.sgml	2013-09-19 16:37:54.000000000 +0300
+++ b/doc/bird.sgml	2013-09-19 17:25:41.543076705 +0300
@@ -936,7 +936,31 @@ incompatible with each other (that is to
 	<tag/ip/ This type can hold a single IP address. Depending on the compile-time configuration of BIRD you are using, it
 	  is either an IPv4 or IPv6 address. IP addresses are written in the standard notation (<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special operator <cf>.mask(<M>num</M>)</cf>
 	  on values of type ip. It masks out all but first <cf><M>num</M></cf> bits from the IP
-	  address. So <cf/1.2.3.4.mask(8) = 1.0.0.0/ is true.
+	  address. So <cf/1.2.3.4.mask(8) = 1.0.0.0/ is true. Also <cf><m/I/.len></cf> operator could be used
+	  to get length in bytes of the IP address, which is useful in filters to determine
+	  address family (IPv4 or IPv6) of the BIRD instance at runtime.
+	  For example
+
+<code>
+	function afi()
+	{
+		case net.ip.len {
+			4:    return 4;
+			16:   return 6;
+			else: return (0-1);	# -1
+		}
+	}
+
+	function is_bird_ipv4()
+	{
+		return afi() = 4;
+	}
+
+	function is_bird_ipv6()
+	{
+		return afi() = 6;
+	}
+</code>
 
 	<tag/prefix/ This type can hold a network prefix consisting of IP address and prefix length. Prefix literals are written as
 	  <cf><M>ipaddress</M>/<M>pxlen</M></cf>, or
@@ -1091,7 +1115,8 @@ incompatible with each other (that is to
 	  <cf/-empty-/ represents empty clist and could be used in
 	  conditional expressions to determine if clist variable is empty,
 	  as <cf>defined()</cf> always succeed on clist even if variable is
-	  not defined.
+	  not defined. Also <cf><m/C/.len</cf> operator might be used to get
+	  number of entries.
 
 	<tag/eclist/
 	  Eclist is a data type used for BGP extended community lists.
diff -purN a/filter/filter.c b/filter/filter.c
--- a/filter/filter.c	2013-09-19 16:37:54.000000000 +0300
+++ b/filter/filter.c	2013-09-19 16:38:41.280524537 +0300
@@ -1135,7 +1135,10 @@ interpret(struct f_inst *what)
     switch(v1.type) {
     case T_PREFIX: res.val.i = v1.val.px.len; break;
     case T_PATH:   res.val.i = as_path_getlen(v1.val.ad); break;
-    default: runtime( "Prefix or path expected" );
+    case T_CLIST:  res.val.i = v1.val.ad->length / 4; break;
+    case T_ECLIST: res.val.i = v1.val.ad->length / 8; break;
+    case T_IP:     res.val.i = sizeof(ip_addr); break; /* Use this to determine AFI in filters */
+    default: runtime( "Prefix, path, clist, eclist or ip expected" );
     }
     break;
   case P('c','p'):	/* Convert prefix to ... */

Reply via email to