320 lines
10 KiB
Nix
320 lines
10 KiB
Nix
{ 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}}"
|
|
''
|