diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2011-08-12 21:03:43 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2011-08-14 13:55:02 +0200 |
commit | 42a0c05408c4151442e6a0ec1c6889acbcfe9c17 (patch) | |
tree | 5a4992c5fa66256987e335ffe94ee960ed1ff9db /nest | |
parent | bde872bba745e5596bdb066df6ef323b7cabcfdd (diff) |
BGP Extended communities.
Diffstat (limited to 'nest')
-rw-r--r-- | nest/a-set.c | 177 | ||||
-rw-r--r-- | nest/attrs.h | 46 | ||||
-rw-r--r-- | nest/route.h | 1 | ||||
-rw-r--r-- | nest/rt-attr.c | 15 |
4 files changed, 222 insertions, 17 deletions
diff --git a/nest/a-set.c b/nest/a-set.c index 3cbd6355..020d0978 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -36,10 +36,11 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz { u32 *z = (u32 *) set->data; byte *end = buf + size - 24; + int from2 = MAX(from, 0); int to = set->length / 4; int i; - for (i = MAX(from, 0); i < to; i++) + for (i = from2; i < to; i++) { if (buf > end) { @@ -50,7 +51,7 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz return i; } - if (i > from) + if (i > from2) *buf++ = ' '; if (way) @@ -63,16 +64,116 @@ int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int siz } int +ec_format(byte *buf, u64 ec) +{ + u32 type, key, val; + char tbuf[16], *kind; + + type = ec >> 48; + switch (type & 0xf0ff) + { + case EC_RT: kind = "rt"; break; + case EC_RO: kind = "ro"; break; + + default: + kind = tbuf; + bsprintf(kind, "unknown 0x%x", type); + } + + switch (ec >> 56) + { + /* RFC 4360 3.1. Two-Octet AS Specific Extended Community */ + case 0x00: + case 0x40: + key = (ec >> 32) & 0xFFFF; + val = ec; + return bsprintf(buf, "(%s, %u, %u)", kind, key, val); + + /* RFC 4360 3.2. IPv4 Address Specific Extended Community */ + case 0x01: + case 0x41: + key = ec >> 16; + val = ec & 0xFFFF; + return bsprintf(buf, "(%s, %R, %u)", kind, key, val); + + /* RFC 5668 4-Octet AS Specific BGP Extended Community */ + case 0x02: + case 0x42: + key = ec >> 16; + val = ec & 0xFFFF; + return bsprintf(buf, "(%s, %u, %u)", kind, key, val); + + /* Generic format for unknown kinds of extended communities */ + default: + key = ec >> 32; + val = ec; + return bsprintf(buf, "(generic, 0x%x, 0x%x)", key, val); + } + +} + +int +ec_set_format(struct adata *set, int from, byte *buf, unsigned int size) +{ + u32 *z = int_set_get_data(set); + byte *end = buf + size - 24; + int from2 = MAX(from, 0); + int to = int_set_get_size(set); + int i; + + for (i = from2; i < to; i += 2) + { + if (buf > end) + { + if (from < 0) + strcpy(buf, " ..."); + else + *buf = 0; + return i; + } + + if (i > from2) + *buf++ = ' '; + + buf += ec_format(buf, ec_get(z, i)); + } + *buf = 0; + return 0; +} + +int int_set_contains(struct adata *list, u32 val) { if (!list) return 0; u32 *l = (u32 *) list->data; - unsigned int i; - for (i=0; i<list->length/4; i++) + int len = int_set_get_size(list); + int i; + + for (i = 0; i < len; i++) if (*l++ == val) return 1; + + return 0; +} + +int +ec_set_contains(struct adata *list, u64 val) +{ + if (!list) + return 0; + + u32 *l = int_set_get_data(list); + int len = int_set_get_size(list); + u32 eh = ec_hi(val); + u32 el = ec_lo(val); + int i; + + for (i=0; i < len; i += 2) + if (l[i] == eh && l[i+1] == el) + return 1; + return 0; } @@ -86,7 +187,7 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val) return list; len = list ? list->length : 0; - res = lp_alloc(pool, len + sizeof(struct adata) + 4); + res = lp_alloc(pool, sizeof(struct adata) + len + 4); res->length = len + 4; * (u32 *) res->data = val; if (list) @@ -95,23 +196,71 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val) } struct adata * -int_set_del(struct linpool *pool, struct adata *list, u32 val) +ec_set_add(struct linpool *pool, struct adata *list, u64 val) { - struct adata *res; - u32 *l, *k; - unsigned int i; + if (ec_set_contains(list, val)) + return list; + + int olen = list ? list->length : 0; + struct adata *res = lp_alloc(pool, sizeof(struct adata) + olen + 8); + res->length = olen + 8; + + if (list) + memcpy(res->data, list->data, list->length); + + u32 *l = (u32 *) (res->data + res->length - 8); + l[0] = ec_hi(val); + l[1] = ec_lo(val); + + return res; +} + +struct adata * +int_set_del(struct linpool *pool, struct adata *list, u32 val) +{ if (!int_set_contains(list, val)) return list; - res = lp_alloc(pool, list->length + sizeof(struct adata) - 4); - res->length = list->length-4; + struct adata *res; + res = lp_alloc(pool, sizeof(struct adata) + list->length - 4); + res->length = list->length - 4; + + u32 *l = int_set_get_data(list); + u32 *k = int_set_get_data(res); + int len = int_set_get_size(list); + int i; - l = (u32 *) list->data; - k = (u32 *) res->data; - for (i=0; i<list->length/4; i++) + for (i = 0; i < len; i++) if (l[i] != val) *k++ = l[i]; return res; } + +struct adata * +ec_set_del(struct linpool *pool, struct adata *list, u64 val) +{ + if (!ec_set_contains(list, val)) + return list; + + struct adata *res; + res = lp_alloc(pool, sizeof(struct adata) + list->length - 8); + res->length = list->length - 8; + + u32 *l = int_set_get_data(list); + u32 *k = int_set_get_data(res); + int len = int_set_get_size(list); + u32 eh = ec_hi(val); + u32 el = ec_lo(val); + int i; + + for (i=0; i < len; i += 2) + if (! (l[i] == eh && l[i+1] == el)) + { + *k++ = l[i]; + *k++ = l[i+1]; + } + + return res; +} diff --git a/nest/attrs.h b/nest/attrs.h index 6ce5755f..85e4e59a 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -50,12 +50,52 @@ int as_path_match(struct adata *path, struct f_path_mask *mask); /* a-set.c */ + +/* Extended Community subtypes (kinds) */ +#define EC_RT 0x0002 +#define EC_RO 0x0003 + +#define EC_GENERIC 0xFFFF + +/* Transitive bit (for first u32 half of EC) */ +#define EC_TBIT 0x40000000 + + +static inline int int_set_get_size(struct adata *list) +{ return list->length / 4; } + +static inline u32 *int_set_get_data(struct adata *list) +{ return (u32 *) list->data; } + +static inline u32 ec_hi(u64 ec) { return ec >> 32; } +static inline u32 ec_lo(u64 ec) { return ec; } +static inline u64 ec_get(const u32 *l, int i) +{ return (((u64) l[i]) << 32) | l[i+1]; } + +/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */ +static inline u64 ec_as2(u64 kind, u64 key, u64 val) +{ return ((kind | 0x0000) << 48) | (key << 32) | val; } + +/* RFC 5668 4-Octet AS Specific BGP Extended Community */ +static inline u64 ec_as4(u64 kind, u64 key, u64 val) +{ return ((kind | 0x0200) << 48) | (key << 16) | val; } + +/* RFC 4360 3.2. IPv4 Address Specific Extended Community */ +static inline u64 ec_ip4(u64 kind, u64 key, u64 val) +{ return ((kind | 0x0100) << 48) | (key << 16) | val; } + +static inline u64 ec_generic(u64 key, u64 val) +{ return (key << 32) | val; } + int int_set_format(struct adata *set, int way, int from, byte *buf, unsigned int size); -struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); +int ec_format(byte *buf, u64 ec); +int ec_set_format(struct adata *set, int from, byte *buf, unsigned int size); int int_set_contains(struct adata *list, u32 val); +int ec_set_contains(struct adata *list, u64 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 *int_set_del(struct linpool *pool, struct adata *list, u32 val); +struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val); -static inline int int_set_get_size(struct adata *list) -{ return list->length / 4; } #endif diff --git a/nest/route.h b/nest/route.h index c77fe417..641b9248 100644 --- a/nest/route.h +++ b/nest/route.h @@ -363,6 +363,7 @@ typedef struct eattr { #define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */ #define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */ #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_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) */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 486a543d..5a78f167 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -465,6 +465,18 @@ ea_show_int_set(struct cli *c, struct adata *ad, int way, byte *pos, byte *buf, } } +static inline void +ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end) +{ + int i = ec_set_format(ad, 0, pos, end - pos); + cli_printf(c, -1012, "%s", buf); + while (i) + { + i = ec_set_format(ad, i, buf, end - buf - 1); + cli_printf(c, -1012, "\t%s", buf); + } +} + /** * ea_show - print an &eattr to CLI * @c: destination CLI @@ -523,6 +535,9 @@ ea_show(struct cli *c, eattr *e) case EAF_TYPE_INT_SET: ea_show_int_set(c, ad, 1, pos, buf, end); return; + case EAF_TYPE_EC_SET: + ea_show_ec_set(c, ad, pos, buf, end); + return; case EAF_TYPE_UNDEF: default: bsprintf(pos, "<type %02x>", e->type); |