Skip to content
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
34 changes: 34 additions & 0 deletions tests/topotests/bgp_evpn_mh/test_evpn_mh.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,40 @@ def test_evpn_uplink_tracking():
assert result is None, assertmsg


def test_evpn_access_vlan_vni_count():
"""
Test EVPN access VLAN VNI count feature

This test verifies the fix for the issue where VLAN 1 acts as a placeholder
when new VNIs are added. The VNI count should track multiple VNIs associated
with the same VLAN and prevent incorrect removal of VLAN-VNI mappings.
"""

tgen = get_topogen()

if tgen.routers_have_failure():
pytest.skip(tgen.errors)

dut_name = "torm11"
dut = tgen.gears[dut_name]

# Test JSON output includes vniCount field
output = dut.vtysh_cmd("show evpn access-vlan json", isjson=True)
if output:
for vlan_entry in output:
if "vniCount" in vlan_entry:
assertmsg = "vniCount should be >= 1 for active VLAN, got {}".format(
vlan_entry["vniCount"]
)
assert vlan_entry["vniCount"] >= 1, assertmsg

# Test text output includes VNI-count column
output = dut.vtysh_cmd("show evpn access-vlan", isjson=False)
if output and "VLAN" in output:
assertmsg = "VNI-count column missing in 'show evpn access-vlan' output"
assert "VNI-count" in output, assertmsg


if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
4 changes: 4 additions & 0 deletions tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
!
log file zebra.log
!
vrf vrf-blue
vni 999
exit-vrf
!
interface lo
ip address 10.10.10.10/32
interface PE1-eth1
Expand Down
4 changes: 4 additions & 0 deletions tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
!
vrf vrf-blue
vni 999
exit-vrf
!
interface lo
ip address 10.30.30.30/32
interface PE2-eth0
Expand Down
140 changes: 140 additions & 0 deletions tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,45 @@ def setup_module(mod):
pe2.run("ip link set dev PE2-eth1 master br101")
p1.run("sysctl -w net.ipv4.ip_forward=1")

# Setup L3 VNI 999 with VLAN interface for testing fix 3ad2a782e6
# PE1
pe1.run("ip link add vrf-blue type vrf table 1000")
pe1.run("ip link set vrf-blue up")
pe1.run("ip link add name br999 type bridge stp_state 0 vlan_filtering 1")
pe1.run("ip link set dev br999 up")
pe1.run(
"ip link add vxlan999 type vxlan id 999 dstport 4789 local 10.10.10.10 nolearning"
)
pe1.run("ip link set dev vxlan999 master br999")
pe1.run("ip link set up dev vxlan999")
# Set VLAN 999 as access VLAN on the bridge
pe1.run("bridge vlan add vid 999 dev br999 self")
pe1.run("bridge vlan del vid 1 dev vxlan999")
pe1.run("bridge vlan add vid 999 dev vxlan999 pvid untagged")
pe1.run("ip link add link br999 name vlan999 type vlan id 999")
pe1.run("ip link set dev vlan999 master vrf-blue")
pe1.run("ip addr add 10.99.99.1/24 dev vlan999")
pe1.run("ip link set dev vlan999 up")

# PE2
pe2.run("ip link add vrf-blue type vrf table 1000")
pe2.run("ip link set vrf-blue up")
pe2.run("ip link add name br999 type bridge stp_state 0 vlan_filtering 1")
pe2.run("ip link set dev br999 up")
pe2.run(
"ip link add vxlan999 type vxlan id 999 dstport 4789 local 10.30.30.30 nolearning"
)
pe2.run("ip link set dev vxlan999 master br999")
pe2.run("ip link set up dev vxlan999")
# Set VLAN 999 as access VLAN on the bridge
pe2.run("bridge vlan add vid 999 dev br999 self")
pe2.run("bridge vlan del vid 1 dev vxlan999")
pe2.run("bridge vlan add vid 999 dev vxlan999 pvid untagged")
pe2.run("ip link add link br999 name vlan999 type vlan id 999")
pe2.run("ip link set dev vlan999 master vrf-blue")
pe2.run("ip addr add 10.99.99.3/24 dev vlan999")
pe2.run("ip link set dev vlan999 up")

# This is a sample of configuration loading.
router_list = tgen.routers()

Expand Down Expand Up @@ -457,6 +496,107 @@ def test_bgp_evpn_route_vni():
logger.info("PE1: Test passed")


def test_evpn_l2vni_vlan_bridge_json():
"""
Test L2 VNI JSON output includes vlan and bridge fields

This verifies the fix where L2 VNI JSON output was missing
"vlan" and "bridge" fields.
"""
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)

pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]

# Check PE1 L2 VNI 101
output = pe1.vtysh_cmd("show evpn vni 101 json", isjson=True)
if output:
assertmsg = "L2 VNI 101 (PE1): 'vlan' field should be present in JSON"
assert "vlan" in output, assertmsg

assertmsg = "L2 VNI 101 (PE1): 'bridge' field should be present in JSON"
assert "bridge" in output, assertmsg

# Check PE2 L2 VNI 101
output = pe2.vtysh_cmd("show evpn vni 101 json", isjson=True)
if output:
assertmsg = "L2 VNI 101 (PE2): 'vlan' field should be present in JSON"
assert "vlan" in output, assertmsg

assertmsg = "L2 VNI 101 (PE2): 'bridge' field should be present in JSON"
assert "bridge" in output, assertmsg


def test_evpn_vni_summary_output():
"""
Test EVPN VNI summary output includes VLAN and BRIDGE columns

This verifies that 'show evpn vni' summary output displays
VLAN and BRIDGE information in both text and JSON formats.
"""
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)

pe1 = tgen.gears["PE1"]

# Test text output has VLAN and BRIDGE columns
output = pe1.vtysh_cmd("show evpn vni", isjson=False)
if output and "VNI" in output:
assertmsg = "'show evpn vni' should have VLAN column in header"
assert "VLAN" in output, assertmsg

assertmsg = "'show evpn vni' should have BRIDGE column in header"
assert "BRIDGE" in output, assertmsg

# Test JSON output has vlan and bridge fields
output = pe1.vtysh_cmd("show evpn vni json", isjson=True)
if output:
for vni_key, vni_data in output.items():
if isinstance(vni_data, dict) and "type" in vni_data:
assertmsg = "VNI {} JSON should have 'vlan' field".format(vni_key)
assert "vlan" in vni_data, assertmsg

assertmsg = "VNI {} JSON should have 'bridge' field".format(vni_key)
assert "bridge" in vni_data, assertmsg
break


def test_evpn_l3vni_vlan_bridge():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)

pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]

# Check PE1 L3 VNI 999 - TEXT output
output = pe1.vtysh_cmd("show evpn vni 999", isjson=False)
if output:
assertmsg = "L3 VNI 999 (PE1): text output should contain 'Vlan: 999'"
assert "Vlan: 999" in output, assertmsg

assertmsg = "L3 VNI 999 (PE1): text output should contain 'Bridge: br999'"
assert "Bridge: br999" in output, assertmsg

assertmsg = "L3 VNI 999 (PE1): text output should contain 'Type: L3'"
assert "Type: L3" in output, assertmsg

# Check PE2 L3 VNI 999 - TEXT output
output = pe2.vtysh_cmd("show evpn vni 999", isjson=False)
if output:
assertmsg = "L3 VNI 999 (PE2): text output should contain 'Vlan: 999'"
assert "Vlan: 999" in output, assertmsg

assertmsg = "L3 VNI 999 (PE2): text output should contain 'Bridge: br999'"
assert "Bridge: br999" in output, assertmsg

assertmsg = "L3 VNI 999 (PE2): text output should contain 'Type: L3'"
assert "Type: L3" in output, assertmsg


def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
Expand Down
15 changes: 10 additions & 5 deletions zebra/zebra_evpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ void zebra_evpn_print(struct zebra_evpn *zevpn, void **ctxt)
} else {
json_object_int_add(json, "vni", zevpn->vni);
json_object_string_add(json, "type", "L2");
json_object_int_add(json, "vlan", zevpn->vid);
json_object_string_add(json, "bridge",
zevpn->bridge_if ? zevpn->bridge_if->name : "");
json_object_string_add(json, "tenantVrf", vrf_id_to_name(zevpn->vrf_id));
}

Expand Down Expand Up @@ -220,11 +223,10 @@ void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[])
num_macs = num_valid_macs(zevpn);
num_neigh = hashcount(zevpn->neigh_table);
if (json == NULL)
vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n",
zevpn->vni, "L2",
zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown",
num_macs, num_neigh, num_vteps,
vrf_id_to_name(zevpn->vrf_id));
vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-15s %-10u %-37s\n", zevpn->vni,
"L2", zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown", num_macs,
num_neigh, num_vteps, vrf_id_to_name(zevpn->vrf_id), zevpn->vid,
zevpn->bridge_if ? zevpn->bridge_if->name : "-");
else {
char vni_str[VNI_STR_LEN];
snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
Expand All @@ -238,6 +240,9 @@ void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[])
json_object_int_add(json_evpn, "numRemoteVteps", num_vteps);
json_object_string_add(json_evpn, "tenantVrf",
vrf_id_to_name(zevpn->vrf_id));
json_object_int_add(json_evpn, "vlan", zevpn->vid);
json_object_string_add(json_evpn, "bridge",
zevpn->bridge_if ? zevpn->bridge_if->name : "-");
if (num_vteps) {
json_vtep_list = json_object_new_array();
for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
Expand Down
15 changes: 8 additions & 7 deletions zebra/zebra_evpn_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,11 @@ int zebra_evpn_mac_remote_macip_add(struct zebra_evpn *zevpn, struct zebra_vrf *
zebra_evpn_mac_clear_sync_info(mac);
zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr,
mac->flags, false);
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
zlog_warn("Received remote mac add for MAC %pEA VNI %u VTEP %pIA, "
"but it is already learnt as local sticky MAC on Intf %s",
macaddr, zevpn->vni, vtep_ip, mac->ifp->name);
}
}

/* Set "auto" and "remote" forwarding info. */
Expand Down Expand Up @@ -2176,14 +2181,10 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf,
old_local_inactive == local_inactive &&
dp_static == old_static && !es_change) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(" Add/Update %sMAC %pEA intf %s(%u) VID %u -> VNI %u%s, "
"entry exists and has not changed ",
sticky ? "sticky " : "",
macaddr, ifp->name,
zlog_debug(" Add/Update %sMAC %pEA intf %s(%u) VID %u -> VNI %u%s, entry exists and has not changed ",
sticky ? "sticky " : "", macaddr, ifp->name,
ifp->ifindex, vid, zevpn->vni,
local_inactive
? " local_inactive"
: "");
local_inactive ? " local_inactive" : "");
return 0;
}
if (mac_sticky != sticky) {
Expand Down
35 changes: 30 additions & 5 deletions zebra/zebra_evpn_mh.c
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id,
struct zebra_if *vxlan_zif)
{
vni_t old_vni;
uint8_t tmp_cnt;
struct zebra_evpn_access_bd *acc_bd;
struct zebra_evpn *old_zevpn;
struct interface *br_if;
Expand All @@ -778,14 +779,27 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id,
acc_bd = zebra_evpn_acc_vl_find(vid, br_if);
if (!acc_bd)
acc_bd = zebra_evpn_acc_bd_alloc_on_ref(vid, br_if);

/* Check if the current vni is active, if active then we have multiple
* VNI's getting mapped to the same VLAN which is momentary hence
* increment the vni count and return else continue processing as the
* VNI has changed for this VLAN and needs to be updated
*/
else if (acc_bd->vxlan_zif && zebra_vxlan_if_vni_find(acc_bd->vxlan_zif, acc_bd->vni)) {
tmp_cnt = acc_bd->vni_refcnt;
acc_bd->vni_refcnt++;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("access_vlan %d increment VNI count: %d->%d", vid, tmp_cnt,
acc_bd->vni_refcnt);
return;
}
old_vni = acc_bd->vni;

if (vni_id == old_vni)
return;

acc_bd->vni = vni_id;
acc_bd->vxlan_zif = vxlan_zif;
acc_bd->vni_refcnt = 1;

old_zevpn = acc_bd->zevpn;
acc_bd->zevpn = zebra_evpn_lookup(vni_id);
Expand All @@ -809,6 +823,7 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id,
{
struct interface *br_if;
struct zebra_evpn_access_bd *acc_bd;
uint8_t tmp_cnt;

if (!vid)
return;
Expand All @@ -823,6 +838,14 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id,
acc_bd = zebra_evpn_acc_vl_find(vid, br_if);
if (!acc_bd)
return;
if (acc_bd->vni_refcnt > 1) {
tmp_cnt = acc_bd->vni_refcnt;
acc_bd->vni_refcnt--;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
zlog_debug("access_vlan %d decrement VNI count: %d->%d", vid, tmp_cnt,
acc_bd->vni_refcnt);
return;
}

/* clear vxlan_if only if it matches */
if (acc_bd->vni != vni_id)
Expand Down Expand Up @@ -1001,6 +1024,7 @@ static void zebra_evpn_acc_vl_json_fill(struct zebra_evpn_access_bd *acc_bd,
if (acc_bd->mbr_zifs)
json_object_int_add(json, "memberIfCount",
listcount(acc_bd->mbr_zifs));
json_object_int_add(json, "vniCount", acc_bd->vni_refcnt);

if (detail) {
json_object *json_mbrs;
Expand Down Expand Up @@ -1044,6 +1068,7 @@ static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty,
}
vty_out(vty, " Member Count: %d\n",
listcount(acc_bd->mbr_zifs));
vty_out(vty, " VNI-count: %d\n", acc_bd->vni_refcnt);
vty_out(vty, " Members: \n");
for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif))
vty_out(vty, " %s\n", zif->ifp->name);
Expand All @@ -1061,10 +1086,10 @@ static void zebra_evpn_acc_vl_show_entry(struct vty *vty,
} else {
snprintf(vlan_str, sizeof(vlan_str), "%s.%u", acc_bd->bridge_zif->ifp->name,
acc_bd->vid);
vty_out(vty, "%-21s %-15s %-8d %-15s %u\n", vlan_str,
vty_out(vty, "%-21s %-15s %-8d %-15s %-15d %u\n", vlan_str,
acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-",
acc_bd->zevpn ? acc_bd->zevpn->vni : 0,
acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-",
acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-", acc_bd->vni_refcnt,
listcount(acc_bd->mbr_zifs));
}
}
Expand Down Expand Up @@ -1099,8 +1124,8 @@ void zebra_evpn_acc_vl_show(struct vty *vty, bool uj)
wctx.detail = false;

if (!uj)
vty_out(vty, "%-21s %-15s %-8s %-15s %s\n", "VLAN", "SVI", "L2-VNI", "VXLAN-IF",
"# Members");
vty_out(vty, "%-21s %-15s %-8s %-15s %-15s %s\n", "VLAN", "SVI", "L2-VNI",
"VXLAN-IF", "VNI-count", "# Members");

hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash,
&wctx);
Expand Down
2 changes: 2 additions & 0 deletions zebra/zebra_evpn_mh.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ struct zebra_evpn_access_bd {
struct zebra_evpn *zevpn;
/* SVI associated with the VLAN */
struct zebra_if *vlan_zif;
/* VNI count */
uint8_t vni_refcnt;
};

/* multihoming information stored in zrouter */
Expand Down
Loading
Loading