summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd/rde_attr.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2009-12-16 15:40:56 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2009-12-16 15:40:56 +0000
commita87cdf1b1be47fda7890da3b72e1073ce938fae8 (patch)
treec2b87f7f12b62de25c3a0c38df55b1f43a6590fa /usr.sbin/bgpd/rde_attr.c
parentfaf772dba08a4b68f728672246d07a9674cecf96 (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.c156
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);
+}