diff options
Diffstat (limited to 'proto/bgp/attrs.c')
-rw-r--r-- | proto/bgp/attrs.c | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index b8371f32..6fe34e56 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -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; |