Commit 66dbdbd9 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

BGP: Support for large communities

Add support for large communities (draft-ietf-idr-large-community),
96bit alternative to RFC 1997 communities.

Thanks to Matt Griswold for the original patch.
parent f51b1f55
Loading
Loading
Loading
Loading
+41 −2
Original line number Diff line number Diff line
@@ -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'; }
+169 −2
Original line number Diff line number Diff line
@@ -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);
    {
+16 −2
Original line number Diff line number Diff line
@@ -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
+17 −0
Original line number Diff line number Diff line
@@ -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();
}

+125 −2
Original line number Diff line number Diff line
@@ -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;
}
Loading