0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

BGP: PMSI tunnel attribute support

PMSI tunnel attribute is required for EVPN IMET routes.
This commit is contained in:
Ondrej Zajicek 2023-11-10 02:32:15 +01:00
parent f0bbb5b049
commit dc17f1982f
4 changed files with 137 additions and 3 deletions

View 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 * 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 static void
bgp_export_aigp(struct bgp_export_state *s, eattr *a) 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, .decode = bgp_decode_as4_aggregator,
.format = bgp_format_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] = { [BA_AIGP] = {
.name = "aigp", .name = "aigp",
.type = EAF_TYPE_OPAQUE, .type = EAF_TYPE_OPAQUE,

View File

@ -657,10 +657,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_preexport(struct channel *, struct rte *);
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen); int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *buf); 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); int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
byte * bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, const net_addr *n, const struct rte *new, const struct rte_src *src); byte * bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, 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_METRIC 1
#define BGP_AIGP_MAX U64(0xffffffffffffffff) #define BGP_AIGP_MAX U64(0xffffffffffffffff)
@ -727,6 +754,7 @@ byte *bgp_create_end_mark_(struct bgp_channel *c, byte *buf);
#define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */ #define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */
#define BA_AS4_PATH 0x11 /* RFC 6793 */ #define BA_AS4_PATH 0x11 /* RFC 6793 */
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */ #define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
#define BA_PMSI_TUNNEL 0x16 /* RFC 6514 */
#define BA_AIGP 0x1a /* RFC 7311 */ #define BA_AIGP 0x1a /* RFC 7311 */
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */ #define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */ #define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */

View File

@ -32,7 +32,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS, LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE, DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER, FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND) RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, BGP_PMSI_TUNNEL)
%type <i> bgp_nh %type <i> bgp_nh
%type <i32> bgp_afi %type <i32> bgp_afi
@ -371,6 +371,8 @@ dynamic_attr: BGP_CLUSTER_LIST
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ; { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ;
dynamic_attr: BGP_EXT_COMMUNITY dynamic_attr: BGP_EXT_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ; { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ;
dynamic_attr: BGP_PMSI_TUNNEL
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_PMSI_TUNNEL)); } ;
dynamic_attr: BGP_AIGP dynamic_attr: BGP_AIGP
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ; { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
dynamic_attr: BGP_LARGE_COMMUNITY dynamic_attr: BGP_LARGE_COMMUNITY

View File

@ -56,6 +56,7 @@
#define EA_BGP_NEXT_HOP EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP) #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_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) #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) static inline const struct adata * ea_get_adata(ea_list *e, uint id)
@ -194,6 +195,9 @@ evpn_announce_imet(struct evpn_proto *p, int new)
struct adata *ad = evpn_export_targets(p, &null_adata); struct adata *ad = evpn_export_targets(p, &null_adata);
ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_EXT_COMMUNITY, 0, EAF_TYPE_EC_SET, ad); ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_EXT_COMMUNITY, 0, 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, 0, EAF_TYPE_OPAQUE, ad);
rte *e = rte_get_temp(a, p->p.main_source); rte *e = rte_get_temp(a, p->p.main_source);
rte_update2(c, n, e, p->p.main_source); rte_update2(c, n, e, p->p.main_source);
} }
@ -259,7 +263,13 @@ evpn_receive_imet(struct evpn_proto *p, const net_addr_evpn_imet *n0, rte *new)
if (new && rte_resolvable(new)) 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); rta *a = alloca(RTA_MAX_SIZE);
*a = (rta) { *a = (rta) {
@ -267,15 +277,19 @@ evpn_receive_imet(struct evpn_proto *p, const net_addr_evpn_imet *n0, rte *new)
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST, .dest = RTD_UNICAST,
.pref = c->preference, .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, .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 *e = rte_get_temp(a, s);
rte_update2(c, n, e, s); rte_update2(c, n, e, s);
} }
else else
{ {
withdraw:
rte_update2(c, n, NULL, s); rte_update2(c, n, NULL, s);
} }
} }