From e618d0caff107350cfb99f9ade01066afe73e8ae Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 20 Jan 2023 20:04:52 +0100 Subject: [PATCH] replace ospf with bgp --- config/default.nix | 2 + config/net/c3d2.nix | 17 +- config/net/cluster.nix | 10 +- config/net/core.nix | 54 +++- config/net/flpk.nix | 10 +- config/net/mgmt.nix | 5 +- config/net/priv.nix | 4 +- config/net/pub.nix | 2 +- config/net/serv.nix | 2 +- config/net/upstream.nix | 27 +- config/net/vpn.nix | 4 +- config/net/yggdrasil.nix | 4 +- config/secrets.nix | 4 - nix/lib/config/options.nix | 83 +++--- nix/nixos-module/container/bird.nix | 439 +++++++++++----------------- 15 files changed, 313 insertions(+), 354 deletions(-) diff --git a/config/default.nix b/config/default.nix index 691912d..9fecd61 100644 --- a/config/default.nix +++ b/config/default.nix @@ -19,4 +19,6 @@ in ++ lib.filesystem.listFilesRecursive ./net; site.net-combined = concatMapAttrsRecursive (name: value: { inherit (value) hosts4 hosts6; }) config.site.net; + + site.bgp.asn = 4242421127; } diff --git a/config/net/c3d2.nix b/config/net/c3d2.nix index 09c34b5..844404a 100644 --- a/config/net/c3d2.nix +++ b/config/net/c3d2.nix @@ -1,4 +1,5 @@ -{ lib, ... }: +{ config, lib, ... }: + { site.net.c3d2 = { dhcp = { @@ -110,28 +111,34 @@ c3d2.hwaddr = "0A:14:48:01:07:05"; core.hwaddr = "0A:14:48:01:07:04"; }; - ospf.allowedUpstreams = [ "anon1" "freifunk" ]; + bgp.allowedUpstreams = [ "anon1" "freifunk" ]; }; c3d2-gw1 = makeGateway { interfaces = { c3d2.hwaddr = "0A:14:48:01:21:01"; core.hwaddr = "0A:14:48:01:21:00"; }; - ospf.allowedUpstreams = [ "flpk-gw" "freifunk" "upstream4" "upstream3" "anon1" ]; + bgp.allowedUpstreams = [ "flpk-gw" "freifunk" "upstream4" "upstream3" "anon1" ]; }; c3d2-gw2 = makeGateway { interfaces = { c3d2.hwaddr = "0A:14:48:01:21:03"; core.hwaddr = "0A:14:48:01:21:02"; }; - ospf.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ]; + bgp.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ]; }; c3d2-gw3 = makeGateway { interfaces = { c3d2.hwaddr = "0A:14:48:01:21:05"; core.hwaddr = "0A:14:48:01:21:04"; }; - ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; + bgp = { + peers.${config.site.net.core.hosts6.dn42.bgp} = { + type = "rr_client"; + name = "rr"; + }; + allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; + }; }; }; } diff --git a/config/net/cluster.nix b/config/net/cluster.nix index d8550b8..983bc4b 100644 --- a/config/net/cluster.nix +++ b/config/net/cluster.nix @@ -1,4 +1,4 @@ -{ lib, ... }: +{ config, lib, ... }: let cephMonServers = [ "server5" "server6" "server8" ]; in @@ -158,7 +158,13 @@ in type = "veth"; }; }; - ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; + bgp = { + peers.${config.site.net.core.hosts6.dn42.bgp} = { + type = "rr_client"; + name = "rr"; + }; + allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; + }; }; server3 = makeServer; server5 = makeServer; diff --git a/config/net/core.nix b/config/net/core.nix index 08a8239..8feb7cf 100644 --- a/config/net/core.nix +++ b/config/net/core.nix @@ -1,3 +1,5 @@ +{ config, lib, ... }: + { site.net.core = { domainName = "core.zentralwerk.org"; @@ -132,6 +134,7 @@ upstream4 = "fd23:42:c3d2:581::b:3"; yggdrasil = "fd23:42:c3d2:581:9000::1"; vpn-gw = "fd23:42:c3d2:581:9001::1"; + flpk-gw = "fd23:42:c3d2:581:9002::1"; }; up4 = { anon1 = "2a00:8180:2c00:281::9:1"; @@ -201,15 +204,33 @@ }; }; - site.hosts = { + site.hosts = lib.mkMerge ([ { bgp = { bgp = { - asn = 4242421127; peers = { - "172.22.99.253" = { asn = 64699; }; - "fe80::a800:42ff:fe7a:3246%c3d2" = { asn = 64699; }; + "172.22.99.253" = { + asn = 64699; + type = "external"; + name = "dn42_4"; + }; + "fe80::a800:42ff:fe7a:3246%c3d2" = { + asn = 64699; + type = "external"; + name = "dn42_6"; + }; + # ${config.site.net.core.subnet4} = {}; + ${config.site.net.core.subnets6.dn42} = { + type = "rr_server"; + name = "rr"; + }; }; + # allowedUpstreams = + # [ "upstream4" "upstream3" "anon1" "freifunk" ]; + nets4 = [ "172.20.0.0/14" "10.0.0.0/8" ]; + nets6 = + [ "fd00::/8" "2a00:8180:2c00:200::/56" ]; }; + role = "container"; interfaces = { c3d2 = { hwaddr = "0A:14:48:01:22:01"; @@ -220,14 +241,21 @@ type = "veth"; }; }; - ospf = { - allowedUpstreams = - [ "upstream4" "upstream3" "anon1" "freifunk" ]; - stubNets4 = [ "172.20.0.0/14" "10.0.0.0/8" ]; - stubNets6 = - [ "fd00::/8" "2a00:8180:2c00:200::/56" ]; - }; - role = "container"; }; - }; + } ] ++ builtins.concatMap (hostName: + if hostName != "bgp" + # everyone in core peers with router "bgp" + then [ { + ${hostName}.bgp = { + # peers.${config.site.net.core.hosts4.bgp} = {}; + peers.${config.site.net.core.hosts6.dn42.bgp} = { + type = "rr_client"; + name = "rr"; + }; + }; + # TODO: upstreams + } ] + # except "bgp" itself :) + else [] + ) (builtins.attrNames config.site.net.core.hosts6.dn42)); } diff --git a/config/net/flpk.nix b/config/net/flpk.nix index 6828499..6b7a3c2 100644 --- a/config/net/flpk.nix +++ b/config/net/flpk.nix @@ -1,3 +1,5 @@ +{ config, ... }: + { site.net.flpk = { domainName = "flpk.zentralwerk.org"; @@ -48,9 +50,13 @@ }; }; }; - ospf = { + bgp = { allowedUpstreams = [ "upstream4" "upstream3" "freifunk" ]; - upstreamInstance = 2; + upstreamTable = "vpn_table"; + peers.${config.site.net.core.subnets6.dn42} = { + type = "upstream"; + name = "up"; + }; }; role = "container"; }; diff --git a/config/net/mgmt.nix b/config/net/mgmt.nix index b61f801..7cbedec 100644 --- a/config/net/mgmt.nix +++ b/config/net/mgmt.nix @@ -192,10 +192,7 @@ type = "veth"; }; }; - ospf = { - allowedUpstreams = - [ "upstream4" "upstream3" "anon1" "freifunk" ]; - }; + bgp.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; role = "container"; }; } diff --git a/config/net/priv.nix b/config/net/priv.nix index af49442..ff95957 100644 --- a/config/net/priv.nix +++ b/config/net/priv.nix @@ -38,7 +38,7 @@ lib.mkMerge ( core.type = "veth"; "priv${toString n}".type = "veth"; }; - ospf.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; + bgp.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; }; } ) (seq 1 privCount) @@ -540,7 +540,7 @@ lib.mkMerge ( hwaddr = "0A:14:47:02:2A:19"; }; }; - ospf.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ]; + bgp.allowedUpstreams = [ "upstream3" "upstream4" "anon1" "freifunk" ]; }; priv18-gw = { interfaces = { diff --git a/config/net/pub.nix b/config/net/pub.nix index 3d54c0e..049c29a 100644 --- a/config/net/pub.nix +++ b/config/net/pub.nix @@ -39,7 +39,7 @@ type = "veth"; }; }; - ospf = { + bgp = { allowedUpstreams = [ "anon1" "freifunk" ]; allowedUpstreams6 = [ "flpk-gw" "anon1" "freifunk" ]; }; diff --git a/config/net/serv.nix b/config/net/serv.nix index f95ff19..9de6d8b 100644 --- a/config/net/serv.nix +++ b/config/net/serv.nix @@ -206,7 +206,7 @@ gw6 = null; }; }; - ospf.allowedUpstreams = + bgp.allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; }; stats = makeContainer { diff --git a/config/net/upstream.nix b/config/net/upstream.nix index 07a8704..c2507e1 100644 --- a/config/net/upstream.nix +++ b/config/net/upstream.nix @@ -24,8 +24,12 @@ in }; }; }; - ospf.upstreamInstance = 7; role = "container"; + bgp.peers.${config.site.net.core.subnets6.dn42} = { + asn = config.site.hosts.upstream3.bgp.asn; + type = "upstream"; + name = "up"; + }; }; upstream4 = rec { @@ -327,17 +331,19 @@ in }; }; }; - ospf = { - upstreamInstance = 8; - stubNets4 = [ + bgp = { + nets4 = [ "${interfaces.up4-pppoe.upstream.staticIpv4Address}/32" ]; + peers.${config.site.net.core.subnets6.dn42} = { + asn = config.site.hosts.upstream4.bgp.asn; + type = "upstream"; + name = "up"; + }; }; role = "container"; }; - freifunk.ospf.upstreamInstance = 6; - anon1 = { interfaces = { core = { @@ -352,9 +358,14 @@ in }; }; }; - ospf = { + bgp = { allowedUpstreams = [ "upstream3" "upstream4" "freifunk" ]; - upstreamInstance = 5; + upstreamTable = "vpn_table"; + peers.${config.site.net.core.subnets6.dn42} = { + asn = config.site.hosts.upstream3.bgp.asn; + type = "upstream"; + name = "up"; + }; }; role = "container"; }; diff --git a/config/net/vpn.nix b/config/net/vpn.nix index 378d444..fb43cc8 100644 --- a/config/net/vpn.nix +++ b/config/net/vpn.nix @@ -33,8 +33,6 @@ type = "wireguard"; }; }; - ospf = { - allowedUpstreams = [ "flpk-gw" "anon1" "freifunk" ]; - }; + bgp.allowedUpstreams = [ "flpk-gw" "anon1" "freifunk" ]; }; } diff --git a/config/net/yggdrasil.nix b/config/net/yggdrasil.nix index 70e8074..e337a3a 100644 --- a/config/net/yggdrasil.nix +++ b/config/net/yggdrasil.nix @@ -7,10 +7,10 @@ type = "veth"; }; }; - ospf = { + bgp = { allowedUpstreams = [ "upstream4" "upstream3" "anon1" "freifunk" ]; - stubNets6 = [ "200::/7" ]; + nets6 = [ "200::/7" ]; }; services.yggdrasil.enable = true; diff --git a/config/secrets.nix b/config/secrets.nix index e9a5ba1..c471333 100644 --- a/config/secrets.nix +++ b/config/secrets.nix @@ -1,9 +1,5 @@ # Dummy secrets for testing { - site.net = { - core.ospf.secret = "encrypted"; - }; - site.hosts = { ap1.password = "encrypted"; ap2.password = "encrypted"; diff --git a/nix/lib/config/options.nix b/nix/lib/config/options.nix index dbd79e4..71458c1 100644 --- a/nix/lib/config/options.nix +++ b/nix/lib/config/options.nix @@ -148,12 +148,6 @@ let type = with types; attrsOf (attrsOf str); default = {}; }; - ospf = { - secret = mkOption { - type = with types; nullOr str; - default = null; - }; - }; dhcp = mkOption { type = with types; nullOr (submodule { options = dhcpOpts; }); default = null; @@ -390,35 +384,10 @@ let }; }); default = []; }; - ospf.stubNets4 = mkOption { - type = with types; listOf str; - default = []; - description = "Additional IPv4 networks to announce"; - }; - ospf.stubNets6 = mkOption { - type = with types; listOf str; - default = []; - description = "Additional IPv6 networks to announce"; - }; - ospf.allowedUpstreams = mkOption { - type = with types; listOf str; - default = []; - description = "Accept default routes from these OSPF routers, in order of preference"; - }; - ospf.allowedUpstreams6 = mkOption { - type = with types; listOf str; - default = config.site.hosts.${name}.ospf.allowedUpstreams; - description = "Accept IPv6 default routes from these OSPF3 routers, in order of preference"; - }; - ospf.upstreamInstance = mkOption { - type = with types; nullOr int; - default = null; - description = "OSPF instance for advertising the default route"; - }; bgp = mkOption { default = null; type = with types; nullOr (submodule { - options = bgpOpts; + options = bgpOpts name; }); }; services.dns = { @@ -499,20 +468,52 @@ let }; }; - bgpOpts = { + bgpOpts = hostName: { asn = mkOption { type = types.int; + default = config.site.bgp.asn; }; peers = mkOption { - type = with types; attrsOf (submodule ({ ... }: { + type = with types; attrsOf (submodule (submoduleArg: { options = { asn = mkOption { type = types.int; + default = config.site.bgp.asn; + }; + name = mkOption { + type = types.str; + }; + type = mkOption { + type = types.enum [ "external" "rr_server" "rr_client" "upstream" ]; }; }; })); default = {}; }; + nets4 = mkOption { + type = with types; listOf str; + default = []; + description = "Additional IPv4 networks to announce"; + }; + nets6 = mkOption { + type = with types; listOf str; + default = []; + description = "Additional IPv6 networks to announce"; + }; + allowedUpstreams = mkOption { + type = with types; listOf str; + default = []; + description = "Accept default routes from these BGP routers, in order of preference"; + }; + allowedUpstreams6 = mkOption { + type = with types; listOf str; + default = config.site.hosts.${hostName}.bgp.allowedUpstreams; + description = "Accept IPv6 default routes from these BGP routers, in order of preference"; + }; + upstreamTable = mkOption { + type = with types; nullOr str; + default = null; + }; }; linkOpts = hostName: { name, ... }: { @@ -623,6 +624,12 @@ in default = "secret"; }; }; + + bgp = { + asn = mkOption { + type = types.int; + }; + }; }; config.warnings = @@ -643,16 +650,16 @@ in reportCollisions = name: getter: xs: map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs); - ospfUpstreamXorGw = + bgpUpstreamXorGw = builtins.concatMap (hostName: let hostConf = config.site.hosts.${hostName}; gwNets = builtins.filter (netName: hostConf.interfaces.${netName}.gw4 != null ) (builtins.attrNames hostConf.interfaces); - in if gwNets != [] && hostConf.ospf.allowedUpstreams != [] + in if gwNets != [] && hostConf.bgp.allowedUpstreams or [] != [] then [ '' - Host ${hostName} has gateway on ${builtins.head gwNets} but accepts default routes from OSPF + Host ${hostName} has gateway on ${builtins.head gwNets} but accepts default routes from BGP '' ] else [] ) (builtins.attrNames config.site.hosts); @@ -660,7 +667,7 @@ in (reportCollisions "VLAN tag" (x: lib.optional (x.vlan != null) x.vlan) config.site.net) ++ (reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++ (reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net) ++ - ospfUpstreamXorGw; + bgpUpstreamXorGw; config.assertions = # Duplicate host/net name check diff --git a/nix/nixos-module/container/bird.nix b/nix/nixos-module/container/bird.nix index 47f972a..718e202 100644 --- a/nix/nixos-module/container/bird.nix +++ b/nix/nixos-module/container/bird.nix @@ -25,6 +25,42 @@ let n = n; x = builtins.head list; } ] ++ (enumerate (n + 1) (builtins.tail list)); + + nets4 = + hostConf.bgp.nets4 + ++ + builtins.concatMap (net: + if net != "core" + then + let + subnet4 = config.site.net.${net}.subnet4 or null; + in lib.optional (subnet4 != null) subnet4 + else + [] + ) (builtins.attrNames hostConf.interfaces); + + nets6 = + hostConf.bgp.nets6 + ++ + builtins.concatMap (net: + if net != "core" + then + builtins.attrValues config.site.net.${net}.subnets6 or {} + else + [] + ) (builtins.attrNames hostConf.interfaces); + + upstreamsToOrder = upstreams: + builtins.foldl' (order: { n, x }: + order // { + ${x} = n; + } + ) {} (enumerate 1 upstreams); + upstream4Order = upstreamsToOrder hostConf.bgp.allowedUpstreams; + upstream6Order = upstreamsToOrder hostConf.bgp.allowedUpstreams6; + allowedUpstreams = lib.unique ( + hostConf.bgp.allowedUpstreams ++ hostConf.bgp.allowedUpstreams6 + ); in { services.bird2 = { @@ -35,31 +71,13 @@ in protocol kernel K4 { learn; ipv4 { - ${if isUpstream - then '' - # Install all routes but the default route on upstreams - export where net != 0.0.0.0/0; - # Learn the upstream default route - import where net = 0.0.0.0/0; - '' - else '' - export all; - ''} + export all; }; } protocol kernel K6 { learn; ipv6 { - ${if isUpstream - then '' - # Install all routes but the default route on upstreams - export where net != ::/0; - # Learn the upstream default route - import where net = ::/0; - '' - else '' - export all; - ''} + export all; }; } protocol device { @@ -84,10 +102,7 @@ in check link yes; } - ${lib.optionalString ( - builtins.match "anon.*" hostName != null || - hostName == "flpk-gw" - ) '' + ${lib.optionalString (hostConf.bgp.upstreamTable != null) '' # BIRD routing table for Wireguard transport ipv4 table vpn_table; @@ -128,235 +143,6 @@ in } ''} - # OSPFv2 for site-local IPv4 - protocol ospf v2 ZW4 { - ipv4 { - import all; - # OSPF is self-contained - export none; - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on networks with a secret. - if config.site.net ? "${net}" && config.site.net.${net}.ospf.secret != null - then '' - interface "${net}" { - hello 10; - wait 20; - - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - else '' - interface "${net}" { - stub yes; - cost 10; - }; - '' - ) hostConf.interfaces - ) - )} - ${builtins.concatStringsSep "\n" ( - map (stubnet4: '' - # Advertise additional route - stubnet ${stubnet4} {}; - '') hostConf.ospf.stubNets4 - )} - }; - } - - ${lib.optionalString isUpstream '' - # OSPFv2 to advertise my default route - protocol ospf v2 ZW4_${hostNameEscaped} { - ipv4 { - export where net = 0.0.0.0/0; - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on interfaces with a secret. - lib.optionalString (config.site.net.${net}.ospf.secret != null) '' - interface "${net}" instance ${toString hostConf.ospf.upstreamInstance} { - # Become the designated router - priority 10; - hello 10; - wait 20; - - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - ) hostConf.physicalInterfaces - ) - )} - }; - } - ''} - - ${( - builtins.foldl' ({ text, n }: upstream: { - text = '' - ${text} - - # OSPFv2 to receive a default route from ${upstream} - protocol ospf v2 ZW4_${ - builtins.replaceStrings [ "-" ] [ "_" ] upstream - } { - ipv4 { - import filter { - preference = preference + ${toString (100 - n)}; - accept; - }; - ${lib.optionalString ( - builtins.match "anon.*" hostName != null || - hostName == "flpk-gw" - ) '' - table vpn_table; - ''} - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on interfaces with a secret. - lib.optionalString (config.site.net.${net}.ospf.secret != null) '' - interface "${net}" instance ${ - builtins.replaceStrings [ "-" ] [ "_" ] ( - toString config.site.hosts.${upstream}.ospf.upstreamInstance - ) - } { - hello 10; - wait 20; - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - ) hostConf.physicalInterfaces - ) - )} - }; - } - ''; - n = n + 1; - }) { text = ""; n = 0; } hostConf.ospf.allowedUpstreams - ).text} - - # OSPFv3 for site-local IPv6 - protocol ospf v3 ZW6 { - ipv6 { - import all; - # OSPF is self-contained - export none; - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on networks with a secret. - if config.site.net.${net}.ospf.secret != null - then '' - interface "${net}" { - hello 10; - wait 20; - - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - else '' - interface "${net}" { - stub yes; - cost 10; - }; - '' - ) hostConf.physicalInterfaces - ) - )} - ${builtins.concatStringsSep "\n" ( - map (stubnet6: '' - # Advertise additional route - stubnet ${stubnet6} {}; - '') - hostConf.ospf.stubNets6 - )} - }; - } - - ${lib.optionalString isUpstream '' - # OSPFv3 to advertise my default route - protocol ospf v3 ZW6_${hostNameEscaped} { - ipv6 { - export where net = ::/0; - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on interfaces with a secret. - lib.optionalString (config.site.net.${net}.ospf.secret != null) '' - interface "${net}" instance ${toString hostConf.ospf.upstreamInstance} { - # Become the designated router - priority 10; - hello 10; - wait 20; - - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - ) hostConf.physicalInterfaces - ) - )} - }; - } - ''} - - ${lib.optionalString (builtins.match "anon.*" hostName == null) ( - builtins.foldl' ({ text, n }: upstream: { - text = '' - ${text} - - # OSPFv3 to receive a default route from ${upstream} - protocol ospf v3 ZW6_${ - builtins.replaceStrings [ "-" ] [ "_" ] upstream - } { - ipv6 { - import filter { - preference = preference + ${toString (100 - n)}; - accept; - }; - }; - area 0 { - ${builtins.concatStringsSep "\n" ( - builtins.attrValues ( - builtins.mapAttrs (net: _: - # Enable OSPF only on interfaces with a secret. - lib.optionalString (config.site.net.${net}.ospf.secret != null) '' - interface "${net}" instance ${ - builtins.replaceStrings [ "-" ] [ "_" ] ( - toString config.site.hosts.${upstream}.ospf.upstreamInstance - ) - } { - hello 10; - wait 20; - authentication cryptographic; - password "${config.site.net.${net}.ospf.secret}"; - }; - '' - ) hostConf.physicalInterfaces - ) - )} - }; - } - ''; - n = n + 1; - }) { text = ""; n = 0; } hostConf.ospf.allowedUpstreams6 - ).text} - # Zentralwerk DN42 protocol static { ipv4; @@ -370,31 +156,146 @@ in } ${lib.optionalString (hostConf.bgp != null) '' - template bgp bgppeer { + # zentralwerk-network + template bgp bgp_rr_server { local as ${toString hostConf.bgp.asn}; + direct; ipv4 { - import all; - export where source=RTS_STATIC; + import filter { + preference = preference + 200; + accept; + }; + ${lib.optionalString (nets4 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets4} ]; + ''} }; ipv6 { + import filter { + preference = preference + 200; + accept; + }; + ${lib.optionalString (nets6 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets6} ]; + ''} + }; + } + template bgp bgp_rr_client { + local as ${toString hostConf.bgp.asn}; + direct; + + ipv4 { + next hop self on; + import filter { + preference = preference + 200; + accept; + }; + ${lib.optionalString (nets4 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets4} ]; + ''} + }; + ipv6 { + next hop self on; + import filter { + preference = preference + 200; + accept; + }; + ${lib.optionalString (nets6 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets6} ]; + ''} + }; + } + # dn42 + template bgp bgp_external { + local as ${toString hostConf.bgp.asn}; + direct; + + ipv4 { + next hop self on; import all; - export where source=RTS_STATIC; + export where source = RTS_STATIC; + }; + ipv6 { + next hop self on; + import all; + export where source = RTS_STATIC; + }; + } + # emitting default routes + template bgp bgp_upstream { + local as ${toString hostConf.bgp.asn}; + direct; + + ipv4 { + next hop self on; + import all; + export where net = 0.0.0.0/0; + }; + ipv6 { + next hop self on; + import all; + export where net = ::/0; }; } - ${builtins.concatStringsSep "\n" ( - map ({ n, x }: - let - peer = x; - peerConf = hostConf.bgp.peers.${peer}; - in '' - protocol bgp bgp_${toString n} from bgppeer { - neighbor ${peer} as ${toString peerConf.asn}; - } - '' - ) (enumerate 1 (builtins.attrNames hostConf.bgp.peers)) - )} + ${lib.concatMapStrings (peer: + let + peerConf = hostConf.bgp.peers.${peer}; + isRange = lib.hasInfix "/" peer; + in '' + protocol bgp bgp_${peerConf.name} from bgp_${peerConf.type} { + neighbor ${lib.optionalString isRange "range"} ${peer} as ${toString peerConf.asn}; + ${lib.optionalString isRange '' + dynamic name "bgp_${peerConf.name}"; + ''} + ${lib.optionalString (peerConf.type == "rr") '' + rr client; + ''} + } + '') (builtins.attrNames hostConf.bgp.peers)} + + ${lib.concatMapStrings ({ n, x }: let upstream = x; in '' + # upstream client instance #${toString n} + protocol bgp bgp_up_${upstream} { + local as ${toString hostConf.bgp.asn}; + neighbor ${config.site.net.core.hosts6.dn42.${upstream}} as ${toString hostConf.bgp.asn}; + direct; + + ipv4 { + ${if (upstream4Order ? ${upstream}) + then '' + import filter { + preference = preference + ${toString (100 - upstream4Order.${upstream})}; + accept; + }; + '' + else '' + import none; + ''} + ${lib.optionalString (nets4 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets4} ]; + ''} + ${lib.optionalString (hostConf.bgp.upstreamTable != null) '' + table ${hostConf.bgp.upstreamTable}; + ''} + }; + ipv6 { + ${if (upstream4Order ? ${upstream}) + then '' + import filter { + preference = preference + ${toString (100 - upstream4Order.${upstream})}; + accept; + }; + '' + else '' + import none; + ''} + ${lib.optionalString (nets6 != []) '' + export where net ~ [ ${lib.concatMapStringsSep ", " (n: "${n}") nets6} ]; + ''} + }; + } + '') (enumerate 1 allowedUpstreams)} ''} ''; };