]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: PMSI tunnel attribute support
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 12 May 2026 16:31:11 +0000 (18:31 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 13 May 2026 09:53:15 +0000 (11:53 +0200)
Add support for BGP PMSI tunnel attribute (RFC 6514), specifically
ingress replication type. This is required for EVPN IMET routes.

proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y

index 184c07775904c7e293f37a224c0f2ea6888524e2..b8c1a26c2cf6e89c8a507601d60a6c533ebe6327 100644 (file)
@@ -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,
index d3db1c2706f4b74a4f981f815b2de4781f3d6f4e..bf801672e84f5f82d57af99fe51aaa3adbd8585a 100644 (file)
@@ -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
index 9834030a95360eeda50977623d62fab62351f657..3be614e795b2b20340c70b8c941e81a2cbceb453 100644 (file)
@@ -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 */
index cc1bfcef6922e2f1220dd229d688dc31654312ad..0ddaf10a049ce3f30a3acd0efff4e0533eb75e76 100644 (file)
@@ -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