]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Allow onlink neighbors
authorMaria Matejka <mq@ucw.cz>
Thu, 19 Jun 2025 16:57:15 +0000 (18:57 +0200)
committerMaria Matejka <mq@ucw.cz>
Thu, 26 Jun 2025 12:57:06 +0000 (14:57 +0200)
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.

doc/bird.sgml
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index fc9040b1a520f0f564af69634a4d66bb161f04a1..5f91a8fd48bb023391e928e3ad731444c9799818 100644 (file)
@@ -2937,8 +2937,9 @@ protocol bgp [<name>] {
                max long lived stale time <number>;
        };
        local [<ip>] [port <number>] [as <number>];
-       neighbor [<ip> | range <prefix>] [port <number>] [as <number>] [internal|external];
+       neighbor [<ip> | range <prefix>] [onlink] [port <number>] [as <number>] [internal|external];
        interface "<text>";
+       onlink <switch>;
        direct;
        multihop [<number>];
        source address <ip>;
@@ -3060,6 +3061,25 @@ protocol bgp [<name>] {
        used for non link-local sessions when it is necessary to explicitly
        specify an interface, but only for direct (not multihop) sessions.
 
+       <tag><label id="bgp-onlink">onlink <m/switch/</tag>
+       For a direct neighbor, the BGP session starts immediately without
+       waiting for the neighbor's address to appear on any interface.
+       This option requires an interface to be configured. Next hops
+       of all routes from this session also have the <cf/onlink/ attribute.
+
+       This option may generally lead to weird behavior without other
+       configuration in place. One may e.g. need to insert a working route
+       for the given neighbor manually to allow for ACKs from the incoming
+       connection to be routed back correctly. That route may also need to
+       be announced via IGP, or <cf/next hop self/ in iBGP may be needed.
+
+       When trying setups with no neighbor route in containerized environments,
+       we got some results with <cf/strict bind/. As of Linux 6.12, we can't
+       recommend running that setup though.
+
+       Onlink behavior may also be specified inside the <cf/neighbor/ option.
+       Default: disabled.
+
        <tag><label id="bgp-direct">direct</tag>
        Specify that the neighbor is directly connected. The IP address of the
        neighbor must be from a directly reachable IP range (i.e. associated
index d38666fab1640e33ec0e7b69b88c3bc83de22245..3ea21eefc41ba8b0b711612b20e0c80d445f22bb 100644 (file)
@@ -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");
 
index 515ef2754c74f26fdee6c84f0070f706775039ca..8799ffb80fb5f6b5289cfabaad5d7e8ecc1511e6 100644 (file)
@@ -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 */
index 4a98df69e52886fd28372e7bcf30a66634d37ff8..31ec8b84efcb682cdc8a44b4e6c61dccb991476c 100644 (file)
@@ -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; }
index f6744ec700d528155a65e57ce08f53a9ea96db21..4581e1f910f62cd5f5ff470a7cee79a0ec141445 100644 (file)
@@ -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 */