{ self, pkgs, hostName, config, hostConfig, ... }: with pkgs; with lib; let ports = self.lib.getOpenwrtPorts hostConfig.model; uciDeleteAll = key: '' while uci -q delete ${key}[-1]; do :; done ''; # hostBridgedNets = # self.lib.unique ( # builtins.concatMap ({ ssids, ... }: # map ({ net, ... }: # net # ) (builtins.attrValues ssids) # ) (builtins.attrValues hostConfig.wifi) # ); openwrtModel = self.lib.getOpenwrtModel hostConfig.model; hasSwitch = any ({ switch ? null, ... }: switch != null) (builtins.attrValues openwrtModel.ports); portsDoc = let portByIndex = builtins.foldl' (result: port: let key = if port ? index then port.index else if port ? interface then port.interface else "How to identify port ${lib.generators.toPretty {} port}?"; in result // { "${key}" = port; } ) {} (builtins.attrValues openwrtModel.ports); in concatMapStringsSep ", " (index: "${index}:${ if portByIndex.${index} ? port then portByIndex.${index}.port else if portByIndex.${index} ? interface then portByIndex.${index}.interface else throw "What is port ${lib.generators.toPretty {} portByIndex.${index}.port}?" }" ) ( builtins.sort builtins.lessThan ( builtins.attrNames portByIndex ) ); switchHostInterface = let hostPorts = sort builtins.lessThan ( map ({ interface, ... }: interface) ( builtins.attrValues ( filterAttrs (_: { type, ... }: type == "host") openwrtModel.ports ) ) ); in if hostPorts == [] then throw "No host ports found for OpenWRT model ${hostConfig.model}" else builtins.head hostPorts; switchPortIndices = f: map ({ index, ... }: index) ( builtins.attrValues ( filterAttrs (_: port: port ? index && f port) openwrtModel.ports ) ); trunked = map (index: "${index}t"); # OpenWRT switch ports string ("0t 1t 2 3 4") for a network (VLAN) switchPortsConfig = net: concatStringsSep " " ( # Host interface trunked (switchPortIndices ({ interface ? null, ... }: interface == switchHostInterface)) ++ # Access networks optionals (hostConfig.links ? ${net}) ( builtins.concatMap (port': switchPortIndices ({ port ? null, ... }: port == port') ) hostConfig.links.${net}.ports ) ++ # Trunk ports builtins.concatMap (port': trunked (switchPortIndices ({ port ? null, ... }: port == port')) ) ( builtins.concatMap ({ ports, ... }: ports) ( builtins.attrValues ( filterAttrs (_: { trunk, ... }: trunk ) hostConfig.links )) ) ); networkInterfaces = net: let inherit (config.site.net.${net}) vlan; ifaces = unique ( builtins.concatMap ({ trunk, ports, switch ? null, ... }: builtins.concatMap (port: builtins.concatMap (portData: if portData ? port && port == portData.port then [ (( if portData ? switch then switchHostInterface else if portData ? interface then portData.interface else throw "Cannot find interface for ${port} on OpenWRT model ${hostConfig.model}" ) + ( if trunk || switch != null then ".${toString vlan}" else "" )) ] else [] ) (builtins.attrValues openwrtModel.ports) ) ports ) ( builtins.attrValues ( filterAttrs (link: { nets, ... }: link == net || builtins.elem net nets ) hostConfig.links ) ) ); in if ifaces == [] then throw "No interfaces found for ${net} on ${hostName}" else ifaces; in '' #! ${pkgs.runtimeShell} -e ${if hostConfig.firstboot then '' ssh-keygen -R 192.168.1.1 ssh root@192.168.1.1 \ "ash -e -x" <<__SSH__ '' else '' ssh root@${config.site.net.mgmt.hosts4.${hostName}} \ "ash -e -x" <<__SSH__ ''} # Set root password echo -e '${hostConfig.password}\n${hostConfig.password}' | passwd # add ssh pubkeys ${concatMapStrings (sshPubKey: '' echo "${sshPubKey}" > /etc/dropbear/authorized_keys '') config.site.sshPubKeys} # System configuration ${uciDeleteAll "network.@switch_vlan"} ${uciDeleteAll "wireless.@wifi"} uci set system.@system[0].hostname=${hostName} uci set dhcp.@dnsmasq[0].enabled=0 uci set system.@system[0].log_ip=${config.site.net.mgmt.hosts4.logging} uci set system.@system[0].log_proto=udp # Switch config ${optionalString hasSwitch '' # Ports ${portsDoc} ${concatMapStrings (net: '' uci add network switch_vlan uci set network.@switch_vlan[-1]=switch_vlan uci set network.@switch_vlan[-1].device='switch0' uci set network.@switch_vlan[-1].vlan='${toString config.site.net.${net}.vlan}' uci set network.@switch_vlan[-1].ports='${switchPortsConfig net}' uci set network.@switch_vlan[-1].comment='${net}' '') ( sort (net1: net2: config.site.net.${net1}.vlan < config.site.net.${net2}.vlan ) ( unique ( builtins.concatMap ({ nets, ... }: nets) (builtins.attrValues hostConfig.links) ) ) )} ''} # mgmt network uci set network.mgmt=interface uci set network.mgmt.ifname=${if builtins.length (networkInterfaces "mgmt") == 1 then builtins.head (networkInterfaces "mgmt") else throw "No interface for mgmt"} uci set network.mgmt.proto=static uci set network.mgmt.ipaddr=${config.site.net.mgmt.hosts4.${hostName}} uci set network.mgmt.netmask=${self.lib.netmasks.${elemAt ( builtins.split "/" config.site.net.mgmt.subnet4 ) 2}} uci set network.mgmt.gateway=${config.site.net.mgmt.hosts4.mgmt-gw} uci set network.mgmt.ip6addr=${config.site.net.mgmt.hosts6.dn42.${hostName}}/64 uci set network.mgmt.ip6gw=${config.site.net.mgmt.hosts6.dn42.mgmt-gw} uci -q delete network.mgmt.dns || true uci add_list network.mgmt.dns=${config.site.net.serv.hosts4.dnscache} uci add_list network.mgmt.dns=${config.site.net.serv.hosts6.dn42.dnscache} uci -q delete network.globals.ula_prefix || true # delete unused networks ${concatMapStrings (net: lib.optionalString (! hostConfig.interfaces ? ${net}) '' uci -q delete network.${net} || true '' ) ([ "lan" "wan" "wan6" ] ++ builtins.attrNames config.site.net)} # bridged networks ${concatMapStrings (net: let iface = hostConfig.interfaces.${net}; in optionalString (net != "mgmt" && iface.type == "bridge") '' uci set network.${net}=interface uci set network.${net}.type=bridge uci set network.${net}.proto=static uci set network.${net}.ifname='${concatStringsSep " " (networkInterfaces net)}' '') (builtins.attrNames hostConfig.interfaces) } uci -q delete wireless.default_radio0 || true uci -q delete wireless.default_radio1 || true ${concatStrings (imap0 (index: path: let radioConfig = hostConfig.wifi.${path}; ifPrefix = if radioConfig.channel < 15 then "wlan2" else "wlan5"; in '' uci set wireless.radio${toString index}=wifi-device uci set wireless.radio${toString index}.type=mac80211 uci set wireless.radio${toString index}.country=DE uci set wireless.radio${toString index}.channel=${toString radioConfig.channel} uci set wireless.radio${toString index}.path=${path} uci set wireless.radio${toString index}.htmode=${radioConfig.htmode} uci set wireless.radio${toString index}.noscan=1 uci -q delete wireless.radio${toString index}.disabled || true ${concatMapStrings (ssid: let ssidConfig = radioConfig.ssids.${ssid}; in '' uci add wireless wifi-iface uci set wireless.@wifi-iface[-1].ifname=${ifPrefix}-${ssidConfig.net} uci set wireless.@wifi-iface[-1].device=radio${toString index} uci set wireless.@wifi-iface[-1].ssid='${ssid}' uci set wireless.@wifi-iface[-1].mode=ap uci set wireless.@wifi-iface[-1].network=${ssidConfig.net} uci set wireless.@wifi-iface[-1].mcast_rate=18000 ${if (ssidConfig.psk != null) then '' uci set wireless.@wifi-iface[-1].encryption=psk2 uci set wireless.@wifi-iface[-1].key='${ssidConfig.psk}' '' else '' uci set wireless.@wifi-iface[-1].encryption=none uci -q delete wireless.@wifi-iface[-1].key || true ''} '' ) (builtins.attrNames radioConfig.ssids)} '') (builtins.attrNames hostConfig.wifi))} uci commit # Cronjob that makes sure WiFi is only visible when server with all # the gateways is reachable cat >/etc/crontabs/root <<__CRON__ * * * * * /usr/sbin/wifi-on-link.sh __CRON__ cat >/usr/sbin/wifi-on-link.sh <<__SH__ #!/bin/sh if (ping -c 1 -W 3 ${config.site.net.mgmt.hosts4.mgmt-gw}) ; then REACHABLE=y else REACHABLE=n fi if [ "\\\$(cat /sys/class/net/wlan2-pub/operstate)" == "up" ] ; then UP=y else UP=n fi if [ -e /sys/class/leds/blue:dome ] ; then ERROR_LED=/sys/class/leds/blue:dome/brightness [ \\\$REACHABLE = y ] && echo 0 > \\\$ERROR_LED [ \\\$REACHABLE = n ] && echo 1 > \\\$ERROR_LED fi [ \\\$REACHABLE = y ] && [ \\\$UP = n ] && wifi up [ \\\$REACHABLE = n ] && [ \\\$UP = y ] && wifi down exit 0 __SH__ chmod a+rx /usr/sbin/wifi-on-link.sh /etc/init.d/cron restart for svc in dnsmasq uhttpd ; do rm -f /etc/rc.d/*\$svc /etc/init.d/\$svc stop || true done ${lib.optionalString hostConfig.firstboot "reboot"} __SSH__ echo "Base configuration done \\o/" echo "Later run: ap_install_collectd.sh ${config.site.net.mgmt.hosts4.${hostName}}" ''