1 { config, lib, pkgs, ... }:
7 cfg = config.services.frr;
28 allServices = services ++ [ "zebra" ];
30 isEnabled = service: cfg.${service}.enable;
32 daemonName = service: if service == "zebra" then service else "${service}d";
36 scfg = cfg.${service};
38 if scfg.configFile != null then scfg.configFile
39 else pkgs.writeText "${daemonName service}.conf"
41 ! FRR ${daemonName service} configuration
43 hostname ${config.networking.hostName}
45 service password-encryption
52 serviceOptions = service:
54 enable = mkEnableOption (lib.mdDoc "the FRR ${toUpper service} routing protocol");
56 configFile = mkOption {
57 type = types.nullOr types.path;
59 example = "/etc/frr/${daemonName service}.conf";
60 description = lib.mdDoc ''
61 Configuration file to use for FRR ${daemonName service}.
62 By default the NixOS generated files are used.
79 network 10.0.0.0/8 area 0
84 neighbor 10.0.0.1 remote-as 65001
88 examples.${service} or "";
89 description = lib.mdDoc ''
90 ${daemonName service} configuration statements.
94 vtyListenAddress = mkOption {
96 default = "localhost";
97 description = lib.mdDoc ''
98 Address to bind to for the VTY interface.
102 vtyListenPort = mkOption {
103 type = types.nullOr types.int;
105 description = lib.mdDoc ''
106 TCP Port to bind to for the VTY interface.
110 extraOptions = mkOption {
111 type = types.listOf types.str;
113 description = lib.mdDoc ''
114 Extra options for the daemon.
126 options.services.frr = {
127 zebra = (serviceOptions "zebra") // {
130 default = any isEnabled services;
131 description = lib.mdDoc ''
132 Whether to enable the Zebra routing manager.
134 The Zebra routing manager is automatically enabled
135 if any routing protocols are configured.
141 { options.services.frr = (genAttrs services serviceOptions); }
144 ###### implementation
146 config = mkIf (any isEnabled allServices) {
148 environment.systemPackages = [
149 pkgs.frr # for the vtysh tool
153 description = "FRR daemon user";
160 # Members of the frrvty group can use vtysh to inspect the FRR daemons
161 frrvty = { members = [ "frr" ]; };
164 environment.etc = let
165 mkEtcLink = service: {
166 name = "frr/${service}.conf";
167 value.source = configFile service;
170 (builtins.listToAttrs
171 (map mkEtcLink (filter isEnabled allServices))) // {
172 "frr/vtysh.conf".text = "";
175 systemd.tmpfiles.rules = [
176 "d /run/frr 0750 frr frr -"
181 frrService = service:
183 scfg = cfg.${service};
184 daemon = daemonName service;
186 nameValuePair daemon ({
187 wantedBy = [ "multi-user.target" ];
188 after = [ "network-pre.target" "systemd-sysctl.service" ] ++ lib.optionals (service != "zebra") [ "zebra.service" ];
189 bindsTo = lib.optionals (service != "zebra") [ "zebra.service" ];
190 wants = [ "network.target" ];
192 description = if service == "zebra" then "FRR Zebra routing manager"
193 else "FRR ${toUpper service} routing daemon";
195 unitConfig.Documentation = if service == "zebra" then "man:zebra(8)"
196 else "man:${daemon}(8) man:zebra(8)";
201 reloadIfChanged = true;
204 PIDFile = "frr/${daemon}.pid";
205 ExecStart = "${pkgs.frr}/libexec/frr/${daemon} -f /etc/frr/${service}.conf"
206 + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
207 + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"
208 + " " + (concatStringsSep " " scfg.extraOptions);
209 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";
210 Restart = "on-abnormal";
214 listToAttrs (map frrService (filter isEnabled allServices));
218 meta.maintainers = with lib.maintainers; [ woffs ];