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 | |
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')
-rw-r--r-- | usr.sbin/bgpd/bgpd.8 | 9 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 48 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 48 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 192 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 39 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 18 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 6 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 156 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 28 | ||||
-rw-r--r-- | usr.sbin/bgpd/util.c | 28 |
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) { |