1 { config, lib, pkgs, ... }:
6 cfg = config.networking.ucarp;
8 ucarpExec = concatStringsSep " " (
10 "${cfg.package}/bin/ucarp"
11 "--interface=${cfg.interface}"
12 "--srcip=${cfg.srcIp}"
13 "--vhid=${toString cfg.vhId}"
14 "--passfile=${cfg.passwordFile}"
16 "--advbase=${toString cfg.advBase}"
17 "--advskew=${toString cfg.advSkew}"
18 "--upscript=${cfg.upscript}"
19 "--downscript=${cfg.downscript}"
20 "--deadratio=${toString cfg.deadratio}"
22 ++ (optional cfg.preempt "--preempt")
23 ++ (optional cfg.neutral "--neutral")
24 ++ (optional cfg.shutdown "--shutdown")
25 ++ (optional cfg.ignoreIfState "--ignoreifstate")
26 ++ (optional cfg.noMcast "--nomcast")
27 ++ (optional (cfg.extraParam != null) "--xparam=${cfg.extraParam}")
30 options.networking.ucarp = {
31 enable = mkEnableOption (lib.mdDoc "ucarp, userspace implementation of CARP");
33 interface = mkOption {
35 description = lib.mdDoc "Network interface to bind to.";
41 description = lib.mdDoc "Source (real) IP address of this host.";
45 type = types.ints.between 1 255;
46 description = lib.mdDoc "Virtual IP identifier shared between CARP hosts.";
50 passwordFile = mkOption {
52 description = lib.mdDoc "File containing shared password between CARP hosts.";
53 example = "/run/keys/ucarp-password";
58 description = lib.mdDoc ''
59 Enable preemptive failover.
60 Thus, this host becomes the CARP master as soon as possible.
67 description = lib.mdDoc "Do not run downscript at start if the host is the backup.";
73 description = lib.mdDoc "Virtual shared IP address.";
77 type = types.ints.unsigned;
78 description = lib.mdDoc "Advertisement frequency in seconds.";
83 type = types.ints.unsigned;
84 description = lib.mdDoc "Advertisement skew in seconds.";
90 description = lib.mdDoc ''
91 Command to run after become master, the interface name, virtual address
92 and optional extra parameters are passed as arguments.
94 example = literalExpression ''
95 pkgs.writeScript "upscript" '''
97 ''${pkgs.iproute2}/bin/ip addr add "$2"/24 dev "$1"
102 downscript = mkOption {
104 description = lib.mdDoc ''
105 Command to run after become backup, the interface name, virtual address
106 and optional extra parameters are passed as arguments.
108 example = literalExpression ''
109 pkgs.writeScript "downscript" '''
111 ''${pkgs.iproute2}/bin/ip addr del "$2"/24 dev "$1"
116 deadratio = mkOption {
117 type = types.ints.unsigned;
118 description = lib.mdDoc "Ratio to consider a host as dead.";
122 shutdown = mkOption {
124 description = lib.mdDoc "Call downscript at exit.";
128 ignoreIfState = mkOption {
130 description = lib.mdDoc "Ignore interface state, e.g., down or no carrier.";
136 description = lib.mdDoc "Use broadcast instead of multicast advertisements.";
140 extraParam = mkOption {
141 type = types.nullOr types.str;
142 description = lib.mdDoc "Extra parameter to pass to the up/down scripts.";
147 type = types.package;
148 description = lib.mdDoc ''
149 Package that should be used for ucarp.
151 Please note that the default package, pkgs.ucarp, has not received any
152 upstream updates for a long time and can be considered as unmaintained.
154 default = pkgs.ucarp;
155 defaultText = literalExpression "pkgs.ucarp";
159 config = mkIf cfg.enable {
160 systemd.services.ucarp = {
161 description = "ucarp, userspace implementation of CARP";
163 wantedBy = [ "multi-user.target" ];
164 after = [ "network.target" ];
168 ExecStart = ucarpExec;
170 ProtectSystem = "strict";
174 ProtectKernelModules = true;
175 ProtectControlGroups = true;
176 MemoryDenyWriteExecute = true;
177 RestrictRealtime = true;
182 meta.maintainers = with lib.maintainers; [ oxzi ];