From: Ondrej Zajicek Date: Tue, 12 May 2026 16:31:11 +0000 (+0200) Subject: BGP: PMSI tunnel attribute support X-Git-Tag: v2.19.0~15 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=bb9c8dff4abad8f24b222c912c04421e92fe03e5;p=thirdparty%2Fbird.git BGP: PMSI tunnel attribute support Add support for BGP PMSI tunnel attribute (RFC 6514), specifically ingress replication type. This is required for EVPN IMET routes. --- diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 184c07775..b8c1a26c2 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -203,6 +203,30 @@ bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size) } +/* + * PMSI tunnel handling + */ + +adata * +bgp_pmsi_new_ingress_replication(linpool *pool, ip_addr addr, u32 label) +{ + int v4 = ipa_is_ip4(addr); + uint dlen = 5 + (v4 ? sizeof(ip4_addr) : sizeof(ip6_addr)); + adata *ad = lp_alloc_adata(pool, dlen); + + ad->data[0] = 0; + ad->data[1] = BGP_PMSI_TYPE_INGRESS_REPLICATION; + put_u24(ad->data + 2, label); + + if (v4) + put_ip4(ad->data + 5, ipa_to_ip4(addr)); + else + put_ip6(ad->data + 5, ipa_to_ip6(addr)); + + return ad; +} + + /* * AIGP handling */ @@ -849,6 +873,64 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt } +static void +bgp_decode_pmsi_tunnel(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to) +{ + if (len < 5) + WITHDRAW(BAD_LENGTH, "PMSI_TUNNEL", len); + + uint dlen = len - 5; + + switch (data[1]) + { + case BGP_PMSI_TYPE_NO_INFO: + if (dlen != 0) + WITHDRAW(BAD_LENGTH, "PMSI_TUNNEL", len); + break; + + case BGP_PMSI_TYPE_INGRESS_REPLICATION: + if ((dlen != sizeof(ip4_addr)) && (dlen != sizeof(ip6_addr))) + WITHDRAW(BAD_LENGTH, "PMSI_TUNNEL", len); + break; + + default: + flags |= BAF_PARTIAL; + } + + bgp_set_attr_data(to, s->pool, BA_PMSI_TUNNEL, flags, data, len); +} + +static void +bgp_format_pmsi_tunnel(const eattr *a, byte *buf, uint size) +{ + const adata *ad = a->u.ptr; + uint type = bgp_pmsi_get_type(ad); + uint label = bgp_pmsi_get_label(ad); + + char mpls[16] = {}; + if (label) + bsprintf(mpls, " mpls %u", label); + + switch (type) + { + case BGP_PMSI_TYPE_NO_INFO: + bsnprintf(buf, size, "no-info%s", mpls); + break; + + case BGP_PMSI_TYPE_INGRESS_REPLICATION:; + ip_addr a = bgp_pmsi_ir_get_endpoint(ad); + bsnprintf(buf, size, "ingress-replication %I%s", a, mpls); + break; + + default:; + int n = bsnprintf(buf, size, "type %u%s ", type, mpls); + ADVANCE(buf, size, n); + bstrbintohex(ad->data + 5, ad->length - 5, buf, size, ':'); + break; + } +} + + static void bgp_export_aigp(struct bgp_export_state *s, eattr *a) { @@ -1112,6 +1194,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = { .decode = bgp_decode_as4_aggregator, .format = bgp_format_aggregator, }, + [BA_PMSI_TUNNEL] = { + .name = "pmsi_tunnel", + .type = EAF_TYPE_OPAQUE, + .flags = BAF_OPTIONAL | BAF_TRANSITIVE, + .encode = bgp_encode_raw, + .decode = bgp_decode_pmsi_tunnel, + .format = bgp_format_pmsi_tunnel, + }, [BA_AIGP] = { .name = "aigp", .type = EAF_TYPE_OPAQUE, diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index d3db1c270..bf801672e 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -89,6 +89,7 @@ * RFC 5668 - 4-Octet AS Specific BGP Extended Community * RFC 5925 - TCP Authentication Option * RFC 6286 - AS-Wide Unique BGP Identifier + * RFC 6514 - BGP PMSI Tunnel Attribute * RFC 6608 - Subcodes for BGP Finite State Machine Error * RFC 6793 - BGP Support for 4-Octet AS Numbers * RFC 7311 - Accumulated IGP Metric Attribute for BGP diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 9834030a9..3be614e79 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -720,10 +720,37 @@ void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *ol int bgp_preexport(struct channel *, struct rte *); int bgp_get_attr(const struct eattr *e, byte *buf, int buflen); void bgp_get_route_info(struct rte *, byte *buf); +adata * bgp_pmsi_new_ingress_replication(linpool *pool, ip_addr addr, u32 label); int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad); byte * bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, byte *end, const net_addr *n, const struct rte *new, const struct rte_src *src); +#define BGP_PMSI_TYPE_NO_INFO 0 +#define BGP_PMSI_TYPE_INGRESS_REPLICATION 6 + +static inline uint +bgp_pmsi_get_type(const adata *ad) +{ return ad->data[1]; } + +static inline u32 +bgp_pmsi_get_label(const adata *ad) +{ return get_u24(ad->data + 2); } + +static inline ip_addr +bgp_pmsi_ir_get_endpoint(const adata *ad) +{ + uint dlen = ad->length - 5; + const byte *data = ad->data + 5; + + if (dlen == sizeof(ip4_addr)) + return ipa_from_ip4(get_ip4(data)); + else if (dlen == sizeof(ip6_addr)) + return ipa_from_ip6(get_ip6(data)); + else + return IPA_NONE; +} + + #define BGP_AIGP_METRIC 1 #define BGP_AIGP_MAX U64(0xffffffffffffffff) @@ -790,6 +817,7 @@ byte *bgp_create_end_mark_(struct bgp_channel *c, byte *buf); #define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */ #define BA_AS4_PATH 0x11 /* RFC 6793 */ #define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */ +#define BA_PMSI_TUNNEL 0x16 /* RFC 6514 */ #define BA_AIGP 0x1a /* RFC 7311 */ #define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */ #define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index cc1bfcef6..0ddaf10a0 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -36,7 +36,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE, FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER, RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, RECV, MIN, MAX, - AUTHENTICATION, NONE, MD5, AO, FORMAT, NATIVE, SINGLE, DOUBLE) + AUTHENTICATION, NONE, MD5, AO, FORMAT, NATIVE, SINGLE, DOUBLE, + BGP_PMSI_TUNNEL) CF_KEYWORDS(KEY, KEYS, SECRET, DEPRECATED, PREFERRED, ALGORITHM, CMAC, AES128) @@ -522,6 +523,8 @@ dynamic_attr: BGP_CLUSTER_LIST { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_EXT_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; +dynamic_attr: BGP_PMSI_TUNNEL + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_PMSI_TUNNEL)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; dynamic_attr: BGP_AIGP { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_LARGE_COMMUNITY