#!/usr/bin/env python3 import json from pathlib import Path import subprocess import sys upstream_config_file = Path(sys.argv[1]).resolve() bird_config_file = Path(sys.argv[2]).resolve() print("Loading upstream config from {}".format(upstream_config_file)) print("Deploying bird peering config file to {}".format(bird_config_file)) print("\n") upstream_config = json.loads(upstream_config_file.read_text()) # collect tunnel interface names tunnel_interface_names = [] # collect bird 2 peering directives bird_config = "" for peering in upstream_config: tunnel_interface_name = "peer{id}as{asn}".format( id=peering["peering-id"], asn=str(peering["remote-as"])[-4:], ) print("Configuring {}".format(tunnel_interface_name)) tunnel_interface_names.append(tunnel_interface_name) tunnel_config = """[Interface] PrivateKey = {local_privatekey} ListenPort = {local_endpoint_port} [Peer] PublicKey = {remote_publickey} Endpoint = {remote_endpoint_addr}:{remote_endpoint_port} AllowedIPs = fd00::/8, fe80::/10""".format( local_privatekey=peering["local-privatekey"], local_endpoint_port=peering["local-endpoint-port"], remote_publickey=peering["remote-publickey"], remote_endpoint_addr=peering["remote-endpoint-addr"], remote_endpoint_port=peering["remote-endpoint-port"], ) tunnel_config_file = Path() / "{}.conf".format(tunnel_interface_name) tunnel_config_file.write_text(tunnel_config) r = subprocess.run(["ip", "address", "show", "dev", tunnel_interface_name], capture_output=True) if r.returncode != 0: subprocess.run(["ip", "link", "add", "dev", tunnel_interface_name, "type", "wireguard"], check=True) subprocess.run(["ip", "link", "set", "up", "dev", tunnel_interface_name], check=True) subprocess.run(["wg", "syncconf", tunnel_interface_name, tunnel_config_file], check=True) subprocess.run(["ip", "address", "change", "{}/128".format(peering["local-ip"]), "peer", "{}/128".format(peering["remote-ip"]), "dev", tunnel_interface_name], check=True) tunnel_config_file.unlink(missing_ok=True) bird_config += """ protocol bgp {tunnel_interface_name} from bgp_dn42 {{ neighbor {remote_ip}%{tunnel_interface_name} as {remote_as}; }} """.format( tunnel_interface_name=tunnel_interface_name, remote_ip=peering["remote-ip"], remote_as=peering["remote-as"], ) # Writing generated bird config bird_config_file.write_text(bird_config) # remove all peer interfaces that are not supported r = subprocess.run(["ip", "--json", "link", "show"], check=True, capture_output=True,text=True) interfaces = json.loads(r.stdout) for interface in interfaces: if interface["ifname"].startswith("peer") and interface["ifname"] not in tunnel_interface_names: print("Removing {}".format(interface["ifname"])) subprocess.run(["ip", "link", "delete", interface["ifname"]], check=True)