diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2004-05-21 15:36:41 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2004-05-21 15:36:41 +0000 |
commit | 66ad79cdaaf232c69e2c23b334663335e31c41b9 (patch) | |
tree | a6de6ef4f185bb33d75dc21e42f684238002cd31 | |
parent | 7ab3008b01a67c9f78cd18812453911c9fd48581 (diff) |
RFC 2796 bgp route reflector support. This is very useful in conjunction
with templates. looks good, go for it henning@
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 5 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 5 | ||||
-rw-r--r-- | usr.sbin/bgpd/config.c | 11 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 31 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 141 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 8 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 38 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 98 |
8 files changed, 260 insertions, 77 deletions
diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 51ffdc06084..ea43798b163 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.25 2004/05/17 12:39:32 djm Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.26 2004/05/21 15:36:40 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -356,6 +356,9 @@ statement defines the maximum hops the neighbor may be away. Do not attempt to actively open a TCP connection to the neighbor system. .It Ar remote-as Set the AS number of the remote system. +.It Ar route-reflector +Act as a RFC 2796 route-reflector for this neighbor. +An optional cluster id can be specified else the own bgp id will be used. .It Ar set Set the .Em AS path attributes diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index ba5d5efac8f..9ddd92d22c8 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.124 2004/05/21 11:48:56 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.125 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -50,6 +50,7 @@ #define BGPD_FLAG_NO_FIB_UPDATE 0x0001 #define BGPD_FLAG_NO_EVALUATE 0x0002 +#define BGPD_FLAG_REFLECTOR 0x0004 #define BGPD_LOG_UPDATES 0x0001 @@ -111,6 +112,7 @@ struct bgpd_config { int opts; u_int16_t as; u_int32_t bgpid; + u_int32_t clusterid; u_int16_t holdtime; u_int16_t min_holdtime; int flags; @@ -203,6 +205,7 @@ struct peer_config { enum enforce_as enforce_as; struct peer_auth auth; u_int8_t capabilities; + u_int8_t reflector_client; enum reconf_action reconf_action; }; diff --git a/usr.sbin/bgpd/config.c b/usr.sbin/bgpd/config.c index 87be1d23878..56f801ca75f 100644 --- a/usr.sbin/bgpd/config.c +++ b/usr.sbin/bgpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.36 2004/05/04 21:22:39 deraadt Exp $ */ +/* $OpenBSD: config.c,v 1.37 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -56,6 +56,9 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, if (!conf->bgpid) conf->bgpid = get_bgpid(); + if ((conf->flags & BGPD_FLAG_REFLECTOR) && conf->clusterid == 0) + conf->clusterid = conf->bgpid; + for (p = peer_l; p != NULL; p = p->next) { p->conf.ebgp = (p->conf.remote_as != conf->as); if (p->conf.announce_type == ANNOUNCE_UNDEF) @@ -64,6 +67,12 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, if (p->conf.enforce_as == ENFORCE_AS_UNDEF) p->conf.enforce_as = p->conf.ebgp == 0 ? ENFORCE_AS_OFF : ENFORCE_AS_ON; + if (p->conf.reflector_client && p->conf.ebgp) { + log_peer_warnx(&p->conf, "configuration error: " + "EBGP neighbors are not allowed in route " + "reflector clusters"); + return (1); + } } memcpy(xconf, conf, sizeof(struct bgpd_config)); diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 92f73b5ff0a..6e645c94512 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.111 2004/05/17 12:39:32 djm Exp $ */ +/* $OpenBSD: parse.y,v 1.112 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -113,7 +113,7 @@ typedef struct { %token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE %token GROUP NEIGHBOR NETWORK %token REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX ANNOUNCE -%token ENFORCE NEIGHBORAS CAPABILITIES +%token ENFORCE NEIGHBORAS CAPABILITIES REFLECTOR %token DUMP TABLE IN OUT %token LOG ROUTECOLL %token TCP MD5SIG PASSWORD KEY @@ -646,6 +646,32 @@ peeropts : REMOTEAS asnumber { sizeof(curpeer->conf.attrset)); } | mrtdump + | REFLECTOR { + if ((conf->flags & BGPD_FLAG_REFLECTOR) && + conf->clusterid != 0) { + yyerror("only one route reflector " + "cluster allowed"); + YYERROR; + } + conf->flags |= BGPD_FLAG_REFLECTOR; + curpeer->conf.reflector_client = 1; + } + | REFLECTOR address { + if ($2.af != AF_INET) { + yyerror("route reflector cluster-id must be " + "an IPv4 address"); + YYERROR; + } + if ((conf->flags & BGPD_FLAG_REFLECTOR) && + conf->clusterid != $2.v4.s_addr) { + yyerror("only one route reflector " + "cluster allowed"); + YYERROR; + } + conf->flags |= BGPD_FLAG_REFLECTOR; + curpeer->conf.reflector_client = 1; + conf->clusterid = $2.v4.s_addr; + } ; espah : ESP { $$ = 1; } @@ -1028,6 +1054,7 @@ lookup(char *s) { "quick", QUICK}, { "remote-as", REMOTEAS}, { "route-collector", ROUTECOLL}, + { "route-reflector", REFLECTOR}, { "router-id", ROUTERID}, { "set", SET}, { "source-AS", SOURCEAS}, diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index def3f552e00..87f3c8f6fae 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.114 2004/05/21 12:10:22 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.115 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -45,15 +45,16 @@ int rde_update_get_prefix(u_char *, u_int16_t, struct bgpd_addr *, u_int8_t *); void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, void *, u_int16_t); +void rde_update_log(const char *, + const struct rde_peer *, const struct attr_flags *, + const struct bgpd_addr *, u_int8_t); +int rde_reflector(struct rde_peer *, struct attr_flags *); void rde_dump_rib_as(struct prefix *, pid_t); void rde_dump_rib_prefix(struct prefix *, pid_t); void rde_dump_upcall(struct pt_entry *, void *); void rde_dump_as(struct as_filter *, pid_t); void rde_dump_prefix_upcall(struct pt_entry *, void *); void rde_dump_prefix(struct ctl_show_rib_prefix *, pid_t); -void rde_update_log(const char *, - const struct rde_peer *, const struct attr_flags *, - const struct bgpd_addr *, u_int8_t); void rde_update_queue_runner(void); void peer_init(u_int32_t); @@ -437,13 +438,57 @@ rde_update_dispatch(struct imsg *imsg) p = imsg->data; memcpy(&len, p, 2); - withdrawn_len = len = ntohs(len); + withdrawn_len = ntohs(len); p += 2; if (imsg->hdr.len < IMSG_HEADER_SIZE + 2 + withdrawn_len + 2) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); return (-1); } + p += withdrawn_len; + memcpy(&len, p, 2); + attrpath_len = len = ntohs(len); + p += 2; + if (imsg->hdr.len < + IMSG_HEADER_SIZE + 2 + withdrawn_len + 2 + attrpath_len) { + rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); + return (-1); + } + if (attrpath_len != 0) { /* 0 = no NLRI information in this message */ + /* parse path attributes */ + attr_init(&attrs); + while (len > 0) { + if ((pos = attr_parse(p, len, &attrs, + peer->conf.ebgp, peer->conf.enforce_as, + peer->conf.remote_as)) < 0) { + emsg = attr_error(p, len, &attrs, + &subtype, &size); + rde_update_err(peer, ERR_UPDATE, subtype, + emsg, size); + attr_free(&attrs); + return (-1); + } + p += pos; + len -= pos; + } + + /* check for missing but necessary attributes */ + if ((subtype = attr_missing(&attrs, peer->conf.ebgp)) != 0) { + rde_update_err(peer, ERR_UPDATE, ERR_UPD_MISSNG_WK_ATTR, + &subtype, sizeof(u_int8_t)); + attr_free(&attrs); + return (-1); + } + + if (rde_reflector(peer, &attrs) != 1) { + attr_free(&attrs); + return (0); + } + } + + p = imsg->data; + len = withdrawn_len; + p += 2; /* withdraw prefix */ while (len > 0) { if ((pos = rde_update_get_prefix(p, len, &prefix, @@ -455,12 +500,16 @@ rde_update_dispatch(struct imsg *imsg) log_peer_warnx(&peer->conf, "bad withdraw prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); + if (attrpath_len != 0) + attr_free(&attrs); return (-1); } if (prefixlen > 32) { log_peer_warnx(&peer->conf, "bad withdraw prefix"); rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, NULL, 0); + if (attrpath_len != 0) + attr_free(&attrs); return (-1); } @@ -476,41 +525,14 @@ rde_update_dispatch(struct imsg *imsg) prefix_remove(peer, &prefix, prefixlen); } - memcpy(&len, p, 2); - attrpath_len = ntohs(len); - p += 2; - if (imsg->hdr.len < - IMSG_HEADER_SIZE + 2 + withdrawn_len + 2 + attrpath_len) { - rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); - return (-1); - } - nlri_len = - imsg->hdr.len - IMSG_HEADER_SIZE - 4 - withdrawn_len - attrpath_len; if (attrpath_len == 0) /* 0 = no NLRI information in this message */ return (0); - /* parse path attributes */ - attr_init(&attrs); - while (attrpath_len > 0) { - if ((pos = attr_parse(p, attrpath_len, &attrs, peer->conf.ebgp, - peer->conf.enforce_as, peer->conf.remote_as)) < 0) { - emsg = attr_error(p, attrpath_len, &attrs, - &subtype, &size); - rde_update_err(peer, ERR_UPDATE, subtype, emsg, size); - attr_free(&attrs); - return (-1); - } - p += pos; - attrpath_len -= pos; - } + /* shift to NLRI information */ + p += 2 + attrpath_len; + nlri_len = + imsg->hdr.len - IMSG_HEADER_SIZE - 4 - withdrawn_len - attrpath_len; - /* check for missing but necessary attributes */ - if ((subtype = attr_missing(&attrs, peer->conf.ebgp)) != 0) { - rde_update_err(peer, ERR_UPDATE, ERR_UPD_MISSNG_WK_ATTR, - &subtype, sizeof(u_int8_t)); - attr_free(&attrs); - return (-1); - } /* aspath needs to be loop free nota bene this is not a hard error */ if (peer->conf.ebgp && !aspath_loopfree(attrs.aspath, conf->as)) { @@ -518,8 +540,7 @@ rde_update_dispatch(struct imsg *imsg) aspath_asprint(&s, attrs.aspath->data, attrs.aspath->hdr.len); log_peer_warnx(&peer->conf, "AS path loop: %s", s); free(s); - aspath_destroy(attrs.aspath); - attr_optfree(&attrs); + attr_free(&attrs); return (0); } @@ -654,6 +675,50 @@ rde_update_log(const char *message, free(nexthop); } +int +rde_reflector(struct rde_peer *peer, struct attr_flags *attrs) +{ + struct attr *a; + u_int16_t len; + + /* check for originator id if eq router_id drop */ + if ((a = attr_optget(attrs, ATTR_ORIGINATOR_ID)) != NULL) { + ENSURE(a->len == 4); + if (memcmp(&conf->bgpid, a->data, sizeof(conf->bgpid)) == 0) + /* this is comming from myself */ + return (0); + } else if ((conf->flags & BGPD_FLAG_REFLECTOR) && + attr_optadd(attrs, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID, + peer->conf.ebgp == 0 ? &peer->remote_bgpid : &conf->bgpid, + sizeof(u_int32_t)) == -1) + fatalx("attr_optadd failed but impossible"); + + /* check for own id in the cluster list */ + if (conf->flags & BGPD_FLAG_REFLECTOR) { + if ((a = attr_optget(attrs, ATTR_CLUSTER_LIST)) != NULL) { + for (len = 0; len < a->len; + len += sizeof(conf->clusterid)) + /* check if comming from my cluster */ + if (memcmp(&conf->clusterid, a->data + len, + sizeof(conf->clusterid)) == 0) + return (0); + + /* prepend own clusterid */ + if ((a->data = realloc(a->data, a->len + + sizeof(conf->clusterid))) == NULL) + fatal("rde_reflector"); + memmove(a->data + sizeof(conf->clusterid), + a->data, a->len); + a->len += sizeof(conf->clusterid); + memcpy(a->data, &conf->clusterid, + sizeof(conf->clusterid)); + } else if (attr_optadd(attrs, ATTR_OPTIONAL, ATTR_CLUSTER_LIST, + &conf->clusterid, sizeof(conf->clusterid)) == -1) + fatalx("attr_optadd failed but impossible"); + } + return (1); +} + /* * control specific functions */ diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 6b37632841e..5b3a519c31d 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.39 2004/05/17 12:39:32 djm Exp $ */ +/* $OpenBSD: rde.h,v 1.40 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -97,7 +97,9 @@ enum attrtypes { ATTR_LOCALPREF, ATTR_ATOMIC_AGGREGATE, ATTR_AGGREGATOR, - ATTR_COMMUNITIES + ATTR_COMMUNITIES, + ATTR_ORIGINATOR_ID, + ATTR_CLUSTER_LIST }; /* attribute flags. 4 low order bits reserved */ @@ -227,7 +229,7 @@ void attr_free(struct attr_flags *); int attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *, u_int16_t); int attr_optadd(struct attr_flags *, u_int8_t, u_int8_t, - u_char *, u_int16_t); + void *, u_int16_t); struct attr *attr_optget(struct attr_flags *, u_int8_t); void attr_optfree(struct attr_flags *); diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index 772aa5bd2f3..511032b7a83 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.30 2004/05/17 12:39:32 djm Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.31 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -176,6 +176,18 @@ attr_parse(u_char *p, u_int16_t len, struct attr_flags *a, int ebgp, ATTR_PARTIAL)) return (-1); goto optattr; + case ATTR_ORIGINATOR_ID: + if (attr_len != 4) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, ATTR_PARTIAL)) + return (-1); + goto optattr; + case ATTR_CLUSTER_LIST: + if ((attr_len & 0x3) != 0) + return (-1); + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, ATTR_PARTIAL)) + return (-1); + goto optattr; default: optattr: if (attr_optadd(a, flags, type, p, attr_len) == -1) @@ -295,8 +307,17 @@ attr_error(u_char *p, u_int16_t len, struct attr_flags *attr, case ATTR_COMMUNITIES: if ((attr_len & 0x3) != 0) return (p); - /* FALLTHROUGH */ + goto optattr; + case ATTR_ORIGINATOR_ID: + if (attr_len != 4) + return (p); + goto optattr; + case ATTR_CLUSTER_LIST: + if ((attr_len & 0x3) != 0) + return (p); + goto optattr; default: +optattr: if ((flags & ATTR_OPTIONAL) == 0) { *suberr = ERR_UPD_UNKNWN_WK_ATTR; return (p); @@ -461,20 +482,11 @@ attr_write(void *p, u_int16_t p_len, u_int8_t flags, u_int8_t type, int attr_optadd(struct attr_flags *attr, u_int8_t flags, u_int8_t type, - u_char *data, u_int16_t len) + void *data, u_int16_t len) { struct attr *a, *p; - /* we need validate known optional attributes */ - - if (flags & ATTR_OPTIONAL && ! flags & ATTR_TRANSITIVE) - /* - * We already know that we're not interested in this attribute. - * Currently only the MED is optional and non-transitive but - * MED is directly stored in struct attr_flags. - */ - return (0); - + /* known optional attributes were validated previously */ a = calloc(1, sizeof(struct attr)); if (a == NULL) fatal("attr_optadd"); diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 1da0564a6e8..bd31894bba0 100644 --- a/usr.sbin/bgpd/rde_update.c +++ b/usr.sbin/bgpd/rde_update.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_update.c,v 1.17 2004/04/30 05:47:50 deraadt Exp $ */ +/* $OpenBSD: rde_update.c,v 1.18 2004/05/21 15:36:40 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -211,6 +211,7 @@ up_generate_updates(struct rde_peer *peer, { struct update_attr *a; struct update_prefix *p; + struct attr *atr; struct attr_flags attrs; ENSURE(peer->state == PEER_UP); @@ -240,10 +241,22 @@ up_generate_updates(struct rde_peer *peer, */ return; - if (peer->conf.ebgp == 0 && old->aspath->peer->conf.ebgp == 0 && - (old->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) == 0) - /* Do not redistribute updates to ibgp peers */ - return; + if (old->aspath->peer->conf.ebgp == 0 && peer->conf.ebgp == 0) { + /* + * redistribution rules: + * 1. if annouce is set -> announce + * 2. old non-client, new non-client -> no + * 3. old client, new non-client -> yes + * 4. old non-client, new client -> yes + * 5. old client, new client -> yes + */ + if (old->aspath->peer->conf.reflector_client == 0 && + peer->conf.reflector_client == 0 && + (old->aspath->nexthop->flags & + NEXTHOP_ANNOUNCE) == 0) + /* Do not redistribute updates to ibgp peers */ + return; + } /* announce type handling */ switch (peer->conf.announce_type) { @@ -273,6 +286,19 @@ up_generate_updates(struct rde_peer *peer, COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPSUBCONFED)) return; + /* + * don't send messages back to originator + * XXX this is not specified in the RFC but seems logical. + */ + if ((atr = attr_optget(&old->aspath->flags, + ATTR_ORIGINATOR_ID)) != NULL) { + ENSURE(atr->len == 4); + if (memcmp(atr->data, &peer->remote_bgpid, + sizeof(peer->remote_bgpid)) == 0) + /* would cause loop don't send */ + return; + } + /* copy attributes for output filter */ attr_copy(&attrs, &old->aspath->flags); @@ -281,7 +307,6 @@ up_generate_updates(struct rde_peer *peer, attr_free(&attrs); return; } - attr_free(&attrs); /* withdraw prefix */ @@ -311,11 +336,23 @@ up_generate_updates(struct rde_peer *peer, return; } - if (peer->conf.ebgp == 0 && new->aspath->peer->conf.ebgp == 0 && - (new->aspath->nexthop->flags & NEXTHOP_ANNOUNCE) == 0) { - /* Do not redistribute updates to ibgp peers */ - up_generate_updates(peer, NULL, old); - return; + if (new->aspath->peer->conf.ebgp == 0 && peer->conf.ebgp == 0) { + /* + * redistribution rules: + * 1. if annouce is set -> announce + * 2. old non-client, new non-client -> no + * 3. old client, new non-client -> yes + * 4. old non-client, new client -> yes + * 5. old client, new client -> yes + */ + if (new->aspath->peer->conf.reflector_client == 0 && + peer->conf.reflector_client == 0 && + (new->aspath->nexthop->flags & + NEXTHOP_ANNOUNCE) == 0) { + /* Do not redistribute updates to ibgp peers */ + up_generate_updates(peer, NULL, old); + return; + } } /* announce type handling */ @@ -365,6 +402,21 @@ up_generate_updates(struct rde_peer *peer, return; } + /* + * don't send messages back to originator + * XXX this is not specified in the RFC but seems logical. + */ + if ((atr = attr_optget(&new->aspath->flags, + ATTR_ORIGINATOR_ID)) != NULL) { + ENSURE(atr->len == 4); + if (memcmp(atr->data, &peer->remote_bgpid, + sizeof(peer->remote_bgpid)) == 0) { + /* would cause loop don't send */ + attr_free(&attrs); + return; + } + } + /* generate update */ p = calloc(1, sizeof(struct update_prefix)); if (p == NULL) @@ -485,7 +537,7 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, * dump all other path attributes. Following rules apply: * 1. well-known attrs: ATTR_ATOMIC_AGGREGATE and ATTR_AGGREGATOR * pass unmodified (enforce flags to correct values) - * 2. non-transitive attrs: don't re-announce + * 2. non-transitive attrs: don't re-announce to ebgp peers * 3. transitive known attrs: announce unmodified * 4. transitive unknown attrs: set partial bit and re-announce */ @@ -498,20 +550,30 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, return (-1); break; case ATTR_AGGREGATOR: - if ((r = attr_write(up_attr_buf + wlen, len, - oa->flags, oa->type, oa->data, oa->len)) == -1) - return (-1); - break; case ATTR_COMMUNITIES: + case ATTR_ORIGINATOR_ID: + case ATTR_CLUSTER_LIST: + if ((!(oa->flags & ATTR_TRANSITIVE)) && + peer->conf.ebgp != 0) { + r = 0; + break; + } if ((r = attr_write(up_attr_buf + wlen, len, oa->flags, oa->type, oa->data, oa->len)) == -1) return (-1); break; default: /* unknown attribute */ - if (!(oa->flags & ATTR_TRANSITIVE)) - /* somehow a non-transitive slipped through */ + if (!(oa->flags & ATTR_TRANSITIVE)) { + /* + * RFC 1771: + * Unrecognized non-transitive optional + * attributes must be quietly ignored and + * not passed along to other BGP peers. + */ + r = 0; break; + } if ((r = attr_write(up_attr_buf + wlen, len, oa->flags | ATTR_PARTIAL, oa->type, oa->data, oa->len)) == -1) |