summaryrefslogtreecommitdiff
path: root/usr.sbin
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
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')
-rw-r--r--usr.sbin/bgpd/bgpd.89
-rw-r--r--usr.sbin/bgpd/bgpd.conf.548
-rw-r--r--usr.sbin/bgpd/bgpd.h48
-rw-r--r--usr.sbin/bgpd/parse.y192
-rw-r--r--usr.sbin/bgpd/printconf.c39
-rw-r--r--usr.sbin/bgpd/rde.c18
-rw-r--r--usr.sbin/bgpd/rde.h6
-rw-r--r--usr.sbin/bgpd/rde_attr.c156
-rw-r--r--usr.sbin/bgpd/rde_filter.c28
-rw-r--r--usr.sbin/bgpd/util.c28
10 files changed, 551 insertions, 21 deletions
diff --git a/usr.sbin/bgpd/bgpd.8 b/usr.sbin/bgpd/bgpd.8
index c3b77586b39..5bcd838e930 100644
--- a/usr.sbin/bgpd/bgpd.8
+++ b/usr.sbin/bgpd/bgpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.8,v 1.32 2009/10/26 09:58:19 claudio Exp $
+.\" $OpenBSD: bgpd.8,v 1.33 2009/12/16 15:40:55 claudio Exp $
.\"
.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: October 26 2009 $
+.Dd $Mdocdate: December 16 2009 $
.Dt BGPD 8
.Os
.Sh NAME
@@ -190,6 +190,11 @@ control socket
.%D April 2004
.Re
.Rs
+.%R RFC 4360
+.%T "BGP Extended Communities Attribute"
+.%D February 2006
+.Re
+.Rs
.%R RFC 4486
.%T "BGP Cease Notification Message Subcodes"
.%D April 2006
diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5
index 21a5d2d7176..be84b281a26 100644
--- a/usr.sbin/bgpd/bgpd.conf.5
+++ b/usr.sbin/bgpd/bgpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.conf.5,v 1.102 2009/11/26 13:40:43 henning Exp $
+.\" $OpenBSD: bgpd.conf.5,v 1.103 2009/12/16 15:40:55 claudio Exp $
.\"
.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,7 +16,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: November 26 2009 $
+.Dd $Mdocdate: December 16 2009 $
.Dt BGPD.CONF 5
.Os
.Sh NAME
@@ -538,7 +538,7 @@ The demotion counter will be increased as soon as
.Xr bgpd 8
starts and decreased
60 seconds after the session went to state
-.Em ESTABLISHED.
+.Em ESTABLISHED .
For neighbors added at runtime, the demotion counter is only increased after
the session has been
.Em ESTABLISHED
@@ -1119,6 +1119,48 @@ Alternately, well-known communities may be specified by name:
or
.Ic NO_PEER .
.Pp
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar as-number Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar IP Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar numvalue
+.Xc
+Set or delete the
+.Em Extended Community
+AS path attribute.
+Extended Communities are specified by a
+.Ar subtype
+and normally two values, a globally unique part (e.g. the AS number) and a
+local part.
+The type is selected depending on the encoding of the global part.
+Two-octet AS Specific Extended Communities and Four-octet AS Specific Extended
+Communities are encoded as
+.Ar as-number Ns Li : Ns Ar local .
+Four-octet encoding is used if the
+.Ar as-number
+is bigger then 65535 or if the AS_DOT encoding is used.
+IPv4 Address Specific Extended Communities are encoded as
+.Ar IP Ns Li : Ns Ar local .
+Opaque Extended Communities are encoded with a single numeric value.
+Currently the following subtypes are supported:
+.Bd -literal -offset indent
+rt Route Target
+soo Source of Origin
+odi OSPF Domain Identifier
+ort OSPF Route Type
+ori OSPF Router ID
+bdc BGP Data Collection
+.Ed
+.Pp
+Not all type and subtype value pairs are allowed by IANA and the parser
+will ensure that no invalid combination is created.
+.Pp
.It Ic localpref Ar number
Set the
.Em LOCAL_PREF
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h
index e17c1bd6c5a..06548ec2dea 100644
--- a/usr.sbin/bgpd/bgpd.h
+++ b/usr.sbin/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.h,v 1.249 2009/12/08 14:03:40 claudio Exp $ */
+/* $OpenBSD: bgpd.h,v 1.250 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -548,6 +548,27 @@ struct filter_community {
int type;
};
+struct filter_extcommunity {
+ u_int8_t type;
+ u_int8_t subtype; /* if extended type */
+ union {
+ struct ext_as {
+ u_int16_t as;
+ u_int32_t val;
+ } ext_as;
+ struct ext_as4 {
+ u_int32_t as4;
+ u_int16_t val;
+ } ext_as4;
+ struct ext_ip {
+ struct in_addr addr;
+ u_int16_t val;
+ } ext_ip;
+ u_int64_t ext_opaq; /* only 48 bits */
+ } data;
+};
+
+
struct ctl_show_rib_request {
char rib[PEER_DESCR_LEN];
struct ctl_neighbor neighbor;
@@ -625,6 +646,27 @@ struct filter_peers {
#define EXT_COMMUNITY_OSPF_RTR_TYPE 6 /* RFC 4577 */
#define EXT_COMMUNITY_OSPF_RTR_ID 7 /* RFC 4577 */
#define EXT_COMMUNITY_BGP_COLLECT 8 /* RFC 4384 */
+/* other handy defines */
+#define EXT_COMMUNITY_OPAQUE_MAX 0xffffffffffffULL
+
+struct ext_comm_pairs {
+ u_int8_t type;
+ u_int8_t subtype;
+ u_int8_t transitive; /* transitive bit needs to be set */
+};
+
+#define IANA_EXT_COMMUNITIES { \
+ { EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_ROUTE_TGT, 0 }, \
+ { EXT_COMMUNITY_TWO_AS, EXT_CUMMUNITY_ROUTE_ORIG, 0 }, \
+ { EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_OSPF_DOM_ID, 0 }, \
+ { EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_BGP_COLLECT, 0 }, \
+ { EXT_COMMUNITY_FOUR_AS, EXT_COMMUNITY_ROUTE_TGT, 0 }, \
+ { EXT_COMMUNITY_FOUR_AS, EXT_CUMMUNITY_ROUTE_ORIG, 0 }, \
+ { EXT_COMMUNITY_IPV4, EXT_COMMUNITY_ROUTE_TGT, 0 }, \
+ { EXT_COMMUNITY_IPV4, EXT_CUMMUNITY_ROUTE_ORIG, 0 }, \
+ { EXT_COMMUNITY_IPV4, EXT_COMMUNITY_OSPF_RTR_ID, 0 }, \
+ { EXT_COMMUNITY_OPAQUE, EXT_COMMUNITY_OSPF_RTR_TYPE, 0 } \
+}
struct filter_prefix {
@@ -675,6 +717,8 @@ enum action_types {
ACTION_SET_NEXTHOP_SELF,
ACTION_SET_COMMUNITY,
ACTION_DEL_COMMUNITY,
+ ACTION_SET_EXT_COMMUNITY,
+ ACTION_DEL_EXT_COMMUNITY,
ACTION_PFTABLE,
ACTION_PFTABLE_ID,
ACTION_RTLABEL,
@@ -691,6 +735,7 @@ struct filter_set {
int32_t relative;
struct bgpd_addr nexthop;
struct filter_community community;
+ struct filter_extcommunity ext_community;
char pftable[PFTABLE_LEN];
char rtlabel[RTLABEL_LEN];
u_int8_t origin;
@@ -810,6 +855,7 @@ const char *log_addr(const struct bgpd_addr *);
const char *log_in6addr(const struct in6_addr *);
const char *log_sockaddr(struct sockaddr *);
const char *log_as(u_int32_t);
+const char *log_ext_subtype(u_int8_t);
int aspath_snprint(char *, size_t, void *, u_int16_t);
int aspath_asprint(char **, void *, u_int16_t);
size_t aspath_strlen(void *, u_int16_t);
diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y
index cf0eb382668..1b780cd36f3 100644
--- a/usr.sbin/bgpd/parse.y
+++ b/usr.sbin/bgpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.245 2009/12/09 12:52:07 claudio Exp $ */
+/* $OpenBSD: parse.y,v 1.246 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -129,6 +129,10 @@ struct filter_rule *get_rule(enum action_types);
int getcommunity(char *);
int parsecommunity(struct filter_community *, char *);
+int parsesubtype(char *);
+int parseextvalue(char *, u_int32_t *);
+int parseextcommunity(struct filter_extcommunity *, char *,
+ char *);
typedef struct {
union {
@@ -171,7 +175,8 @@ typedef struct {
%token QUICK
%token FROM TO ANY
%token CONNECTED STATIC
-%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS COMMUNITY DELETE
+%token COMMUNITY EXTCOMMUNITY
+%token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE
%token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
%token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN
%token ERROR INCLUDE
@@ -1791,6 +1796,24 @@ filter_set_opt : LOCALPREF NUMBER {
YYERROR;
}
}
+ | EXTCOMMUNITY delete STRING STRING {
+ if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+ fatal(NULL);
+ if ($2)
+ $$->type = ACTION_DEL_EXT_COMMUNITY;
+ else
+ $$->type = ACTION_SET_EXT_COMMUNITY;
+
+ if (parseextcommunity(&$$->action.ext_community,
+ $3, $4) == -1) {
+ free($3);
+ free($4);
+ free($$);
+ YYERROR;
+ }
+ free($3);
+ free($4);
+ }
| ORIGIN origincode {
if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
fatal(NULL);
@@ -1888,6 +1911,7 @@ lookup(char *s)
{ "enforce", ENFORCE},
{ "esp", ESP},
{ "evaluate", EVALUATE},
+ { "ext-community", EXTCOMMUNITY},
{ "fib-update", FIBUPDATE},
{ "from", FROM},
{ "group", GROUP},
@@ -2552,6 +2576,156 @@ parsecommunity(struct filter_community *c, char *s)
return (0);
}
+int
+parsesubtype(char *type)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "bdc", EXT_COMMUNITY_BGP_COLLECT },
+ { "odi", EXT_COMMUNITY_OSPF_DOM_ID },
+ { "ori", EXT_COMMUNITY_OSPF_RTR_ID },
+ { "ort", EXT_COMMUNITY_OSPF_RTR_TYPE },
+ { "rt", EXT_COMMUNITY_ROUTE_TGT },
+ { "soo", EXT_CUMMUNITY_ROUTE_ORIG }
+ };
+ const struct keywords *p;
+
+ p = bsearch(type, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (-1);
+}
+
+int
+parseextvalue(char *s, u_int32_t *v)
+{
+ const char *errstr;
+ char *p;
+ struct in_addr ip;
+ u_int32_t uvalh = 0, uval;
+
+ if ((p = strchr(s, '.')) == NULL) {
+ /* AS_PLAIN number (4 or 2 byte) */
+ uval = strtonum(s, 0, UINT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", s, errstr);
+ return (-1);
+ }
+ *v = uval;
+ if (uval > USHRT_MAX)
+ return (EXT_COMMUNITY_FOUR_AS);
+ else
+ return (EXT_COMMUNITY_TWO_AS);
+ } else if (strchr(p + 1, '.') == NULL) {
+ /* AS_DOT number (4-byte) */
+ *p++ = '\0';
+ uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", s, errstr);
+ return (-1);
+ }
+ uval = strtonum(p, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", p, errstr);
+ return (-1);
+ }
+ *v = uval | (uvalh << 16);
+ return (EXT_COMMUNITY_FOUR_AS);
+ } else {
+ /* more then one dot -> IP address */
+ if (inet_aton(s, &ip) == 0) {
+ yyerror("Bad ext-community %s not parseable", s);
+ return (-1);
+ }
+ *v = ip.s_addr;
+ return (EXT_COMMUNITY_IPV4);
+ }
+ return (-1);
+}
+
+int
+parseextcommunity(struct filter_extcommunity *c, char *t, char *s)
+{
+ const struct ext_comm_pairs iana[] = IANA_EXT_COMMUNITIES;
+ const char *errstr;
+ u_int64_t ullval;
+ u_int32_t uval;
+ char *p, *ep;
+ unsigned int i;
+ int type, subtype;
+
+ if ((subtype = parsesubtype(t)) == -1) {
+ yyerror("Bad ext-community unknown type");
+ return (-1);
+ }
+
+ if ((p = strchr(s, ':')) == NULL) {
+ type = EXT_COMMUNITY_OPAQUE,
+ errno = 0;
+ ullval = strtoull(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0') {
+ yyerror("Bad ext-community bad value");
+ return (-1);
+ }
+ if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
+ yyerror("Bad ext-community value to big");
+ return (-1);
+ }
+ c->data.ext_opaq = ullval;
+ } else {
+ *p++ = '\0';
+ if ((type = parseextvalue(s, &uval)) == -1)
+ return (-1);
+ switch (type) {
+ case EXT_COMMUNITY_TWO_AS:
+ ullval = strtonum(p, 0, UINT_MAX, &errstr);
+ break;
+ case EXT_COMMUNITY_IPV4:
+ case EXT_COMMUNITY_FOUR_AS:
+ ullval = strtonum(p, 0, USHRT_MAX, &errstr);
+ break;
+ default:
+ fatalx("parseextcommunity: unexpected result");
+ }
+ if (errstr) {
+ yyerror("Bad ext-community %s is %s", p,
+ errstr);
+ return (-1);
+ }
+ switch (type) {
+ case EXT_COMMUNITY_TWO_AS:
+ c->data.ext_as.as = uval;
+ c->data.ext_as.val = ullval;
+ break;
+ case EXT_COMMUNITY_IPV4:
+ c->data.ext_ip.addr.s_addr = uval;
+ c->data.ext_ip.val = ullval;
+ break;
+ case EXT_COMMUNITY_FOUR_AS:
+ c->data.ext_as4.as4 = uval;
+ c->data.ext_as4.val = ullval;
+ break;
+ }
+ }
+ c->type = type;
+ c->subtype = subtype;
+
+ /* verify type/subtype combo */
+ for (i = 0; i < sizeof(iana)/sizeof(iana[0]); i++) {
+ if (iana[i].type == type && iana[i].subtype == subtype) {
+ if (iana[i].transitive)
+ c->type |= EXT_COMMUNITY_TRANSITIVE;
+ return (0);
+ }
+ }
+
+ yyerror("Bad ext-community bad format for type");
+ return (-1);
+}
+
struct peer *
alloc_peer(void)
{
@@ -2933,6 +3107,11 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
yyerror("community is already set");
else if (s->type == ACTION_DEL_COMMUNITY)
yyerror("community will already be deleted");
+ else if (s->type == ACTION_SET_EXT_COMMUNITY)
+ yyerror("ext-community is already set");
+ else if (s->type == ACTION_DEL_EXT_COMMUNITY)
+ yyerror(
+ "ext-community will already be deleted");
else
yyerror("redefining set parameter %s",
filterset_name(s->type));
@@ -2959,6 +3138,15 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
return (0);
}
break;
+ case ACTION_SET_EXT_COMMUNITY:
+ case ACTION_DEL_EXT_COMMUNITY:
+ if (memcmp(&s->action.ext_community,
+ &t->action.ext_community,
+ sizeof(s->action.ext_community)) < 0) {
+ TAILQ_INSERT_BEFORE(t, s, entry);
+ return (0);
+ }
+ break;
case ACTION_SET_NEXTHOP:
if (s->action.nexthop.aid <
t->action.nexthop.aid) {
diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c
index 2ab8f97d5bf..007e19cfc50 100644
--- a/usr.sbin/bgpd/printconf.c
+++ b/usr.sbin/bgpd/printconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: printconf.c,v 1.75 2009/12/08 14:03:40 claudio Exp $ */
+/* $OpenBSD: printconf.c,v 1.76 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -27,6 +27,7 @@
void print_op(enum comp_ops);
void print_community(int, int);
+void print_extcommunity(struct filter_extcommunity *);
void print_origin(u_int8_t);
void print_set(struct filter_set_head *);
void print_mainconf(struct bgpd_config *);
@@ -95,6 +96,32 @@ print_community(int as, int type)
}
void
+print_extcommunity(struct filter_extcommunity *c)
+{
+ switch (c->type & EXT_COMMUNITY_VALUE) {
+ case EXT_COMMUNITY_TWO_AS:
+ printf("%s %i:%i", log_ext_subtype(c->subtype),
+ c->data.ext_as.as, c->data.ext_as.val);
+ break;
+ case EXT_COMMUNITY_IPV4:
+ printf("%s %s:%i", log_ext_subtype(c->subtype),
+ inet_ntoa(c->data.ext_ip.addr), c->data.ext_ip.val);
+ break;
+ case EXT_COMMUNITY_FOUR_AS:
+ printf("%s %s:%i", log_ext_subtype(c->subtype),
+ log_as(c->data.ext_as4.as4), c->data.ext_as.val);
+ break;
+ case EXT_COMMUNITY_OPAQUE:
+ printf("%s 0x%x", log_ext_subtype(c->subtype),
+ c->data.ext_opaq);
+ break;
+ default:
+ printf("0x%x 0x%llx", c->type, c->data.ext_opaq);
+ break;
+ }
+}
+
+void
print_origin(u_int8_t o)
{
if (o == ORIGIN_IGP)
@@ -184,6 +211,16 @@ print_set(struct filter_set_head *set)
/* not possible */
printf("king bula saiz: config broken");
break;
+ case ACTION_SET_EXT_COMMUNITY:
+ printf("ext-community ");
+ print_extcommunity(&s->action.ext_community);
+ printf(" ");
+ break;
+ case ACTION_DEL_EXT_COMMUNITY:
+ printf("ext-community delete ");
+ print_extcommunity(&s->action.ext_community);
+ printf(" ");
+ break;
}
}
printf("}");
diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c
index 0b7ac1c1aac..442032d202c 100644
--- a/usr.sbin/bgpd/rde.c
+++ b/usr.sbin/bgpd/rde.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.c,v 1.276 2009/12/08 14:03:40 claudio Exp $ */
+/* $OpenBSD: rde.c,v 1.277 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -1389,6 +1389,22 @@ bad_flags:
ATTR_PARTIAL))
goto bad_flags;
goto optattr;
+ case ATTR_EXT_COMMUNITIES:
+ if ((attr_len & 0x7) != 0) {
+ /*
+ * mark update as bad and withdraw all routes as per
+ * draft-ietf-idr-optional-transitive-00.txt
+ * but only if partial bit is set
+ */
+ if ((flags & ATTR_PARTIAL) == 0)
+ goto bad_len;
+ else
+ a->flags |= F_ATTR_PARSE_ERR;
+ }
+ if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
+ ATTR_PARTIAL))
+ goto bad_flags;
+ goto optattr;
case ATTR_ORIGINATOR_ID:
if (attr_len != 4)
goto bad_len;
diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h
index da899b1255b..eb8122fa6d1 100644
--- a/usr.sbin/bgpd/rde.h
+++ b/usr.sbin/bgpd/rde.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.h,v 1.124 2009/12/08 14:03:40 claudio Exp $ */
+/* $OpenBSD: rde.h,v 1.125 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
@@ -346,6 +346,10 @@ int aspath_match(struct aspath *, enum as_spec, u_int32_t);
int community_match(void *, u_int16_t, int, int);
int community_set(struct rde_aspath *, int, int);
void community_delete(struct rde_aspath *, int, int);
+int community_ext_set(struct rde_aspath *,
+ struct filter_extcommunity *, u_int16_t);
+void community_ext_delete(struct rde_aspath *,
+ struct filter_extcommunity *, u_int16_t);
/* rde_rib.c */
extern u_int16_t rib_size;
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);
+}
diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c
index 4d313dcca3f..a248dd68578 100644
--- a/usr.sbin/bgpd/rde_filter.c
+++ b/usr.sbin/bgpd/rde_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde_filter.c,v 1.59 2009/12/01 14:28:05 claudio Exp $ */
+/* $OpenBSD: rde_filter.c,v 1.60 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -253,6 +253,14 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh,
case ACTION_SET_ORIGIN:
asp->origin = set->action.origin;
break;
+ case ACTION_SET_EXT_COMMUNITY:
+ community_ext_set(asp, &set->action.ext_community,
+ peer->conf.remote_as);
+ break;
+ case ACTION_DEL_EXT_COMMUNITY:
+ community_ext_delete(asp, &set->action.ext_community,
+ peer->conf.remote_as);
+ break;
}
}
}
@@ -486,6 +494,12 @@ filterset_cmp(struct filter_set *a, struct filter_set *b)
return (a->action.community.type - b->action.community.type);
}
+ if (a->type == ACTION_SET_EXT_COMMUNITY ||
+ a->type == ACTION_DEL_EXT_COMMUNITY) { /* a->type == b->type */
+ return (memcmp(&a->action.ext_community,
+ &b->action.ext_community, sizeof(a->action.ext_community)));
+ }
+
if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) {
/*
* This is the only interesting case, all others are considered
@@ -589,6 +603,14 @@ filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh)
a->action.origin == b->action.origin)
continue;
break;
+ case ACTION_SET_EXT_COMMUNITY:
+ case ACTION_DEL_EXT_COMMUNITY:
+ if (a->type == b->type && memcmp(
+ &a->action.ext_community,
+ &b->action.ext_community,
+ sizeof(a->action.ext_community)) == 0)
+ continue;
+ break;
}
/* compare failed */
return (0);
@@ -633,6 +655,10 @@ filterset_name(enum action_types type)
return ("rtlabel");
case ACTION_SET_ORIGIN:
return ("origin");
+ case ACTION_SET_EXT_COMMUNITY:
+ return ("ext-community");
+ case ACTION_DEL_EXT_COMMUNITY:
+ return ("ext-community delete");
}
fatalx("filterset_name: got lost");
diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c
index 0a24001e53c..507ab7f94f9 100644
--- a/usr.sbin/bgpd/util.c
+++ b/usr.sbin/bgpd/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.8 2009/12/08 14:03:40 claudio Exp $ */
+/* $OpenBSD: util.c,v 1.9 2009/12/16 15:40:55 claudio Exp $ */
/*
* Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
@@ -90,6 +90,32 @@ log_as(u_int32_t as)
return (buf);
}
+/* XXX this function does not check if the type/subtype combo is
+ * actually valid. */
+const char *
+log_ext_subtype(u_int8_t subtype)
+{
+ static char etype[6];
+
+ switch (subtype) {
+ case EXT_COMMUNITY_ROUTE_TGT:
+ return ("rt"); /* route target */
+ case EXT_CUMMUNITY_ROUTE_ORIG:
+ return ("soo"); /* source of origin */
+ case EXT_COMMUNITY_OSPF_DOM_ID:
+ return ("odi"); /* ospf domain id */
+ case EXT_COMMUNITY_OSPF_RTR_TYPE:
+ return ("ort"); /* ospf route type */
+ case EXT_COMMUNITY_OSPF_RTR_ID:
+ return ("ori"); /* ospf router id */
+ case EXT_COMMUNITY_BGP_COLLECT:
+ return ("bdc"); /* bgp data collection */
+ default:
+ snprintf(etype, sizeof(etype), "[%i]", (int)subtype);
+ return (etype);
+ }
+}
+
int
aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
{