{ config, pkgs, lib, vmixLib, ... }: with lib; with vmixLib.network; let vmixCfg = config.vmix; mkServices4aVM = name: cfg: let vmCfg = cfg // { inherit name; }; netName = head (attrNames vmCfg.network.vmix); netCfg = vmCfg.network.vmix.${netName} // { name = netName; }; mkTap4aLan = lanName: tapCfg: let tapInterfaceName = "vt-${vmCfg.name}-${lanName}"; lanInterfaceName = "brx-${lanName}"; in { name = lanName; iface = tapInterfaceName; mac = tapCfg.mac; create = '' ip tuntap add dev ${tapInterfaceName} mode tap ip link set dev ${tapInterfaceName} up ip link set dev ${tapInterfaceName} master ${lanInterfaceName} ''; delete = '' ip link del ${tapInterfaceName} ''; }; mkMacvtap = macvtapName: macvtapVmCfg: let macvtapNetworkCfg = config.vmix.networks.${netCfg.name}.bridges.macvtaps.${macvtapName}; macvtapInterfaceName = "mt-${vmCfg.name}-${macvtapNetworkCfg.uplink.iface}"; in { name = macvtapName; iface = macvtapInterfaceName; mac = macvtapVmCfg.mac; create = '' ip link add link ${macvtapNetworkCfg.uplink.iface} name ${macvtapInterfaceName} type macvtap mode bridge ${lib.optionalString (macvtapVmCfg.mac != null) "ip link set dev ${macvtapInterfaceName} address ${macvtapVmCfg.mac}"} ip link set ${macvtapInterfaceName} netns ${netName}.vmix ip netns exec ${netName}.vmix ip link set dev ${macvtapInterfaceName} up ''; delete = '' ip netns exec ${netName}.vmix ip link del ${macvtapInterfaceName} ''; }; allTaps = (mapAttrsToList mkTap4aLan netCfg.lans); allMacvtaps = (mapAttrsToList mkMacvtap netCfg.macvtaps); createTapsforLansScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" ( concatStringsSep "\n" (builtins.map (tap: tap.create) allTaps) ); deleteTapsforLansScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" ( concatStringsSep "\n" (builtins.map (tap: tap.delete) allTaps) ); createMacvapsScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" ( concatStringsSep "\n" (builtins.map (macvtap: macvtap.create) allMacvtaps) ); deleteMacvapsScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" ( concatStringsSep "\n" (builtins.map (macvtap: macvtap.delete) allMacvtaps) ); osImage = vmixLib.customizeImage vmCfg.disks.os.file { name = vmCfg.name; commands = '' truncate /etc/machine-id run-command systemd-machine-id-setup run-command ssh-keygen -A ''; }; qemuStartVMScript = pkgs.writeShellScript "${vmCfg.name}-qemu-vmix" '' exec qemu-system-${vmCfg.arch} \ -nographic \ ${optionalString vmCfg.kvm "-accel kvm"} \ -name ${vmCfg.name} \ -m ${toString vmCfg.mem.size} \ -smp cores=${toString vmCfg.cpu.cores} \ -cpu ${vmCfg.cpu.model} \ -machine type=${vmCfg.pc.type} \ ${optionalString vmCfg.bios.efi "-bios ${pkgs.OVMF.fd}/FV/OVMF.fd"} \ ${optionalString vmCfg.bios.tpm "-chardev socket,id=chrtpm,path=/tmp/mytpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"} \ -drive file=${toString osImage},format=qcow2,if=virtio${optionalString (vmCfg.disks.os.persist == false) ",snapshot=on"} \ ${optionalString (vmCfg.disks.iso.file != null) "-drive file=${toString vmCfg.disks.iso.file},media=cdrom,readonly=on"} \ ${concatMapStrings (diskCfg: '' -drive file=${diskCfg.file},format=qcow2,if=${toString vmCfg.disks.bus} \ '') (attrValues vmCfg.disks.add)} \ ${concatMapStrings (shareCfg: '' -virtfs local,path=${toString shareCfg.source},security_model=passthrough,mount_tag=${shareCfg.target} \ '') (attrValues vmCfg.shares)} \ ${concatMapStrings (tapCfg: '' -device virtio-net-pci,netdev=lan-${tapCfg.name},mac=${tapCfg.mac} \ -netdev tap,id=lan-${tapCfg.name},ifname=${tapCfg.iface},script=no,downscript=no \ '') allTaps} \ ${optionalString cfg.network.user.enable " -netdev user,id=user \ -device virtio-net-pci,netdev=user \ "} \ ${optionalString (vmCfg.boot.menu == true) "-boot menu=on"} \ ${concatStrings (imap1 (i: macvtap: '' -device virtio-net-pci,netdev=macvtap-${macvtap.name},mac=$(ip l show ${macvtap.iface} | awk '/link\/ether/{print $2}') \ -netdev tap,id=macvtap-${macvtap.name},fd=${toString (i+2)} ${toString (i+2)}<>/dev/tap$(ip l show ${macvtap.iface} | awk -F':' '/${macvtap.iface}/{print $1}') \ '') allMacvtaps)} \ #${optionalString (length vmCfg.boot.order > 0) "-boot order=${concatStringsSep "," vmCfg.boot.order}"} \ ''; in { "vm.vmix@${vmCfg.name}" = rec { bindsTo = [ "net.vmix@${netCfg.name}.target" "macvtaps.vm.vmix@${vmCfg.name}.service" ]; unitConfig.JoinsNamespaceOf = "ns.net.vmix@${netCfg.name}.service"; after = bindsTo; path = with pkgs; [ iproute2 qemu gawk ]; serviceConfig = { ExecStartPre = createTapsforLansScript; ExecStart = qemuStartVMScript; ExecStopPost = deleteTapsforLansScript; PrivateTmp = true; ProtectSystem = true; ProtectHome = true; PrivateNetwork = true; }; }; "macvtaps.vm.vmix@${vmCfg.name}" = rec { bindsTo = [ "net.vmix@${netCfg.name}.target" ]; after = bindsTo; partOf = [ "vm.vmix@${vmCfg.name}.service" ]; path = with pkgs; [ iproute2 ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = createMacvapsScript; ExecStop = deleteMacvapsScript; }; }; }; vmServices = concatMapAttrs mkServices4aVM vmixCfg.vms; in { config.systemd.services = vmServices; }