From 881ab0e49a146951d259f27dfa473a2e77c4dd69 Mon Sep 17 00:00:00 2001 From: Matteo Perin Date: Thu, 4 Dec 2025 10:41:00 +0100 Subject: [PATCH 1/4] Nest: Add net_peer route type to track discovered peers The definition and helper functions for a new route-like object to track peers discovery data has been added. It only contains the (v4 or v6) remote peer address and the ingress iface index, for now. The main intent of this is, currently, to enable BGP unnumbered auto peer discovery via RAdv incoming advertisments, but in the future the same data structure could be used to allow discovery coming from different protocols. Signed-off-by: Matteo Perin --- lib/net.c | 16 ++++++++++++++++ lib/net.h | 32 +++++++++++++++++++++++++++++++- nest/config.Y | 3 ++- nest/proto.c | 2 +- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/net.c b/lib/net.c index 64cf9e047..b5dee2355 100644 --- a/lib/net.c +++ b/lib/net.c @@ -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[] = { @@ -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[] = { @@ -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[] = { @@ -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 */ @@ -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)); @@ -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 @@ -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"); @@ -172,6 +180,7 @@ net_pxmask(const net_addr *a) case NET_MPLS: case NET_ASPA: + case NET_PEER: default: return IPA_NONE; } @@ -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; } @@ -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"); } } @@ -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; } } @@ -280,6 +293,7 @@ net_normalize(net_addr *N) case NET_MPLS: case NET_ASPA: + case NET_PEER: return; } } @@ -308,6 +322,7 @@ net_classify(const net_addr *N) case NET_MPLS: case NET_ASPA: + case NET_PEER: return IADDR_HOST | SCOPE_UNIVERSE; } @@ -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; } diff --git a/lib/net.h b/lib/net.h index 24aae87d2..ed08f6dc8 100644 --- a/lib/net.h +++ b/lib/net.h @@ -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) @@ -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) @@ -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; @@ -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; @@ -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) \ @@ -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); } @@ -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)) @@ -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); @@ -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); } @@ -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); @@ -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); } diff --git a/nest/config.Y b/nest/config.Y index 681d0edd9..f12103d91 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -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) @@ -231,6 +231,7 @@ net_type_base: | FLOW4{ $$ = NET_FLOW4; } | FLOW6{ $$ = NET_FLOW6; } | ASPA { $$ = NET_ASPA; } + | PEER { $$ = NET_PEER; } ; net_type: diff --git a/nest/proto.c b/nest/proto.c index d66200658..e8d0021f7 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -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); From e48917a033c17376a4c6ca6c079bd8fd51a97ffe Mon Sep 17 00:00:00 2001 From: Matteo Perin Date: Thu, 4 Dec 2025 16:44:00 +0100 Subject: [PATCH 2/4] RAdv: Add neighbor discovery based on incoming RAs to RAdv proto Implementation of peer discovery based on incoming Router Advertisments to the RAdv protocol. Up until this point no much use has been make of incoming RAs, this commit tries to amend that by importing peer-based routes to a new peers channel. This will allow to use the information discovered about remote routers on other protocols which can export peers on this same channel. RA staleness has also been taken into consideration and the routes are withdrawn whenever the advertised router lifetime expires. The feature is meant to be enabled via a new configuration option in the RAdv protocol called router discovery [yes/no]. Signed-off-by: Matteo Perin --- doc/bird.sgml | 11 +++ proto/radv/config.Y | 3 +- proto/radv/packets.c | 41 ++++++++++- proto/radv/radv.c | 165 ++++++++++++++++++++++++++++++++++++++++++- proto/radv/radv.h | 4 ++ 5 files changed, 219 insertions(+), 5 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index b0ac094ae..b0256b73c 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -5841,6 +5841,17 @@ custom option type 38 value hex:0e:10:20:01:0d:b8:00:0a:00:0b:00:00:00:00;