PMSI tunnel attribute is required for EVPN IMET routes.
}
+/*
+ * 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
*/
}
+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)
{
.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,
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)
#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 */
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)
{ $$ = 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
#define EA_BGP_NEXT_HOP EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)
#define EA_BGP_EXT_COMMUNITY EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)
+#define EA_BGP_PMSI_TUNNEL EA_CODE(PROTOCOL_BGP, BA_PMSI_TUNNEL)
#define EA_BGP_MPLS_LABEL_STACK EA_CODE(PROTOCOL_BGP, BA_MPLS_LABEL_STACK)
static inline const struct adata * ea_get_adata(ea_list *e, uint id)
struct adata *ad = evpn_export_targets(p, &null_adata);
ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_EXT_COMMUNITY, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, ad);
+ ad = bgp_pmsi_new_ingress_replication(tmp_linpool, p->router_addr, p->vni);
+ ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_PMSI_TUNNEL, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, ad);
+
rte *e = rte_get_temp(a, p->p.main_source);
rte_update2(c, n, e, p->p.main_source);
}
if (new && rte_resolvable(new))
{
- eattr *nh = ea_find(new->attrs->eattrs, EA_BGP_NEXT_HOP);
+ eattr *pt = ea_find(new->attrs->eattrs, EA_BGP_PMSI_TUNNEL);
+ if (!pt)
+ BAD("Missing PMSI_TUNNEL attribute in %N", n0);
+
+ uint pmsi_type = bgp_pmsi_get_type(pt->u.ptr);
+ if (pmsi_type != BGP_PMSI_TYPE_INGRESS_REPLICATION)
+ BAD("Unsupported PMSI_TUNNEL type %u in %N", pmsi_type, n0);
rta *a = alloca(RTA_MAX_SIZE);
*a = (rta) {
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.pref = c->preference,
- .nh.gw = nh ? *((ip_addr *) nh->u.ptr->data) : IPA_NONE,
+ .nh.gw = bgp_pmsi_ir_get_endpoint(pt->u.ptr),
.nh.iface = p->tunnel_dev,
};
+ a->nh.labels = 1;
+ a->nh.label[0] = bgp_pmsi_get_label(pt->u.ptr);
+
rte *e = rte_get_temp(a, s);
rte_update2(c, n, e, s);
}
else
{
+ withdraw:
rte_update2(c, n, NULL, s);
}
}