diff options
Diffstat (limited to 'frr-evpn-route-watcher/task.py')
| -rwxr-xr-x | frr-evpn-route-watcher/task.py | 113 | 
1 files changed, 113 insertions, 0 deletions
diff --git a/frr-evpn-route-watcher/task.py b/frr-evpn-route-watcher/task.py new file mode 100755 index 0000000..dfd981e --- /dev/null +++ b/frr-evpn-route-watcher/task.py @@ -0,0 +1,113 @@ +import json +import ipaddress +import subprocess +import re +import time + +def get_frr_vrfs(): +    """Gets the list of vrf VNIs configured in FRR""" +    process = subprocess.run(["vtysh", "-c", "show vrf vni json"], capture_output=True) +    return json.loads(process.stdout)["vrfs"] + +def get_frr_evpn_info(): +    """Lists all the routes learned via evpn""" +    process = subprocess.run( +        ["vtysh", "-c", "show ip bgp l2vpn evpn json"], capture_output=True +    ) +    return json.loads(process.stdout) + +def add_route_vrf(ip, vrf): +    """Add a route using the ip route command""" +    subprocess.run( +        ["ip", "route", "add", str(ip), "dev", vrf, "metric", "0"], +    ) + +def remove_route_vrf(ip): +    subprocess.run(["ip", "route", "del", str(ip)]) + +def currently_routed(vrfs_names): +    process = subprocess.run(["ip", "route"], capture_output=True, text=True) +    routed_ips = [] +    for route in process.stdout.splitlines(): +        parts = route.split(" ") +        if len(parts) == 6: +            addr, dev, vrf, scope, link = ( +                parts[0], +                parts[1], +                parts[2], +                parts[3], +                parts[4], +            ) +            addr = parse_ip(addr) +            if ( +                addr != None +                and dev == "dev" +                and vrf in vrfs_names +                and scope == "scope" +                and link == "link" +            ): +                routed_ips.append(addr) +    return routed_ips + +def parse_ip(str): +    try: +        return ipaddress.ip_network(str) +    except ValueError: +        return False + +def get_vrfs(): +    return {str(vrf["vni"]): vrf["vrf"] for vrf in get_frr_vrfs()} + +def resolve_routes(): +    json = get_frr_evpn_info() +    vrfs = get_vrfs() + +    # The local router-id to search for IPs in the evpn routes information +    localRouterId = json["bgpLocalRouterId"] +    localAS = json["localAS"] + +    currentlyRouted = currently_routed([vrfs[vrf] for vrf in vrfs]) + +    for jsonKey in json: +        rd = jsonKey.split(":") +        if len(rd) == 2: +            peer, _ = rd + +            if parse_ip(peer) and peer == localRouterId: +                rdObject = json[jsonKey] +                for route in rdObject: +                    matches = re.findall(r"(?:\[([^\]]*)\]:?)", route) +                    if len(matches) == 6: +                        route_type = matches[0] +                        if route_type == "2": +                            ip = parse_ip(matches[5]) + +                            if ip != None and not ip.is_link_local: +                                extendedCommunities = rdObject[route]["paths"][0][ +                                    "extendedCommunity" +                                ]["string"] +                                rts = re.finditer( +                                    f"RT:{localAS}:(\d+)", extendedCommunities +                                ) + +                                # For all the matches we have in the extentendCommunity value +                                for rt in rts: +                                    vni = rt.group(1) +                                    if vni in vrfs: +                                        vrf = vrfs[vni] +                                        if ip in currentlyRouted: +                                            currentlyRouted.remove(ip) +                                            break +                                         +                                        print( +                                            f"wanting to add route {ip} to {vrf} ({vni}) vrf" +                                        ) +                                        add_route_vrf(ip, vrf) +                                         + +                                        break +    for remove in currentlyRouted: +        print(f"removing route for {remove}") +        remove_route_vrf(remove) + +resolve_routes()
\ No newline at end of file  | 
