# SPDX-FileCopyrightText: 2020 Luke Granger-Brown # # SPDX-License-Identifier: Apache-2.0 { lib, config, ... }: let generateSnippet = base: args: lib.concatStringsSep "\n" (lib.mapAttrsToList ( ixName: ix: generateSnippetForIX (args // { ixName = ixName; ix = ix; }) ) base ); generateSnippetForIX = { ixName, ix, ... }@args: '' ipv4 table ${ixName}4; ipv6 table ${ixName}6; protocol pipe ${ixName}pipe_4 { table ${ixName}4; peer table master4; import where ((ro, ${toString ix.local.asn}, ${toString ix.remote.export_community}) ~ bgp_ext_community); export all; }; protocol pipe ${ixName}pipe_6 { table ${ixName}6; peer table master6; import where ((ro, ${toString ix.local.asn}, ${toString ix.remote.export_community}) ~ bgp_ext_community); export all; }; '' + lib.concatImapStringsSep "\n" ( i: v: generateSnippetForRouter (args // { routerNum = i; router = v; }) ) ix.remote.routers; enabledSnippet = { enabled ? true, ... }: "disabled ${if enabled then "off" else "on"}"; generateSnippetForRouter = { ixName, ix, routerNum, router, ... }: '' protocol bgp ${ixName}${toString routerNum}_4 { ${enabledSnippet router}; local ${ix.local.v4} as ${toString ix.local.asn}; neighbor ${router.v4} as ${toString ix.remote.asn}; ipv4 { table ${ixName}4; import all; export where ((ro, ${toString ix.local.asn}, 1000) ~ bgp_ext_community); }; }; protocol bgp ${ixName}${toString routerNum}_6 { ${enabledSnippet router}; local ${ix.local.v6} as ${toString ix.local.asn}; neighbor ${router.v6} as ${toString ix.remote.asn}; ipv6 { table ${ixName}6; import all; export where ((ro, ${toString ix.local.asn}, 1000) ~ bgp_ext_community); }; }; ''; inherit (lib) mkOption mkAfter types; in { options.services.lukegbgp = { enable = mkOption { type = types.bool; default = false; }; config = mkOption { # lukegbgp.config type = with types; submodule { options = { local = mkOption { # lukegbgp.config.local type = submodule { options = { routerID = mkOption { # lukegbgp.config.local.routerID type = str; }; }; }; }; peering = mkOption { # lukegbgp.config.peering type = attrsOf (submodule { options = { local = mkOption { # lukegbgp.config.peering..local type = submodule { options = { asn = mkOption { # lukegbgp.config.peering..local.asn type = int; }; v4 = mkOption { # lukegbgp.config.peering..local.v4 type = str; }; v6 = mkOption { # lukegbgp.config.peering..local.v6 type = str; }; }; }; }; remote = mkOption { # lukegbgp.config.peering..remote type = submodule { options = { asn = mkOption { # lukegbgp.config.peering..remote.asn type = int; }; export_community = mkOption { # lukegbgp.config.peering..remote.export_community type = int; }; routers = mkOption { # lukegbgp.config.peering..remote.routers type = listOf (submodule { options = { enabled = mkOption { # lukegbgp.config.peering..remote.routers..enabled type = bool; default = true; }; v4 = mkOption { # lukegbgp.config.peering..remote.routers..v4 type = str; }; v6 = mkOption { # lukegbgp.config.peering..remote.routers..v6 type = str; }; }; }); }; }; }; }; }; }); }; export = mkOption { # lukegbgp.config.export type = submodule { options = { v4 = mkOption { # lukegbgp.config.export.v4 type = listOf str; default = ["92.118.31.0/24"]; }; v6 = mkOption { # lukegbgp.config.export.v6 type = listOf str; default = ["2a09:a440::/48"]; }; }; }; }; }; }; }; }; config = { services.bird2 = lib.mkIf config.services.lukegbgp.enable { enable = true; config = '' router id ${config.services.lukegbgp.config.local.routerID}; ${generateSnippet config.services.lukegbgp.config.peering {}} protocol kernel { persist; ipv4 { import none; export all; }; }; protocol kernel { persist; ipv6 { import none; export all; }; }; protocol device { }; protocol static export4 { ipv4 { import filter { bgp_ext_community.add((ro, 205479, 1000)); bgp_ext_community.add((ro, 205479, 2000)); bgp_ext_community.add((ro, 205479, 2001)); bgp_ext_community.add((ro, 205479, 2002)); bgp_ext_community.add((ro, 205479, 2003)); bgp_ext_community.add((ro, 205479, 3000)); accept; }; }; ${lib.concatMapStrings (ip: "route ${ip} blackhole;") config.services.lukegbgp.config.export.v4} }; protocol static export6 { ipv6 { import filter { bgp_ext_community.add((ro, 205479, 1000)); bgp_ext_community.add((ro, 205479, 2000)); bgp_ext_community.add((ro, 205479, 2001)); bgp_ext_community.add((ro, 205479, 2002)); bgp_ext_community.add((ro, 205479, 2003)); bgp_ext_community.add((ro, 205479, 3000)); accept; }; }; ${lib.concatMapStrings (ip: "route ${ip} blackhole;") config.services.lukegbgp.config.export.v6} }; ''; }; networking.firewall.allowedTCPPorts = lib.mkIf config.services.lukegbgp.enable (lib.mkAfter [ 179 ]); boot.kernel.sysctl = { "net.ipv6.conf.default.accept_ra" = 0; "net.ipv6.conf.all.accept_ra" = 0; "net.ipv6.conf.default.autoconf" = 0; "net.ipv6.conf.all.autoconf" = 0; }; }; }