121 lines
No EOL
4.6 KiB
Nix
121 lines
No EOL
4.6 KiB
Nix
{ pkgs, lib, ... }:
|
|
let
|
|
ipcalcFn = input: command:
|
|
let
|
|
runCmd = pkgs.runCommand "ipcalc-${command}" {} "export `${pkgs.ipcalc}/bin/ipcalc ${input} --${command}`; echo \$${lib.toUpper command} > $out;";
|
|
in
|
|
lib.removeSuffix "\n" (builtins.readFile runCmd);
|
|
in
|
|
{
|
|
namespaceGlobalService = {
|
|
"ns.vmix@" = {
|
|
description = "network namespace %I for vmix";
|
|
before = [ "network.target" ];
|
|
path = with pkgs; [ iproute2 utillinux ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
PrivateNetwork = true;
|
|
ExecStart = (pkgs.writeShellScript "ns.vmix-start" ''
|
|
NAMESPACE="$1.vmix"
|
|
ip netns add $NAMESPACE
|
|
umount /var/run/netns/$NAMESPACE
|
|
mount --bind /proc/self/ns/net /var/run/netns/$NAMESPACE
|
|
'') + " %I";
|
|
ExecStop = "${pkgs.iproute2}/bin/ip netns del %I.vmix";
|
|
};
|
|
};
|
|
};
|
|
|
|
mkNetworkService = name: cfg:
|
|
let
|
|
netCfg = cfg // { inherit name; };
|
|
lanInterfaceName = "brx-${netCfg.name}";
|
|
lanInterfaceIPAddress = ipcalcFn netCfg.ipv4Range "minaddr";
|
|
hostIPAddressOnLan = ipcalcFn netCfg.ipv4Range "maxaddr";
|
|
networkAddress = ipcalcFn netCfg.ipv4Range "network";
|
|
netmask = ipcalcFn netCfg.ipv4Range "netmask";
|
|
networkPrefix = builtins.elemAt (lib.splitString "/" netCfg.ipv4Range) 1;
|
|
namespace = if netCfg.namespace != null then "${netCfg.namespace}.vmix" else "";
|
|
|
|
createLanInterface = pkgs.writeShellScript "create-lan-${netCfg.name}-vmix" ''
|
|
ip link add ${lanInterfaceName} type bridge
|
|
ip address add ${lanInterfaceIPAddress}/${networkPrefix} dev ${lanInterfaceName}
|
|
ip link set ${lanInterfaceName} up
|
|
'';
|
|
deleteLanInterface = pkgs.writeShellScript "delete-lan-${netCfg.name}-vmix" "ip link del ${lanInterfaceName}";
|
|
|
|
lanDomainName = "${netCfg.name}.vmix";
|
|
lanDnsmasqConf = pkgs.writeText "dnsmasq-${netCfg.name}.conf" ''
|
|
listen-address=${lanInterfaceIPAddress}
|
|
dhcp-range=${netCfg.dhcp.startAddress},${netCfg.dhcp.endAddress},${netmask},12h
|
|
dhcp-option=3,${hostIPAddressOnLan}
|
|
interface=${lanInterfaceName}
|
|
bind-interfaces
|
|
except-interface=lo
|
|
dhcp-authoritative
|
|
domain=${lanDomainName}
|
|
domain-needed
|
|
localise-queries
|
|
no-hosts
|
|
expand-hosts
|
|
dhcp-leasefile=/tmp/dhcp.leases
|
|
'' +
|
|
lib.concatStringsSep "\n" (lib.optionals (netCfg.dns.upstream != []) ([ "no-resolv" ] ++ (lib.map (dnsServer: "server ${dnsServer}") netCfg.dns.upstream)));
|
|
|
|
vethToHostInNS = "vh-${netCfg.name}";
|
|
vethOnHostToNS = "vn-${netCfg.name}";
|
|
createWanInterface = pkgs.writeShellScript "create-wan-${netCfg.name}-vmix" ''
|
|
ip link add ${vethOnHostToNS} type veth peer name ${vethToHostInNS}
|
|
ip address add ${hostIPAddressOnLan}/${networkPrefix} dev ${vethOnHostToNS}
|
|
|
|
#iptables -A FORWARD -i ${vethOnHostToNS} -j ACCEPT
|
|
#iptables -A FORWARD -o ${vethOnHostToNS} -j ACCEPT
|
|
#iptables -A INPUT -i ${vethOnHostToNS} -j DROP
|
|
iptables -t nat -A POSTROUTING -s ${networkAddress}/${networkPrefix} -j MASQUERADE
|
|
|
|
ip link set ${vethToHostInNS} netns ${netCfg.namespace}
|
|
ip netns exec ${netCfg.namespace} ip link set ${vethToHostInNS} master ${lanInterfaceName}
|
|
|
|
ip link set ${vethOnHostToNS} up
|
|
ip netns exec ${netCfg.namespace} ip link set ${vethToHostInNS} up
|
|
'';
|
|
|
|
deleteWanInterface = "";
|
|
in
|
|
{
|
|
"lan.net.vmix@${netCfg.name}" = lib.recursiveUpdate {
|
|
wantedBy = lib.optional netCfg.startOnBoot [ "net.vmix.target" ];
|
|
path = with pkgs; [ iproute2 ];
|
|
serviceConfig = {
|
|
ExecStartPre = createLanInterface;
|
|
ExecStart = "${pkgs.dnsmasq}/bin/dnsmasq -d -C ${lanDnsmasqConf}";
|
|
ExecReload = pkgs.writeShellScript "reload-dnsmasq" "kill -HUP $MAINPID";
|
|
ExecStopPost = deleteLanInterface;
|
|
Restart = "on-failure";
|
|
RestartSec = "5";
|
|
PrivateTmp = true;
|
|
ProtectSystem = true;
|
|
ProtectHome = true;
|
|
};
|
|
} (lib.optionalAttrs (netCfg.namespace != null) rec {
|
|
bindsTo = [ "ns.vmix@${netCfg.namespace}.service" ];
|
|
after = bindsTo;
|
|
unitConfig.JoinsNamespaceOf = "ns.vmix@${netCfg.namespace}.service";
|
|
serviceConfig.PrivateNetwork = true;
|
|
});
|
|
|
|
"wan.net.vmix@${netCfg.name}" = rec {
|
|
wantedBy = lib.optional netCfg.startOnBoot [ "net.vmix.target" ];
|
|
path = with pkgs; [ iproute2 iptables ];
|
|
bindsTo = [ "lan.net.vmix@${netCfg.name}.service" ];
|
|
after = bindsTo;
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = createWanInterface;
|
|
ExecStop = deleteWanInterface;
|
|
};
|
|
};
|
|
};
|
|
} |