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

Update FRR to recent stable release #1307

Merged
merged 3 commits into from
Feb 27, 2025
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
38 changes: 22 additions & 16 deletions pkgs/fc/check-rib-integrity/check_rib_integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ def ip_route_json(net):


def bridge_macs(bridge, vxlan):
# XXX: newer versions of iproute2 support bridge -j for json
# output
data = subprocess.check_output(["bridge", "fdb", "show", "br", bridge])
data = [item.split() for item in data.decode("utf-8").splitlines()]
data = json_cmd(["bridge", "-j", "fdb", "show", "br", bridge])
data = [
item
for item in data
Expand All @@ -52,23 +49,32 @@ def bridge_macs(bridge, vxlan):
]

local_macs = {
item[0]: (item[2] if item[2] != vxlan else bridge)
item["mac"]: (item["ifname"] if item["ifname"] != vxlan else bridge)
for item in data
# extern_learn records managed by zebra, self records handled
# internally by device drivers
if "extern_learn" not in item and "self" not in item
# ignore the mac addresses assigned to the host side of tap
# interfaces, but include the mac address assigned to the
# bridge interface
and ("permanent" not in item or item[2] == vxlan)
# extern_learn records managed by zebra
if "extern_learn" not in item["flags"]
# self records handled internally by device drivers
and "self" not in item["flags"]
# ignore mac addresses assigned to the host side of tap
# interfaces (marked permanent), but include the mac address
# assigned to the bridge interface itself
and (item["state"] != "permanent" or item["ifname"] == vxlan)
}

remote_macs = {
mac[0]: IPv4Address(dest[4])
for mac in data
if mac[2] == vxlan and mac[4] == "master" and mac[5] == bridge
lladdr["mac"]: IPv4Address(dest["dst"])
for lladdr in data
# entries indicating the vxlan interface as the port on the
# bridge for a given mac address
if lladdr["ifname"] == vxlan
and "master" in lladdr
and lladdr["master"] == bridge
# join with entries associating the same mac address with a
# remote vtep address
for dest in data
if dest[0] == mac[0] and dest[2] == vxlan and dest[3] == "dst"
if dest["mac"] == lladdr["mac"]
and dest["ifname"] == vxlan
and "dst" in dest
}

return local_macs, remote_macs
Expand Down
4 changes: 2 additions & 2 deletions pkgs/overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ builtins.mapAttrs (_: patchPhps (fetchpatch {
});

frr = super.frr.overrideAttrs (old: rec {
version = "8.5.7";
version = "10.1.2";
src = super.fetchFromGitHub {
owner = "FRRouting";
repo = old.pname;
rev = "${old.pname}-${version}";
hash = "sha256-2ViapJNLO+jwtORtarj+UTdHN/uE2PqyJTwf4dkXBmg=";
hash = "sha256-yenWMFHQ8F3/GJ+BVnoi5t//6qtFqH8i3uNq4X0/qdI==";
};

patches = [
Expand Down
71 changes: 69 additions & 2 deletions tests/frr.nix
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ let
};
};

makeEvpnHost = { idx, taps }: { pkgs, lib, ... }:
makeEvpnHost = { idx, taps, tapsEnabled ? true }: { pkgs, lib, ... }:
let
underlayAddr = makeUnderlayAddress idx;
bridgeMac = makeHostMac idx;
Expand All @@ -185,7 +185,7 @@ let
macaddr = makeTapMac tapidx;
in lib.nameValuePair "ping-${iface}" (rec {
description = "Respond to ping and arp on ${iface}";
wantedBy = [ "multi-user.target" ];
wantedBy = if tapsEnabled then [ "multi-user.target" ] else [];
requires = [ "network-addresses-${iface}.service" ];
after = requires;
serviceConfig.ExecStart = "${pkgs.fc.ping-on-tap}/bin/ping-on-tap ${iface} ${macaddr} ${ipaddr}";
Expand Down Expand Up @@ -397,5 +397,72 @@ in {
host1.wait_until_succeeds("check_rib_integrity check-evpn-rib -n 23")
'';
};
migration = {
name = "migration";
nodes = {
host1 = makeEvpnHost { idx = 1; taps = [ 1 ]; tapsEnabled = false; };
switch1 = makeFrrHost { idx = 2; redistribute = true; evpn = true; };
host2 = makeEvpnHost { idx = 3; taps = [ 1 ]; tapsEnabled = false; };
switch2 = makeFrrHost { idx = 4; redistribute = true; evpn = true; };
};

testScript = ''
start_all()
all_vms = [host1, host2, switch1, switch2]
for vm in all_vms:
vm.wait_for_unit("network-online.target")

for vm in all_vms:
x = vm.succeed("vtysh -c 'show version'")

guest_mac = "06:00:00:00:23:01"
guest_ip = "192.168.23.101"

with subtest("wait for peer SVI MAC addresses to appear"):
for host, addr in [(host1, "06:00:00:00:42:03"), (host2, "06:00:00:00:42:01")]:
host.wait_until_succeeds(f"bridge fdb show br br0 | grep -F {addr}", timeout=10)

with subtest("checking SVI addresses are reachable"):
host1.succeed("ping -c1 192.168.23.3")
host2.succeed("ping -c1 192.168.23.1")

with subtest("start guest TAP responder"):
host1.succeed("systemctl start ping-tap0")
with subtest("wait for local mac to appear"):
host1.wait_until_succeeds(f"bridge fdb show br br0 | grep -F '{guest_mac} dev tap0'", timeout=10)
with subtest("wait for remote mac to appear"):
host2.wait_until_succeeds(f"bridge fdb show br br0 | grep -F extern_learn | grep -F {guest_mac}", timeout=10)

with subtest("check TAP responder is reachable"):
# ensure the neighbour tables on both hosts are primed.
host1.succeed(f"ping -A -c5 {guest_ip}")
host2.succeed(f"ping -A -c5 {guest_ip}")

with subtest("rib and fib should not have mismatches"):
for host in [host2, host1]:
host.wait_until_succeeds("check_rib_integrity check-unicast-rib -p 192.168.42.0/24", timeout=10)
host.wait_until_succeeds("check_rib_integrity check-evpn-rib -n 23", timeout=10)


with subtest("migrate guest TAP responder"):
# pathological case: the guest interface starts on the receiving
# host before it stops on the sending host.
host2.succeed("systemctl start ping-tap0")
host1.succeed("systemctl stop ping-tap0")

with subtest("wait for local mac to appear"):
host2.wait_until_succeeds(f"bridge fdb show br br0 | grep -F '{guest_mac} dev tap0'", timeout=10)
with subtest("wait for remote mac to appear"):
host1.wait_until_succeeds(f"bridge fdb show br br0 | grep -F extern_learn | grep -F {guest_mac}", timeout=10)

with subtest("check remote TAP responder is reachable"):
host1.succeed(f"ping -A -c5 {guest_ip}")

with subtest("rib and fib should not have mismatches"):
for host in [host2, host1]:
host.wait_until_succeeds("check_rib_integrity check-unicast-rib -p 192.168.42.0/24", timeout=10)
host.wait_until_succeeds("check_rib_integrity check-evpn-rib -n 23", timeout=10)
'';
};
};
})