From e8ba557c7f66aaf02dd24f554fba8b3607c2b3d5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 13 Mar 2009 12:49:44 +0100 Subject: [PATCH] Update capability handshake options Add 'capabilities' option, change default behavior to advertise ipv4, add some checks and ignore incoming capabilities when capabilities are disabled. --- doc/bird.sgml | 24 +++++++++++++++--------- proto/bgp/bgp.c | 14 ++++++++++---- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 8 ++++++-- proto/bgp/packets.c | 31 ++++++++++++++++++++++--------- 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 5095b780..ee4697ec 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -669,6 +669,8 @@ MD5 password authentication (RFC 2385), route reflectors (RFC 4456), +multiprotocol extensions +(RFC 4760), and 4B AS numbers (RFC 4893). @@ -762,15 +764,19 @@ for each neighbor using the following configuration parameters: Even when disabled (off), BIRD behaves internally as AS4-aware BGP router. Default: on. - advertise ipv4 Advertise IPv4 multiprotocol capability. - This is not correct behavior but it is required by some BGP - implementations (Cisco and Quagga). Without this option, - the session establishment with these implementations takes - more time, session is degraded (no optional capabilities) - and the problem is logged. When the option is used, there might - be compatibility issues with other BGP implementations. - This option is relevant to IPv4 mode only. - Default: disabled. + capabilities Use capability advertisement + to advertise optional capabilities. This is standard behavior + for newer BGP implementations, but there might be some older + BGP implementations that reject such connection attempts. + When disabled (off), features that request it (4B AS support) + are also disabled. Default: on, with automatic fallback to + off when received capability-related error. + + advertise ipv4 Advertise IPv4 multiprotocol capability. + This is not a correct behavior according to the strict interpretation + of RFC 4760, but it is widespread and required by some BGP + implementations (Cisco and Quagga). This option is relevant + to IPv4 mode with enabled capability advertisement only. Default: on. disable after error When an error is encountered (either locally or by the other side), disable the instance automatically diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 558b1e8c..f44d016b 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -113,7 +113,7 @@ bgp_open(struct bgp_proto *p) } } - p->start_state = BSS_CONNECT; + p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP; return 0; } @@ -823,16 +823,22 @@ bgp_check(struct bgp_config *c) { if (!c->local_as) cf_error("Local AS number must be set"); + if (!c->remote_as) cf_error("Neighbor must be configured"); + if (!bgp_as4_support && c->enable_as4) cf_error("AS4 support disabled globally"); - if (!c->enable_as4 && (c->local_as > 0xFFFF)) + + if (!bgp_as4_support && (c->local_as > 0xFFFF)) cf_error("Local AS number out of range"); - if (!c->enable_as4 && (c->remote_as > 0xFFFF)) - cf_error("Neighbor AS number out of range"); + + if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF)) + cf_error("Neighbor AS number out of range (AS4 not available)"); + if ((c->local_as != c->remote_as) && (c->rr_client)) cf_error("Only internal neighbor can be RR client"); + if ((c->local_as == c->remote_as) && (c->rs_client)) cf_error("Only external neighbor can be RS client"); } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index bdad1a4d..d5448a68 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -25,6 +25,7 @@ struct bgp_config { int compare_path_lengths; /* Use path lengths when selecting best route */ u32 default_local_pref; /* Default value for LOCAL_PREF attribute */ u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */ + int capabilities; /* Enable capability handshake [RFC3392] */ int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */ u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */ int rr_client; /* Whether neighbor is RR client of me */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 4b64ed5f..c5ea87de 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -21,7 +21,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, ERROR, START, DELAY, FORGET, WAIT, ENABLE, DISABLE, AFTER, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, - PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4) + PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, + CAPABILITIES) CF_GRAMMAR @@ -40,6 +41,8 @@ bgp_proto_start: proto_start BGP { BGP_CFG->error_delay_time_min = 60; BGP_CFG->error_delay_time_max = 300; BGP_CFG->enable_as4 = bgp_as4_support; + BGP_CFG->capabilities = 2; + BGP_CFG->advertise_ipv4 = 1; } ; @@ -71,7 +74,8 @@ bgp_proto: | bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; } | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; } | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; } - | bgp_proto ADVERTISE IPV4 ';' { BGP_CFG->advertise_ipv4 = 1; } + | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } + | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; } ; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index dc3748f0..18412053 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -104,6 +104,7 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) #ifdef IPV6 cap = bgp_put_cap_ipv6(conn, cap); #endif + if (conn->want_as4_support) cap = bgp_put_cap_as4(conn, cap); @@ -504,6 +505,7 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) static int bgp_parse_options(struct bgp_conn *conn, byte *opt, int len) { + struct bgp_proto *p = conn->bgp; int ol; while (len > 0) @@ -524,7 +526,10 @@ bgp_parse_options(struct bgp_conn *conn, byte *opt, int len) switch (opt[0]) { case 2: - bgp_parse_capabilities(conn, opt + 2, ol); + if (conn->start_state == BSS_CONNECT_NOCAP) + BGP_TRACE(D_PACKETS, "Ignoring received capabilities"); + else + bgp_parse_capabilities(conn, opt + 2, ol); break; default: @@ -550,6 +555,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) struct bgp_proto *p = conn->bgp; struct bgp_config *cf = p->cf; unsigned hold; + u16 base_as; u32 id; /* Check state */ @@ -561,7 +567,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) { bgp_error(conn, 1, 2, pkt+16, 2); return; } if (pkt[19] != BGP_VERSION) { bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */ - conn->advertised_as = get_u16(pkt+20); + conn->advertised_as = base_as = get_u16(pkt+20); hold = get_u16(pkt+22); id = get_u32(pkt+24); BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", conn->advertised_as, hold, id); @@ -575,10 +581,11 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) if (!id || id == 0xffffffff || id == p->local_id) { bgp_error(conn, 2, 3, pkt+24, -4); return; } + if ((conn->advertised_as != base_as) && (base_as != AS_TRANS)) + log(L_WARN "%s: Peer advertised inconsistent AS numbers", p->p.name); + if (conn->advertised_as != p->remote_as) - { - bgp_error(conn, 2, 2, (byte *) &(conn->advertised_as), -4); return; - } + { bgp_error(conn, 2, 2, (byte *) &(conn->advertised_as), -4); return; } /* Check the other connection */ other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn; @@ -963,14 +970,20 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len) bgp_store_error(conn->bgp, conn, BE_BGP_RX, (code << 16) | subcode); #ifndef IPV6 - if ((code == 2) && ((subcode == 4) || (subcode == 7))) - { + if ((code == 2) && ((subcode == 4) || (subcode == 7)) /* Error related to capability: * 4 - Peer does not support capabilities at all. * 7 - Peer request some capability. Strange unless it is IPv6 only peer. - * We try connect without capabilities */ - log(L_WARN "%s: Capability related error received, capabilities disabled", p->p.name); + && (p->cf->capabilities == 2) + /* Capabilities are not explicitly enabled or disabled, therefore heuristic is used */ + && (conn->start_state == BSS_CONNECT) + /* Failed connection attempt have used capabilities */ + && (p->cf->remote_as <= 0xFFFF)) + /* Not possible with disabled capabilities */ + { + /* We try connect without capabilities */ + log(L_WARN "%s: Capability related error received, retry with capabilities disabled", p->p.name); conn->bgp->start_state = BSS_CONNECT_NOCAP; delay = 0; }