When carrying out a BGP topology migration such as an AS renumbering or an AS merge, which involves changing the local AS being reported by one or both ends of a BGP session, being able to avoid the need to reconfigure both peers at the same time can significantly reduce the complexity of such a migration.
Also, if the peers are managed by different administrative entities, it might not even be possible to reconfigure both peers as closely together in time as you would like. One way to make this easier is to allow configuring two local AS numbers on a BGP session, one AS number to try first, and another AS number to fall back to if connecting with the preferred AS number gives us back a NOTIFICATION with OPEN message error "Bad Peer AS" from the peer. You can then add this option on your end of the BGP session, and then ask your peer to reconfigure their end, and the session will remain up in the time interval between your change and the peer's change. However, doing it this way adds quite some complexity to the BGP connection state machine. An easier way of achieving the same thing is to allow specifying two different AS numbers that we will accept from the peer, and let the BGP session establish if the peer sends us either one of those AS numbers as its local AS. You would then first reconfigure one or both peer(s) to accept a secondary remote AS number in addition to the one that is currently being used, and then reconfigure one or both peer(s) to change the local AS number being sent in the OPEN message. This is both easier to implement and avoids the unnecessary but unavoidable reconnection attempts that stem from the fact that we cannot know that we are going to be trying to OPEN the connection using a local AS that our peer does not expect before it sends us a "Bad Peer AS". This patch implements the latter option, and this functionality is enabled by specifying two different remote AS numbers for a peer in the neighbor statement. Instead of specifying: neighbor 1.2.3.4 as 12345; Specify this: neighbor 1.2.3.4 as 12345 as 23456; Which will allow peer 1.2.3.4 to connect to us using either AS 12345 or AS 23456. (bird's config grammar already allows specifying multiple remote AS numbers for a peer, and it will use the last AS number you specify this way if you do that. With this patch, it will use the last two AS numbers you specify.) --- proto/bgp/bgp.c | 17 +++++++++-------- proto/bgp/bgp.h | 2 +- proto/bgp/config.Y | 2 +- proto/bgp/packets.c | 8 ++++++-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 0f1c944..bceae4a 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1278,8 +1278,6 @@ bgp_init(struct proto_config *C) p->cf = c; p->local_as = c->local_as; - p->remote_as = c->remote_as; - p->is_internal = (c->local_as == c->remote_as); p->rs_client = c->rs_client; p->rr_client = c->rr_client; p->igp_table = get_igp_table(c); @@ -1291,8 +1289,6 @@ bgp_init(struct proto_config *C) void bgp_check_config(struct bgp_config *c) { - int internal = (c->local_as == c->remote_as); - /* Do not check templates at all */ if (c->c.class == SYM_TEMPLATE) return; @@ -1300,7 +1296,12 @@ bgp_check_config(struct bgp_config *c) /* EBGP direct by default, IBGP multihop by default */ if (c->multihop < 0) - c->multihop = internal ? 64 : 0; + { + if (c->local_as != c->remote_as || (c->remote_as2 != 0 && c->local_as != c->remote_as2)) + c->multihop = 0; + else + c->multihop = 64; + } /* Different default for gw_mode */ if (!c->gw_mode) @@ -1330,13 +1331,13 @@ bgp_check_config(struct bgp_config *c) if (!ipa_is_link_local(c->remote_ip) != !c->iface) cf_error("Link-local address and interface scope must be used together"); - if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF)) + if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF || c->remote_as2 > 0xFFFF)) cf_error("Neighbor AS number out of range (AS4 not available)"); - if (!internal && c->rr_client) + if (c->rr_client && (c->local_as != c->remote_as || (c->remote_as2 != 0 && c->local_as != c->remote_as2))) cf_error("Only internal neighbor can be RR client"); - if (internal && c->rs_client) + if (c->rs_client && (c->local_as == c->remote_as || c->local_as == c->remote_as2)) cf_error("Only external neighbor can be RS client"); if (c->multihop && (c->gw_mode == GW_DIRECT)) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index e47a0eb..0b8cb46 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -19,7 +19,7 @@ struct eattr; struct bgp_config { struct proto_config c; - u32 local_as, remote_as; + u32 local_as, remote_as, remote_as2; ip_addr remote_ip; ip_addr source_addr; /* Source address to use */ struct iface *iface; /* Interface for link-local addresses */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 55c602f..8cf70c1 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -61,7 +61,7 @@ bgp_proto_start: proto_start BGP { bgp_nbr_opts: /* empty */ | bgp_nbr_opts PORT expr { BGP_CFG->remote_port = $3; if (($3<1) || ($3>65535)) cf_error("Invalid port number"); } - | bgp_nbr_opts AS expr { BGP_CFG->remote_as = $3; } + | bgp_nbr_opts AS expr { BGP_CFG->remote_as2 = BGP_CFG->remote_as; BGP_CFG->remote_as = $3; } ; bgp_proto: diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index d100b7d..e402288 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -966,7 +966,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len) 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) + if (conn->advertised_as != p->cf->remote_as && conn->advertised_as != p->cf->remote_as2) { if (conn->peer_as4_support) { @@ -979,6 +979,9 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len) return; } + p->remote_as = conn->advertised_as; + p->is_internal = (p->local_as == p->remote_as); + /* Check the other connection */ other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn; switch (other->state) @@ -1550,7 +1553,8 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len) /* 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)) + && (p->cf->remote_as <= 0xFFFF) + && (p->cf->remote_as2 <= 0xFFFF)) /* Not possible with disabled capabilities */ { /* We try connect without capabilities */ -- 2.9.3