Skip to content

Commit

Permalink
Merge pull request #16341 from crosser/preserve-needed-rmacs
Browse files Browse the repository at this point in the history
zebra: evpn: not coerce VTEP IP to IPv4 in nh_list
  • Loading branch information
ton31337 authored Jul 22, 2024
2 parents 04b818d + f883c71 commit 8eb78b2
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 45 deletions.
4 changes: 4 additions & 0 deletions tests/topotests/bgp_evpn_rt5/r1/bgpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ router bgp 65000 vrf r1-vrf-101
address-family ipv4 unicast
network 192.168.102.21/32
exit-address-family
address-family ipv6 unicast
network fd00::1/128
exit-address-family
address-family l2vpn evpn
advertise ipv4 unicast
advertise ipv6 unicast
exit-address-family
!
1 change: 1 addition & 0 deletions tests/topotests/bgp_evpn_rt5/r1/zebra.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface r1-eth0
!
interface loop101 vrf r1-vrf-101
ip address 192.168.102.21/32
ipv6 address fd00::1/128
!


4 changes: 4 additions & 0 deletions tests/topotests/bgp_evpn_rt5/r2/bgpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ router bgp 65000 vrf r2-vrf-101
address-family ipv4 unicast
network 192.168.101.41/32
exit-address-family
address-family ipv6 unicast
network fd00::2/128
exit-address-family
address-family l2vpn evpn
advertise ipv4 unicast
advertise ipv6 unicast
exit-address-family
!
1 change: 1 addition & 0 deletions tests/topotests/bgp_evpn_rt5/r2/zebra.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ vrf r2-vrf-101
!
interface loop101 vrf r2-vrf-101
ip address 192.168.101.41/32
ipv6 address fd00::2/128
!
interface r2-eth0
ip address 192.168.100.41/24
Expand Down
125 changes: 124 additions & 1 deletion tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.bgp import verify_bgp_rib
from lib.common_config import apply_raw_config
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger

Expand Down Expand Up @@ -179,21 +181,68 @@ def test_protocols_convergence():
output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101 ipv4", isjson=False)
logger.info("==== result from show bgp vrf r1-vrf-101 ipv4")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101 ipv6", isjson=False)
logger.info("==== result from show bgp vrf r1-vrf-101 ipv6")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101", isjson=False)
logger.info("==== result from show bgp vrf r1-vrf-101 ")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show ip route vrf r1-vrf-101", isjson=False)
logger.info("==== result from show ip route vrf r1-vrf-101")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show ipv6 route vrf r1-vrf-101", isjson=False)
logger.info("==== result from show ipv6 route vrf r1-vrf-101")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show evpn vni detail", isjson=False)
logger.info("==== result from show evpn vni detail")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show evpn next-hops vni all", isjson=False)
logger.info("==== result from show evpn next-hops vni all")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show evpn rmac vni all", isjson=False)
logger.info("==== result from show evpn next-hops vni all")
logger.info("==== result from show evpn rmac vni all")
logger.info(output)

expected = {
"fd00::2/128": [
{
"prefix": "fd00::2/128",
"vrfName": "r1-vrf-101",
"nexthops": [
{
"ip": "::ffff:c0a8:6429",
}
],
}
]
}
result = topotest.router_json_cmp(
tgen.gears["r1"], "show ipv6 route vrf r1-vrf-101 fd00::2/128 json", expected
)
assert result is None, "ipv6 route check failed"

expected = {
"101": {
"numNextHops": 2,
"192.168.100.41": {
"nexthopIp": "192.168.100.41",
},
"::ffff:c0a8:6429": {
"nexthopIp": "::ffff:c0a8:6429",
},
}
}
result = topotest.router_json_cmp(
tgen.gears["r1"], "show evpn next-hops vni all json", expected
)
assert result is None, "evpn next-hops check failed"

expected = {"101": {"numRmacs": 1}}
result = topotest.router_json_cmp(
tgen.gears["r1"], "show evpn rmac vni all json", expected
)
assert result is None, "evpn rmac number check failed"

# Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn)
pingrouter = tgen.gears["r1"]
logger.info(
Expand All @@ -209,6 +258,80 @@ def test_protocols_convergence():
else:
logger.info("Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK")

logger.info("Check Ping IPv6 from R1(r1-vrf-101) to R2(r2-vrf-101 = fd00::2)")
output = pingrouter.run("ip netns exec r1-vrf-101 ping fd00::2 -f -c 1000")
logger.info(output)
if "1000 packets transmitted, 1000 received" not in output:
assert 0, "expected ping IPv6 from R1(r1-vrf-101) to R2(fd00::2) should be ok"
else:
logger.info("Check Ping IPv6 from R1(r1-vrf-101) to R2(fd00::2) OK")

config_no_ipv6 = {
"r2": {
"raw_config": [
"router bgp 65000 vrf r2-vrf-101",
"address-family ipv6 unicast",
"no network fd00::2/128",
]
}
}

logger.info("==== Remove IPv6 network on R2")
result = apply_raw_config(tgen, config_no_ipv6)
assert result is True, "Failed to remove IPv6 network on R2, Error: {} ".format(
result
)
ipv6_routes = {
"r1": {
"static_routes": [
{
"vrf": "r1-vrf-101",
"network": ["fd00::2/128"],
}
]
}
}
result = verify_bgp_rib(tgen, "ipv6", "r1", ipv6_routes, expected=False)
assert result is not True, "expect IPv6 route fd00::2/128 withdrawn"
output = tgen.gears["r1"].vtysh_cmd("show evpn next-hops vni all", isjson=False)
logger.info("==== result from show evpn next-hops vni all")
logger.info(output)
output = tgen.gears["r1"].vtysh_cmd("show evpn rmac vni all", isjson=False)
logger.info("==== result from show evpn next-hops vni all")
logger.info(output)

expected = {
"101": {
"numNextHops": 1,
"192.168.100.41": {
"nexthopIp": "192.168.100.41",
},
}
}
result = topotest.router_json_cmp(
tgen.gears["r1"], "show evpn next-hops vni all json", expected
)
assert result is None, "evpn next-hops check failed"

expected = {"101": {"numRmacs": 1}}
result = topotest.router_json_cmp(
tgen.gears["r1"], "show evpn rmac vni all json", expected
)
assert result is None, "evpn rmac number check failed"

logger.info(
"Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)"
)
output = pingrouter.run("ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000")
logger.info(output)
if "1000 packets transmitted, 1000 received" not in output:
assertmsg = (
"expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok"
)
assert 0, assertmsg
else:
logger.info("Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK")


def test_memory_leak():
"Run the memory leak test and report results."
Expand Down
78 changes: 34 additions & 44 deletions zebra/zebra_vxlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,18 @@ static int zl3vni_remote_rmac_add(struct zebra_l3vni *zl3vni,
{
struct zebra_mac *zrmac = NULL;
struct ipaddr *vtep = NULL;
struct ipaddr ipv4_vtep;

/* vtep_ip may be v4 or v6-mapped-v4. But zrmac->fwd_info
* can only contain v4 version. So convert if needed
*/
memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep_ip->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
&(ipv4_vtep.ipaddr_v4));
else
IPV4_ADDR_COPY(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4);

zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
if (!zrmac) {
Expand All @@ -1369,7 +1381,7 @@ static int zl3vni_remote_rmac_add(struct zebra_l3vni *zl3vni,
return -1;
}
memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
zrmac->fwd_info.r_vtep_ip = ipv4_vtep.ipaddr_v4;

vtep = XCALLOC(MTYPE_EVPN_VTEP, sizeof(struct ipaddr));
memcpy(vtep, vtep_ip, sizeof(struct ipaddr));
Expand All @@ -1383,14 +1395,14 @@ static int zl3vni_remote_rmac_add(struct zebra_l3vni *zl3vni,
/* install rmac in kernel */
zl3vni_rmac_install(zl3vni, zrmac);
} else if (!IPV4_ADDR_SAME(&zrmac->fwd_info.r_vtep_ip,
&vtep_ip->ipaddr_v4)) {
&(ipv4_vtep.ipaddr_v4))) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"L3VNI %u Remote VTEP change(%pI4 -> %pIA) for RMAC %pEA",
zl3vni->vni, &zrmac->fwd_info.r_vtep_ip,
vtep_ip, rmac);

zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
zrmac->fwd_info.r_vtep_ip = ipv4_vtep.ipaddr_v4;

vtep = XCALLOC(MTYPE_EVPN_VTEP, sizeof(struct ipaddr));
memcpy(vtep, vtep_ip, sizeof(struct ipaddr));
Expand All @@ -1410,36 +1422,29 @@ static void zl3vni_remote_rmac_del(struct zebra_l3vni *zl3vni,
struct zebra_mac *zrmac,
struct ipaddr *vtep_ip)
{
struct ipaddr ipv4_vtep;

if (!zl3vni_nh_lookup(zl3vni, vtep_ip)) {
memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep_ip->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
&ipv4_vtep.ipaddr_v4);
else
memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4,
sizeof(struct in_addr));

/* remove nh from rmac's list */
l3vni_rmac_nh_list_nh_delete(zl3vni, zrmac, &ipv4_vtep);
/* delete nh is same as current selected, fall back to
* one present in the list
*/
if (IPV4_ADDR_SAME(&zrmac->fwd_info.r_vtep_ip,
&ipv4_vtep.ipaddr_v4) &&
listcount(zrmac->nh_list)) {
l3vni_rmac_nh_list_nh_delete(zl3vni, zrmac, vtep_ip);
/* If there are remaining entries, use IPv4 from one */
if (listcount(zrmac->nh_list)) {
struct ipaddr *vtep;
struct ipaddr ipv4_vtep;

vtep = listgetdata(listhead(zrmac->nh_list));
zrmac->fwd_info.r_vtep_ip = vtep->ipaddr_v4;
memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep->ipaddr_v6,
&(ipv4_vtep.ipaddr_v4));
else
IPV4_ADDR_COPY(&(ipv4_vtep.ipaddr_v4),
&vtep->ipaddr_v4);
zrmac->fwd_info.r_vtep_ip = ipv4_vtep.ipaddr_v4;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"L3VNI %u Remote VTEP nh change(%pIA -> %pI4) for RMAC %pEA",
zl3vni->vni, &ipv4_vtep,
&zrmac->fwd_info.r_vtep_ip,
&zrmac->macaddr);
zlog_debug("L3VNI %u Remote VTEP nh change(%pIA -> %pI4) for RMAC %pEA",
zl3vni->vni, vtep_ip,
&zrmac->fwd_info.r_vtep_ip,
&zrmac->macaddr);

/* install rmac in kernel */
zl3vni_rmac_install(zl3vni, zrmac);
Expand Down Expand Up @@ -2531,7 +2536,6 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
const struct prefix *host_prefix)
{
struct zebra_l3vni *zl3vni = NULL;
struct ipaddr ipv4_vtep;

zl3vni = zl3vni_from_vrf(vrf_id);
if (!zl3vni || !is_l3vni_oper_up(zl3vni))
Expand All @@ -2547,24 +2551,10 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac,
svd_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);

/*
* if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4
* address. Rmac is programmed against the ipv4 vtep because we only
* support ipv4 tunnels in the h/w right now
*/
memset(&ipv4_vtep, 0, sizeof(ipv4_vtep));
ipv4_vtep.ipa_type = IPADDR_V4;
if (vtep_ip->ipa_type == IPADDR_V6)
ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
&(ipv4_vtep.ipaddr_v4));
else
memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4,
sizeof(struct in_addr));

/*
* add the rmac - remote rmac to be installed is against the ipv4
* add the rmac - remote rmac to be installed is against the
* nexthop address
*/
zl3vni_remote_rmac_add(zl3vni, rmac, &ipv4_vtep);
zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip);
}

/* handle evpn vrf route delete */
Expand Down

0 comments on commit 8eb78b2

Please sign in to comment.