Merge pull request #18656 from leonshaw/evpn-strip-ecom

bgpd: Clean extended communities for VRF routes imported from EVPN
This commit is contained in:
Donatas Abraitis 2025-04-23 05:21:26 +03:00 committed by GitHub
commit 3e647e71d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 206 additions and 1 deletions

View file

@ -863,6 +863,9 @@ unsigned int attrhash_key_make(const void *p)
key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance);
MIX3(attr->bh_type, attr->otc, bgp_attr_get_aigp_metric(attr));
MIX3(attr->mm_seqnum, attr->df_alg, attr->df_pref);
MIX(attr->encap_tunneltype);
key = jhash(&attr->rmac, sizeof(attr->rmac), key);
return key;
}
@ -912,6 +915,7 @@ bool attrhash_cmp(const void *p1, const void *p2)
overlay_index_same(attr1, attr2) &&
!memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) &&
attr1->es_flags == attr2->es_flags &&
attr1->mm_seqnum == attr2->mm_seqnum &&
attr1->mm_sync_seqnum == attr2->mm_sync_seqnum &&
attr1->df_pref == attr2->df_pref &&
attr1->df_alg == attr2->df_alg &&
@ -923,7 +927,9 @@ bool attrhash_cmp(const void *p1, const void *p2)
srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) &&
attr1->srte_color == attr2->srte_color &&
attr1->nh_type == attr2->nh_type &&
attr1->bh_type == attr2->bh_type && attr1->otc == attr2->otc)
attr1->bh_type == attr2->bh_type &&
attr1->otc == attr2->otc &&
!memcmp(&attr1->rmac, &attr2->rmac, sizeof(struct ethaddr)))
return true;
}

View file

@ -1712,6 +1712,46 @@ bool ecommunity_strip_non_transitive(struct ecommunity *ecom)
return true;
}
/*
* Filter source Extended Communities Attribute. May return NULL if the result
* is empty, or the source ecommunity, if not modified.
*/
struct ecommunity *ecommunity_filter(struct ecommunity *ecom,
bool (*filter)(uint8_t *, uint8_t, void *), void *arg)
{
struct ecommunity *new;
uint8_t *new_val, *p, *q;
uint32_t c, new_size;
if (!ecom || !ecom->size)
return NULL;
new_val = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
new_size = 0;
for (c = 0, p = ecom->val, q = new_val; c < ecom->size; c++, p += ecom->unit_size) {
if (filter(p, ecom->unit_size, arg)) {
memcpy(q, p, ecom->unit_size);
q += ecom->unit_size;
new_size++;
}
}
if (!new_size) {
XFREE(MTYPE_ECOMMUNITY_VAL, new_val);
return NULL;
}
if (new_size == ecom->size) {
XFREE(MTYPE_ECOMMUNITY_VAL, new_val);
return ecom;
}
new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
new->size = new_size;
new->unit_size = ecom->unit_size;
new->val = new_val;
return new;
}
/*
* Remove specified extended community value from extended community.
* Returns 1 if value was present (and hence, removed), 0 otherwise.

View file

@ -405,6 +405,9 @@ extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
uint8_t subtype);
extern struct ecommunity *ecommunity_new(void);
extern bool ecommunity_strip_non_transitive(struct ecommunity *ecom);
extern struct ecommunity *ecommunity_filter(struct ecommunity *ecom,
bool (*filter)(uint8_t *val, uint8_t usize, void *arg),
void *arg);
extern bool ecommunity_del_val(struct ecommunity *ecom,
struct ecommunity_val *eval);
struct bgp_pbr_entry_action;

View file

@ -3050,6 +3050,33 @@ bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi,
return pi;
}
/*
* According to draft-ietf-bess-evpn-ipvpn-interworking-13, strip the following
* extended communities for VRF routes imported from EVPN.
*
* a. BGP Encapsulation extended communities.
* b. Route Target extended communities.
* c. All the extended communities of type EVPN.
*/
static bool bgp_evpn_filter_ecommunity(uint8_t *val, uint8_t size, void *arg)
{
switch (val[0]) {
case ECOMMUNITY_ENCODE_AS:
case ECOMMUNITY_ENCODE_IP:
case ECOMMUNITY_ENCODE_AS4:
if (val[1] == ECOMMUNITY_ROUTE_TARGET)
return false;
break;
case ECOMMUNITY_ENCODE_OPAQUE:
if (val[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
return false;
break;
case ECOMMUNITY_ENCODE_EVPN:
return false;
}
return true;
}
/*
* Install route entry into the VRF routing table and invoke route selection.
*/
@ -3071,6 +3098,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
bool is_l3nhg_active = false;
char buf1[INET6_ADDRSTRLEN];
struct bgp_route_evpn *bre;
struct ecommunity *ecom;
memset(pp, 0, sizeof(struct prefix));
ip_prefix_from_evpn_prefix(evp, pp);
@ -3142,6 +3170,9 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
if (is_l3nhg_active)
SET_FLAG(attr.es_flags, ATTR_ES_L3_NHG_ACTIVE);
ecom = ecommunity_filter(bgp_attr_get_ecommunity(&attr), bgp_evpn_filter_ecommunity, NULL);
bgp_attr_set_ecommunity(&attr, ecom);
/* Check if route entry is already present. */
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
if (pi->extra && pi->extra->vrfleak &&

View file

@ -0,0 +1,23 @@
{
"vrfName": "r1-vrf-101",
"routerId": "192.168.102.21",
"localAS": 65000,
"routes": {
"192.168.101.41/32": [
{
"importedFrom": "65000:201",
"vni": "101",
"valid": true,
"extendedCommunity": null,
"nexthops": [
{
"ip": "192.168.100.41",
"hostname": "r2",
"afi": "ipv4",
"used": true
}
]
}
]
}
}

View file

@ -0,0 +1,24 @@
{
"vrfName": "r1-vrf-101",
"routerId": "192.168.102.21",
"localAS": 65000,
"routes": {
"fd00::2/128": [
{
"importedFrom": "65000:201",
"vni": "101",
"valid": true,
"extendedCommunity": null,
"nexthops": [
{
"ip": "::ffff:192.168.100.41",
"hostname": "r2",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View file

@ -0,0 +1,23 @@
{
"vrfName": "r2-vrf-101",
"routerId": "192.168.101.41",
"localAS": 65000,
"routes": {
"192.168.102.21/32": [
{
"importedFrom": "65000:101",
"vni": "101",
"valid": true,
"extendedCommunity": null,
"nexthops": [
{
"ip": "192.168.100.21",
"hostname": "r1",
"afi": "ipv4",
"used": true
}
]
}
]
}
}

View file

@ -0,0 +1,24 @@
{
"vrfName": "r2-vrf-101",
"routerId": "192.168.101.41",
"localAS": 65000,
"routes": {
"fd00::1/128": [
{
"importedFrom": "65000:101",
"vni": "101",
"valid": true,
"extendedCommunity": null,
"nexthops": [
{
"ip": "::ffff:192.168.100.21",
"hostname": "r1",
"afi": "ipv6",
"scope": "global",
"used": true
}
]
}
]
}
}

View file

@ -281,6 +281,37 @@ def test_protocols_dump_info():
logger.info(output)
def test_bgp_vrf_routes():
"""
Check routes are correctly imported to VRF
"""
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ("r1", "r2"):
router = tgen.gears[rname]
for af in ("ipv4", "ipv6"):
json_file = "{}/{}/bgp_vrf_{}_routes_detail.json".format(
CWD, router.name, af
)
if not os.path.isfile(json_file):
assert 0, "bgp vrf routes file not found"
expected = json.loads(open(json_file).read())
test_func = partial(
topotest.router_json_cmp,
router,
"show bgp vrf {}-vrf-101 {} unicast detail json".format(
router.name, af
),
expected,
)
_, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(router.name)
assert result is None, assertmsg
def test_router_check_ip():
"""
Check routes are correctly installed