]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Support for large communities
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 1 Oct 2016 10:50:29 +0000 (12:50 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Mon, 3 Oct 2016 10:48:56 +0000 (12:48 +0200)
Add support for large communities (draft-ietf-idr-large-community),
96bit alternative to RFC 1997 communities.

Thanks to Matt Griswold for the original patch.

filter/config.Y
filter/filter.c
filter/filter.h
filter/test.conf
nest/a-set.c
nest/attrs.h
nest/route.h
nest/rt-attr.c
proto/bgp/attrs.c
proto/bgp/bgp.h
proto/bgp/config.Y

index 9fffd6517c368550a958109a0d9d0c76b0eaa5b2..79e274ab0292cdc8c5d64308257ddfd78ef90cc8 100644 (file)
@@ -36,6 +36,7 @@ f_valid_set_type(int type)
   case T_ENUM:
   case T_IP:
   case T_EC:
+  case T_LC:
     return 1;
 
   default:
@@ -148,6 +149,9 @@ f_generate_empty(struct f_inst *dyn)
     case EAF_TYPE_EC_SET:
       e->aux = T_ECLIST;
       break;
+    case EAF_TYPE_LC_SET:
+      e->aux = T_LCLIST;
+      break;
     default:
       cf_error("Can't empty that attribute");
   }
@@ -268,14 +272,44 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
   return rv;
 }
 
+static inline struct f_inst *
+f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
+{
+  struct f_inst *rv;
+
+  if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) {
+    if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT))
+      cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
+
+    rv = f_new_inst();
+    rv->code = 'C';
+
+    NEW_F_VAL;
+    rv->a1.p = val;
+    val->type = T_LC;
+    val->val.lc = (lcomm) { t1->a2.i, t2->a2.i, t3->a2.i };
+  }
+  else
+  {
+    rv = cfg_allocz(sizeof(struct f_inst3));
+    rv->lineno = ifs->lino;
+    rv->code = P('m','l');
+    rv->a1.p = t1;
+    rv->a2.p = t2;
+    INST3(rv).p = t3;
+  }
+
+  return rv;
+}
+
 
 
 CF_DECLS
 
 CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        ACCEPT, REJECT, ERROR, QUITBIRD,
-       INT, BOOL, IP, PREFIX, PAIR, QUAD, EC,
-       SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST,
+       INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, LC,
+       SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
        IF, THEN, ELSE, CASE,
        TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
        FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX,
@@ -327,17 +361,20 @@ type:
  | PAIR { $$ = T_PAIR; }
  | QUAD { $$ = T_QUAD; }
  | EC { $$ = T_EC; }
+ | LC { $$ = T_LC; }
  | STRING { $$ = T_STRING; }
  | BGPMASK { $$ = T_PATH_MASK; }
  | BGPPATH { $$ = T_PATH; }
  | CLIST { $$ = T_CLIST; }
  | ECLIST { $$ = T_ECLIST; }
+ | LCLIST { $$ = T_LCLIST; }
  | type SET { 
        switch ($1) {
          case T_INT:
          case T_PAIR:
          case T_QUAD:
          case T_EC:
+         case T_LC:
          case T_IP:
               $$ = T_SET;
               break;
@@ -657,6 +694,7 @@ constant:
 constructor:
    '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
  | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
+ | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
  ;
 
 
@@ -767,6 +805,7 @@ term:
  | '+' 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'; }
index 2f5f00d81f3ca31347c809ba279e21d5cce4b632..510303a766d404695913efb494728aa6519438dc 100644 (file)
@@ -106,6 +106,18 @@ u64_cmp(u64 i1, u64 i2)
   return (int)(i1 > i2) - (int)(i1 < i2);
 }
 
+static inline int
+lcomm_cmp(lcomm v1, lcomm v2)
+{
+  if (v1.asn != v2.asn)
+    return (v1.asn > v2.asn) ? 1 : -1;
+  if (v1.ldp1 != v2.ldp1)
+    return (v1.ldp1 > v2.ldp1) ? 1 : -1;
+  if (v1.ldp2 != v2.ldp2)
+    return (v1.ldp2 > v2.ldp2) ? 1 : -1;
+  return 0;
+}
+
 /**
  * val_compare - compare two values
  * @v1: first value
@@ -149,6 +161,8 @@ val_compare(struct f_val v1, struct f_val v2)
     return uint_cmp(v1.val.i, v2.val.i);
   case T_EC:
     return u64_cmp(v1.val.ec, v2.val.ec);
+  case T_LC:
+    return lcomm_cmp(v1.val.lc, v2.val.lc);
   case T_IP:
     return ipa_compare(v1.val.px.ip, v2.val.px.ip);
   case T_PREFIX:
@@ -214,6 +228,7 @@ val_same(struct f_val v1, struct f_val v2)
   case T_PATH:
   case T_CLIST:
   case T_ECLIST:
+  case T_LCLIST:
     return adata_same(v1.val.ad, v2.val.ad);
   case T_SET:
     return same_tree(v1.val.t, v2.val.t);
@@ -266,6 +281,10 @@ static inline int
 eclist_set_type(struct f_tree *set)
 { return set->from.type == T_EC; }
 
+static inline int
+lclist_set_type(struct f_tree *set)
+{ return set->from.type == T_LC; }
+
 static int
 clist_match_set(struct adata *clist, struct f_tree *set)
 {
@@ -311,6 +330,30 @@ eclist_match_set(struct adata *list, struct f_tree *set)
   return 0;
 }
 
+static int
+lclist_match_set(struct adata *list, struct f_tree *set)
+{
+  if (!list)
+    return 0;
+
+  if (!lclist_set_type(set))
+    return CMP_ERROR;
+
+  struct f_val v;
+  u32 *l = int_set_get_data(list);
+  int len = int_set_get_size(list);
+  int i;
+
+  v.type = T_LC;
+  for (i = 0; i < len; i += 3) {
+    v.val.lc = lc_get(l, i);
+    if (find_tree(set, v))
+      return 1;
+  }
+
+  return 0;
+}
+
 static struct adata *
 clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
 {
@@ -380,6 +423,38 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po
   return res;
 }
 
+static struct adata *
+lclist_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);
+  u32 *l = int_set_get_data(list);
+  u32 tmp[len];
+  u32 *k = tmp;
+  int i;
+
+  v.type = T_LC;
+  for (i = 0; i < len; i += 3) {
+    v.val.lc = lc_get(l, i);
+    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
+    if ((tree ? !!find_tree(set.val.t, v) : lc_set_contains(set.val.ad, v.val.lc)) == pos)
+      k = lc_copy(k, l+i);
+  }
+
+  int nl = (k - tmp) * 4;
+  if (nl == list->length)
+    return list;
+
+  struct adata *res = adata_empty(pool, nl);
+  memcpy(res->data, tmp, nl);
+  return res;
+}
+
 /**
  * val_in_range - implement |~| operator
  * @v1: element
@@ -407,6 +482,9 @@ val_in_range(struct f_val v1, struct f_val v2)
   if ((v1.type == T_EC) && (v2.type == T_ECLIST))
     return ec_set_contains(v2.val.ad, v1.val.ec);
 
+  if ((v1.type == T_LC) && (v2.type == T_LCLIST))
+    return lc_set_contains(v2.val.ad, v1.val.lc);
+
   if ((v1.type == T_STRING) && (v2.type == T_STRING))
     return patmatch(v2.val.s, v1.val.s);
 
@@ -433,6 +511,9 @@ val_in_range(struct f_val v1, struct f_val v2)
   if (v1.type == T_ECLIST)
     return eclist_match_set(v1.val.ad, v2.val.t);
 
+  if (v1.type == T_LCLIST)
+    return lclist_match_set(v1.val.ad, v2.val.t);
+
   if (v1.type == T_PATH)
     return as_path_match_set(v1.val.ad, v2.val.t);
 
@@ -457,12 +538,14 @@ val_format(struct f_val v, buffer *buf)
   case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return;
   case T_QUAD: buffer_print(buf, "%R", v.val.i); return;
   case T_EC:   ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return;
+  case T_LC:   lc_format(buf2, v.val.lc); buffer_print(buf, "%s", buf2); return;
   case T_PREFIX_SET: trie_format(v.val.ti, buf); return;
   case T_SET:  tree_format(v.val.t, buf); return;
   case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return;
   case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
   case T_CLIST:        int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
   case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
+  case T_LCLIST: lc_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
   case T_PATH_MASK: pm_format(v.val.path_mask, buf); return;
   default:     buffer_print(buf, "[unknown type %x]", v.type); return;
   }
@@ -656,6 +739,7 @@ interpret(struct f_inst *what)
        runtime("Can't operate with value of non-integer type in EC constructor");
       val = v2.val.i;
 
+      /* XXXX */
       res.type = T_EC;
 
       if (what->aux == EC_GENERIC) {
@@ -677,6 +761,24 @@ interpret(struct f_inst *what)
       break;
     }
 
+  case P('m','l'):
+    {
+      TWOARGS;
+
+      /* Third argument hack */
+      struct f_val v3 = interpret(INST3(what).p);
+      if (v3.type & T_RETURN)
+       return v3;
+
+      if ((v1.type != T_INT) || (v2.type != T_INT) || (v3.type != T_INT))
+       runtime( "Can't operate with value of non-integer type in LC constructor" );
+
+      res.type = T_LC;
+      res.val.lc = (lcomm) { v1.val.i, v2.val.i, v3.val.i };
+
+      break;
+    }
+
 /* Relational operators */
 
 #define COMPARE(x) \
@@ -912,6 +1014,13 @@ interpret(struct f_inst *what)
          break;
        }
 
+       /* The same special case for lc_set */
+       if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_LC_SET) {
+         res.type = T_LCLIST;
+         res.val.ad = adata_empty(f_pool, 0);
+         break;
+       }
+
        /* Undefined value */
        res.type = T_VOID;
        break;
@@ -951,6 +1060,10 @@ interpret(struct f_inst *what)
        res.type = T_ECLIST;
        res.val.ad = e->u.ptr;
        break;
+      case EAF_TYPE_LC_SET:
+       res.type = T_LCLIST;
+       res.val.ad = e->u.ptr;
+       break;
       case EAF_TYPE_UNDEF:
        res.type = T_VOID;
        break;
@@ -1041,6 +1154,11 @@ interpret(struct f_inst *what)
          runtime( "Setting eclist attribute to non-eclist value" );
        l->attrs[0].u.ptr = v1.val.ad;
        break;
+      case EAF_TYPE_LC_SET:
+       if (v1.type != T_LCLIST)
+         runtime( "Setting lclist attribute to non-lclist value" );
+       l->attrs[0].u.ptr = v1.val.ad;
+       break;
       case EAF_TYPE_UNDEF:
        if (v1.type != T_VOID)
          runtime( "Setting void attribute to non-void value" );
@@ -1082,6 +1200,7 @@ interpret(struct f_inst *what)
     case T_PATH:   res.val.i = as_path_getlen(v1.val.ad); break;
     case T_CLIST:  res.val.i = int_set_get_size(v1.val.ad); break;
     case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break;
+    case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break;
     default: runtime( "Prefix, path, clist or eclist expected" );
     }
     break;
@@ -1277,7 +1396,7 @@ interpret(struct f_inst *what)
       else if (v2.type == T_ECLIST)
        arg_set = 2;
       else if (v2.type != T_EC)
-       runtime("Can't add/delete non-pair");
+       runtime("Can't add/delete non-ec");
 
       res.type = T_ECLIST;
       switch (what->aux)
@@ -1308,8 +1427,50 @@ interpret(struct f_inst *what)
        bug("unknown Ca operation");
       }
     }
+    else if (v1.type == T_LCLIST)
+    {
+      /* Large community list */
+      int arg_set = 0;
+
+      /* v2.val is either LC or LC-set */
+      if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
+       arg_set = 1;
+      else if (v2.type == T_LCLIST)
+       arg_set = 2;
+      else if (v2.type != T_LC)
+       runtime("Can't add/delete non-lc");
+
+      res.type = T_LCLIST;
+      switch (what->aux)
+      {
+      case 'a':
+       if (arg_set == 1)
+         runtime("Can't add set");
+       else if (!arg_set)
+         res.val.ad = lc_set_add(f_pool, v1.val.ad, v2.val.lc);
+       else
+         res.val.ad = lc_set_union(f_pool, v1.val.ad, v2.val.ad);
+       break;
+
+      case 'd':
+       if (!arg_set)
+         res.val.ad = lc_set_del(f_pool, v1.val.ad, v2.val.lc);
+       else
+         res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 0);
+       break;
+
+      case 'f':
+       if (!arg_set)
+         runtime("Can't filter lc");
+       res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 1);
+       break;
+
+      default:
+       bug("unknown Ca operation");
+      }
+    }
     else
-      runtime("Can't add/delete to non-(e)clist");
+      runtime("Can't add/delete to non-[e|l]clist");
 
     break;
 
@@ -1401,6 +1562,12 @@ i_same(struct f_inst *f1, struct f_inst *f2)
   case '~': TWOARGS; break;
   case P('d','e'): ONEARG; break;
 
+  case P('m','l'):
+    TWOARGS;
+    if (!i_same(INST3(f1).p, INST3(f2).p))
+      return 0;
+    break;
+
   case 's':
     ARG(v2, a2.p);
     {
index e59c822602c2f7f474cce90d902c85d3a251bd72..e0f34e4fa023a882b8fb1c9ac495f892fcdc327a 100644 (file)
@@ -38,6 +38,17 @@ struct f_inst_roa_check {
   struct roa_table_config *rtc;        
 };
 
+struct f_inst3 {
+  struct f_inst i;
+  union {
+    int i;
+    void *p;
+  } a3;
+};
+
+#define INST3(x) (((struct f_inst3 *) x)->a3)
+
+
 struct f_prefix {
   ip_addr ip;
   int len;
@@ -53,6 +64,7 @@ struct f_val {
   union {
     uint i;
     u64 ec;
+    lcomm lc;
     /*    ip_addr ip; Folded into prefix */    
     struct f_prefix px;
     char *s;
@@ -168,8 +180,10 @@ void val_format(struct f_val v, buffer *buf);
 #define T_PATH_MASK 0x23       /* mask for BGP path */
 #define T_PATH 0x24            /* BGP path */
 #define T_CLIST 0x25           /* Community list */
-#define T_ECLIST 0x26          /* Extended community list */
-#define T_EC 0x27              /* Extended community value, u64 */
+#define T_EC 0x26              /* Extended community value, u64 */
+#define T_ECLIST 0x27          /* Extended community list */
+#define T_LC 0x28              /* Large community value, lcomm */
+#define T_LCLIST 0x29          /* Large community list */
 
 #define T_RETURN 0x40
 #define T_SET 0x80
index f61f0658e05bfb3ea3c0d5e453697367a0a0e752..e8873fbf877fb73ab0420500ccbb38a5c6de4630 100644 (file)
@@ -27,6 +27,11 @@ function 'mkpair-a'(int a)
        return (1, a);
 }
 
+function mktrip(int a)
+{
+       return (a, 2*a, 3*a);
+}
+
 function mkpath(int a; int b)
 {
        return [= a b 3 2 1 =];
@@ -89,6 +94,7 @@ clist l;
 clist l2;
 eclist el;
 eclist el2;
+lclist ll;
 {
        print "Entering path test...";
        pm1 =  / 4 3 2 1 /;
@@ -203,6 +209,17 @@ eclist el2;
        print "eclist A isect B: ", filter( el, el2 );
        print "eclist A \  B: ", delete( el, el2 );
 
+       ll = --- empty ---;
+       ll = add(ll, (ten, 20, 30));
+       ll = add(ll, (1000, 2000, 3000));
+       ll = add(ll, mktrip(100000));
+       print "LC list (10, 20, 30) (1000, 2000, 3000) (100000, 200000, 300000):";
+       print ll;
+       print "LC len: ", el.len;
+       print "Should be true: ", mktrip(1000) ~ ll, " ", ll ~ [(5,10,15), (10,20,30)], " ", ll ~ [(10,15..25,*)], " ", ll ~ [(ten, *, *)];
+       print "Should be false: ", mktrip(100) ~ ll, " ", ll ~ [(5,10,15), (10,21,30)], " ", ll ~ [(10,21..25,*)], " ", ll ~ [(11, *, *)];
+       print "LC filtered: ", filter(ll, [(5..15, *, *), (100000, 500..500000, *)]);
+
 #      test_roa();
 }
 
index a61160223753a508e06d75fb99e00715ba7ffaf8..0484ab9865b36202bce0305817255cb7bd846eca 100644 (file)
@@ -116,7 +116,7 @@ int
 ec_set_format(struct adata *set, int from, byte *buf, uint size)
 {
   u32 *z = int_set_get_data(set);
-  byte *end = buf + size - 24;
+  byte *end = buf + size - 64;
   int from2 = MAX(from, 0);
   int to = int_set_get_size(set);
   int i;
@@ -141,6 +141,43 @@ ec_set_format(struct adata *set, int from, byte *buf, uint size)
   return 0;
 }
 
+int
+lc_format(byte *buf, lcomm lc)
+{
+  return bsprintf(buf, "(%d, %d, %d)", lc.asn, lc.ldp1, lc.ldp2);
+}
+
+int
+lc_set_format(struct adata *set, int from, byte *buf, uint bufsize)
+{
+  u32 *d = (u32 *) set->data;
+  byte *end = buf + bufsize - 64;
+  int from2 = MAX(from, 0);
+  int to = set->length / 4;
+  int i;
+
+  for (i = from2; i < to; i += 3)
+    {
+      if (buf > end)
+       {
+         if (from < 0)
+           strcpy(buf, "...");
+         else
+           buf[-1] = 0;
+         return i;
+       }
+
+      buf += bsprintf(buf, "(%d, %d, %d)", d[i], d[i+1], d[i+2]);
+      *buf++ = ' ';
+    }
+
+  if (i != from2)
+    buf--;
+
+  *buf = 0;
+  return 0;
+}
+
 int
 int_set_contains(struct adata *list, u32 val)
 {
@@ -177,6 +214,24 @@ ec_set_contains(struct adata *list, u64 val)
   return 0;
 }
 
+int
+lc_set_contains(struct adata *list, lcomm val)
+{
+  if (!list)
+    return 0;
+
+  u32 *l = int_set_get_data(list);
+  int len = int_set_get_size(list);
+  int i;
+
+  for (i = 0; i < len; i += 3)
+    if (lc_match(l, i, val))
+      return 1;
+
+  return 0;
+}
+
+
 struct adata *
 int_set_add(struct linpool *pool, struct adata *list, u32 val)
 {
@@ -208,13 +263,30 @@ ec_set_add(struct linpool *pool, struct adata *list, u64 val)
   if (list)
     memcpy(res->data, list->data, list->length);
 
-  u32 *l = (u32 *) (res->data + res->length - 8);
+  u32 *l = (u32 *) (res->data + olen);
   l[0] = ec_hi(val);
   l[1] = ec_lo(val);
 
   return res;
 }
 
+struct adata *
+lc_set_add(struct linpool *pool, struct adata *list, lcomm val)
+{
+  if (lc_set_contains(list, val))
+    return list;
+
+  int olen = list ? list->length : 0;
+  struct adata *res = lp_alloc(pool, sizeof(struct adata) + olen + LCOMM_LENGTH);
+  res->length = olen + LCOMM_LENGTH;
+
+  if (list)
+    memcpy(res->data, list->data, list->length);
+
+  lc_put((u32 *) (res->data + olen), val);
+
+  return res;
+}
 
 struct adata *
 int_set_del(struct linpool *pool, struct adata *list, u32 val)
@@ -265,6 +337,27 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val)
   return res;
 }
 
+struct adata *
+lc_set_del(struct linpool *pool, struct adata *list, lcomm val)
+{
+  if (!lc_set_contains(list, val))
+    return list;
+
+  struct adata *res;
+  res = lp_alloc(pool, sizeof(struct adata) + list->length - LCOMM_LENGTH);
+  res->length = list->length - LCOMM_LENGTH;
+
+  u32 *l = int_set_get_data(list);
+  u32 *k = int_set_get_data(res);
+  int len = int_set_get_size(list);
+  int i;
+
+  for (i=0; i < len; i += 3)
+    if (! lc_match(l, i, val))
+      k = lc_copy(k, l+i);
+
+  return res;
+}
 
 struct adata *
 int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
@@ -328,3 +421,33 @@ ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
   memcpy(res->data + l1->length, tmp, len);
   return res;
 }
+
+struct adata *
+lc_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 += 3)
+    if (!lc_set_contains(l1, lc_get(l, i)))
+      k = lc_copy(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;
+}
index 670b048f26843dbf7d524f8d9bce13dad8ccdf6f..548d71a9aac8ad9ddc675e73ef633ddcbe842a46 100644 (file)
@@ -68,6 +68,7 @@ int as_path_match(struct adata *path, struct f_path_mask *mask);
 /* Transitive bit (for first u32 half of EC) */
 #define EC_TBIT 0x40000000
 
+#define ECOMM_LENGTH 8
 
 static inline int int_set_get_size(struct adata *list)
 { return list->length / 4; }
@@ -75,6 +76,9 @@ static inline int int_set_get_size(struct adata *list)
 static inline int ec_set_get_size(struct adata *list)
 { return list->length / 8; }
 
+static inline int lc_set_get_size(struct adata *list)
+{ return list->length / 12; }
+
 static inline u32 *int_set_get_data(struct adata *list)
 { return (u32 *) list->data; }
 
@@ -98,17 +102,45 @@ static inline u64 ec_ip4(u64 kind, u64 key, u64 val)
 static inline u64 ec_generic(u64 key, u64 val)
 { return (key << 32) | val; }
 
+/* Large community value */
+typedef struct lcomm {
+  u32 asn;
+  u32 ldp1;
+  u32 ldp2;
+} lcomm;
+
+#define LCOMM_LENGTH 12
+
+static inline lcomm lc_get(const u32 *l, int i)
+{ return (lcomm) { l[i], l[i+1], l[i+2] }; }
+
+static inline void lc_put(u32 *l, lcomm v)
+{ l[0] = v.asn; l[1] = v.ldp1; l[2] = v.ldp2; }
+
+static inline int lc_match(const u32 *l, int i, lcomm v)
+{ return (l[i] == v.asn && l[i+1] == v.ldp1 && l[i+2] == v.ldp2); }
+
+static inline u32 *lc_copy(u32 *dst, const u32 *src)
+{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; }
+
+
 int int_set_format(struct adata *set, int way, int from, byte *buf, uint size);
 int ec_format(byte *buf, u64 ec);
 int ec_set_format(struct adata *set, int from, byte *buf, uint size);
+int lc_format(byte *buf, lcomm lc);
+int lc_set_format(struct adata *set, int from, byte *buf, uint size);
 int int_set_contains(struct adata *list, u32 val);
 int ec_set_contains(struct adata *list, u64 val);
+int lc_set_contains(struct adata *list, lcomm val);
 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 *lc_set_add(struct linpool *pool, struct adata *list, lcomm 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 *lc_set_del(struct linpool *pool, struct adata *list, lcomm 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);
+struct adata *lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
 
 
 #endif
index 2fcb189af04ad2f0ff5740bc1ddfdf1b92d7e5dc..07320566d6d912c1e363a04a8cdab26f4e748884 100644 (file)
@@ -442,7 +442,7 @@ typedef struct eattr {
 #define EA_ALLOW_UNDEF 0x10000         /* ea_find: allow EAF_TYPE_UNDEF */
 #define EA_BIT(n) ((n) << 24)          /* Used in bitfield accessors */
 
-#define EAF_TYPE_MASK 0x0f             /* Mask with this to get type */
+#define EAF_TYPE_MASK 0x1f             /* Mask with this to get type */
 #define EAF_TYPE_INT 0x01              /* 32-bit unsigned integer number */
 #define EAF_TYPE_OPAQUE 0x02           /* Opaque byte string (not filterable) */
 #define EAF_TYPE_IP_ADDRESS 0x04       /* IP address */
@@ -451,7 +451,8 @@ typedef struct eattr {
 #define EAF_TYPE_BITFIELD 0x09         /* 32-bit embedded bitfield */
 #define EAF_TYPE_INT_SET 0x0a          /* Set of u32's (e.g., a community list) */
 #define EAF_TYPE_EC_SET 0x0e           /* Set of pairs of u32's - ext. community list */
-#define EAF_TYPE_UNDEF 0x0f            /* `force undefined' entry */
+#define EAF_TYPE_LC_SET 0x12           /* Set of triplets of u32's - large community list */
+#define EAF_TYPE_UNDEF 0x1f            /* `force undefined' entry */
 #define EAF_EMBEDDED 0x01              /* Data stored in eattr.u.data (part of type spec) */
 #define EAF_VAR_LENGTH 0x02            /* Attribute length is variable (part of type spec) */
 #define EAF_ORIGINATED 0x40            /* The attribute has originated locally */
index ab0069b58f1c4ce8578bf17cda056b413bb22646..9aeca8465f5e67b8afcaeff36afcaddbf8cfdd1f 100644 (file)
@@ -828,6 +828,18 @@ ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
     }
 }
 
+static inline void
+ea_show_lc_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
+{
+  int i = lc_set_format(ad, 0, pos, end - pos);
+  cli_printf(c, -1012, "\t%s", buf);
+  while (i)
+    {
+      i = lc_set_format(ad, i, buf, end - buf - 1);
+      cli_printf(c, -1012, "\t\t%s", buf);
+    }
+}
+
 /**
  * ea_show - print an &eattr to CLI
  * @c: destination CLI
@@ -892,6 +904,9 @@ ea_show(struct cli *c, eattr *e)
        case EAF_TYPE_EC_SET:
          ea_show_ec_set(c, ad, pos, buf, end);
          return;
+       case EAF_TYPE_LC_SET:
+         ea_show_lc_set(c, ad, pos, buf, end);
+         return;
        case EAF_TYPE_UNDEF:
        default:
          bsprintf(pos, "<type %02x>", e->type);
index b8371f322508096967440802f01b37211b97ac82..6fe34e56ffcd3e7b35b4bacde3738ec7e29ae959 100644 (file)
@@ -289,6 +289,12 @@ bgp_check_ext_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
   return ((len % 8) == 0) ? 0 : WITHDRAW;
 }
 
+static int
+bgp_check_large_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+{
+  return ((len % 12) == 0) ? 0 : WITHDRAW;
+}
+
 
 static struct attr_desc bgp_attr_table[] = {
   { NULL, -1, 0, 0, 0,                                                         /* Undefined */
@@ -325,7 +331,10 @@ static struct attr_desc bgp_attr_table[] = {
   { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,         /* BA_AS4_PATH */
     NULL, NULL },
   { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,   /* BA_AS4_PATH */
-    NULL, NULL }
+    NULL, NULL },
+  [BA_LARGE_COMMUNITY] =
+  { "large_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_LC_SET, 1,
+    bgp_check_large_community, NULL }
 };
 
 /* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH.
@@ -575,7 +584,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
       len = bgp_get_attr_len(a);
 
       /* Skip empty sets */ 
-      if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET)) && (len == 0))
+      if (((type == EAF_TYPE_INT_SET) || (type == EAF_TYPE_EC_SET) || (type == EAF_TYPE_LC_SET)) && (len == 0))
        continue; 
 
       if (remains < len + 4)
@@ -601,6 +610,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
            break;
          }
        case EAF_TYPE_INT_SET:
+       case EAF_TYPE_LC_SET:
        case EAF_TYPE_EC_SET:
          {
            u32 *z = int_set_get_data(a->u.ptr);
@@ -683,6 +693,25 @@ bgp_normalize_ec_set(struct adata *ad, u32 *src, int internal)
   qsort(dst, ad->length / 8, 8, (int(*)(const void *, const void *)) bgp_compare_ec);
 }
 
+static int
+bgp_compare_lc(const u32 *x, const u32 *y)
+{
+  if (x[0] != y[0])
+    return (x[0] > y[0]) ? 1 : -1;
+  if (x[1] != y[1])
+    return (x[1] > y[1]) ? 1 : -1;
+  if (x[2] != y[2])
+    return (x[2] > y[2]) ? 1 : -1;
+  return 0;
+}
+
+static inline void
+bgp_normalize_lc_set(u32 *dest, u32 *src, unsigned cnt)
+{
+  memcpy(dest, src, LCOMM_LENGTH * cnt);
+  qsort(dest, cnt, LCOMM_LENGTH, (int(*)(const void *, const void *)) bgp_compare_lc);
+}
+
 static void
 bgp_rehash_buckets(struct bgp_proto *p)
 {
@@ -827,6 +856,14 @@ bgp_get_bucket(struct bgp_proto *p, net *n, ea_list *attrs, int originate)
            d->u.ptr = z;
            break;
          }
+       case EAF_TYPE_LC_SET:
+         {
+           struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
+           z->length = d->u.ptr->length;
+           bgp_normalize_lc_set((u32 *) z->data, (u32 *) d->u.ptr->data, z->length / LCOMM_LENGTH);
+           d->u.ptr = z;
+           break;
+         }
        default: ;
        }
       d++;
@@ -1797,6 +1834,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, uint len, struct linpool *po
          ipa_ntoh(*(ip_addr *)ad->data);
          break;
        case EAF_TYPE_INT_SET:
+       case EAF_TYPE_LC_SET:
        case EAF_TYPE_EC_SET:
          {
            u32 *z = (u32 *) ad->data;
index b1cca2d9994e5225051ddff2d525b4985945d5e8..d5747c18839089fc34717a002b9f44faf68e9233 100644 (file)
@@ -308,6 +308,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
 #define BA_EXT_COMMUNITY       0x10    /* [RFC4360] */
 #define BA_AS4_PATH             0x11    /* [RFC4893] */
 #define BA_AS4_AGGREGATOR       0x12
+#define BA_LARGE_COMMUNITY     0x1e    /* [draft-ietf-idr-large-community] */
 
 /* BGP connection states */
 
index f3ba0e16b72437d014601d8bec892f60c2b697e4..32ae88a3d83623ce1962d0743634fcb727d557f6 100644 (file)
@@ -27,7 +27,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
        INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
        TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
        SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
-       CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY)
+       CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, BGP_LARGE_COMMUNITY)
 
 CF_GRAMMAR
 
@@ -159,6 +159,8 @@ CF_ADDTO(dynamic_attr, BGP_CLUSTER_LIST
        { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP, BA_CLUSTER_LIST)); })
 CF_ADDTO(dynamic_attr, BGP_EXT_COMMUNITY
        { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(EAP_BGP, BA_EXT_COMMUNITY)); })
+CF_ADDTO(dynamic_attr, BGP_LARGE_COMMUNITY
+       { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(EAP_BGP, BA_LARGE_COMMUNITY)); })