@ <cf/vpn6 multicast/ | <cf/vpn6/ | <cf/ipv4/ and <cf/ipv6/ | 2 | 129
@ <cf/flow4/ | <cf/flow4/ | --- | 1 | 133
@ <cf/flow6/ | <cf/flow6/ | --- | 2 | 133
+@ <cf/neighbors/ | <cf/neighbor/ | --- | --- | ---
</tabular>
</table>
configuration). Note that blanket policies like <cf/all/ or <cf/none/ can still
be used in explicit configuration.
+<p>The <cf/neighbors/ channel is a special channel used for BGP unnumbered
+automatic peering. Unlike traditional AFI/SAFI channels that exchange routing
+information, the <cf/neighbors/ channel is used to dynamically trigger the
+creation of new BGP sessions. When present in a dynamic BGP configuration,
+exporting neighbor objects (typically from RAdv with router discovery enabled)
+to this channel will automatically spawn new BGP sessions for each discovered
+neighbor. The lifetime of these automatic sessions is tied to the presence of
+neighbor objects in the neighbor routing table, withdrawing an object will
+trigger deprecation of the corresponding BGP session. This channel is only
+allowed in dynamic BGP instances (those configured with <cf/neighbor range/).
+
<p>BGP channels have additional config options (together with the common ones):
<descrip>
}
</code>
+<p>Example configuration for BGP unnumbered automatic peering using router discovery:
+
+<p><code>
+# Table for discovered peers
+neighbor table peers;
+
+# RAdv protocol with router discovery enabled
+protocol radv {
+ neighbors { table peers; }; # Export discovered peers to this table
+
+ interface "eth*" {
+ # Normal RAdv configuration options...
+ router discovery yes; # Enable reception of ICMPv6 RAs
+ };
+}
+
+# Dynamic BGP with neighbor channel for automatic peering
+protocol bgp bgp_root {
+ local as 65000;
+ neighbor range fe80::/64 external; # Accept link-local peers
+ dynamic name "bgp_auto_"; # Name spawned sessions with this prefix
+ dynamic name digits 2; # Number of digits in name after prefix
+
+ ipv4 {
+ export all;
+ import all;
+ extended next hop; # Allow IPv6 next hops for IPv4 routes
+ };
+
+ ipv6 {
+ export all;
+ import all;
+ };
+
+ neighbors {
+ table peers; # Import peer discovery objects from this table
+ export all; # Allow all discovered peers to trigger sessions
+ };
+}
+</code>
+
<sect>BMP
<label id="bmp">
* BGP protocol glue
*/
+int
+bgp_preexport_nbr(struct bgp_proto *p, rte *e)
+{
+ const net_addr_nbr *nbr = (const net_addr_nbr *) e->net->n.addr;
+
+ if (!bgp_is_dynamic(p))
+ return -1;
+
+ /* Matching remote range */
+ if (p->cf->remote_range && !ipa_in_netX(nbr->addr, p->cf->remote_range))
+ return -1;
+
+ /* Matching interface */
+ if (p->cf->iface && (nbr->ifindex != p->cf->iface->index))
+ return -1;
+
+ /* Interface is valid */
+ struct iface *iface = if_find_by_index(nbr->ifindex);
+ if (!iface)
+ return -1;
+
+ /* Matching interface range */
+ if (p->cf->ipatt && !iface_patt_match(p->cf->ipatt, iface, NULL))
+ return -1;
+
+ return 0;
+}
+
+void
+bgp_rt_notify_nbr(struct bgp_proto *p, net *n, rte *new, rte *old)
+{
+ const net_addr_nbr *nbr = (const net_addr_nbr *) n->n.addr;
+
+ if (!bgp_is_dynamic(p))
+ return;
+
+ /* Protocol is not-yet-reconfigured */
+ if (p->p.cf->global != config)
+ {
+ log(L_WARN "%s: Ignoring neighbor %N received during reconfiguration", p->p.name, n->n.addr);
+ return;
+ }
+
+ if (new && !old)
+ return bgp_add_nbr(p, nbr);
+
+ if (!new && old)
+ return bgp_remove_nbr(p, nbr);
+}
+
int
bgp_preexport(struct channel *C, rte *e)
{
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
struct bgp_channel *c = (struct bgp_channel *) C;
- /* Ignore non-BGP channels */
+ /* Handle non-BGP channels */
if (C->channel != &channel_bgp)
+ {
+ if (C->net_type == NET_NEIGHBOR)
+ return bgp_preexport_nbr(p, e);
+
return -1;
+ }
/* Reject our routes */
if (src == p)
struct bgp_prefix *px;
u32 path;
- /* Ignore non-BGP channels */
+ /* Handle non-BGP channels */
if (C->channel != &channel_bgp)
+ {
+ if (C->net_type == NET_NEIGHBOR)
+ return bgp_rt_notify_nbr(p, n, new, old);
+
return;
+ }
if (new)
{
BGP_TRACE(D_EVENTS, "Started");
p->start_state = BSS_CONNECT;
+ /* For dynamic BGP, start neighbor channel immediately */
+ if (bgp_is_dynamic(p) && p->nbr_channel && !p->nbr_channel->disabled)
+ channel_set_state(p->nbr_channel, CS_UP);
+
if (!p->passive)
bgp_active(p);
cf->iface = iface;
cf->ipatt = NULL;
+ /* Remove neighbot channel from spawned session config */
+ struct channel_config *pc = proto_cf_find_channel(&cf->c, NET_NEIGHBOR);
+ if (pc)
+ rem_node(&pc->n);
+
return SKIP_BACK(struct bgp_proto, p, proto_spawn(sym->proto, 0));
}
return 0;
}
+static void
+bgp_spawn_nbr(struct bgp_proto *pp, const net_addr_nbr *nbr)
+{
+ struct iface *iface = if_find_by_index(nbr->ifindex);
+ if (!iface)
+ return;
+
+ struct bgp_proto *p = bgp_spawn(pp, nbr->addr, pp->cf->local_ip, iface);
+ p->claimed = true;
+}
+
+static struct bgp_proto *
+bgp_find_child_proto(struct bgp_proto *pp, const net_addr_nbr *nbr)
+{
+ WALK_LIST_(struct bgp_proto, p, proto_list)
+ {
+ /* Not a BGP */
+ if (p->p.proto != &proto_bgp)
+ continue;
+
+ /* Not spawned by the parent proto */
+ if (!p->p.cf->parent || (p->p.cf->parent->proto != &pp->p))
+ continue;
+
+ /* Remote address mismatch */
+ if (!ipa_equal(p->remote_ip, nbr->addr))
+ continue;
+
+ /* The interface mismatch */
+ if (p->cf->iface && (p->cf->iface->index != nbr->ifindex))
+ continue;
+
+ return p;
+ }
+
+ return NULL;
+}
+
+void
+bgp_add_nbr(struct bgp_proto *pp, const net_addr_nbr *nbr)
+{
+ struct bgp_proto *cp = bgp_find_child_proto(pp, nbr);
+
+ if (cp)
+ {
+ if (cp->claimed)
+ return;
+
+ TRACE_(pp, D_EVENTS, "Claiming BGP instance %s for neighbor %N", cp->p.name, nbr);
+
+ cp->claimed = true;
+ }
+ else
+ bgp_spawn_nbr(pp, nbr);
+}
+
+void
+bgp_remove_nbr(struct bgp_proto *pp, const net_addr_nbr *nbr)
+{
+ struct bgp_proto *cp = bgp_find_child_proto(pp, nbr);
+ if (!cp || !cp->claimed)
+ return;
+
+ TRACE_(pp, D_EVENTS, "Releasing BGP instance %s for neighbor %N", cp->p.name, nbr);
+
+ cp->claimed = false;
+}
+
+
void
bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len)
{
return;
}
-static inline int bgp_is_dynamic(struct bgp_proto *p)
-{ return ipa_zero(p->remote_ip); }
-
/**
* bgp_find_proto - find existing proto for incoming connection
* @sk: TCP socket
/* Add MPLS channel */
proto_configure_channel(P, &P->mpls_channel, proto_cf_mpls_channel(CF));
+ /* Add neighbor channel for dynamic BGP neighbor discovery */
+ proto_configure_channel(P, &p->nbr_channel, proto_cf_find_channel(CF, NET_NEIGHBOR));
+
return P;
}
cc->min_llgr_time, cc->max_llgr_time);
}
+
+ struct channel_config *nbr = proto_cf_find_channel(CF, NET_NEIGHBOR);
+ if (nbr && !cf->remote_range)
+ cf_error("Neighbor channel requires dynamic BGP (neighbor range option)");
}
static int
new->iface = old->iface;
new->ipatt = NULL;
+
+ /* Remove neighbor channel from spawned session config */
+ struct channel_config *pc = proto_cf_find_channel(&new->c, NET_NEIGHBOR);
+ if (pc)
+ rem_node(&pc->n);
}
int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
/* Reconfigure MPLS channel */
same = proto_configure_channel(P, &P->mpls_channel, proto_cf_mpls_channel(CF)) && same;
+ /* Reconfigure neighbor channel */
+ same = proto_configure_channel(P, &p->nbr_channel, proto_cf_find_channel(CF, NET_NEIGHBOR)) && same;
+
WALK_LIST_DELSAFE(C, C2, p->p.channels)
if (C->stale)
same = proto_configure_channel(P, &C, NULL) && same;
.template = "bgp%d",
.class = PROTOCOL_BGP,
.preference = DEF_PREF_BGP,
- .channel_mask = NB_IP | NB_VPN | NB_FLOW | NB_MPLS,
+ .channel_mask = NB_IP | NB_VPN | NB_FLOW | NB_MPLS | NB_NEIGHBOR,
.proto_size = sizeof(struct bgp_proto),
.config_size = sizeof(struct bgp_config),
.postconfig = bgp_postconfig,
list listen; /* Requests for shared listening sockets */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
+ struct channel *nbr_channel; /* Neighbor channel for dynamic BGP */
struct bgp_ao_state ao;
struct bgp_stats stats; /* BGP statistics */
btime last_established; /* Last time of enter/leave of established state */
u8 last_error_class; /* Error class of last error */
u32 last_error_code; /* Error code of last error. BGP protocol errors
are encoded as (bgp_err_code << 16 | bgp_err_subcode) */
+ bool claimed; /* Claimed dynamic protocols are kept, unclaimed may be removed */
};
struct bgp_channel {
void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len);
void bgp_close_conn(struct bgp_conn *c);
void bgp_update_startup_delay(struct bgp_proto *p);
+void bgp_add_nbr(struct bgp_proto *pp, const net_addr_nbr *nbr);
+void bgp_remove_nbr(struct bgp_proto *pp, const net_addr_nbr *nbr);
void bgp_conn_enter_openconfirm_state(struct bgp_conn *conn);
void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
return rt->attrs->dest != RTD_UNREACHABLE;
}
+static inline int
+bgp_is_dynamic(struct bgp_proto *p)
+{
+ return ipa_zero(p->remote_ip);
+}
+
#ifdef LOCAL_DEBUG
#define BGP_FORCE_DEBUG 1
| bgp_proto proto_item ';'
| bgp_proto bgp_proto_channel ';'
| bgp_proto mpls_channel ';'
+ | bgp_proto nbrs_channel ';'
| bgp_proto LOCAL bgp_loc_opts ';'
| bgp_proto LOCAL ipa ipa_scope bgp_loc_opts ';' {
BGP_CFG->local_ip = $3;