From 36654301a259460944e1562e6c5243f1f550f80d Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 14 Mar 2024 21:42:08 +0100 Subject: [PATCH] added dn42 bgp communities --- checks/extended-next-hop.nix | 15 ++++ checks/two-peers.nix | 15 ++++ modules/bird2.nix | 117 ++++++++++++------------------- modules/default.nix | 35 ++++++++++ resources/community_filter.conf | 46 ++++++++++++ resources/filters.conf | 119 ++++++++++++++++++++++++++++++++ 6 files changed, 274 insertions(+), 73 deletions(-) create mode 100644 resources/community_filter.conf create mode 100644 resources/filters.conf diff --git a/checks/extended-next-hop.nix b/checks/extended-next-hop.nix index d75e93d..b5fa2e7 100644 --- a/checks/extended-next-hop.nix +++ b/checks/extended-next-hop.nix @@ -6,6 +6,7 @@ let networking.dn42.enable = true; virtualisation.interfaces.enp1s0.vlan = 1; networking.useNetworkd = true; + networking.domain = "test.nixos"; systemd.network.netdevs.dummy0.netdevConfig = { Kind = "dummy"; Name = "dummy0"; @@ -20,14 +21,21 @@ pkgs.nixosTest rec { nodes = { foo = { imports = [ common ]; + networking.hostName = "foo"; networking.dn42 = { as = 64600; + bandwidth = 25; + geo = 41; + country = 1276; addr.v4 = "172.20.0.1"; nets.v4 = [ "172.20.0.0/24" ]; addr.v6 = "fec0::1"; nets.v6 = [ "fec0::/64" ]; peers.bar = { as = 64601; + latency = 1; + bandwidth = 25; + crypto = 31; extendedNextHop = true; addr.v6 = (builtins.head nodes.bar.networking.interfaces.enp1s0.ipv6.addresses).address; srcAddr.v6 = (builtins.head nodes.foo.networking.interfaces.enp1s0.ipv6.addresses).address; @@ -57,14 +65,21 @@ pkgs.nixosTest rec { }; bar = { imports = [ common ]; + networking.hostName = "bar"; networking.dn42 = { as = 64601; + bandwidth = 25; + geo = 41; + country = 1276; addr.v4 = "172.20.1.1"; nets.v4 = [ "172.20.1.0/24" ]; addr.v6 = "fec0:0:0:1::1"; nets.v6 = [ "fec0:0:0:1::/64" ]; peers.foo = { as = 64600; + latency = 1; + bandwidth = 25; + crypto = 31; extendedNextHop = true; addr.v6 = (builtins.head nodes.foo.networking.interfaces.enp1s0.ipv6.addresses).address; srcAddr.v6 = (builtins.head nodes.bar.networking.interfaces.enp1s0.ipv6.addresses).address; diff --git a/checks/two-peers.nix b/checks/two-peers.nix index 017de4b..0c90eac 100644 --- a/checks/two-peers.nix +++ b/checks/two-peers.nix @@ -6,6 +6,7 @@ let networking.dn42.enable = true; virtualisation.interfaces.enp1s0.vlan = 1; networking.useNetworkd = true; + networking.domain = "text.nixos"; systemd.network.netdevs.dummy0.netdevConfig = { Kind = "dummy"; Name = "dummy0"; @@ -20,14 +21,21 @@ pkgs.nixosTest rec { nodes = { foo = { imports = [ common ]; + networking.hostName = "foo"; networking.dn42 = { as = 64600; + bandwidth = 25; + geo = 41; + country = 1276; addr.v4 = "172.20.0.1"; nets.v4 = [ "172.20.0.0/24" ]; addr.v6 = "fec1::1"; nets.v6 = [ "fec1::/64" ]; peers.bar = { as = 64601; + latency = 1; + bandwidth = 25; + crypto = 31; addr.v4 = (builtins.head nodes.bar.networking.interfaces.enp1s0.ipv4.addresses).address; addr.v6 = (builtins.head nodes.bar.networking.interfaces.enp1s0.ipv6.addresses).address; srcAddr.v4 = (builtins.head nodes.foo.networking.interfaces.enp1s0.ipv4.addresses).address; @@ -58,14 +66,21 @@ pkgs.nixosTest rec { }; bar = { imports = [ common ]; + networking.hostName = "bar"; networking.dn42 = { as = 64601; + bandwidth = 25; + geo = 41; + country = 1276; addr.v4 = "172.20.1.1"; nets.v4 = [ "172.20.1.0/24" ]; addr.v6 = "fec1:0:0:1::1"; nets.v6 = [ "fec1:0:0:1::/64" ]; peers.foo = { as = 64600; + latency = 1; + bandwidth = 25; + crypto = 31; addr.v4 = (builtins.head nodes.foo.networking.interfaces.enp1s0.ipv4.addresses).address; addr.v6 = (builtins.head nodes.foo.networking.interfaces.enp1s0.ipv6.addresses).address; srcAddr.v4 = (builtins.head nodes.bar.networking.interfaces.enp1s0.ipv4.addresses).address; diff --git a/modules/bird2.nix b/modules/bird2.nix index 798710f..518778b 100644 --- a/modules/bird2.nix +++ b/modules/bird2.nix @@ -1,4 +1,5 @@ { config, lib, ... }: + let cfg = config.networking.dn42; in @@ -18,15 +19,14 @@ in services.bird2 = { enable = true; config = '' - router id ${cfg.routerId}; + define OWNAS = ${toString cfg.as}; + define OWNIP = ${toString cfg.addr.v6}; - protocol device { - scan time 10; - } + define BANDWIDTH = ${toString cfg.bandwidth}; + define REGION_GEO = ${toString cfg.geo}; + define REGION_COUNTRY = ${toString cfg.country}; - /* - * Utility functions - */ + define ASN_BLACKLIST = []; function is_self_net_v4() -> bool { return net ~ [${builtins.concatStringsSep ", " cfg.nets.v4}]; @@ -36,39 +36,18 @@ in return net ~ [${builtins.concatStringsSep ", " cfg.nets.v6}]; } - function is_valid_network_v4() -> bool { - return net ~ [ - 172.20.0.0/14{21,29}, # dn42 - 172.20.0.0/24{28,32}, # dn42 Anycast - 172.21.0.0/24{28,32}, # dn42 Anycast - 172.22.0.0/24{28,32}, # dn42 Anycast - 172.23.0.0/24{28,32}, # dn42 Anycast - 172.31.0.0/16+, # ChaosVPN - 10.100.0.0/14+, # ChaosVPN - 10.127.0.0/16{16,32}, # neonetwork - 10.0.0.0/8{15,24} # Freifunk.net - ]; + function is_self_net() -> bool { + return is_self_net_v4() || is_self_net_v6(); } - /* - roa4 table dn42_roa; - roa6 table dn42_roa_v6; + include "${../resources/community_filter.conf}"; + include "${../resources/filters.conf}"; + + router id ${cfg.routerId}; + hostname "${config.networking.hostName}.${config.networking.domain}"; - protocol static { - roa4 { table dn42_roa; }; - include "/etc/bird/roa_dn42.conf"; - }; - - protocol static { - roa6 { table dn42_roa_v6; }; - include "/etc/bird/roa_dn42_v6.conf"; - }; - */ - - function is_valid_network_v6() -> bool { - return net ~ [ - fd00::/8{44,64} # ULA address space as per RFC 4193 - ]; + protocol device { + scan time 10; } protocol kernel { @@ -121,46 +100,17 @@ in template bgp dnpeers { local as ${builtins.toString cfg.as}; - path metric 1; + enforce first as on; graceful restart on; long lived graceful restart on; - interpret communities on; + advertise hostname on; prefer older on; - ipv4 { - import filter { - if is_valid_network_v4() && !is_self_net_v4() then { - /*if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then { - # Reject when unknown or invalid according to ROA - print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; - reject; - } else*/ accept; - } else reject; - }; - - export filter { if is_valid_network_v4() && source ~ [RTS_STATIC, RTS_BGP] then accept; else reject; }; - - import limit 9000 action block; - import table on; - }; - - ipv6 { - import filter { - if is_valid_network_v6() && !is_self_net_v6() then { - /*if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then { - # Reject when unknown or invalid according to ROA - print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last; - reject; - } else*/ accept; - } else reject; - }; - - export filter { if is_valid_network_v6() && source ~ [RTS_STATIC, RTS_BGP] then accept; else reject; }; - - import limit 9000 action block; - import table on; - }; + # defaults + enable route refresh on; + interpret communities on; + default bgp_local_pref 100; } ${builtins.concatStringsSep "\n" (builtins.attrValues @@ -170,6 +120,14 @@ in protocol bgp ${name}_4 from dnpeers { neighbor ${conf.addr.v4} as ${builtins.toString conf.as}; source address ${conf.srcAddr.v4}; + + ipv4 { + import limit 9000 action block; + import table on; + + import where dn_import_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); + export where dn_export_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); + }; } ''} @@ -178,9 +136,22 @@ in enable extended messages on; ipv4 { - extended next hop on; + import limit 9000 action block; + import table on; + + extended next hop on; + import where dn_import_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); + export where dn_export_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); }; ''} + + ipv6 { + import limit 9000 action block; + import table on; + + import where dn_import_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); + export where dn_export_filter(${toString conf.latency}, ${toString conf.bandwidth}, ${toString conf.crypto}); + }; neighbor ${conf.addr.v6}%'${conf.interface}' as ${builtins.toString conf.as}; source address ${conf.srcAddr.v6}; diff --git a/modules/default.nix b/modules/default.nix index 283726d..8fc8f54 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -22,6 +22,26 @@ in description = "Autonomous System Number"; }; + bandwidth = lib.mkOption { + type = lib.types.int; + description = ""; + }; + + geo = lib.mkOption { + type = lib.types.int; + description = ""; + }; + + country = lib.mkOption { + type = lib.types.int; + description = ""; + }; + + blockedAs = lib.mkOption { + type = lib.types.listOf lib.types.int; + description = ""; + }; + addr = { v4 = lib.mkOption { type = lib.types.str; @@ -60,6 +80,21 @@ in default = false; }; + latency = lib.mkOption { + type = lib.types.int; + description = ""; + }; + + bandwidth = lib.mkOption { + type = lib.types.int; + description = ""; + }; + + crypto = lib.mkOption { + type = lib.types.int; + description = ""; + }; + addr = { v4 = lib.mkOption { type = lib.types.nullOr lib.types.str; diff --git a/resources/community_filter.conf b/resources/community_filter.conf new file mode 100644 index 0000000..567cce3 --- /dev/null +++ b/resources/community_filter.conf @@ -0,0 +1,46 @@ +function update_latency(int link_latency) { + bgp_community.add((64511, link_latency)); + if (64511, 9) ~ bgp_community then { bgp_community.delete([(64511, 1..8)]); } + else if (64511, 8) ~ bgp_community then { bgp_community.delete([(64511, 1..7)]); } + else if (64511, 7) ~ bgp_community then { bgp_community.delete([(64511, 1..6)]); } + else if (64511, 6) ~ bgp_community then { bgp_community.delete([(64511, 1..5)]); } + else if (64511, 5) ~ bgp_community then { bgp_community.delete([(64511, 1..4)]); } + else if (64511, 4) ~ bgp_community then { bgp_community.delete([(64511, 1..3)]); } + else if (64511, 3) ~ bgp_community then { bgp_community.delete([(64511, 1..2)]); } + else if (64511, 2) ~ bgp_community then { bgp_community.delete([(64511, 1..1)]); } +} + +function update_bandwidth(int link_bandwidth) { + bgp_community.add((64511, link_bandwidth)); + if (64511, 21) ~ bgp_community then { bgp_community.delete([(64511, 22..29)]); } + else if (64511, 22) ~ bgp_community then { bgp_community.delete([(64511, 23..29)]); } + else if (64511, 23) ~ bgp_community then { bgp_community.delete([(64511, 24..29)]); } + else if (64511, 24) ~ bgp_community then { bgp_community.delete([(64511, 25..29)]); } + else if (64511, 25) ~ bgp_community then { bgp_community.delete([(64511, 26..29)]); } + else if (64511, 26) ~ bgp_community then { bgp_community.delete([(64511, 27..29)]); } + else if (64511, 27) ~ bgp_community then { bgp_community.delete([(64511, 28..29)]); } + else if (64511, 28) ~ bgp_community then { bgp_community.delete([(64511, 29..29)]); } +} + +function update_crypto(int link_crypto) { + bgp_community.add((64511, link_crypto)); + if (64511, 31) ~ bgp_community then { bgp_community.delete([(64511, 32..34)]); } + else if (64511, 32) ~ bgp_community then { bgp_community.delete([(64511, 33..34)]); } + else if (64511, 33) ~ bgp_community then { bgp_community.delete([(64511, 34..34)]); } +} + +function update_flags(int link_latency; int link_bandwidth; int link_crypto) { + if link_bandwidth > BANDWIDTH then link_bandwidth = BANDWIDTH; + + update_latency(link_latency); + update_bandwidth(link_bandwidth); + update_crypto(link_crypto); +} + +function update_region() { + if is_self_net() then { + bgp_community.add((64511, REGION_GEO)); + bgp_community.add((64511, REGION_COUNTRY)); + } +} + diff --git a/resources/filters.conf b/resources/filters.conf new file mode 100644 index 0000000..75246fa --- /dev/null +++ b/resources/filters.conf @@ -0,0 +1,119 @@ +function is_valid_network_v4() -> bool{ + return net ~ [ + 172.20.0.0/14{21,29}, # dn42 + 172.20.0.0/24{28,32}, # dn42 Anycast + 172.21.0.0/24{28,32}, # dn42 Anycast + 172.22.0.0/24{28,32}, # dn42 Anycast + 172.23.0.0/24{28,32}, # dn42 Anycast + 172.31.0.0/16+, # ChaosVPN + 10.100.0.0/14+, # ChaosVPN + 10.127.0.0/16{16,32}, # neonetwork + 10.0.0.0/8{15,24} # Freifunk.net + ]; +} + +function is_valid_network_v6() -> bool { + return net ~ [ + fd00::/8{44,64} + ]; +} + +function is_valid_network() -> bool { + return is_valid_network_v4() || is_valid_network_v6(); +} + +function kernel_export() { + krt_prefsrc = OWNIP; + accept; +} + +function reject_invalid_roa() { + #if (roa_check(dnroa, net, bgp_path.last) != ROA_VALID) then { + # print "Reject: ROA failed|", net, "|", bgp_path; + # reject; + #} +} + +function reject_default_route() { + if (net = fd00::/8 || net = ::/0) then + reject; +} + +function reject_blacklisted() +int set blacklist; +{ + blacklist = ASN_BLACKLIST; + + if ( bgp_path ~ blacklist ) then { + print "Reject: blacklisted ASN|", bgp_path; + reject; + } +} + +function honor_graceful_shutdown() { + if (65535, 0) ~ bgp_community then { + bgp_local_pref = 0; + } +} + +function dn_import_filter(int link_latency; int link_bandwidth; int link_crypto) { + #if ( net.type != NET_IP6 ) then { + # print "Reject: non-IPv6 on IPv6 Channel|", net, "|", bgp_path; + # reject; + #} + + if ( ! is_valid_network() ) then { + print "Reject: invalid network|", net, "|", bgp_path; + reject; + } + + if ( is_self_net() ) then { + print "Reject: export our network|", net, "|", bgp_path.first; + reject; + } + + if ( bgp_path.len > 25 ) then { + print "Reject: AS path too long|", net, "|", bgp_path; + reject; + } + + reject_blacklisted(); + reject_invalid_roa(); + reject_default_route(); + + if (bgp_path.len = 1) then + bgp_local_pref = bgp_local_pref + 500; + + update_flags(link_latency, link_bandwidth, link_crypto); + + accept; +} + +function dn_export_filter(int link_latency; int link_bandwidth; int link_crypto) { + if (source !~ [RTS_STATIC, RTS_BGP]) then + reject; + + if (bgp_path.last != bgp_path.first) then + reject; + + reject_default_route(); + + update_flags(link_latency, link_bandwidth, link_crypto); + update_region(); + + bgp_med = 0; + bgp_med = bgp_med + ( ( 4 - ( link_crypto - 30 ) ) * 600 ); + bgp_med = bgp_med + ( ( 9 - ( link_bandwidth - 20 ) ) * 100); + bgp_med = bgp_med + ( ( link_latency - 1) * 300); + + accept; +} + +function dn_export_collector() { + if (source !~ [RTS_STATIC, RTS_BGP]) then + reject; + + update_region(); + accept; +} +