Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bgpd:aggr summary-only remove suppressed from evpn #15222

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 77 additions & 6 deletions bgpd/bgp_evpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,18 +315,13 @@ static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn)
* This would be following category:
* Non-imported route,
* Non-EVPN imported route,
* Non Aggregate suppressed route.
*/
bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
bool is_route_injectable_into_evpn_non_supp(struct bgp_path_info *pi)
{
struct bgp_path_info *parent_pi;
struct bgp_table *table;
struct bgp_dest *dest;

/* do not import aggr suppressed routes */
if (bgp_path_suppressed(pi))
return false;

if (pi->sub_type != BGP_ROUTE_IMPORTED || !pi->extra ||
!pi->extra->vrfleak || !pi->extra->vrfleak->parent)
return true;
Expand All @@ -344,6 +339,21 @@ bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
return true;
}

/* Flag if the route is injectable into EVPN.
* This would be following category:
* Non-imported route,
* Non-EVPN imported route,
* Non Aggregate suppressed route.
*/
bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
{
/* do not import aggr suppressed routes */
if (bgp_path_suppressed(pi))
return false;

return is_route_injectable_into_evpn_non_supp(pi);
}

/*
* Compare Route Targets.
*/
Expand Down Expand Up @@ -7711,3 +7721,64 @@ bool bgp_evpn_mpath_has_dvni(const struct bgp *bgp_vrf,

return false;
}

/* Upon aggregate set trigger unimport suppressed routes
* from EVPN
*/
void bgp_aggr_supp_withdraw_from_evpn(struct bgp *bgp, afi_t afi, safi_t safi)
{
struct bgp_dest *agg_dest, *dest, *top;
const struct prefix *aggr_p;
struct bgp_aggregate *bgp_aggregate;
struct bgp_table *table;
struct bgp_path_info *pi;

if (!bgp_get_evpn() && !advertise_type5_routes(bgp, afi))
return;

/* Aggregate-address table walk. */
table = bgp->rib[afi][safi];
for (agg_dest = bgp_table_top(bgp->aggregate[afi][safi]); agg_dest;
agg_dest = bgp_route_next(agg_dest)) {
bgp_aggregate = bgp_dest_get_bgp_aggregate_info(agg_dest);

if (bgp_aggregate == NULL)
continue;

aggr_p = bgp_dest_get_prefix(agg_dest);

/* Look all nodes below the aggregate prefix in
* global AFI/SAFI table (IPv4/IPv6).
* Trigger withdrawal (this will be Type-5 routes only)
* from EVPN Global table.
*/
top = bgp_node_get(table, aggr_p);
for (dest = bgp_node_get(table, aggr_p); dest;
dest = bgp_route_next_until(dest, top)) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);

if (dest_p->prefixlen <= aggr_p->prefixlen)
continue;

for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
if (pi->sub_type == BGP_ROUTE_AGGREGATE)
continue;

/* Only Suppressed route remove from EVPN */
if (!bgp_path_suppressed(pi))
continue;

if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s aggregated %pFX remove suppressed route %pFX",
__func__, aggr_p, dest_p);

if (!is_route_injectable_into_evpn_non_supp(pi))
ton31337 marked this conversation as resolved.
Show resolved Hide resolved
continue;

bgp_evpn_withdraw_type5_route(bgp, dest_p, afi,
safi);
}
}
}
}
3 changes: 3 additions & 0 deletions bgpd/bgp_evpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,8 @@ extern vni_t bgp_evpn_path_info_get_l3vni(const struct bgp_path_info *pi);
extern bool bgp_evpn_mpath_has_dvni(const struct bgp *bgp_vrf,
struct bgp_path_info *mpinfo);
extern bool is_route_injectable_into_evpn(struct bgp_path_info *pi);
extern bool is_route_injectable_into_evpn_non_supp(struct bgp_path_info *pi);
extern void bgp_aggr_supp_withdraw_from_evpn(struct bgp *bgp, afi_t afi,
safi_t safi);

#endif /* _QUAGGA_BGP_EVPN_H */
3 changes: 3 additions & 0 deletions bgpd/bgp_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -7799,6 +7799,9 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
lcommunity = lcommunity_dup(aggregate->lcommunity);
}

/* Unimport suppressed routes from EVPN */
bgp_aggr_supp_withdraw_from_evpn(bgp, afi, safi);

bgp_aggregate_install(bgp, afi, safi, p, origin, aspath, community,
ecommunity, lcommunity, atomic_aggregate,
aggregate);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[16]:[10.27.0.0]":{"prefix":"[5]:[0]:[16]:[10.27.0.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":16,"ip":"10.27.0.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":2,"numPaths":2}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"27.3.0.85:3":{"rd":"27.3.0.85:3","[5]:[0]:[24]:[10.27.7.0]":{"prefix":"[5]:[0]:[24]:[10.27.7.0]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":24,"ip":"10.27.7.0","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[30]:[10.27.7.8]":{"prefix":"[5]:[0]:[30]:[10.27.7.8]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":30,"ip":"10.27.7.8","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]},"[5]:[0]:[32]:[10.27.7.22]":{"prefix":"[5]:[0]:[32]:[10.27.7.22]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"external","routeType":5,"ethTag":0,"ipLen":32,"ip":"10.27.7.22","metric":0,"weight":32768,"peerId":"(unspec)","path":"","origin":"incomplete","extendedCommunity":{"string":"ET:8 RT:65000:4000 Rmac:44:00:00:ff:ff:01"},"nexthops":[{"ip":"10.10.10.10","hostname":"PE1","afi":"ipv4","used":true}]}]]}},"30.0.0.3:2":{"rd":"30.0.0.3:2","[5]:[0]:[32]:[4.4.4.1]":{"prefix":"[5]:[0]:[32]:[4.4.4.1]","prefixLen":352,"paths":[[{"valid":true,"bestpath":true,"selectionReason":"First path received","pathFrom":"internal","routeType":5,"ethTag":0,"ipLen":32,"ip":"4.4.4.1","metric":0,"locPrf":100,"weight":0,"peerId":"10.30.30.30","path":"","origin":"incomplete","extendedCommunity":{"string":"RT:65000:300 ET:8 Rmac:44:20:00:ff:ff:01"},"nexthops":[{"ip":"10.30.30.30","hostname":"PE2","afi":"ipv4","used":true}]}]]}},"numPrefix":4,"numPaths":4}
13 changes: 13 additions & 0 deletions tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,22 @@ router bgp 65000
advertise-svi-ip
!
router bgp 65000 vrf vrf-red
address-family ipv4 unicast
redistribute static
exit-address-family
!
address-family l2vpn evpn
route-target import *:300
route-target import auto
exit-address-family
!
router bgp 65000 vrf vrf-purple
address-family ipv4 unicast
redistribute static
exit-address-family
!
address-family l2vpn evpn
advertise ipv4 unicast
exit-address-family

!
6 changes: 6 additions & 0 deletions tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ vrf vrf-red
vni 100
exit-vrf
!
vrf vrf-purple
vni 4000
ip route 10.27.7.0/24 blackhole
ip route 10.27.7.22/32 blackhole
ip route 10.27.7.10/30 blackhole
exit-vrf
!
interface lo
ip address 10.10.10.10/32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
pe.run("ip link add name bridge type bridge stp_state 0")
pe.run("ip link set dev bridge type bridge vlan_filtering 1")
pe.run("bridge vlan add vid 1 dev bridge self")
if pe_name == "PE1":
pe.run("ip link set dev bridge address 44:00:00:ff:ff:01")
if pe_name == "PE2":
pe.run("ip link set dev bridge address 44:20:00:ff:ff:01")
pe.run("ip link set dev bridge up")

# setup svi
Expand All @@ -113,14 +117,26 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101")
pe.run("ip link set up dev vxlan0")

# VRF creation
pe.run("ip link add vrf-purple type vrf table 1003")
pe.run("ip link set dev vrf-purple up")
if pe_name == "PE1":
pe.run("ip link add vrf-blue type vrf table 1002")
pe.run("ip link set dev vrf-blue up")
pe.run("ip addr add 27.2.0.85/32 dev vrf-blue")
pe.run("ip addr add 27.3.0.85/32 dev vrf-purple")
if pe_name == "PE2":
pe.run("ip link add vrf-blue type vrf table 2400")
pe.run("ip link set dev vrf-blue up")

# setup PE interface
pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf))
pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf))
pe.run("bridge vlan del vid 1 untagged pvid dev {0}-{1}".format(pe_name, intf))
pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf))
pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf))

# l3vni 100
# L3VNI 100
pe.run("ip link add vrf-red type vrf table 1400")
pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q")
pe.run("ip link set dev vlan100 master vrf-blue")
Expand All @@ -129,9 +145,16 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
pe.run("bridge vlan add dev vxlan0 vid 100")
pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100")

# L3VNI 4000
pe.run("ip link add link bridge name vlan400 type vlan id 400 protocol 802.1q")
pe.run("ip link set dev vlan400 master vrf-purple")
pe.run("ip link set dev vlan400 up")
pe.run("bridge vlan add vid 400 dev bridge self")
pe.run("bridge vlan add dev vxlan0 vid 400")
pe.run("bridge vlan add dev vxlan0 vid 400 tunnel_info id 4000")

# add a vrf for testing DVNI
if pe_name == "PE2":
pe.run("ip link add vrf-blue type vrf table 2400")
pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q")
pe.run("ip link set dev vlan300 master vrf-blue")
pe.run("ip link set dev vlan300 up")
Expand Down Expand Up @@ -199,6 +222,14 @@ def show_vni_json_elide_ifindex(pe, vni, expected):
return topotest.json_cmp(output_json, expected)


def show_bgp_l2vpn_evpn_route_type_prefix_json(pe, expected):
output_json = pe.vtysh_cmd(
"show bgp l2vpn evpn route type prefix json", isjson=True
)

return topotest.json_cmp(output_json, expected)


def check_vni_macs_present(tgen, router, vni, maclist):
result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True)
for rname, ifname in maclist:
Expand Down Expand Up @@ -331,6 +362,7 @@ def mac_test_local_remote(local, remote):
remote_output_json = json.loads(remote_output)
local_output_vni_json = json.loads(local_output_vni)

# breakpoint()
for vni in local_output_json:
if vni not in remote_output_json:
continue
Expand Down Expand Up @@ -542,6 +574,42 @@ def test_dvni():
# tgen.mininet_cli()


def test_pe_advertise_aggr_evpn_route():
"BGP advertise aggregated Type-5 prefix route"

tgen = get_topogen()
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
pytest.skip(tgen.errors)

logger.info("Checking BGP EVPN route contains non-aggregate prefixes")
pe1 = tgen.gears["PE1"]
json_file = "{}/{}/bgp.evpn.route.prefix.before.json".format(CWD, pe1.name)
expected = json.loads(open(json_file).read())

test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected)
_, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
assert result is None, assertmsg

logger.info("Configure BGP aggregate-address summary-only under ipv4-unicast")
pe1.cmd(
'vtysh -c "config t" -c "router bgp 65000 vrf vrf-purple" '
+ ' -c "address-family ipv4 unicast" '
+ ' -c "aggregate-address 10.27.0.0/16 summary-only" '
)

logger.info("Checking BGP EVPN route contains aggregated prefix")
pe1 = tgen.gears["PE1"]
json_file = "{}/{}/bgp.evpn.route.prefix.after.json".format(CWD, pe1.name)
expected = json.loads(open(json_file).read())

test_func = partial(show_bgp_l2vpn_evpn_route_type_prefix_json, pe1, expected)
_, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
assert result is None, assertmsg


def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
Expand Down
Loading