diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-12-16 15:40:56 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-12-16 15:40:56 +0000 |
commit | a87cdf1b1be47fda7890da3b72e1073ce938fae8 (patch) | |
tree | c2b87f7f12b62de25c3a0c38df55b1f43a6590fa /usr.sbin/bgpd/rde_attr.c | |
parent | faf772dba08a4b68f728672246d07a9674cecf96 (diff) |
Implement "set ext-community [delete] subtype key:value" to set and delete
extended communities as specified in RFC 4360. No matching implemented yet
and stuff like * and neighbor-as are neither supported but will be soon.
Looks good henning & sthen, manpage fixed by jmc
Diffstat (limited to 'usr.sbin/bgpd/rde_attr.c')
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 156 |
1 files changed, 148 insertions, 8 deletions
diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index 7b4591f179f..8aa418e1dc4 100644 --- a/usr.sbin/bgpd/rde_attr.c +++ b/usr.sbin/bgpd/rde_attr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_attr.c,v 1.79 2009/03/19 06:52:59 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.80 2009/12/16 15:40:55 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -971,6 +971,12 @@ aspath_match(struct aspath *a, enum as_spec type, u_int32_t as) return (0); } +/* + * Functions handling communities and extended communities. + */ + +int community_ext_conv(struct filter_extcommunity *, u_int16_t, u_int64_t *); + int community_match(void *data, u_int16_t len, int as, int type) { @@ -1000,7 +1006,6 @@ community_set(struct rde_aspath *asp, int as, int type) u_int8_t *p = NULL; unsigned int i, ncommunities = 0; u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; - u_int8_t t = ATTR_COMMUNITIES; attr = attr_optget(asp, ATTR_COMMUNITIES); if (attr != NULL) { @@ -1017,7 +1022,7 @@ community_set(struct rde_aspath *asp, int as, int type) p += 4; } - if (ncommunities++ >= 0x3fff) + if (ncommunities++ >= USHRT_MAX / 4) /* overflow */ return (0); @@ -1032,11 +1037,10 @@ community_set(struct rde_aspath *asp, int as, int type) if (attr != NULL) { memcpy(p + 4, attr->data, attr->len); f = attr->flags; - t = attr->type; attr_free(asp, attr); } - attr_optadd(asp, f, t, p, ncommunities << 2); + attr_optadd(asp, f, ATTR_COMMUNITIES, p, ncommunities << 2); free(p); return (1); @@ -1049,7 +1053,7 @@ community_delete(struct rde_aspath *asp, int as, int type) u_int8_t *p, *n; u_int16_t l, len = 0; u_int16_t eas, etype; - u_int8_t f, t; + u_int8_t f; attr = attr_optget(asp, ATTR_COMMUNITIES); if (attr == NULL) @@ -1100,10 +1104,146 @@ community_delete(struct rde_aspath *asp, int as, int type) } f = attr->flags; - t = attr->type; attr_free(asp, attr); - attr_optadd(asp, f, t, n, len); + attr_optadd(asp, f, ATTR_COMMUNITIES, n, len); + free(n); +} + +int +community_ext_set(struct rde_aspath *asp, struct filter_extcommunity *c, + u_int16_t neighas) +{ + struct attr *attr; + u_int8_t *p = NULL; + u_int64_t community; + unsigned int i, ncommunities = 0; + u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; + + if (community_ext_conv(c, neighas, &community)) + return (0); + + attr = attr_optget(asp, ATTR_EXT_COMMUNITIES); + if (attr != NULL) { + p = attr->data; + ncommunities = attr->len / 8; /* 64bit per ext-community */ + } + + /* first check if the community is not already set */ + for (i = 0; i < ncommunities; i++) { + if (memcmp(&community, p, sizeof(community)) == 0) + /* already present, nothing todo */ + return (1); + p += sizeof(community); + } + + if (ncommunities++ >= USHRT_MAX / sizeof(community)) + /* overflow */ + return (0); + + if ((p = malloc(ncommunities * sizeof(community))) == NULL) + fatal("community_ext_set"); + + memcpy(p, &community, sizeof(community)); + if (attr != NULL) { + memcpy(p + sizeof(community), attr->data, attr->len); + f = attr->flags; + attr_free(asp, attr); + } + + attr_optadd(asp, f, ATTR_EXT_COMMUNITIES, p, + ncommunities * sizeof(community)); + + free(p); + return (1); +} + +void +community_ext_delete(struct rde_aspath *asp, struct filter_extcommunity *c, + u_int16_t neighas) +{ + struct attr *attr; + u_int8_t *p, *n; + u_int64_t community; + u_int16_t l, len = 0; + u_int8_t f; + + if (community_ext_conv(c, neighas, &community)) + return; + + attr = attr_optget(asp, ATTR_EXT_COMMUNITIES); + if (attr == NULL) + /* no attr nothing to do */ + return; + + p = attr->data; + for (l = 0; l < attr->len; l += sizeof(community)) { + if (memcmp(&community, p + l, sizeof(community)) == 0) + /* match */ + continue; + len += sizeof(community); + } + + if (len == 0) { + attr_free(asp, attr); + return; + } + + if ((n = malloc(len)) == NULL) + fatal("community_delete"); + + p = attr->data; + for (l = 0; l < len && p < attr->data + attr->len; + p += sizeof(community)) { + if (memcmp(&community, p, sizeof(community)) == 0) + /* match */ + continue; + memcpy(n + l, p, sizeof(community)); + l += sizeof(community); + } + + f = attr->flags; + + attr_free(asp, attr); + attr_optadd(asp, f, ATTR_EXT_COMMUNITIES, n, len); free(n); } +int +community_ext_conv(struct filter_extcommunity *c, u_int16_t neighas, + u_int64_t *community) +{ + u_int64_t com; + u_int32_t ip; + + com = (u_int64_t)c->type << 56; + switch (c->type & EXT_COMMUNITY_VALUE) { + case EXT_COMMUNITY_TWO_AS: + com |= (u_int64_t)c->subtype << 48; + com |= (u_int64_t)c->data.ext_as.as << 32; + com |= c->data.ext_as.val; + break; + case EXT_COMMUNITY_IPV4: + com |= (u_int64_t)c->subtype << 48; + ip = ntohl(c->data.ext_ip.addr.s_addr); + com |= (u_int64_t)ip << 16; + com |= c->data.ext_ip.val; + break; + case EXT_COMMUNITY_FOUR_AS: + com |= (u_int64_t)c->subtype << 48; + com |= (u_int64_t)c->data.ext_as4.as4 << 16; + com |= c->data.ext_as4.val; + break; + case EXT_COMMUNITY_OPAQUE: + com |= (u_int64_t)c->subtype << 48; + com |= c->data.ext_opaq & EXT_COMMUNITY_OPAQUE_MAX; + break; + default: + com |= c->data.ext_opaq & 0xffffffffffffffULL; + break; + } + + *community = htobe64(com); + + return (0); +} |