summaryrefslogtreecommitdiff
path: root/net/openbgpd/files/patch-bgpd_rde_attr.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_rde_attr.c')
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_attr.c562
1 files changed, 562 insertions, 0 deletions
diff --git a/net/openbgpd/files/patch-bgpd_rde_attr.c b/net/openbgpd/files/patch-bgpd_rde_attr.c
new file mode 100644
index 000000000000..2c0192abe5da
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_attr.c
@@ -0,0 +1,562 @@
+Index: bgpd/rde_attr.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_attr.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.7
+diff -u -p -r1.1.1.6 -r1.7
+--- bgpd/rde_attr.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/rde_attr.c 13 Oct 2012 18:36:00 -0000 1.7
+@@ -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.90 2012/04/12 17:27:20 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+@@ -17,14 +17,22 @@
+ */
+
+ #include <sys/types.h>
++#if defined(__FreeBSD__) /* sys/hash.h */
++#include "hash.h"
++#else
+ #include <sys/hash.h>
++#endif /* defined(__FreeBSD__) */
+ #include <sys/queue.h>
+
+ #include <netinet/in.h>
+
++#include <limits.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
++#if defined(__FreeBSD__) /* limits.h */
++#include <limits.h>
++#endif /* defined(__FreeBSD__) */
+
+ #include "bgpd.h"
+ #include "rde.h"
+@@ -36,12 +44,12 @@ attr_write(void *p, u_int16_t p_len, u_i
+ u_char *b = p;
+ u_int16_t tmp, tot_len = 2; /* attribute header (without len) */
+
++ flags &= ~ATTR_DEFMASK;
+ if (data_len > 255) {
+ tot_len += 2 + data_len;
+ flags |= ATTR_EXTLEN;
+ } else {
+ tot_len += 1 + data_len;
+- flags &= ~ATTR_EXTLEN;
+ }
+
+ if (tot_len > p_len)
+@@ -63,26 +71,26 @@ attr_write(void *p, u_int16_t p_len, u_i
+ }
+
+ int
+-attr_writebuf(struct buf *buf, u_int8_t flags, u_int8_t type, void *data,
++attr_writebuf(struct ibuf *buf, u_int8_t flags, u_int8_t type, void *data,
+ u_int16_t data_len)
+ {
+ u_char hdr[4];
+
++ flags &= ~ATTR_DEFMASK;
+ if (data_len > 255) {
+ flags |= ATTR_EXTLEN;
+ hdr[2] = (data_len >> 8) & 0xff;
+ hdr[3] = data_len & 0xff;
+ } else {
+- flags &= ~ATTR_EXTLEN;
+ hdr[2] = data_len & 0xff;
+ }
+
+ hdr[0] = flags;
+ hdr[1] = type;
+
+- if (buf_add(buf, hdr, flags & ATTR_EXTLEN ? 4 : 3) == -1)
++ if (ibuf_add(buf, hdr, flags & ATTR_EXTLEN ? 4 : 3) == -1)
+ return (-1);
+- if (buf_add(buf, data, data_len) == -1)
++ if (ibuf_add(buf, data, data_len) == -1)
+ return (-1);
+ return (0);
+ }
+@@ -146,8 +154,11 @@ attr_optadd(struct rde_aspath *asp, u_in
+ for (l = 0; l < asp->others_len; l++) {
+ if (asp->others[l] == NULL)
+ break;
+- if (type == asp->others[l]->type)
++ if (type == asp->others[l]->type) {
++ if (a->refcnt == 0)
++ attr_put(a);
+ return (-1);
++ }
+ }
+
+ /* add attribute to the table but first bump refcnt */
+@@ -318,6 +329,7 @@ attr_alloc(u_int8_t flags, u_int8_t type
+ fatal("attr_optadd");
+ rdemem.attr_cnt++;
+
++ flags &= ~ATTR_DEFMASK; /* normalize mask */
+ a->flags = flags;
+ a->hash = hash32_buf(&flags, sizeof(flags), HASHINIT);
+ a->type = type;
+@@ -347,6 +359,7 @@ attr_lookup(u_int8_t flags, u_int8_t typ
+ struct attr *a;
+ u_int32_t hash;
+
++ flags &= ~ATTR_DEFMASK; /* normalize mask */
+ hash = hash32_buf(&flags, sizeof(flags), HASHINIT);
+ hash = hash32_buf(&type, sizeof(type), hash);
+ hash = hash32_buf(&len, sizeof(len), hash);
+@@ -405,6 +418,7 @@ aspath_verify(void *data, u_int16_t len,
+ u_int8_t *seg = data;
+ u_int16_t seg_size, as_size = 2;
+ u_int8_t seg_len, seg_type;
++ int err = 0;
+
+ if (len & 1)
+ /* odd length aspath are invalid */
+@@ -419,7 +433,15 @@ aspath_verify(void *data, u_int16_t len,
+ seg_type = seg[0];
+ seg_len = seg[1];
+
+- if (seg_type != AS_SET && seg_type != AS_SEQUENCE)
++ /*
++ * BGP confederations should not show up but consider them
++ * as a soft error which invalidates the path but keeps the
++ * bgp session running.
++ */
++ if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
++ err = AS_ERR_SOFT;
++ if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
++ seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET)
+ return (AS_ERR_TYPE);
+
+ seg_size = 2 + as_size * seg_len;
+@@ -431,7 +453,7 @@ aspath_verify(void *data, u_int16_t len,
+ /* empty aspath segments are not allowed */
+ return (AS_ERR_BAD);
+ }
+- return (0); /* aspath is valid but probably not loop free */
++ return (err); /* aspath is valid but probably not loop free */
+ }
+
+ void
+@@ -762,15 +784,9 @@ aspath_countcopy(struct aspath *aspath,
+ u_int32_t
+ aspath_neighbor(struct aspath *aspath)
+ {
+- /*
+- * Empty aspath is OK -- internal as route.
+- * But what is the neighbor? For now let's return 0.
+- * That should not break anything.
+- */
+-
++ /* Empty aspath is OK -- internal AS route. */
+ if (aspath->len == 0)
+- return (0);
+-
++ return (rde_local_as());
+ return (aspath_extract(aspath->data, 0));
+ }
+
+@@ -910,76 +926,63 @@ aspath_prepend(struct aspath *asp, u_int
+ return (p);
+ }
+
+-/* we need to be able to search more than one as */
+ int
+-aspath_match(struct aspath *a, enum as_spec type, u_int32_t as)
++aspath_lenmatch(struct aspath *a, enum aslen_spec type, u_int aslen)
+ {
+ u_int8_t *seg;
+- int final;
++ u_int32_t as, lastas = 0;
++ u_int count = 0;
+ u_int16_t len, seg_size;
+ u_int8_t i, seg_type, seg_len;
+
+- if (type == AS_EMPTY) {
+- if (a->len == 0)
++ if (type == ASLEN_MAX) {
++ if (aslen < aspath_count(a->data, a->len))
+ return (1);
+ else
+ return (0);
+ }
+
+- final = 0;
++ /* type == ASLEN_SEQ */
+ seg = a->data;
+ for (len = a->len; len > 0; len -= seg_size, seg += seg_size) {
+ seg_type = seg[0];
+ seg_len = seg[1];
+ seg_size = 2 + sizeof(u_int32_t) * seg_len;
+
+- final = (len == seg_size);
+-
+- /* just check the first (leftmost) AS */
+- if (type == AS_PEER) {
+- if (as == aspath_extract(seg, 0))
+- return (1);
+- else
+- return (0);
+- }
+- /* just check the final (rightmost) AS */
+- if (type == AS_SOURCE) {
+- /* not yet in the final segment */
+- if (!final)
+- continue;
+-
+- if (as == aspath_extract(seg, seg_len - 1))
+- return (1);
+- else
+- return (0);
+- }
+-
+- /* AS_TRANSIT or AS_ALL */
+ for (i = 0; i < seg_len; i++) {
+- if (as == aspath_extract(seg, i)) {
+- /*
+- * the source (rightmost) AS is excluded from
+- * AS_TRANSIT matches.
+- */
+- if (final && i == seg_len - 1 &&
+- type == AS_TRANSIT)
+- return (0);
+- return (1);
+- }
++ /* what should we do with AS_SET? */
++ as = aspath_extract(seg, i);
++ if (as == lastas) {
++ if (aslen < ++count)
++ return (1);
++ } else
++ count = 1;
++ lastas = as;
+ }
+ }
+ return (0);
+ }
+
++/*
++ * Functions handling communities and extended communities.
++ */
++
++int community_ext_matchone(struct filter_extcommunity *, u_int16_t, u_int64_t);
++
+ int
+-community_match(void *data, u_int16_t len, int as, int type)
++community_match(struct rde_aspath *asp, int as, int type)
+ {
+- u_int8_t *p = data;
+- u_int16_t eas, etype;
++ struct attr *a;
++ u_int8_t *p;
++ u_int16_t eas, etype, len;
+
+- len >>= 2; /* divide by four */
++ a = attr_optget(asp, ATTR_COMMUNITIES);
++ if (a == NULL)
++ /* no communities, no match */
++ return (0);
+
+- for (; len > 0; len--) {
++ p = a->data;
++ for (len = a->len / 4; len > 0; len--) {
+ eas = *p++;
+ eas <<= 8;
+ eas |= *p++;
+@@ -1000,7 +1003,6 @@ community_set(struct rde_aspath *asp, in
+ 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 +1019,7 @@ community_set(struct rde_aspath *asp, in
+ p += 4;
+ }
+
+- if (ncommunities++ >= 0x3fff)
++ if (ncommunities++ >= USHRT_MAX / 4)
+ /* overflow */
+ return (0);
+
+@@ -1032,11 +1034,10 @@ community_set(struct rde_aspath *asp, in
+ 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 +1050,7 @@ community_delete(struct rde_aspath *asp,
+ 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 +1101,250 @@ community_delete(struct rde_aspath *asp,
+ }
+
+ 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_match(struct rde_aspath *asp, struct filter_extcommunity *c,
++ u_int16_t neighas)
++{
++ struct attr *attr;
++ u_int8_t *p;
++ u_int64_t ec;
++ u_int16_t len;
++
++ attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
++ if (attr == NULL)
++ /* no communities, no match */
++ return (0);
++
++ p = attr->data;
++ for (len = attr->len / sizeof(ec); len > 0; len--) {
++ memcpy(&ec, p, sizeof(ec));
++ if (community_ext_matchone(c, neighas, ec))
++ return (1);
++ p += sizeof(ec);
++ }
++
++ return (0);
++}
++
++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 / sizeof(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);
++}
++
++int
++community_ext_matchone(struct filter_extcommunity *c, u_int16_t neighas,
++ u_int64_t community)
++{
++ u_int64_t com, mask;
++ u_int32_t ip;
++
++ community = betoh64(community);
++
++ com = (u_int64_t)c->type << 56;
++ mask = 0xffULL << 56;
++ if ((com & mask) != (community & mask))
++ return (0);
++
++ switch (c->type & EXT_COMMUNITY_VALUE) {
++ case EXT_COMMUNITY_TWO_AS:
++ case EXT_COMMUNITY_IPV4:
++ case EXT_COMMUNITY_FOUR_AS:
++ case EXT_COMMUNITY_OPAQUE:
++ com = (u_int64_t)c->subtype << 48;
++ mask = 0xffULL << 48;
++ if ((com & mask) != (community & mask))
++ return (0);
++ break;
++ default:
++ com = c->data.ext_opaq & 0xffffffffffffffULL;
++ mask = 0xffffffffffffffULL;
++ if ((com & mask) == (community & mask))
++ return (1);
++ return (0);
++ }
++
++
++ switch (c->type & EXT_COMMUNITY_VALUE) {
++ case EXT_COMMUNITY_TWO_AS:
++ com = (u_int64_t)c->data.ext_as.as << 32;
++ mask = 0xffffULL << 32;
++ if ((com & mask) != (community & mask))
++ return (0);
++
++ com = c->data.ext_as.val;
++ mask = 0xffffffffULL;
++ if ((com & mask) == (community & mask))
++ return (1);
++ break;
++ case EXT_COMMUNITY_IPV4:
++ ip = ntohl(c->data.ext_ip.addr.s_addr);
++ com = (u_int64_t)ip << 16;
++ mask = 0xffffffff0000ULL;
++ if ((com & mask) != (community & mask))
++ return (0);
++
++ com = c->data.ext_ip.val;
++ mask = 0xffff;
++ if ((com & mask) == (community & mask))
++ return (1);
++ break;
++ case EXT_COMMUNITY_FOUR_AS:
++ com = (u_int64_t)c->data.ext_as4.as4 << 16;
++ mask = 0xffffffffULL << 16;
++ if ((com & mask) != (community & mask))
++ return (0);
++
++ com = c->data.ext_as4.val;
++ mask = 0xffff;
++ if ((com & mask) == (community & mask))
++ return (1);
++ break;
++ case EXT_COMMUNITY_OPAQUE:
++ com = c->data.ext_opaq & EXT_COMMUNITY_OPAQUE_MAX;
++ mask = EXT_COMMUNITY_OPAQUE_MAX;
++ if ((com & mask) == (community & mask))
++ return (1);
++ break;
++ }
++
++ return (0);
++}