2022-01-13 20:06:32 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
|
|
|
|
|
|
cfg = config.services.frr;
|
|
|
|
|
|
|
|
services = [
|
|
|
|
"static"
|
|
|
|
"bgp"
|
|
|
|
"ospf"
|
|
|
|
"ospf6"
|
|
|
|
"rip"
|
|
|
|
"ripng"
|
|
|
|
"isis"
|
|
|
|
"pim"
|
|
|
|
"ldp"
|
|
|
|
"nhrp"
|
|
|
|
"eigrp"
|
|
|
|
"babel"
|
|
|
|
"sharp"
|
|
|
|
"pbr"
|
|
|
|
"bfd"
|
|
|
|
"fabric"
|
|
|
|
];
|
|
|
|
|
2024-06-05 15:53:02 +00:00
|
|
|
allServices = services ++ [ "zebra" "mgmt" ];
|
2022-01-13 20:06:32 +00:00
|
|
|
|
|
|
|
isEnabled = service: cfg.${service}.enable;
|
|
|
|
|
|
|
|
daemonName = service: if service == "zebra" then service else "${service}d";
|
|
|
|
|
|
|
|
configFile = service:
|
|
|
|
let
|
|
|
|
scfg = cfg.${service};
|
|
|
|
in
|
|
|
|
if scfg.configFile != null then scfg.configFile
|
|
|
|
else pkgs.writeText "${daemonName service}.conf"
|
|
|
|
''
|
|
|
|
! FRR ${daemonName service} configuration
|
|
|
|
!
|
|
|
|
hostname ${config.networking.hostName}
|
|
|
|
log syslog
|
|
|
|
service password-encryption
|
|
|
|
!
|
|
|
|
${scfg.config}
|
|
|
|
!
|
|
|
|
end
|
|
|
|
'';
|
|
|
|
|
|
|
|
serviceOptions = service:
|
|
|
|
{
|
2024-09-19 14:19:46 +00:00
|
|
|
enable = lib.mkEnableOption "the FRR ${lib.toUpper service} routing protocol";
|
2022-01-13 20:06:32 +00:00
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
configFile = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
2022-01-13 20:06:32 +00:00
|
|
|
default = null;
|
|
|
|
example = "/etc/frr/${daemonName service}.conf";
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-01-13 20:06:32 +00:00
|
|
|
Configuration file to use for FRR ${daemonName service}.
|
|
|
|
By default the NixOS generated files are used.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
config = lib.mkOption {
|
|
|
|
type = lib.types.lines;
|
2022-01-13 20:06:32 +00:00
|
|
|
default = "";
|
|
|
|
example =
|
|
|
|
let
|
|
|
|
examples = {
|
|
|
|
rip = ''
|
|
|
|
router rip
|
|
|
|
network 10.0.0.0/8
|
|
|
|
'';
|
|
|
|
|
|
|
|
ospf = ''
|
|
|
|
router ospf
|
|
|
|
network 10.0.0.0/8 area 0
|
|
|
|
'';
|
|
|
|
|
|
|
|
bgp = ''
|
|
|
|
router bgp 65001
|
|
|
|
neighbor 10.0.0.1 remote-as 65001
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
in
|
|
|
|
examples.${service} or "";
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-01-13 20:06:32 +00:00
|
|
|
${daemonName service} configuration statements.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
vtyListenAddress = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
2022-01-13 20:06:32 +00:00
|
|
|
default = "localhost";
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-01-13 20:06:32 +00:00
|
|
|
Address to bind to for the VTY interface.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
vtyListenPort = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.int;
|
2022-01-13 20:06:32 +00:00
|
|
|
default = null;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-01-13 20:06:32 +00:00
|
|
|
TCP Port to bind to for the VTY interface.
|
|
|
|
'';
|
|
|
|
};
|
2022-06-16 17:23:12 +00:00
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
extraOptions = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
2022-06-16 17:23:12 +00:00
|
|
|
default = [];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-06-16 17:23:12 +00:00
|
|
|
Extra options for the daemon.
|
|
|
|
'';
|
|
|
|
};
|
2022-01-13 20:06:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
###### interface
|
|
|
|
imports = [
|
|
|
|
{
|
|
|
|
options.services.frr = {
|
|
|
|
zebra = (serviceOptions "zebra") // {
|
2024-09-19 14:19:46 +00:00
|
|
|
enable = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
|
|
|
default = lib.any isEnabled services;
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2022-01-13 20:06:32 +00:00
|
|
|
Whether to enable the Zebra routing manager.
|
|
|
|
|
|
|
|
The Zebra routing manager is automatically enabled
|
|
|
|
if any routing protocols are configured.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
2024-06-05 15:53:02 +00:00
|
|
|
mgmt = (serviceOptions "mgmt") // {
|
2024-09-19 14:19:46 +00:00
|
|
|
enable = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
2024-06-05 15:53:02 +00:00
|
|
|
default = isEnabled "static";
|
|
|
|
defaultText = lib.literalExpression "config.services.frr.static.enable";
|
|
|
|
description = ''
|
|
|
|
Whether to enable the Configuration management daemon.
|
|
|
|
|
|
|
|
The Configuration management daemon is automatically
|
|
|
|
enabled if needed, at the moment this is when staticd
|
|
|
|
is enabled.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
2022-01-13 20:06:32 +00:00
|
|
|
};
|
|
|
|
}
|
2024-09-19 14:19:46 +00:00
|
|
|
{ options.services.frr = (lib.genAttrs services serviceOptions); }
|
2022-01-13 20:06:32 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
###### implementation
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
config = lib.mkIf (lib.any isEnabled allServices) {
|
2022-01-13 20:06:32 +00:00
|
|
|
|
|
|
|
environment.systemPackages = [
|
|
|
|
pkgs.frr # for the vtysh tool
|
|
|
|
];
|
|
|
|
|
|
|
|
users.users.frr = {
|
|
|
|
description = "FRR daemon user";
|
|
|
|
isSystemUser = true;
|
|
|
|
group = "frr";
|
|
|
|
};
|
|
|
|
|
|
|
|
users.groups = {
|
|
|
|
frr = {};
|
|
|
|
# Members of the frrvty group can use vtysh to inspect the FRR daemons
|
|
|
|
frrvty = { members = [ "frr" ]; };
|
|
|
|
};
|
|
|
|
|
|
|
|
environment.etc = let
|
|
|
|
mkEtcLink = service: {
|
2024-06-05 15:53:02 +00:00
|
|
|
name = "frr/${daemonName service}.conf";
|
2022-01-13 20:06:32 +00:00
|
|
|
value.source = configFile service;
|
|
|
|
};
|
|
|
|
in
|
|
|
|
(builtins.listToAttrs
|
2024-09-19 14:19:46 +00:00
|
|
|
(map mkEtcLink (lib.filter isEnabled allServices))) // {
|
2022-01-13 20:06:32 +00:00
|
|
|
"frr/vtysh.conf".text = "";
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.tmpfiles.rules = [
|
|
|
|
"d /run/frr 0750 frr frr -"
|
|
|
|
];
|
|
|
|
|
|
|
|
systemd.services =
|
|
|
|
let
|
|
|
|
frrService = service:
|
|
|
|
let
|
|
|
|
scfg = cfg.${service};
|
|
|
|
daemon = daemonName service;
|
|
|
|
in
|
2024-09-19 14:19:46 +00:00
|
|
|
lib.nameValuePair daemon ({
|
2022-01-13 20:06:32 +00:00
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "network-pre.target" "systemd-sysctl.service" ] ++ lib.optionals (service != "zebra") [ "zebra.service" ];
|
|
|
|
bindsTo = lib.optionals (service != "zebra") [ "zebra.service" ];
|
|
|
|
wants = [ "network.target" ];
|
|
|
|
|
|
|
|
description = if service == "zebra" then "FRR Zebra routing manager"
|
2024-09-19 14:19:46 +00:00
|
|
|
else "FRR ${lib.toUpper service} routing daemon";
|
2022-01-13 20:06:32 +00:00
|
|
|
|
|
|
|
unitConfig.Documentation = if service == "zebra" then "man:zebra(8)"
|
|
|
|
else "man:${daemon}(8) man:zebra(8)";
|
|
|
|
|
2024-09-19 14:19:46 +00:00
|
|
|
restartTriggers = lib.mkIf (service != "mgmt") [
|
2022-01-13 20:06:32 +00:00
|
|
|
(configFile service)
|
|
|
|
];
|
2024-06-05 15:53:02 +00:00
|
|
|
reloadIfChanged = (service != "mgmt");
|
2022-01-13 20:06:32 +00:00
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
PIDFile = "frr/${daemon}.pid";
|
2024-06-05 15:53:02 +00:00
|
|
|
ExecStart = "${pkgs.frr}/libexec/frr/${daemon}"
|
2024-09-19 14:19:46 +00:00
|
|
|
+ lib.optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
|
|
|
|
+ lib.optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"
|
|
|
|
+ " " + (lib.concatStringsSep " " scfg.extraOptions);
|
|
|
|
ExecReload = lib.mkIf (service != "mgmt") "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemon} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${daemon}.conf";
|
2022-01-13 20:06:32 +00:00
|
|
|
Restart = "on-abnormal";
|
|
|
|
};
|
|
|
|
});
|
|
|
|
in
|
2024-09-19 14:19:46 +00:00
|
|
|
lib.listToAttrs (map frrService (lib.filter isEnabled allServices));
|
2022-01-13 20:06:32 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
meta.maintainers = with lib.maintainers; [ woffs ];
|
|
|
|
|
|
|
|
}
|