summaryrefslogtreecommitdiff
path: root/usr.sbin/bgpd/rde_attr.c
diff options
context:
space:
mode:
authorPeter Hessler <phessler@cvs.openbsd.org>2016-10-14 16:05:37 +0000
committerPeter Hessler <phessler@cvs.openbsd.org>2016-10-14 16:05:37 +0000
commit01b441ff7f82f081c867c730843a20ced322c69a (patch)
treeca0453d28e3be1dd47c8ca80ee1921b62665f03a /usr.sbin/bgpd/rde_attr.c
parentab11fdd7396c99faa03965917bfae604b99150fb (diff)
Add support for draft-ietf-idr-large-community
Joint work with Job Snijders, many thanks! OK benno@ deraadt@
Diffstat (limited to 'usr.sbin/bgpd/rde_attr.c')
-rw-r--r--usr.sbin/bgpd/rde_attr.c148
1 files changed, 147 insertions, 1 deletions
diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c
index 5aa4f02bb45..c629f9d34df 100644
--- a/usr.sbin/bgpd/rde_attr.c
+++ b/usr.sbin/bgpd/rde_attr.c
@@ -1,7 +1,9 @@
-/* $OpenBSD: rde_attr.c,v 1.95 2015/10/24 08:00:42 claudio Exp $ */
+/* $OpenBSD: rde_attr.c,v 1.96 2016/10/14 16:05:36 phessler Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2016 Job Snijders <job@instituut.net>
+ * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -1352,3 +1354,147 @@ community_ext_matchone(struct filter_extcommunity *c, u_int16_t neighas,
return (0);
}
+
+int
+community_large_match(struct rde_aspath *asp, int64_t as, int64_t ld1,
+ int64_t ld2)
+{
+ struct wire_largecommunity *bar;
+ struct attr *a;
+ u_int8_t *p;
+ u_int16_t len;
+ u_int32_t eas, eld1, eld2;
+
+ a = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+ if (a == NULL)
+ /* no communities, no match */
+ return (0);
+
+ p = a->data;
+ for (len = a->len / 12; len > 0; len--) {
+ bar = (struct wire_largecommunity *)p;
+ p += 12;
+ eas = betoh32(bar->as);
+ eld1 = betoh32(bar->ld1);
+ eld2 = betoh32(bar->ld2);
+
+ if ((as == COMMUNITY_ANY || as == eas) &&
+ (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+ (ld2 == COMMUNITY_ANY || ld2 == eld2))
+ return (1);
+ }
+ return (0);
+}
+
+int
+community_large_set(struct rde_aspath *asp, int64_t as, int64_t ld1,
+ int64_t ld2)
+{
+ struct wire_largecommunity *bar;
+ struct attr *attr;
+ u_int8_t *p = NULL;
+ unsigned int i, ncommunities = 0;
+ u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
+
+ attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+ if (attr != NULL) {
+ p = attr->data;
+ ncommunities = attr->len / 12;
+ }
+
+ /* first check if the community is not already set */
+ for (i = 0; i < ncommunities; i++) {
+ bar = (struct wire_largecommunity *)p;
+ if (bar->as == as && bar->ld1 == ld1 && bar->ld2 == ld2)
+ /* already present, nothing todo */
+ return (1);
+ p += 12;
+ }
+
+ if (ncommunities++ >= USHRT_MAX / 12)
+ /* overflow */
+ return (0);
+
+ if ((p = reallocarray(NULL, ncommunities, 12)) == NULL)
+ fatal("community_set");
+
+ bar = (struct wire_largecommunity *)p;
+ bar->as = htobe32(as);
+ bar->ld1 = htobe32(ld1);
+ bar->ld2 = htobe32(ld2);
+
+ if (attr != NULL) {
+ memcpy(p + 12, attr->data, attr->len);
+ f = attr->flags;
+ attr_free(asp, attr);
+ }
+
+ attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, p, ncommunities * 12);
+
+ free(p);
+ return (1);
+}
+
+void
+community_large_delete(struct rde_aspath *asp, int64_t as, int64_t ld1,
+ int64_t ld2)
+{
+ struct wire_largecommunity *bar;
+ struct attr *attr;
+ u_int8_t *p, *n;
+ u_int16_t l = 0, len = 0;
+ u_int32_t eas, eld1, eld2;
+ u_int8_t f;
+
+ attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES);
+ if (attr == NULL)
+ /* no attr nothing to do */
+ return;
+
+ p = attr->data;
+ for (len = 0; l < attr->len; l += 12) {
+ bar = (struct wire_largecommunity *)p;
+ p += 12;
+ eas = betoh32(bar->as);
+ eld1 = betoh32(bar->ld1);
+ eld2 = betoh32(bar->ld2);
+
+ if ((as == COMMUNITY_ANY || as == eas) &&
+ (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+ (ld2 == COMMUNITY_ANY || ld2 == eld2))
+ /* match */
+ continue;
+ len += 12;
+ }
+
+ 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; ) {
+ bar = (struct wire_largecommunity *)p;
+ p += 12;
+ eas = betoh32(bar->as);
+ eld1 = betoh32(bar->ld1);
+ eld2 = betoh32(bar->ld2);
+
+ if ((as == COMMUNITY_ANY || as == eas) &&
+ (ld1 == COMMUNITY_ANY || ld1 == eld1) &&
+ (ld2 == COMMUNITY_ANY || ld2 == eld2))
+ /* match */
+ continue;
+ memcpy(n + l, bar, sizeof(*bar));
+ l += 12;
+ }
+
+ f = attr->flags;
+
+ attr_free(asp, attr);
+ attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len);
+ free(n);
+}