at 24.11-pre 5.7 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.frr; 8 9 services = [ 10 "static" 11 "bgp" 12 "ospf" 13 "ospf6" 14 "rip" 15 "ripng" 16 "isis" 17 "pim" 18 "ldp" 19 "nhrp" 20 "eigrp" 21 "babel" 22 "sharp" 23 "pbr" 24 "bfd" 25 "fabric" 26 "mgmt" 27 ]; 28 29 allServices = services ++ [ "zebra" ]; 30 31 isEnabled = service: cfg.${service}.enable; 32 33 daemonName = service: if service == "zebra" then service else "${service}d"; 34 35 configFile = service: 36 let 37 scfg = cfg.${service}; 38 in 39 if scfg.configFile != null then scfg.configFile 40 else pkgs.writeText "${daemonName service}.conf" 41 '' 42 ! FRR ${daemonName service} configuration 43 ! 44 hostname ${config.networking.hostName} 45 log syslog 46 service password-encryption 47 ! 48 ${scfg.config} 49 ! 50 end 51 ''; 52 53 serviceOptions = service: 54 { 55 enable = mkEnableOption "the FRR ${toUpper service} routing protocol"; 56 57 configFile = mkOption { 58 type = types.nullOr types.path; 59 default = null; 60 example = "/etc/frr/${daemonName service}.conf"; 61 description = '' 62 Configuration file to use for FRR ${daemonName service}. 63 By default the NixOS generated files are used. 64 ''; 65 }; 66 67 config = mkOption { 68 type = types.lines; 69 default = ""; 70 example = 71 let 72 examples = { 73 rip = '' 74 router rip 75 network 10.0.0.0/8 76 ''; 77 78 ospf = '' 79 router ospf 80 network 10.0.0.0/8 area 0 81 ''; 82 83 bgp = '' 84 router bgp 65001 85 neighbor 10.0.0.1 remote-as 65001 86 ''; 87 }; 88 in 89 examples.${service} or ""; 90 description = '' 91 ${daemonName service} configuration statements. 92 ''; 93 }; 94 95 vtyListenAddress = mkOption { 96 type = types.str; 97 default = "localhost"; 98 description = '' 99 Address to bind to for the VTY interface. 100 ''; 101 }; 102 103 vtyListenPort = mkOption { 104 type = types.nullOr types.int; 105 default = null; 106 description = '' 107 TCP Port to bind to for the VTY interface. 108 ''; 109 }; 110 111 extraOptions = mkOption { 112 type = types.listOf types.str; 113 default = []; 114 description = '' 115 Extra options for the daemon. 116 ''; 117 }; 118 }; 119 120in 121 122{ 123 124 ###### interface 125 imports = [ 126 { 127 options.services.frr = { 128 zebra = (serviceOptions "zebra") // { 129 enable = mkOption { 130 type = types.bool; 131 default = any isEnabled services; 132 description = '' 133 Whether to enable the Zebra routing manager. 134 135 The Zebra routing manager is automatically enabled 136 if any routing protocols are configured. 137 ''; 138 }; 139 }; 140 }; 141 } 142 { options.services.frr = (genAttrs services serviceOptions); } 143 ]; 144 145 ###### implementation 146 147 config = mkIf (any isEnabled allServices) { 148 149 environment.systemPackages = [ 150 pkgs.frr # for the vtysh tool 151 ]; 152 153 users.users.frr = { 154 description = "FRR daemon user"; 155 isSystemUser = true; 156 group = "frr"; 157 }; 158 159 users.groups = { 160 frr = {}; 161 # Members of the frrvty group can use vtysh to inspect the FRR daemons 162 frrvty = { members = [ "frr" ]; }; 163 }; 164 165 environment.etc = let 166 mkEtcLink = service: { 167 name = "frr/${service}.conf"; 168 value.source = configFile service; 169 }; 170 in 171 (builtins.listToAttrs 172 (map mkEtcLink (filter isEnabled allServices))) // { 173 "frr/vtysh.conf".text = ""; 174 }; 175 176 systemd.tmpfiles.rules = [ 177 "d /run/frr 0750 frr frr -" 178 ]; 179 180 systemd.services = 181 let 182 frrService = service: 183 let 184 scfg = cfg.${service}; 185 daemon = daemonName service; 186 in 187 nameValuePair daemon ({ 188 wantedBy = [ "multi-user.target" ]; 189 after = [ "network-pre.target" "systemd-sysctl.service" ] ++ lib.optionals (service != "zebra") [ "zebra.service" ]; 190 bindsTo = lib.optionals (service != "zebra") [ "zebra.service" ]; 191 wants = [ "network.target" ]; 192 193 description = if service == "zebra" then "FRR Zebra routing manager" 194 else "FRR ${toUpper service} routing daemon"; 195 196 unitConfig.Documentation = if service == "zebra" then "man:zebra(8)" 197 else "man:${daemon}(8) man:zebra(8)"; 198 199 restartTriggers = [ 200 (configFile service) 201 ]; 202 reloadIfChanged = true; 203 204 serviceConfig = { 205 PIDFile = "frr/${daemon}.pid"; 206 ExecStart = "${pkgs.frr}/libexec/frr/${daemon} -f /etc/frr/${service}.conf" 207 + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}" 208 + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}" 209 + " " + (concatStringsSep " " scfg.extraOptions); 210 ExecReload = "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemonName service} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${service}.conf"; 211 Restart = "on-abnormal"; 212 }; 213 }); 214 in 215 listToAttrs (map frrService (filter isEnabled allServices)); 216 217 }; 218 219 meta.maintainers = with lib.maintainers; [ woffs ]; 220 221}