From: Maria Matejka Date: Thu, 19 Jun 2025 16:57:15 +0000 (+0200) Subject: BGP: Allow onlink neighbors X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=adbef6f974356582291c54a4b74f8dc34b9395ef;p=thirdparty%2Fbird.git BGP: Allow onlink neighbors In certain scenarios, the direct neighbor is not inside any prefix assigned to the appropriate interface. There is a route for that address pointing to that interface, though. In such cases, the user may specify the neighbor as onlink, effectively disabling the prefix check and trying to connect immediately. It is expected that the operator ensures that the neighbor is indeed there. --- diff --git a/doc/bird.sgml b/doc/bird.sgml index fc9040b1a..5f91a8fd4 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2937,8 +2937,9 @@ protocol bgp [] { max long lived stale time ; }; local [] [port ] [as ]; - neighbor [ | range ] [port ] [as ] [internal|external]; + neighbor [ | range ] [onlink] [port ] [as ] [internal|external]; interface ""; + onlink ; direct; multihop []; source address ; @@ -3060,6 +3061,25 @@ protocol bgp [] { used for non link-local sessions when it is necessary to explicitly specify an interface, but only for direct (not multihop) sessions. + Specify that the neighbor is directly connected. The IP address of the neighbor must be from a directly reachable IP range (i.e. associated diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index d38666fab..3ea21eefc 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -2032,7 +2032,7 @@ bgp_start_locked(struct object_lock *lock) return; } - neighbor *n = neigh_find(&p->p, p->remote_ip, cf->iface, NEF_STICKY); + neighbor *n = neigh_find(&p->p, p->remote_ip, cf->iface, NEF_STICKY | (cf->onlink ? NEF_ONLINK : 0)); if (!n) { log(L_ERR "%s: Invalid remote address %I%J", p->p.name, p->remote_ip, cf->iface); @@ -2575,6 +2575,12 @@ bgp_postconfig(struct proto_config *CF) if (cf->multihop && cf->bfd && ipa_zero(cf->local_ip)) cf_error("Multihop BGP with BFD requires specified local address"); + if (cf->multihop && cf->onlink) + cf_error("Multihop BGP cannot be configured onlink"); + + if (cf->onlink && !cf->iface) + cf_error("Onlink BGP must have interface configured"); + if (!cf->gr_mode && cf->llgr_mode) cf_error("Long-lived graceful restart requires basic graceful restart"); diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 515ef2754..8799ffb80 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -86,6 +86,7 @@ struct bgp_config { int multihop; /* Number of hops if multihop */ int strict_bind; /* Bind listening socket to local address */ int free_bind; /* Bind listening socket with SKF_FREEBIND */ + int onlink; /* Enable direct connection to a host not configured on any iface */ int ttl_security; /* Enable TTL security [RFC 5082] */ int compare_path_lengths; /* Use path lengths when selecting best route */ int med_metric; /* Compare MULTI_EXIT_DISC even between routes from differen ASes */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 4a98df69e..31ec8b84e 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -103,6 +103,7 @@ bgp_nbr_opts: | bgp_nbr_opts AS expr { BGP_CFG->remote_as = $3; } | bgp_nbr_opts INTERNAL { BGP_CFG->peer_type = BGP_PT_INTERNAL; } | bgp_nbr_opts EXTERNAL { BGP_CFG->peer_type = BGP_PT_EXTERNAL; } + | bgp_nbr_opts ONLINK { BGP_CFG->onlink = 1; } ; bgp_cease_mask: @@ -167,6 +168,7 @@ bgp_proto: BGP_CFG->remote_range = n; } | bgp_proto INTERFACE text ';' { BGP_CFG->iface = if_get_by_name($3); } + | bgp_proto ONLINK bool ';' { BGP_CFG->onlink = $3; } | bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; } | bgp_proto RR CLIENT bool ';' { BGP_CFG->rr_client = $4; } | bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index f6744ec70..4581e1f91 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -1084,14 +1084,16 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll) if (c->cf->gw_mode == GW_DIRECT) { neighbor *nbr = NULL; + uint nb_flags = p->cf->onlink ? NEF_ONLINK : 0; + struct iface *default_iface = p->cf->onlink ? p->neigh->iface : NULL; /* GW_DIRECT -> single_hop -> p->neigh != NULL */ if ((c->cf->next_hop_prefer == NHP_GLOBAL) && ipa_nonzero2(gw)) - nbr = neigh_find(&p->p, gw, NULL, 0); + nbr = neigh_find(&p->p, gw, default_iface, nb_flags); else if (ipa_nonzero(ll)) - nbr = neigh_find(&p->p, ll, p->neigh->iface, 0); + nbr = neigh_find(&p->p, ll, p->neigh->iface, nb_flags); else if (ipa_nonzero2(gw)) - nbr = neigh_find(&p->p, gw, NULL, 0); + nbr = neigh_find(&p->p, gw, default_iface, nb_flags); else WITHDRAW(BAD_NEXT_HOP " - zero address"); @@ -1104,6 +1106,7 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll) a->dest = RTD_UNICAST; a->nh.gw = nbr->addr; a->nh.iface = nbr->iface; + a->nh.flags = nbr->flags & NEF_ONLINK ? RNF_ONLINK : 0; a->igp_metric = c->cf->cost; } else /* GW_RECURSIVE */