1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.quagga;
8
9 services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ];
10 allServices = services ++ [ "zebra" ];
11
12 isEnabled = service: cfg.${service}.enable;
13
14 daemonName = service: if service == "zebra" then service else "${service}d";
15
16 configFile = service:
17 let
18 scfg = cfg.${service};
19 in
20 if scfg.configFile != null then scfg.configFile
21 else pkgs.writeText "${daemonName service}.conf"
22 ''
23 ! Quagga ${daemonName service} configuration
24 !
25 hostname ${config.networking.hostName}
26 log syslog
27 service password-encryption
28 !
29 ${scfg.config}
30 !
31 end
32 '';
33
34 serviceOptions = service:
35 {
36 enable = mkEnableOption "the Quagga ${toUpper service} routing protocol";
37
38 configFile = mkOption {
39 type = types.nullOr types.path;
40 default = null;
41 example = "/etc/quagga/${daemonName service}.conf";
42 description = ''
43 Configuration file to use for Quagga ${daemonName service}.
44 By default the NixOS generated files are used.
45 '';
46 };
47
48 config = mkOption {
49 type = types.lines;
50 default = "";
51 example =
52 let
53 examples = {
54 rip = ''
55 router rip
56 network 10.0.0.0/8
57 '';
58
59 ospf = ''
60 router ospf
61 network 10.0.0.0/8 area 0
62 '';
63
64 bgp = ''
65 router bgp 65001
66 neighbor 10.0.0.1 remote-as 65001
67 '';
68 };
69 in
70 examples.${service} or "";
71 description = ''
72 ${daemonName service} configuration statements.
73 '';
74 };
75
76 vtyListenAddress = mkOption {
77 type = types.str;
78 default = "127.0.0.1";
79 description = ''
80 Address to bind to for the VTY interface.
81 '';
82 };
83
84 vtyListenPort = mkOption {
85 type = types.nullOr types.int;
86 default = null;
87 description = ''
88 TCP Port to bind to for the VTY interface.
89 '';
90 };
91 };
92
93in
94
95{
96
97 ###### interface
98
99 options.services.quagga =
100 {
101
102 zebra = (serviceOptions "zebra") // {
103
104 enable = mkOption {
105 type = types.bool;
106 default = any isEnabled services;
107 description = ''
108 Whether to enable the Zebra routing manager.
109
110 The Zebra routing manager is automatically enabled
111 if any routing protocols are configured.
112 '';
113 };
114
115 };
116
117 } // (genAttrs services serviceOptions);
118
119 ###### implementation
120
121 config = mkIf (any isEnabled allServices) {
122
123 environment.systemPackages = [
124 pkgs.quagga # for the vtysh tool
125 ];
126
127 users.users.quagga = {
128 description = "Quagga daemon user";
129 isSystemUser = true;
130 group = "quagga";
131 };
132
133 users.groups = {
134 quagga = {};
135 # Members of the quaggavty group can use vtysh to inspect the Quagga daemons
136 quaggavty = {};
137 };
138
139 systemd.services =
140 let
141 quaggaService = service:
142 let
143 scfg = cfg.${service};
144 daemon = daemonName service;
145 in
146 nameValuePair daemon ({
147 wantedBy = [ "multi-user.target" ];
148 restartTriggers = [ (configFile service) ];
149
150 serviceConfig = {
151 Type = "forking";
152 PIDFile = "/run/quagga/${daemon}.pid";
153 ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}"
154 + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
155 + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}";
156 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
157 Restart = "on-abort";
158 };
159 } // (
160 if service == "zebra" then
161 {
162 description = "Quagga Zebra routing manager";
163 unitConfig.Documentation = "man:zebra(8)";
164 after = [ "network.target" ];
165 preStart = ''
166 install -m 0755 -o quagga -g quagga -d /run/quagga
167
168 ${pkgs.iproute}/bin/ip route flush proto zebra
169 '';
170 }
171 else
172 {
173 description = "Quagga ${toUpper service} routing daemon";
174 unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)";
175 bindsTo = [ "zebra.service" ];
176 after = [ "network.target" "zebra.service" ];
177 }
178 ));
179 in
180 listToAttrs (map quaggaService (filter isEnabled allServices));
181
182 };
183
184 meta.maintainers = with lib.maintainers; [ tavyc ];
185
186}