at 25.11-pre 6.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.ndppd; 12 13 render = s: f: concatStringsSep "\n" (mapAttrsToList f s); 14 prefer = a: b: if a != null then a else b; 15 16 ndppdConf = prefer cfg.configFile ( 17 pkgs.writeText "ndppd.conf" '' 18 route-ttl ${toString cfg.routeTTL} 19 ${render cfg.proxies ( 20 proxyInterfaceName: proxy: '' 21 proxy ${prefer proxy.interface proxyInterfaceName} { 22 router ${boolToString proxy.router} 23 timeout ${toString proxy.timeout} 24 ttl ${toString proxy.ttl} 25 ${render proxy.rules ( 26 ruleNetworkName: rule: '' 27 rule ${prefer rule.network ruleNetworkName} { 28 ${rule.method}${optionalString (rule.method == "iface") " ${rule.interface}"} 29 }'' 30 )} 31 }'' 32 )} 33 '' 34 ); 35 36 proxy = types.submodule { 37 options = { 38 interface = mkOption { 39 type = types.nullOr types.str; 40 description = '' 41 Listen for any Neighbor Solicitation messages on this interface, 42 and respond to them according to a set of rules. 43 Defaults to the name of the attrset. 44 ''; 45 default = null; 46 }; 47 router = mkOption { 48 type = types.bool; 49 description = '' 50 Turns on or off the router flag for Neighbor Advertisement Messages. 51 ''; 52 default = true; 53 }; 54 timeout = mkOption { 55 type = types.int; 56 description = '' 57 Controls how long to wait for a Neighbor Advertisement Message before 58 invalidating the entry, in milliseconds. 59 ''; 60 default = 500; 61 }; 62 ttl = mkOption { 63 type = types.int; 64 description = '' 65 Controls how long a valid or invalid entry remains in the cache, in 66 milliseconds. 67 ''; 68 default = 30000; 69 }; 70 rules = mkOption { 71 type = types.attrsOf rule; 72 description = '' 73 This is a rule that the target address is to match against. If no netmask 74 is provided, /128 is assumed. You may have several rule sections, and the 75 addresses may or may not overlap. 76 ''; 77 default = { }; 78 }; 79 }; 80 }; 81 82 rule = types.submodule { 83 options = { 84 network = mkOption { 85 type = types.nullOr types.str; 86 description = '' 87 This is the target address is to match against. If no netmask 88 is provided, /128 is assumed. The addresses of several rules 89 may or may not overlap. 90 Defaults to the name of the attrset. 91 ''; 92 default = null; 93 }; 94 method = mkOption { 95 type = types.enum [ 96 "static" 97 "iface" 98 "auto" 99 ]; 100 description = '' 101 static: Immediately answer any Neighbor Solicitation Messages 102 (if they match the IP rule). 103 iface: Forward the Neighbor Solicitation Message through the specified 104 interface and only respond if a matching Neighbor Advertisement 105 Message is received. 106 auto: Same as iface, but instead of manually specifying the outgoing 107 interface, check for a matching route in /proc/net/ipv6_route. 108 ''; 109 default = "auto"; 110 }; 111 interface = mkOption { 112 type = types.nullOr types.str; 113 description = "Interface to use when method is iface."; 114 default = null; 115 }; 116 }; 117 }; 118 119in 120{ 121 options.services.ndppd = { 122 enable = mkEnableOption "daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces"; 123 interface = mkOption { 124 type = types.nullOr types.str; 125 description = '' 126 Interface which is on link-level with router. 127 (Legacy option, use services.ndppd.proxies.\<interface\>.rules.\<network\> instead) 128 ''; 129 default = null; 130 example = "eth0"; 131 }; 132 network = mkOption { 133 type = types.nullOr types.str; 134 description = '' 135 Network that we proxy. 136 (Legacy option, use services.ndppd.proxies.\<interface\>.rules.\<network\> instead) 137 ''; 138 default = null; 139 example = "1111::/64"; 140 }; 141 configFile = mkOption { 142 type = types.nullOr types.path; 143 description = "Path to configuration file."; 144 default = null; 145 }; 146 routeTTL = mkOption { 147 type = types.int; 148 description = '' 149 This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route, 150 in milliseconds. 151 ''; 152 default = 30000; 153 }; 154 proxies = mkOption { 155 type = types.attrsOf proxy; 156 description = '' 157 This sets up a listener, that will listen for any Neighbor Solicitation 158 messages, and respond to them according to a set of rules. 159 ''; 160 default = { }; 161 example = literalExpression '' 162 { 163 eth0.rules."1111::/64" = {}; 164 } 165 ''; 166 }; 167 }; 168 169 config = mkIf cfg.enable { 170 warnings = mkIf (cfg.interface != null && cfg.network != null) [ 171 '' 172 The options services.ndppd.interface and services.ndppd.network will probably be removed soon, 173 please use services.ndppd.proxies.<interface>.rules.<network> instead. 174 '' 175 ]; 176 177 services.ndppd.proxies = mkIf (cfg.interface != null && cfg.network != null) { 178 ${cfg.interface}.rules.${cfg.network} = { }; 179 }; 180 181 systemd.services.ndppd = { 182 description = "NDP Proxy Daemon"; 183 documentation = [ 184 "man:ndppd(1)" 185 "man:ndppd.conf(5)" 186 ]; 187 after = [ "network-pre.target" ]; 188 wantedBy = [ "multi-user.target" ]; 189 serviceConfig = { 190 ExecStart = "${pkgs.ndppd}/bin/ndppd -c ${ndppdConf}"; 191 192 # Sandboxing 193 CapabilityBoundingSet = "CAP_NET_RAW CAP_NET_ADMIN"; 194 ProtectSystem = "strict"; 195 ProtectHome = true; 196 PrivateTmp = true; 197 PrivateDevices = true; 198 ProtectKernelTunables = true; 199 ProtectKernelModules = true; 200 ProtectControlGroups = true; 201 RestrictAddressFamilies = "AF_INET6 AF_PACKET AF_NETLINK"; 202 RestrictNamespaces = true; 203 LockPersonality = true; 204 MemoryDenyWriteExecute = true; 205 RestrictRealtime = true; 206 RestrictSUIDSGID = true; 207 }; 208 }; 209 }; 210}