diff options
author | 2007-04-23 13:04:24 +0000 | |
---|---|---|
committer | 2007-04-23 13:04:24 +0000 | |
commit | 0c88bf70c0a32b8b136f16df8694c401ec527219 (patch) | |
tree | 3d77d6b93907f8b7f461f88cff1677a0b617eb73 | |
parent | increment opackets and ipackets. clear OACTIVE when tx completes. (diff) | |
download | wireguard-openbsd-0c88bf70c0a32b8b136f16df8694c401ec527219.tar.xz wireguard-openbsd-0c88bf70c0a32b8b136f16df8694c401ec527219.zip |
Make bgpd 4-byte AS compatible. All internal representations of AS numbers
are now 4-byte instead of the old 2-byte numbers. The only exception are
communities because they can not be switched. The RDE will inflate and deflate
the ASPATH and AGGREGATOR attributes on demand and create the NEW_ASPATH and
NEW_AGGREGATOR field whenever needed. Both old and new stile sessions are
supported and can be mixed. Currently new stile sessions with the 4-byte AS
number capability turned on are only enabled if one of the AS numbers involved
is a 4-byte one.
This is based on an initial diff by Geoff Huston gih (at) apnic (dot) net
Cleanup, testing and bug-fixes by myself (via AS 3.10).
Currently mrt table dumps are producing incompatible output this will be fixed
afterwards -- this diff is already big enough.
"get it in if you think it is ready" henning@
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 17 | ||||
-rw-r--r-- | usr.sbin/bgpd/log.c | 3 | ||||
-rw-r--r-- | usr.sbin/bgpd/mrt.c | 16 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 68 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 20 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 142 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 60 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 317 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 19 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 76 | ||||
-rw-r--r-- | usr.sbin/bgpd/session.c | 71 | ||||
-rw-r--r-- | usr.sbin/bgpd/session.h | 7 | ||||
-rw-r--r-- | usr.sbin/bgpd/util.c | 56 |
13 files changed, 699 insertions, 173 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index e9351078a2d..2afd9cd77ff 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.215 2007/03/28 12:33:32 henning Exp $ */ +/* $OpenBSD: bgpd.h,v 1.216 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -165,7 +165,8 @@ struct bgpd_config { u_int rtableid; u_int32_t bgpid; u_int32_t clusterid; - u_int16_t as; + u_int32_t as; + u_int16_t short_as; u_int16_t holdtime; u_int16_t min_holdtime; }; @@ -224,6 +225,7 @@ struct capabilities { u_int8_t mp_v6; u_int8_t refresh; /* route refresh, RFC 2918 */ u_int8_t restart; /* draft-ietf-idr-restart */ + u_int8_t as4byte; }; struct peer_config { @@ -237,12 +239,12 @@ struct peer_config { char demote_group[IFNAMSIZ]; u_int32_t id; u_int32_t groupid; + u_int32_t remote_as; u_int32_t max_prefix; enum announce_type announce_type; enum enforce_as enforce_as; enum reconf_action reconf_action; u_int16_t max_prefix_restart; - u_int16_t remote_as; u_int16_t holdtime; u_int16_t min_holdtime; u_int8_t template; @@ -459,6 +461,7 @@ struct session_up { struct capabilities capa_announced; struct capabilities capa_received; u_int32_t remote_bgpid; + u_int16_t short_as; }; struct pftable_msg { @@ -531,7 +534,7 @@ enum as_spec { struct filter_as { enum as_spec type; - u_int16_t as; + u_int32_t as; }; struct filter_community { @@ -696,6 +699,9 @@ struct rde_memstats { #define SAFI_MULTICAST 0x02 #define SAFI_ALL 0xff +/* 4-byte magic AS number */ +#define AS_TRANS 23456 + /* prototypes */ /* bgpd.c */ void send_nexthop_update(struct kroute_nexthop *); @@ -798,7 +804,8 @@ const char *filterset_name(enum action_types); /* util.c */ 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_sockaddr(struct sockaddr *); +const char *log_as(u_int32_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/log.c b/usr.sbin/bgpd/log.c index 39ff54fccc9..c426ee96037 100644 --- a/usr.sbin/bgpd/log.c +++ b/usr.sbin/bgpd/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD: log.c,v 1.49 2007/02/25 12:02:40 henning Exp $ */ +/* $OpenBSD: log.c,v 1.50 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -33,7 +33,6 @@ int debug; -char *log_fmt_peer(const struct peer_config *); void logit(int, const char *, ...); char * diff --git a/usr.sbin/bgpd/mrt.c b/usr.sbin/bgpd/mrt.c index 3b16001cc72..9f05a7a654f 100644 --- a/usr.sbin/bgpd/mrt.c +++ b/usr.sbin/bgpd/mrt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mrt.c,v 1.52 2007/02/12 19:15:14 claudio Exp $ */ +/* $OpenBSD: mrt.c,v 1.53 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -120,10 +120,10 @@ mrt_dump_bgp_msg(struct mrt *mrt, void *pkg, u_int16_t pkglen, } if (!incoming) - DUMP_SHORT(buf, bgp->as); - DUMP_SHORT(buf, peer->conf.remote_as); + DUMP_SHORT(buf, bgp->short_as); + DUMP_SHORT(buf, peer->short_as); if (incoming) - DUMP_SHORT(buf, bgp->as); + DUMP_SHORT(buf, bgp->short_as); DUMP_SHORT(buf, /* ifindex */ 0); switch (peer->sa_local.ss_family) { case AF_INET: @@ -206,8 +206,8 @@ mrt_dump_state(struct mrt *mrt, u_int16_t old_state, u_int16_t new_state, return (-1); } - DUMP_SHORT(buf, bgp->as); - DUMP_SHORT(buf, peer->conf.remote_as); + DUMP_SHORT(buf, bgp->short_as); + DUMP_SHORT(buf, peer->short_as); DUMP_SHORT(buf, /* ifindex */ 0); switch (peer->sa_local.ss_family) { case AF_INET: @@ -376,7 +376,7 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum, } DUMP_SHORT(buf, rde_local_as()); - DUMP_SHORT(buf, peer->conf.remote_as); + DUMP_SHORT(buf, peer->short_as); DUMP_SHORT(buf, /* ifindex */ 0); switch (af) { @@ -492,7 +492,7 @@ mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum, DUMP_BYTE(buf, 1); /* state */ DUMP_LONG(buf, p->lastchange); /* originated */ DUMP_NLONG(buf, peer->remote_addr.v4.s_addr); - DUMP_SHORT(buf, peer->conf.remote_as); + DUMP_SHORT(buf, peer->short_as); DUMP_SHORT(buf, attr_len); if ((bptr = buf_reserve(buf, attr_len)) == NULL) { diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 64c5852fe94..45bddeef586 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.203 2007/04/17 17:17:45 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.204 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -172,8 +172,8 @@ typedef struct { %token IPV4 IPV6 %token QUALIFY VIA %token <v.string> STRING -%type <v.number> number asnumber optnumber yesno inout espah -%type <v.number> family restart +%type <v.number> number asnumber as4number optnumber yesno inout +%type <v.number> espah family restart %type <v.string> string %type <v.addr> address %type <v.prefix> prefix addrspec @@ -224,7 +224,44 @@ asnumber : number { } } -string : string STRING { +as4number : STRING { + const char *errstr; + char *dot; + u_int32_t uvalh = 0, uval; + + if ((dot = strchr($1,'.')) != NULL) { + *dot++ = '\0'; + uvalh = strtonum($1, 0, USHRT_MAX, &errstr); + if (errstr) { + yyerror("number %s is %s", $1, errstr); + free($1); + YYERROR; + } + uval = strtonum(dot, 0, USHRT_MAX, &errstr); + if (errstr) { + yyerror("number %s is %s", dot, errstr); + free($1); + YYERROR; + } + free($1); + } else { + uval = strtonum($1, 0, USHRT_MAX - 1, &errstr); + if (errstr) { + yyerror("number %s is %s", $1, errstr); + free($1); + YYERROR; + } + free($1); + } + if (uvalh == 0 && uval == AS_TRANS) { + yyerror("AS %u is reserved and may not be used", + AS_TRANS); + YYERROR; + } + $$ = uval | (uvalh << 16); + } + +string : string STRING { if (asprintf(&$$, "%s %s", $1, $2) == -1) fatal("string: asprintf"); free($1); @@ -233,7 +270,7 @@ string : string STRING { | STRING ; -yesno : STRING { +yesno : STRING { if (!strcmp($1, "yes")) $$ = 1; else if (!strcmp($1, "no")) @@ -256,7 +293,7 @@ varset : STRING '=' string { } ; -include : INCLUDE STRING { +include : INCLUDE STRING { struct file *nfile; if ((nfile = include_file($2)) == NULL) { @@ -271,8 +308,16 @@ include : INCLUDE STRING { } ; -conf_main : AS asnumber { +conf_main : AS as4number { conf->as = $2; + if ($2 > USHRT_MAX) + conf->short_as = AS_TRANS; + else + conf->short_as = $2; + } + | AS as4number asnumber { + conf->as = $2; + conf->short_as = $3; } | ROUTERID address { if ($2.af != AF_INET) { @@ -654,7 +699,7 @@ peeroptsl : peeropts nl | error nl ; -peeropts : REMOTEAS asnumber { +peeropts : REMOTEAS as4number { curpeer->conf.remote_as = $2; } | DESCR string { @@ -1214,7 +1259,7 @@ filter_as_l : filter_as } ; -filter_as : asnumber { +filter_as : as4number { if (($$ = calloc(1, sizeof(struct filter_as_l))) == NULL) fatal(NULL); @@ -2250,6 +2295,7 @@ alloc_peer(void) p->conf.capabilities.mp_v6 = SAFI_NONE; p->conf.capabilities.refresh = 1; p->conf.capabilities.restart = 0; + p->conf.capabilities.as4byte = 0; p->conf.softreconfig_in = 1; p->conf.softreconfig_out = 1; @@ -2511,6 +2557,10 @@ neighbor_consistent(struct peer *p) return (-1); } + /* for testing: enable 4-byte AS number capability if necessary */ + if (conf->as > USHRT_MAX || p->conf.remote_as > USHRT_MAX) + p->conf.capabilities.as4byte = 1; + /* set default values if they where undefined */ p->conf.ebgp = (p->conf.remote_as != conf->as); if (p->conf.announce_type == ANNOUNCE_UNDEF) diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index f72ef14e957..c6a7b2dd6a5 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.61 2007/03/29 13:09:26 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.62 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -176,9 +176,11 @@ print_mainconf(struct bgpd_config *conf) struct in_addr ina; struct listen_addr *la; - printf("AS %u\n", conf->as); + printf("AS %s", log_as(conf->as)); + if (conf->as > USHRT_MAX && conf->short_as != AS_TRANS) + printf(" %u", conf->short_as); ina.s_addr = conf->bgpid; - printf("router-id %s\n", inet_ntoa(ina)); + printf("\nrouter-id %s\n", inet_ntoa(ina)); if (conf->holdtime) printf("holdtime %u\n", conf->holdtime); if (conf->min_holdtime) @@ -270,7 +272,7 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c) if (p->descr[0]) printf("%s\tdescr \"%s\"\n", c, p->descr); if (p->remote_as) - printf("%s\tremote-as %u\n", c, p->remote_as); + printf("%s\tremote-as %s\n", c, log_as(p->remote_as)); if (p->distance > 1) printf("%s\tmultihop %u\n", c, p->distance); if (p->passive) @@ -469,15 +471,15 @@ print_rule(struct peer *peer_l, struct filter_rule *r) if (r->match.as.type) { if (r->match.as.type == AS_ALL) - printf("AS %u ", r->match.as.as); + printf("AS %s ", log_as(r->match.as.as)); else if (r->match.as.type == AS_SOURCE) - printf("source-as %u ", r->match.as.as); + printf("source-as %s ", log_as(r->match.as.as)); else if (r->match.as.type == AS_TRANSIT) - printf("transit-as %u ", r->match.as.as); + printf("transit-as %s ", log_as(r->match.as.as)); else if (r->match.as.type == AS_PEER) - printf("peer-as %u ", r->match.as.as); + printf("peer-as %s ", log_as(r->match.as.as)); else - printf("unfluffy-as %u ", r->match.as.as); + printf("unfluffy-as %s ", log_as(r->match.as.as)); } if (r->match.community.as != 0) { diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 39f4930d38f..5d66de45396 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.224 2007/04/06 18:03:50 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.225 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -58,6 +58,7 @@ void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, void rde_update_log(const char *, const struct rde_peer *, const struct bgpd_addr *, const struct bgpd_addr *, u_int8_t); +void rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *); int rde_reflector(struct rde_peer *, struct rde_aspath *); void rde_dump_rib_as(struct prefix *, struct rde_aspath *,pid_t, @@ -787,6 +788,13 @@ rde_update_dispatch(struct imsg *imsg) goto done; } + /* + * if either ATTR_NEW_AGGREGATOR or ATTR_NEW_ASPATH is present + * try to fixup the attributes. + */ + if (asp->flags & F_ATTR_AS4BYTE_NEW) + rde_as4byte_fixup(peer, asp); + /* enforce remote AS if requested */ if (asp->flags & F_ATTR_ASPATH && peer->conf.enforce_as == ENFORCE_AS_ON) @@ -1115,9 +1123,9 @@ rde_attr_parse(u_char *p, u_int16_t len, struct rde_peer *peer, struct rde_aspath *a, struct mpattr *mpa) { struct bgpd_addr nexthop; - u_char *op = p; + u_char *op = p, *npath; u_int32_t tmp32; - u_int16_t attr_len; + u_int16_t attr_len, nlen; u_int16_t plen = 0; u_int8_t flags; u_int8_t type; @@ -1176,15 +1184,22 @@ bad_flags: case ATTR_ASPATH: if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) goto bad_flags; - if (aspath_verify(p, attr_len) != 0) { + if (aspath_verify(p, attr_len, rde_as4byte(peer)) != 0) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, NULL, 0); return (-1); } if (a->flags & F_ATTR_ASPATH) goto bad_list; + if (rde_as4byte(peer)) { + npath = p; + nlen = attr_len; + } else + npath = aspath_inflate(p, attr_len, &nlen); a->flags |= F_ATTR_ASPATH; - a->aspath = aspath_get(p, attr_len); + a->aspath = aspath_get(npath, nlen); + if (npath != p) + free(npath); plen += attr_len; break; case ATTR_NEXTHOP: @@ -1253,10 +1268,23 @@ bad_flags: goto bad_flags; goto optattr; case ATTR_AGGREGATOR: - if (attr_len != 6) + if ((!rde_as4byte(peer) && attr_len != 6) || + (rde_as4byte(peer) && attr_len != 8)) goto bad_len; if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, 0)) goto bad_flags; + if (!rde_as4byte(peer)) { + /* need to inflate aggregator AS to 4-byte */ + u_char t[8]; + t[0] = t[1] = 0; + UPD_READ(&t[2], p, plen, 2); + UPD_READ(&t[4], p, plen, 4); + if (attr_optadd(a, flags, type, t, + sizeof(t)) == -1) + goto bad_list; + break; + } + /* 4-byte ready server take the default route */ goto optattr; case ATTR_COMMUNITIES: if ((attr_len & 0x3) != 0) @@ -1305,6 +1333,26 @@ bad_flags: mpa->unreach_len = attr_len; plen += attr_len; break; + case ATTR_NEW_AGGREGATOR: + if (attr_len != 8) + goto bad_len; + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + goto bad_flags; + a->flags |= F_ATTR_AS4BYTE_NEW; + goto optattr; + case ATTR_NEW_ASPATH: + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + goto bad_flags; + if (aspath_verify(p, attr_len, 1) != 0) { + /* XXX draft does not specify how to handle errors */ + rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, + NULL, 0); + return (-1); + } + a->flags |= F_ATTR_AS4BYTE_NEW; + goto optattr; default: if ((flags & ATTR_OPTIONAL) == 0) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_UNKNWN_WK_ATTR, @@ -1493,31 +1541,82 @@ rde_update_log(const char *message, const struct rde_peer *peer, const struct bgpd_addr *next, const struct bgpd_addr *prefix, u_int8_t prefixlen) { - char *nexthop = NULL; + char *n = NULL; char *p = NULL; if (!(conf->log & BGPD_LOG_UPDATES)) return; if (next != NULL) - if (asprintf(&nexthop, " via %s", - log_addr(next)) == -1) - nexthop = NULL; - + if (asprintf(&n, " via %s", log_addr(next)) == -1) + n = NULL; if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1) p = NULL; - log_info("neighbor %s (AS%u) %s %s %s", - log_addr(&peer->conf.remote_addr), peer->conf.remote_as, message, - p ? p : "out of memory", nexthop ? nexthop : ""); + log_info("%s AS%s: %s %s%s", + log_fmt_peer(&peer->conf), log_as(peer->conf.remote_as), message, + p ? p : "out of memory", n ? n : ""); - free(nexthop); + free(n); free(p); } /* - * route reflector helper function + * 4-Byte ASN helper function. + * Two scenarios need to be considered: + * - NEW session with NEW attributes present -> just remove the attributes + * - OLD session with NEW attributes present -> try to merge them */ +void +rde_as4byte_fixup(struct rde_peer *peer, struct rde_aspath *a) +{ + struct attr *nasp, *naggr, *oaggr; + u_int32_t as; + + /* first get the attributes */ + nasp = attr_optget(a, ATTR_NEW_ASPATH); + naggr = attr_optget(a, ATTR_NEW_AGGREGATOR); + + if (rde_as4byte(peer)) { + /* NEW session using 4-byte ASNs */ + if (nasp) + attr_free(a, nasp); + if (naggr) + attr_free(a, naggr); + return; + } + /* OLD session using 2-byte ASNs */ + /* try to merge the new attributes into the old ones */ + if ((oaggr = attr_optget(a, ATTR_AGGREGATOR))) { + memcpy(&as, oaggr->data, sizeof(as)); + if (ntohl(as) != AS_TRANS) { + /* per RFC draft ignore NEW_ASPATH and NEW_AGGREGATOR */ + if (nasp) + attr_free(a, nasp); + if (naggr) + attr_free(a, naggr); + return; + } + if (naggr) { + /* switch over to new AGGREGATOR */ + attr_free(a, oaggr); + if (attr_optadd(a, ATTR_OPTIONAL | ATTR_TRANSITIVE, + ATTR_AGGREGATOR, naggr->data, naggr->len)) + fatalx("attr_optadd failed but impossible"); + } + } + /* there is no need for NEW_AGGREGATOR any more */ + if (naggr) + attr_free(a, naggr); + /* merge NEW_ASPATH with ASPATH */ + if (nasp) + aspath_merge(a, nasp); +} + + +/* + * route reflector helper function + */ int rde_reflector(struct rde_peer *peer, struct rde_aspath *asp) { @@ -2228,7 +2327,7 @@ rde_update6_queue_runner(void) /* * generic helper function */ -u_int16_t +u_int32_t rde_local_as(void) { return (conf->as); @@ -2250,6 +2349,12 @@ rde_decisionflags(void) return (conf->flags & BGPD_FLAG_DECISION_MASK); } +int +rde_as4byte(struct rde_peer *peer) +{ + return (peer->capa_announced.as4byte && peer->capa_received.as4byte); +} + /* * peer functions */ @@ -2407,6 +2512,7 @@ peer_up(u_int32_t id, struct session_up *sup) if (peer->state != PEER_DOWN && peer->state != PEER_NONE) fatalx("peer_up: bad state"); peer->remote_bgpid = ntohl(sup->remote_bgpid); + peer->short_as = sup->short_as; memcpy(&peer->remote_addr, &sup->remote_addr, sizeof(peer->remote_addr)); memcpy(&peer->capa_announced, &sup->capa_announced, @@ -2538,12 +2644,14 @@ network_init(struct network_head *net_l) peerself.remote_bgpid = ntohl(conf->bgpid); id.s_addr = conf->bgpid; peerself.conf.remote_as = conf->as; + peerself.short_as = conf->short_as; snprintf(peerself.conf.descr, sizeof(peerself.conf.descr), "LOCAL: ID %s", inet_ntoa(id)); bzero(&peerdynamic, sizeof(peerdynamic)); peerdynamic.state = PEER_UP; peerdynamic.remote_bgpid = ntohl(conf->bgpid); peerdynamic.conf.remote_as = conf->as; + peerdynamic.short_as = conf->short_as; snprintf(peerdynamic.conf.descr, sizeof(peerdynamic.conf.descr), "LOCAL: ID %s", inet_ntoa(id)); diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index f9492cf9abf..1340a70c8a9 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.98 2007/04/06 18:03:51 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.99 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -73,6 +73,7 @@ struct rde_peer { u_int32_t up_nlricnt; u_int32_t up_wcnt; enum peer_state state; + u_int16_t short_as; u_int8_t reconf_in; /* in filter changed */ u_int8_t reconf_out; /* out filter changed */ }; @@ -104,7 +105,9 @@ enum attrtypes { ATTR_ORIGINATOR_ID, ATTR_CLUSTER_LIST, ATTR_MP_REACH_NLRI=14, - ATTR_MP_UNREACH_NLRI=15 + ATTR_MP_UNREACH_NLRI=15, + ATTR_NEW_ASPATH=17, + ATTR_NEW_AGGREGATOR=18 }; /* attribute flags. 4 low order bits reserved */ @@ -142,22 +145,23 @@ struct path_table { LIST_HEAD(prefix_head, prefix); -#define F_ATTR_ORIGIN 0x0001 -#define F_ATTR_ASPATH 0x0002 -#define F_ATTR_NEXTHOP 0x0004 -#define F_ATTR_LOCALPREF 0x0008 -#define F_ATTR_MED 0x0010 -#define F_ATTR_MED_ANNOUNCE 0x0020 -#define F_ATTR_MP_REACH 0x0040 -#define F_ATTR_MP_UNREACH 0x0080 -#define F_PREFIX_ANNOUNCED 0x0100 -#define F_NEXTHOP_REJECT 0x0200 -#define F_NEXTHOP_BLACKHOLE 0x0400 -#define F_NEXTHOP_NOMODIFY 0x0800 -#define F_NEXTHOP_SELF 0x1000 -#define F_ATTR_LINKED 0x2000 -#define F_LOCAL 0x4000 /* Local-RIB */ -#define F_ORIGINAL 0x8000 /* Adj-RIB-In */ +#define F_ATTR_ORIGIN 0x00001 +#define F_ATTR_ASPATH 0x00002 +#define F_ATTR_NEXTHOP 0x00004 +#define F_ATTR_LOCALPREF 0x00008 +#define F_ATTR_MED 0x00010 +#define F_ATTR_MED_ANNOUNCE 0x00020 +#define F_ATTR_MP_REACH 0x00040 +#define F_ATTR_MP_UNREACH 0x00080 +#define F_ATTR_AS4BYTE_NEW 0x00100 /* NEW_ASPATH or NEW_AGGREGATOR */ +#define F_PREFIX_ANNOUNCED 0x01000 +#define F_NEXTHOP_REJECT 0x02000 +#define F_NEXTHOP_BLACKHOLE 0x04000 +#define F_NEXTHOP_NOMODIFY 0x08000 +#define F_NEXTHOP_SELF 0x10000 +#define F_ATTR_LINKED 0x20000 +#define F_LOCAL 0x40000 /* Local-RIB */ +#define F_ORIGINAL 0x80000 /* Adj-RIB-In */ #define ORIGIN_IGP 0 @@ -179,9 +183,9 @@ struct rde_aspath { u_int32_t prefix_cnt; /* # of prefixes */ u_int32_t active_cnt; /* # of active prefixes */ u_int32_t adjrib_cnt; /* # of p. in Adj-RIB-In */ + u_int32_t flags; /* internally used */ u_int16_t rtlabelid; /* route label id */ u_int16_t pftableid; /* pf table id */ - u_int16_t flags; /* internally used */ u_int8_t origin; u_int8_t others_len; }; @@ -277,9 +281,10 @@ void rde_send_pftable(u_int16_t, struct bgpd_addr *, void rde_send_pftable_commit(void); void rde_generate_updates(struct prefix *, struct prefix *); -u_int16_t rde_local_as(void); +u_int32_t rde_local_as(void); int rde_noevaluate(void); int rde_decisionflags(void); +int rde_as4byte(struct rde_peer *); /* rde_attr.c */ int attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *, @@ -296,7 +301,7 @@ void attr_free(struct rde_aspath *, struct attr *); #define attr_optlen(x) \ ((x)->len > 255 ? (x)->len + 4 : (x)->len + 3) -int aspath_verify(void *, u_int16_t); +int aspath_verify(void *, u_int16_t, int); #define AS_ERR_LEN -1 #define AS_ERR_TYPE -2 #define AS_ERR_BAD -3 @@ -304,14 +309,17 @@ void aspath_init(u_int32_t); void aspath_shutdown(void); struct aspath *aspath_get(void *, u_int16_t); void aspath_put(struct aspath *); +u_char *aspath_inflate(void *, u_int16_t, u_int16_t *); +u_char *aspath_deflate(u_char *, u_int16_t *, int *); +void aspath_merge(struct rde_aspath *, struct attr *); u_char *aspath_dump(struct aspath *); u_int16_t aspath_length(struct aspath *); u_int16_t aspath_count(const void *, u_int16_t); -u_int16_t aspath_neighbor(struct aspath *); -int aspath_loopfree(struct aspath *, u_int16_t); +u_int32_t aspath_neighbor(struct aspath *); +int aspath_loopfree(struct aspath *, u_int32_t); int aspath_compare(struct aspath *, struct aspath *); -struct aspath *aspath_prepend(struct aspath *, u_int16_t, int); -int aspath_match(struct aspath *, enum as_spec, u_int16_t); +u_char *aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *); +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); @@ -403,6 +411,6 @@ int rde_filter_equal(struct filter_head *, struct filter_head *, struct rde_peer *, enum directions); /* util.c */ -u_int16_t aspath_extract(const void *, int); +u_int32_t aspath_extract(const void *, int); #endif /* __RDE_H__ */ diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index eaf692f5a9a..870fa1f5a93 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.70 2007/03/06 16:52:48 henning Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.71 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -361,6 +361,9 @@ attr_put(struct attr *a) /* aspath specific functions */ +u_int16_t aspath_countlength(struct aspath *, u_int16_t, int); +void aspath_countcopy(struct aspath *, u_int16_t, u_int8_t *, + u_int16_t, int); struct aspath *aspath_lookup(const void *, u_int16_t); struct aspath_table { @@ -372,18 +375,21 @@ struct aspath_table { &astable.hashtbl[(x) & astable.hashmask] int -aspath_verify(void *data, u_int16_t len) +aspath_verify(void *data, u_int16_t len, int as4byte) { u_int8_t *seg = data; - u_int16_t seg_size; + u_int16_t seg_size, as_size = 2; u_int8_t seg_len, seg_type; if (len & 1) /* odd length aspath are invalid */ return (AS_ERR_BAD); + if (as4byte) + as_size = 4; + for (; len > 0; len -= seg_size, seg += seg_size) { - if (len < 2) + if (len < 2) /* header length check */ return (AS_ERR_BAD); seg_type = seg[0]; seg_len = seg[1]; @@ -391,7 +397,7 @@ aspath_verify(void *data, u_int16_t len) if (seg_type != AS_SET && seg_type != AS_SEQUENCE) return (AS_ERR_TYPE); - seg_size = 2 + 2 * seg_len; + seg_size = 2 + as_size * seg_len; if (seg_size > len) return (AS_ERR_LEN); @@ -485,6 +491,134 @@ aspath_put(struct aspath *aspath) } u_char * +aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen) +{ + u_int8_t *seg, *nseg, *ndata; + u_int16_t seg_size, olen, nlen; + u_int8_t seg_len; + + /* first calculate the length of the aspath */ + seg = data; + nlen = 0; + for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) { + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int16_t) * seg_len; + nlen += 2 + sizeof(u_int32_t) * seg_len; + + if (seg_size > len) + fatalx("aspath_inflate: bula bula"); + } + + *newlen = nlen; + if ((ndata = malloc(nlen)) == NULL) + fatal("aspath_inflate"); + + /* then copy the aspath */ + seg = data; + for (nseg = ndata; nseg < ndata + nlen; ) { + *nseg++ = *seg++; + *nseg++ = seg_len = *seg++; + for (; seg_len > 0; seg_len--) { + *nseg++ = 0; + *nseg++ = 0; + *nseg++ = *seg++; + *nseg++ = *seg++; + } + } + + return (ndata); +} + +/* convert a 4 byte aspath to a 2byte one. data is freed by aspath_deflate */ +u_char * +aspath_deflate(u_char *data, u_int16_t *len, int *flagnew) +{ + u_int8_t *seg, *nseg, *ndata; + u_int32_t as; + int i; + u_int16_t seg_size, olen, nlen; + u_int8_t seg_len; + + /* first calculate the length of the aspath */ + nlen = 0; + seg = data; + olen = *len; + for (; olen > 0; olen -= seg_size, seg += seg_size) { + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + nlen += 2 + sizeof(u_int16_t) * seg_len; + + if (seg_size > olen) + fatalx("aspath_deflate: bula bula"); + } + + if ((ndata = malloc(nlen)) == NULL) + fatal("aspath_deflate"); + + /* then copy the aspath */ + seg = data; + olen = *len; + for (nseg = ndata; seg < data + olen; seg += seg_size) { + *nseg++ = seg[0]; + *nseg++ = seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + + for (i = 0; i < seg_len; i++) { + as = aspath_extract(seg, i); + if (as > USHRT_MAX) { + as = AS_TRANS; + *flagnew = 1; + } + *nseg++ = (as >> 8) & 0xff; + *nseg++ = as & 0xff; + } + } + + free(data); + *len = nlen; + return (ndata); +} + +void +aspath_merge(struct rde_aspath *a, struct attr *attr) +{ + u_int8_t *np; + u_int16_t ascnt, diff, nlen, difflen; + int hroom = 0; + + ascnt = aspath_count(attr->data, attr->len); + if (ascnt > a->aspath->ascnt) { + /* ASPATH is shorter then NEW_ASPATH no way to merge */ + attr_free(a, attr); + return; + } + + diff = a->aspath->ascnt - ascnt; + if (attr->len > 2 && attr->data[0] == AS_SEQUENCE) + hroom = attr->data[1]; + difflen = aspath_countlength(a->aspath, diff, hroom); + nlen = attr->len + difflen; + + if ((np = malloc(nlen)) == NULL) + fatal("aspath_merge"); + + /* copy head from old aspath */ + aspath_countcopy(a->aspath, diff, np, difflen, hroom); + + /* copy tail from new aspath */ + if (hroom > 0) + memcpy(np + nlen - attr->len + 2, attr->data + 2, + attr->len - 2); + else + memcpy(np + nlen - attr->len, attr->data, attr->len); + + aspath_put(a->aspath); + a->aspath = aspath_get(np, nlen); + free(np); + attr_free(a, attr); +} + +u_char * aspath_dump(struct aspath *aspath) { return (aspath->data); @@ -508,7 +642,7 @@ aspath_count(const void *data, u_int16_t len) for (; len > 0; len -= seg_size, seg += seg_size) { seg_type = seg[0]; seg_len = seg[1]; - seg_size = 2 + 2 * seg_len; + seg_size = 2 + sizeof(u_int32_t) * seg_len; if (seg_type == AS_SET) cnt += 1; @@ -522,6 +656,85 @@ aspath_count(const void *data, u_int16_t len) } u_int16_t +aspath_countlength(struct aspath *aspath, u_int16_t cnt, int headcnt) +{ + const u_int8_t *seg; + u_int16_t seg_size, len, clen; + u_int8_t seg_type = 0, seg_len = 0; + + seg = aspath->data; + clen = 0; + for (len = aspath->len; len > 0 && cnt > 0; + len -= seg_size, seg += seg_size) { + seg_type = seg[0]; + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + + if (seg_type == AS_SET) + cnt -= 1; + else if (seg_len > cnt) { + seg_len = cnt; + clen += 2 + sizeof(u_int32_t) * cnt; + break; + } else + cnt -= seg_len; + + clen += seg_size; + + if (seg_size > len) + fatalx("aspath_countlenght: bula bula"); + } + if (headcnt > 0 && seg_type == AS_SEQUENCE && headcnt + seg_len < 256) + /* no need for additional header from the new aspath. */ + clen -= 2; + + return (clen); +} + +void +aspath_countcopy(struct aspath *aspath, u_int16_t cnt, u_int8_t *buf, + u_int16_t size, int headcnt) +{ + const u_int8_t *seg; + u_int16_t seg_size, len; + u_int8_t seg_type, seg_len; + + if (headcnt > 0) + /* + * additional room because we steal the segment header + * from the other aspath + */ + size += 2; + seg = aspath->data; + for (len = aspath->len; len > 0 && cnt > 0; + len -= seg_size, seg += seg_size) { + seg_type = seg[0]; + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + + if (seg_type == AS_SET) + cnt -= 1; + else if (seg_len > cnt) { + seg_len = cnt + headcnt; + seg_size = 2 + sizeof(u_int32_t) * cnt; + cnt = 0; + } else { + cnt -= seg_len; + if (cnt == 0) + seg_len += headcnt; + } + + memcpy(buf, seg, seg_size); + buf[0] = seg_type; + buf[1] = seg_len; + buf += seg_size; + if (size < seg_size) + fatalx("aspath_countlength: would overflow"); + size -= seg_size; + } +} + +u_int32_t aspath_neighbor(struct aspath *aspath) { /* @@ -537,7 +750,7 @@ aspath_neighbor(struct aspath *aspath) } int -aspath_loopfree(struct aspath *aspath, u_int16_t myAS) +aspath_loopfree(struct aspath *aspath, u_int32_t myAS) { u_int8_t *seg; u_int16_t len, seg_size; @@ -547,7 +760,7 @@ aspath_loopfree(struct aspath *aspath, u_int16_t myAS) for (len = aspath->len; len > 0; len -= seg_size, seg += seg_size) { seg_type = seg[0]; seg_len = seg[1]; - seg_size = 2 + 2 * seg_len; + seg_size = 2 + sizeof(u_int32_t) * seg_len; for (i = 0; i < seg_len; i++) { if (myAS == aspath_extract(seg, i)) @@ -598,11 +811,11 @@ aspath_lookup(const void *data, u_int16_t len) /* * Returns a new prepended aspath. Old needs to be freed by caller. */ -struct aspath * -aspath_prepend(struct aspath *asp, u_int16_t as, int quantum) +u_char * +aspath_prepend(struct aspath *asp, u_int32_t as, int quantum, u_int16_t *len) { u_char *p; - int len, overflow = 0, shift = 0, size, wpos = 0; + int l, overflow = 0, shift = 0, size, wpos = 0; u_int8_t type; /* lunatic prepends are blocked in the parser and limited */ @@ -620,32 +833,35 @@ aspath_prepend(struct aspath *asp, u_int16_t as, int quantum) } if (quantum == 0) { - /* no change needed but increase refcnt as we return a copy */ - asp->refcnt++; - rdemem.aspath_refs++; - return (asp); + /* no change needed but return a copy */ + p = malloc(asp->len); + if (p == NULL) + fatal("aspath_prepend"); + memcpy(p, asp->data, asp->len); + *len = asp->len; + return (p); } else if (type == AS_SET || size + quantum > 255) { /* need to attach a new AS_SEQUENCE */ - len = 2 + quantum * 2 + asp->len; + l = 2 + quantum * sizeof(u_int32_t) + asp->len; overflow = type == AS_SET ? quantum : (size + quantum) & 0xff; } else - len = quantum * 2 + asp->len; + l = quantum * sizeof(u_int32_t) + asp->len; quantum -= overflow; - p = malloc(len); + p = malloc(l); if (p == NULL) fatal("aspath_prepend"); /* first prepends */ - as = htons(as); + as = htonl(as); if (overflow > 0) { p[wpos++] = AS_SEQUENCE; p[wpos++] = overflow; for (; overflow > 0; overflow--) { - memcpy(p + wpos, &as, 2); - wpos += 2; + memcpy(p + wpos, &as, sizeof(u_int32_t)); + wpos += sizeof(u_int32_t); } } if (quantum > 0) { @@ -654,24 +870,22 @@ aspath_prepend(struct aspath *asp, u_int16_t as, int quantum) p[wpos++] = quantum + size; for (; quantum > 0; quantum--) { - memcpy(p + wpos, &as, 2); - wpos += 2; + memcpy(p + wpos, &as, sizeof(u_int32_t)); + wpos += sizeof(u_int32_t); } } memcpy(p + wpos, asp->data + shift, asp->len - shift); - asp = aspath_get(p, len); - free(p); - - return (asp); + *len = l; + return (p); } /* we need to be able to search more than one as */ int -aspath_match(struct aspath *a, enum as_spec type, u_int16_t as) +aspath_match(struct aspath *a, enum as_spec type, u_int32_t as) { u_int8_t *seg; - int final, first; + int final; u_int16_t len, seg_size; u_int8_t i, seg_type, seg_len; @@ -683,36 +897,45 @@ aspath_match(struct aspath *a, enum as_spec type, u_int16_t as) } final = 0; - first = 1; 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 + 2 * seg_len; + seg_size = 2 + sizeof(u_int32_t) * seg_len; final = (len == seg_size); - if (type == AS_SOURCE && !final) + /* 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 */ - continue; + 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)) { - if (type == AS_PEER) { - if (first) - return (1); - else - return (0); - } else if (final && i + 1 >= seg_len) - /* the final (rightmost) as */ - if (type == AS_TRANSIT) - return (0); - else - return (1); - else if (type != AS_SOURCE) - return (1); + /* + * the source (rightmost) AS is excluded from + * AS_TRANSIT matches. + */ + if (final && i == seg_len - 1 && + type == AS_TRANSIT) + return (0); + return (1); } - first = 0; } } return (0); diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 97d4b992a32..8bb857d70f1 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.50 2006/05/28 23:24:15 claudio Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.51 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -74,9 +74,10 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, sa_family_t af, struct rde_peer *from, struct rde_peer *peer) { struct filter_set *set; - struct aspath *new; + u_char *np; int as, type; - u_int16_t prep_as; + u_int32_t prep_as; + u_int16_t nl; u_int8_t prepend; if (asp == NULL) @@ -141,20 +142,22 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, } break; case ACTION_SET_PREPEND_SELF: - as = rde_local_as(); + prep_as = rde_local_as(); prepend = set->action.prepend; - new = aspath_prepend(asp->aspath, as, prepend); + np = aspath_prepend(asp->aspath, prep_as, prepend, &nl); aspath_put(asp->aspath); - asp->aspath = new; + asp->aspath = aspath_get(np, nl); + free(np); break; case ACTION_SET_PREPEND_PEER: if (from == NULL) break; prep_as = from->conf.remote_as; prepend = set->action.prepend; - new = aspath_prepend(asp->aspath, prep_as, prepend); + np = aspath_prepend(asp->aspath, prep_as, prepend, &nl); aspath_put(asp->aspath); - asp->aspath = new; + asp->aspath = aspath_get(np, nl); + free(np); break; case ACTION_SET_NEXTHOP: case ACTION_SET_NEXTHOP_REJECT: diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 4346fa26535..d0517d6b16f 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.56 2007/04/06 18:03:51 claudio Exp $ */ +/* $OpenBSD: rde_update.c,v 1.57 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -610,12 +610,12 @@ int up_generate_attr(struct rde_peer *peer, struct update_attr *upa, struct rde_aspath *a, sa_family_t af) { - struct aspath *path; struct attr *oa; - u_int32_t tmp32; + u_char *pdata; + u_int32_t tmp32, aggr_as; in_addr_t nexthop; - int r, ismp = 0; - u_int16_t len = sizeof(up_attr_buf), wlen = 0; + int r, ismp = 0, neednewpath = 0, neednewaggr = 0; + u_int16_t len = sizeof(up_attr_buf), wlen = 0, plen; u_int8_t l; /* origin */ @@ -627,15 +627,18 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, /* aspath */ if (!peer->conf.ebgp || rde_decisionflags() & BGPD_FLAG_DECISION_TRANS_AS) - path = aspath_prepend(a->aspath, rde_local_as(), 0); + pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen); else - path = aspath_prepend(a->aspath, rde_local_as(), 1); + pdata = aspath_prepend(a->aspath, rde_local_as(), 1, &plen); + + if (!rde_as4byte(peer)) + pdata = aspath_deflate(pdata, &plen, &neednewpath); if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN, - ATTR_ASPATH, path->data, path->len)) == -1) + ATTR_ASPATH, pdata, plen)) == -1) return (-1); - aspath_put(path); wlen += r; len -= r; + free(pdata); switch (af) { case AF_INET: @@ -676,6 +679,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) + * Actually ATTR_AGGREGATOR may be deflated for OLD 2-byte peers. * 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 @@ -691,6 +695,34 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, return (-1); break; case ATTR_AGGREGATOR: + if (!rde_as4byte(peer)) { + /* need to deflate the aggregator */ + u_int8_t t[6]; + u_int16_t tas; + + if ((!(oa->flags & ATTR_TRANSITIVE)) && + peer->conf.ebgp != 0) { + r = 0; + break; + } + + memcpy(&aggr_as, oa->data, sizeof(aggr_as)); + if (htonl(aggr_as) > USHRT_MAX) { + tas = htons(AS_TRANS); + neednewaggr = 1; + } else + tas = htons(ntohl(aggr_as)); + + memcpy(t, &tas, sizeof(tas)); + memcpy(t + sizeof(tas), + oa->data + sizeof(aggr_as), + oa->len - sizeof(aggr_as)); + if ((r = attr_write(up_attr_buf + wlen, len, + oa->flags, oa->type, &t, sizeof(t))) == -1) + return (-1); + break; + } + /* FALLTHROUGH */ case ATTR_COMMUNITIES: case ATTR_ORIGINATOR_ID: case ATTR_CLUSTER_LIST: @@ -724,6 +756,32 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa, wlen += r; len -= r; } + /* NEW to OLD conversion when going sending stuff to a 2byte AS peer */ + if (neednewpath) { + if (!peer->conf.ebgp || + rde_decisionflags() & BGPD_FLAG_DECISION_TRANS_AS) + pdata = aspath_prepend(a->aspath, rde_local_as(), 0, + &plen); + else + pdata = aspath_prepend(a->aspath, rde_local_as(), 1, + &plen); + if (plen == 0) + r = 0; + else if ((r = attr_write(up_attr_buf + wlen, len, + ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_NEW_ASPATH, + pdata, plen)) == -1) + return (-1); + wlen += r; len -= r; + free(pdata); + } + if (neednewaggr) { + if ((r = attr_write(up_attr_buf + wlen, len, + ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_NEW_AGGREGATOR, + &aggr_as, sizeof(aggr_as))) == -1) + return (-1); + wlen += r; len -= r; + } + /* write mp attribute to different buffer */ if (ismp) if (up_generate_mp_reach(peer, upa, a, AF_INET6) == -1) diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index 9f8e7718d22..1f00f61a599 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.272 2007/03/28 11:53:48 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.273 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org> @@ -32,6 +32,7 @@ #include <err.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <poll.h> #include <pwd.h> #include <signal.h> @@ -82,7 +83,7 @@ int parse_open(struct peer *); int parse_update(struct peer *); int parse_refresh(struct peer *); int parse_notification(struct peer *); -int parse_capabilities(struct peer *, u_char *, u_int16_t); +int parse_capabilities(struct peer *, u_char *, u_int16_t, u_int32_t *); void session_dispatch_imsg(struct imsgbuf *, int, u_int *); void session_up(struct peer *); void session_down(struct peer *); @@ -1247,6 +1248,7 @@ session_capa_ann_none(struct peer *peer) peer->capa.ann.mp_v4 = SAFI_NONE; peer->capa.ann.refresh = 0; peer->capa.ann.restart = 0; + peer->capa.ann.as4byte = 0; } int @@ -1380,6 +1382,15 @@ session_open(struct peer *p) errs += buf_add(opb, &c, 4); } + /* 4-bytes AS numbers, draft-ietf-idr-as4bytes-12 */ + if (p->capa.ann.as4byte) { /* 4 bytes data */ + u_int32_t nas; + + nas = htonl(conf->as); + errs += session_capa_add(p, opb, CAPA_AS4BYTE, 4, &optparamlen); + errs += buf_add(opb, &nas, 4); + } + len = MSGSIZE_OPEN_MIN + optparamlen; if (errs || (buf = session_newmsg(OPEN, len)) == NULL) { buf_free(opb); @@ -1388,7 +1399,10 @@ session_open(struct peer *p) } msg.version = 4; - msg.myas = htons(conf->as); + if (conf->as > USHRT_MAX) + msg.myas = htons(conf->short_as); + else + msg.myas = htons(conf->as); if (p->conf.holdtime) msg.holdtime = htons(p->conf.holdtime); else @@ -1816,9 +1830,9 @@ parse_open(struct peer *peer) { u_char *p, *op_val; u_int8_t version, rversion; - u_int16_t as, msglen; + u_int16_t short_as, msglen; u_int16_t holdtime, oholdtime, myholdtime; - u_int32_t bgpid; + u_int32_t as, bgpid; u_int8_t optparamlen, plen; u_int8_t op_type, op_len; @@ -1846,22 +1860,16 @@ parse_open(struct peer *peer) return (-1); } - memcpy(&as, p, sizeof(as)); - p += sizeof(as); + memcpy(&short_as, p, sizeof(short_as)); + p += sizeof(short_as); + as = peer->short_as = ntohs(short_as); /* if remote-as is zero and it's a cloned neighbor, accept any */ - if (peer->conf.cloned && !peer->conf.remote_as) { - peer->conf.remote_as = ntohs(as); + if (peer->conf.cloned && !peer->conf.remote_as && as != AS_TRANS) { + peer->conf.remote_as = as; peer->conf.ebgp = (peer->conf.remote_as != conf->as); } - if (peer->conf.remote_as != ntohs(as)) { - log_peer_warnx(&peer->conf, "peer sent wrong AS %u", ntohs(as)); - session_notification(peer, ERR_OPEN, ERR_OPEN_AS, NULL, 0); - change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN); - return (-1); - } - memcpy(&oholdtime, p, sizeof(oholdtime)); p += sizeof(oholdtime); @@ -1940,7 +1948,8 @@ parse_open(struct peer *peer) switch (op_type) { case OPT_PARAM_CAPABILITIES: /* RFC 3392 */ - if (parse_capabilities(peer, op_val, op_len) == -1) { + if (parse_capabilities(peer, op_val, op_len, + &as) == -1) { session_notification(peer, ERR_OPEN, 0, NULL, 0); change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN); @@ -1968,6 +1977,14 @@ parse_open(struct peer *peer) } } + if (peer->conf.remote_as != as) { + log_peer_warnx(&peer->conf, "peer sent wrong AS %s", + log_as(as)); + session_notification(peer, ERR_OPEN, ERR_OPEN_AS, NULL, 0); + change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN); + return (-1); + } + return (0); } @@ -2103,6 +2120,11 @@ parse_notification(struct peer *peer) log_peer_warnx(&peer->conf, "disabling restart capability"); break; + case CAPA_AS4BYTE: + peer->capa.ann.as4byte = 0; + log_peer_warnx(&peer->conf, + "disabling 4-byte AS num capability"); + break; default: /* should not happen... */ log_peer_warnx(&peer->conf, "received " "\"unsupported capability\" notification " @@ -2126,7 +2148,7 @@ parse_notification(struct peer *peer) } int -parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen) +parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as) { u_int16_t len; u_int8_t capa_code; @@ -2134,6 +2156,7 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen) u_char *capa_val; u_int16_t mp_afi; u_int8_t mp_safi; + u_int32_t remote_as; len = dlen; while (len > 0) { @@ -2203,6 +2226,17 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen) peer->capa.peer.restart = 1; /* we don't care about the further restart capas yet */ break; + case CAPA_AS4BYTE: + if (capa_len != 4) { + log_peer_warnx(&peer->conf, + "parse_capabilities: " + "expect len 4, len is %u", capa_len); + return (-1); + } + memcpy(&remote_as, capa_val, sizeof(remote_as)); + *as = ntohl(remote_as); + peer->capa.peer.as4byte = 1; + break; default: break; } @@ -2754,6 +2788,7 @@ session_up(struct peer *p) } sup.remote_bgpid = p->remote_bgpid; + sup.short_as = p->short_as; memcpy(&sup.capa_announced, &p->capa.ann, sizeof(sup.capa_announced)); memcpy(&sup.capa_received, &p->capa.peer, sizeof(sup.capa_received)); p->stats.last_updown = time(NULL); diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h index 3bdcd721b96..f6973ef2f60 100644 --- a/usr.sbin/bgpd/session.h +++ b/usr.sbin/bgpd/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.91 2007/04/06 18:03:51 claudio Exp $ */ +/* $OpenBSD: session.h,v 1.92 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -104,7 +104,8 @@ enum capa_codes { CAPA_NONE, CAPA_MP, CAPA_REFRESH, - CAPA_RESTART = 64 + CAPA_RESTART = 64, + CAPA_AS4BYTE = 65 }; struct bgp_msg { @@ -194,6 +195,7 @@ struct peer { u_int32_t remote_bgpid; enum session_state state; enum session_state prev_state; + u_int16_t short_as; u_int16_t holdtime; u_int8_t depend_ok; u_int8_t demoted; @@ -215,6 +217,7 @@ int imsg_compose_parent(int, pid_t, void *, u_int16_t); int imsg_compose_rde(int, pid_t, void *, u_int16_t); /* log.c */ +char *log_fmt_peer(const struct peer_config *); void log_statechange(struct peer *, enum session_state, enum session_events); void log_notification(const struct peer *, u_int8_t, u_int8_t, diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c index d301dce878d..2043ca6ab29 100644 --- a/usr.sbin/bgpd/util.c +++ b/usr.sbin/bgpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.1 2006/01/03 22:19:59 claudio Exp $ */ +/* $OpenBSD: util.c,v 1.2 2007/04/23 13:04:24 claudio Exp $ */ /* * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> @@ -74,6 +74,22 @@ log_sockaddr(struct sockaddr *sa) return (buf); } +const char * +log_as(u_int32_t as) +{ + static char buf[12]; /* "65000.65000\0" */ + + if (as < USHRT_MAX) { + if (snprintf(buf, sizeof(buf), "%u", as) == -1) + return ("?"); + } else { + if (snprintf(buf, sizeof(buf), "%u.%u", as >> 16, + as & 0xffff) == -1) + return ("?"); + } + return (buf); +} + int aspath_snprint(char *buf, size_t size, void *data, u_int16_t len) { @@ -100,7 +116,7 @@ aspath_snprint(char *buf, size_t size, void *data, u_int16_t len) for (; len > 0; len -= seg_size, seg += seg_size) { seg_type = seg[0]; seg_len = seg[1]; - seg_size = 2 + 2 * seg_len; + seg_size = 2 + sizeof(u_int32_t) * seg_len; if (seg_type == AS_SET) { if (total_size != 0) @@ -114,7 +130,8 @@ aspath_snprint(char *buf, size_t size, void *data, u_int16_t len) } for (i = 0; i < seg_len; i++) { - r = snprintf(buf, size, "%hu", aspath_extract(seg, i)); + r = snprintf(buf, size, "%s", + log_as(aspath_extract(seg, i))); UPDATE(); if (i + 1 < seg_len) { r = snprintf(buf, size, " "); @@ -160,7 +177,8 @@ aspath_strlen(void *data, u_int16_t len) { u_int8_t *seg; int total_size; - u_int16_t as, seg_size; + u_int32_t as; + u_int16_t seg_size; u_int8_t i, seg_type, seg_len; total_size = 0; @@ -168,7 +186,7 @@ aspath_strlen(void *data, u_int16_t len) for (; len > 0; len -= seg_size, seg += seg_size) { seg_type = seg[0]; seg_len = seg[1]; - seg_size = 2 + 2 * seg_len; + seg_size = 2 + sizeof(u_int32_t) * seg_len; if (seg_type == AS_SET) if (total_size != 0) @@ -180,6 +198,21 @@ aspath_strlen(void *data, u_int16_t len) for (i = 0; i < seg_len; i++) { as = aspath_extract(seg, i); + if (as > USHRT_MAX) { + u_int32_t a = as >> 16; + + if (a >= 10000) + total_size += 5; + else if (a >= 1000) + total_size += 4; + else if (a >= 100) + total_size += 3; + else if (a >= 10) + total_size += 2; + else + total_size += 1; + total_size += 1; /* dot between hi & lo */ + } if (as >= 10000) total_size += 5; else if (as >= 1000) @@ -206,16 +239,13 @@ aspath_strlen(void *data, u_int16_t len) * Direct access is not possible because of non-aligned reads. * ATTENTION: no bounds check are done. */ -u_int16_t +u_int32_t aspath_extract(const void *seg, int pos) { const u_char *ptr = seg; - u_int16_t as = 0; + u_int32_t as; - ptr += 2 + 2 * pos; - as = *ptr++; - as <<= 8; - as |= *ptr; - return (as); + ptr += 2 + sizeof(u_int32_t) * pos; + memcpy(&as, ptr, sizeof(u_int32_t)); + return (ntohl(as)); } - |