diff --git a/bundles/bird/files/bird.conf b/bundles/bird/files/bird.conf new file mode 100644 index 0000000..0e6e876 --- /dev/null +++ b/bundles/bird/files/bird.conf @@ -0,0 +1,41 @@ +log syslog all; +router id ${node.metadata.get('bird/my_ip')}; +debug protocols all; + +ipv4 table master4; + +protocol device { +} + +protocol kernel { + scan time 30; + ipv4 { + export where source != RTS_STATIC; + }; +} +% if node.metadata.get('bird/static_routes', set()): + +protocol static { + ipv4; + +% for route in sorted(node.metadata.get('bird/static_routes', set())): + route ${route} via ${node.metadata.get('bird/my_ip')}; +% endfor +} +% endif +% for name, config in sorted(node.metadata.get('bird/bgp_neighbors', {}).items()): + +protocol bgp '${name}' { + local ${config['local_ip']} as ${config['local_as']}; + neighbor ${config['neighbor_ip']} as ${config['neighbor_as']}; + hold time ${config.get('hold_time', 15)}; + error wait time 5, 10; + direct; + + ipv4 { + next hop self; + import all; + export all; + }; +} +% endfor diff --git a/bundles/bird/items.py b/bundles/bird/items.py new file mode 100644 index 0000000..58a281c --- /dev/null +++ b/bundles/bird/items.py @@ -0,0 +1,17 @@ +files = { + '/etc/bird/bird.conf': { + 'content_type': 'mako', + 'triggers': { + 'svc_systemd:bird:reload', + }, + }, +} + +svc_systemd = { + 'bird': { + 'needs': { + 'file:/etc/bird/bird.conf', + 'pkg_apt:bird2', + }, + }, +} diff --git a/bundles/bird/metadata.py b/bundles/bird/metadata.py new file mode 100644 index 0000000..15d3fe9 --- /dev/null +++ b/bundles/bird/metadata.py @@ -0,0 +1,76 @@ +from ipaddress import ip_network +from bundlewrap.exceptions import NoSuchNode +from bundlewrap.metadata import atomic + +defaults = { + 'apt': { + 'packages': { + 'bird2': {}, + }, + }, + 'sysctl': { + 'options': { + 'net.ipv4.ip_forward': '1', + 'net.ipv6.conf.all.forwarding': '1', + }, + }, +} + +@metadata_reactor.provides( + 'bird/bgp_neighbors', +) +def neighbor_info_from_wireguard(metadata): + neighbors = {} + my_as = repo.libs.s2s.AS_NUMBERS[metadata.get('location')] + + for name, config in metadata.get('wireguard/peers', {}).items(): + try: + rnode = repo.get_node(name) + except NoSuchNode: + continue + + neighbors[name] = { + 'local_ip': config['my_ip'], + 'local_as': my_as, + 'neighbor_ip': config['their_ip'], + 'neighbor_as': repo.libs.s2s.AS_NUMBERS[rnode.metadata.get('location')], + } + + return { + 'bird': { + 'bgp_neighbors': neighbors, + }, + } + + +@metadata_reactor.provides( + 'bird/my_ip', +) +def my_ip(metadata): + if node.has_bundle('wireguard'): + my_ip = sorted(metadata.get('interfaces/wg0/ips'))[0].split('/')[0] + else: + my_ip = str(sorted(repo.libs.tools.resolve_identifier(repo, node.name))[0]) + + return { + 'bird': { + 'my_ip': my_ip, + }, + } + + +@metadata_reactor.provides( + 'firewall/port_rules', +) +def firewall(metadata): + sources = set() + for config in metadata.get('bird/bgp_neighbors', {}).values(): + sources.add(config['neighbor_ip']) + + return { + 'firewall': { + 'port_rules': { + '179': atomic(sources), + }, + }, + }