mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
Merge pull request #18656 from leonshaw/evpn-strip-ecom
bgpd: Clean extended communities for VRF routes imported from EVPN
This commit is contained in:
commit
3e647e71d5
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue