Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions doc/bird.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -3807,6 +3807,7 @@ together with their appropriate channels follows.
@ <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/peers/ | <cf/peer/ | --- | --- | ---
</tabular>
</table>

Expand All @@ -3828,6 +3829,17 @@ policies of <cf/import all/ and <cf/export none/ are used in absence of explicit
configuration). Note that blanket policies like <cf/all/ or <cf/none/ can still
be used in explicit configuration.

<p>The <cf/peers/ channel is a special channel type used for BGP unnumbered
automatic peering. Unlike traditional AFI/SAFI channels that exchange routing
information, the peers channel is used to dynamically trigger the creation of
new BGP sessions. When present in a dynamic BGP configuration, exporting
peer discovery objects (typically from RAdv with router discovery enabled) to
this channel will automatically spawn new BGP sessions for each discovered peer.
The lifetime of these automatic sessions is tied to the presence of the peer
objects in the peers routing table, withdrawing an object will trigger the
corresponding BGP session shutdown. 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>
Expand Down Expand Up @@ -4072,6 +4084,12 @@ be used in explicit configuration.
set <ref id="bgp-max-long-lived-stale-time" name="max long lived stale time">
per AFI/SAFI pair instead of per protocol. Default: set by protocol-wide
option.

<tag><label id="bgp-peers-persist">peers persist <m/switch/</tag>
This option controls whether automatically spawned BGP sessions should be permanent.
When enabled, a BGP session that was initially created automatically through the peers
discovery or the dynamic BGP mechanisms will continue to exist even after it has been
taken down. Default: on.
</descrip>

<sect1>Reconfiguration
Expand Down Expand Up @@ -4338,6 +4356,42 @@ protocol bgp {
}
</code>

<p>Example configuration for BGP unnumbered automatic peering using router discovery:

<p><code>
# Table for discovered peers
peers table bgp_peers;

# RAdv protocol with router discovery enabled
protocol radv {
peers { table bgp_peers; }; # Export discovered peers to this table

interface "eth*" {
# Normal RAdv configuration options...
router discovery yes; # Enable reception of ICMPv6 RAs
};
}

# Dynamic BGP with peers channel for automatic peering
protocol bgp {
local as 65000;
neighbor range fe80::/10 external; # Accept link-local peers
dynamic name "bgp_auto_"; # Name spawned sessions with this prefix

peers persist yes;

ipv6 {
export all;
import all;
};

peers {
table bgp_peers; # Import peer discovery objects from this table
export all; # Allow all discovered peers to trigger sessions
};
}
</code>


<sect>BMP
<label id="bmp">
Expand Down Expand Up @@ -5841,6 +5895,17 @@ custom option type 38 value hex:0e:10:20:01:0d:b8:00:0a:00:0b:00:00:00:00;
<tag><label id="radv-iface-custom-local">custom option local <m/switch/</tag>
Use only local custom option definitions for this interface. See <cf/rdnss local/
option above. Default: no.

<tag><label id="radv-router-discovery">router discovery <m/switch/</tag>
Enable reception and processing of incoming ICMPv6 Router Advertisement
messages. When enabled, the RAdv protocol listens for RA messages on
configured interfaces and creates route-like objects in a peers routing
table based on detected advertisements. These objects contain information
about discovered routers and their lifetimes are managed by the Router
Lifetime field in the RA messages. When an RA expires, the corresponding
object is automatically removed from the peers table. Currently, this feature
is primarily used in conjunction with BGP unnumbered automatic peering to
automatically discover BGP neighbors. Default: no.
</descrip>

<p>Prefix specific options
Expand Down
16 changes: 16 additions & 0 deletions lib/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const char * const net_label[] = {
[NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls",
[NET_ASPA] = "aspa",
[NET_PEER] = "peer",
};

const u16 net_addr_length[] = {
Expand All @@ -31,6 +32,7 @@ const u16 net_addr_length[] = {
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls),
[NET_ASPA] = sizeof(net_addr_aspa),
[NET_PEER] = sizeof(net_addr_peer),
};

const u8 net_max_prefix_length[] = {
Expand All @@ -45,6 +47,7 @@ const u8 net_max_prefix_length[] = {
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0,
[NET_ASPA] = 0,
[NET_PEER] = IP6_MAX_PREFIX_LENGTH,
};

const u16 net_max_text_length[] = {
Expand All @@ -59,6 +62,7 @@ const u16 net_max_text_length[] = {
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_MPLS] = 7, /* "1048575" */
[NET_ASPA] = 10, /* "4294967295" */
[NET_PEER] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
};

/* There should be no implicit padding in net_addr structures */
Expand All @@ -74,6 +78,7 @@ STATIC_ASSERT(sizeof(net_addr_flow6) == 20);
STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40);
STATIC_ASSERT(sizeof(net_addr_mpls) == 8);
STATIC_ASSERT(sizeof(net_addr_aspa) == 8);
STATIC_ASSERT(sizeof(net_addr_peer) == 24);

/* Ensure that all net_addr structures have the same alignment */
STATIC_ASSERT(alignof(net_addr_ip4) == alignof(net_addr));
Expand All @@ -89,6 +94,7 @@ STATIC_ASSERT(alignof(net_addr_flow6) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_ip6_sadr) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_mpls) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_aspa) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_peer) == alignof(net_addr));


int
Expand Down Expand Up @@ -147,6 +153,8 @@ net_format(const net_addr *N, char *buf, int buflen)
return bsnprintf(buf, buflen, "%u", n->mpls.label);
case NET_ASPA:
return bsnprintf(buf, buflen, "%u", n->aspa.asn);
case NET_PEER:
return bsnprintf(buf, buflen, "%I from iface %u", n->peer.addr, n->peer.ifindex);
}

bug("unknown network type");
Expand All @@ -172,6 +180,7 @@ net_pxmask(const net_addr *a)

case NET_MPLS:
case NET_ASPA:
case NET_PEER:
default:
return IPA_NONE;
}
Expand Down Expand Up @@ -207,6 +216,8 @@ net_compare(const net_addr *a, const net_addr *b)
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
case NET_ASPA:
return net_compare_aspa((const net_addr_aspa *) a, (const net_addr_aspa *) b);
case NET_PEER:
return net_compare_peer((const net_addr_peer *) a, (const net_addr_peer *) b);
}
return 0;
}
Expand All @@ -229,6 +240,7 @@ net_hash(const net_addr *n)
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
case NET_MPLS: return NET_HASH(n, mpls);
case NET_ASPA: return NET_HASH(n, aspa);
case NET_PEER: return NET_HASH(n, peer);
default: bug("invalid type");
}
}
Expand All @@ -252,6 +264,7 @@ net_validate(const net_addr *n)
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
case NET_MPLS: return NET_VALIDATE(n, mpls);
case NET_ASPA: return NET_VALIDATE(n, aspa);
case NET_PEER: return NET_VALIDATE(n, peer);
default: return 0;
}
}
Expand Down Expand Up @@ -280,6 +293,7 @@ net_normalize(net_addr *N)

case NET_MPLS:
case NET_ASPA:
case NET_PEER:
return;
}
}
Expand Down Expand Up @@ -308,6 +322,7 @@ net_classify(const net_addr *N)

case NET_MPLS:
case NET_ASPA:
case NET_PEER:
return IADDR_HOST | SCOPE_UNIVERSE;
}

Expand Down Expand Up @@ -342,6 +357,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)

case NET_MPLS:
case NET_ASPA:
case NET_PEER:
default:
return 0;
}
Expand Down
32 changes: 31 additions & 1 deletion lib/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
#define NET_IP6_SADR 9
#define NET_MPLS 10
#define NET_ASPA 11
#define NET_MAX 12
#define NET_PEER 12
#define NET_MAX 13

#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
Expand All @@ -37,6 +38,7 @@
#define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS)
#define NB_ASPA (1 << NET_ASPA)
#define NB_PEER (1 << NET_PEER)

#define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6)
Expand Down Expand Up @@ -133,6 +135,14 @@ typedef struct net_addr_aspa {
u32 asn;
} net_addr_aspa;

typedef struct net_addr_peer {
u8 type;
u8 pxlen;
u16 length;
ip_addr addr; /* Peer IP address (IPv6 or IPv4) */
u32 ifindex; /* Interface index */
} net_addr_peer;

typedef struct net_addr_ip6_sadr {
u8 type;
u8 dst_pxlen;
Expand All @@ -155,6 +165,7 @@ typedef union net_addr_union {
net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls;
net_addr_aspa aspa;
net_addr_peer peer;
} net_addr_union;


Expand Down Expand Up @@ -182,6 +193,7 @@ extern const u16 net_max_text_length[];
#define NET_PTR_FLOW6(_n) NET_PTR_GEN((_n), NET_FLOW6, flow6)
#define NET_PTR_IP6_SADR(_n) NET_PTR_GEN((_n), NET_IP6_SADR, ip6_sadr)
#define NET_PTR_MPLS(_n) NET_PTR_GEN((_n), NET_MPLS, mpls)
#define NET_PTR_PEER(_n) NET_PTR_GEN((_n), NET_PEER, peer)


#define NET_ADDR_IP4(prefix,pxlen) \
Expand Down Expand Up @@ -217,6 +229,9 @@ extern const u16 net_max_text_length[];
#define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })

#define NET_ADDR_PEER(addr, ifindex) \
((net_addr_peer) { NET_PEER, 0, sizeof(net_addr_peer), addr, ifindex })


static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
Expand Down Expand Up @@ -245,6 +260,9 @@ static inline void net_fill_mpls(net_addr *a, u32 label)
static inline void net_fill_aspa(net_addr *a, u32 asn)
{ *(net_addr_aspa *)a = NET_ADDR_ASPA(asn); }

static inline void net_fill_peer(net_addr *a, ip_addr addr, u32 ifindex)
{ *(net_addr_peer *)a = NET_ADDR_PEER(addr, ifindex); }

static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
{
if (ipa_is_ip4(prefix))
Expand Down Expand Up @@ -489,6 +507,9 @@ static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *
static inline int net_compare_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
{ return uint_cmp(a->asn, b->asn); }

static inline int net_compare_peer(const net_addr_peer *a, const net_addr_peer *b)
{ return ipa_compare(a->addr, b->addr) ?: uint_cmp(a->ifindex, b->ifindex); }

int net_compare(const net_addr *a, const net_addr *b);


Expand Down Expand Up @@ -528,6 +549,9 @@ static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
static inline void net_copy_aspa(net_addr_aspa *dst, const net_addr_aspa *src)
{ memcpy(dst, src, sizeof(net_addr_aspa)); }

static inline void net_copy_peer(net_addr_peer *dst, const net_addr_peer *src)
{ memcpy(dst, src, sizeof(net_addr_peer)); }


static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
{ return ip4_hash(prefix) ^ (pxlen << 26); }
Expand Down Expand Up @@ -574,6 +598,9 @@ static inline u32 net_hash_mpls(const net_addr_mpls *n)
static inline u32 net_hash_aspa(const net_addr_aspa *n)
{ return u32_hash(n->asn); }

static inline u32 net_hash_peer(const net_addr_peer *n)
{ return ipa_hash(n->addr) ^ u32_hash(n->ifindex); }

u32 net_hash(const net_addr *a);


Expand Down Expand Up @@ -626,6 +653,9 @@ static inline int net_validate_mpls(const net_addr_mpls *n)
static inline int net_validate_aspa(const net_addr_aspa *n)
{ return n->asn > 0; }

static inline int net_validate_peer(const net_addr_peer *n)
{ return ipa_nonzero(n->addr); }

static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }

Expand Down
3 changes: 2 additions & 1 deletion nest/config.Y
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ CF_DECLS

CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, TABLES, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA, PEER)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
Expand Down Expand Up @@ -231,6 +231,7 @@ net_type_base:
| FLOW4{ $$ = NET_FLOW4; }
| FLOW6{ $$ = NET_FLOW6; }
| ASPA { $$ = NET_ASPA; }
| PEER { $$ = NET_PEER; }
;

net_type:
Expand Down
2 changes: 1 addition & 1 deletion nest/proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty
if (!net_val_match(net_type, proto->protocol->channel_mask))
cf_error("Unsupported channel type");

if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS))
if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS) && (net_type != NET_PEER))
cf_error("Different channel type");

tab = rt_get_default_table(new_config, net_type);
Expand Down
3 changes: 3 additions & 0 deletions nest/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ struct proto {
TLIST_LIST(proto_neigh) neighbors; /* List of neighbor structures */
struct iface_subscription iface_sub; /* Interface notification subscription */
struct channel *mpls_channel; /* MPLS channel, when used */
struct channel *peers_channel; /* Peers channel for dynamic BGP peer discovery */

const char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */
Expand Down Expand Up @@ -754,6 +755,8 @@ static inline struct channel_config *proto_cf_main_channel(struct proto_config *
{ return proto_cf_find_channel(pc, pc->net_type); }
static inline struct channel_config *proto_cf_mpls_channel(struct proto_config *pc)
{ return (pc->net_type != NET_MPLS) ? proto_cf_find_channel(pc, NET_MPLS) : NULL; }
static inline struct channel_config *proto_cf_peers_channel(struct proto_config *pc)
{ return proto_cf_find_channel(pc, NET_PEER); }

struct channel *proto_find_channel_by_table(struct proto *p, rtable *t);
struct channel *proto_find_channel_by_name(struct proto *p, const char *n);
Expand Down
Loading