diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index 61f0236..2aed034 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -117,6 +117,9 @@ struct Run: AsyncParsableCommand { discussion: "Learn how to configure Softnet for use with Tart here: https://github.com/cirruslabs/softnet")) var netSoftnet: Bool = false + @Flag(help: ArgumentHelp("Restrict network access to the host-only network")) + var netHost: Bool = false + #if arch(arm64) @Flag(help: ArgumentHelp("Disables audio and entropy devices and switches to only Mac-specific input devices.", discussion: "Useful for running a VM that can be suspended via \"tart suspend\".")) #endif @@ -133,8 +136,14 @@ struct Run: AsyncParsableCommand { throw ValidationError("--vnc and --vnc-experimental are mutually exclusive") } - if netBridged.count > 0 && netSoftnet { - throw ValidationError("--net-bridged and --net-softnet are mutually exclusive") + // check that not more than one network option is specified + var netFlags = 0 + if netBridged.count > 0 { netFlags += 1 } + if netSoftnet { netFlags += 1 } + if netHost { netFlags += 1 } + + if netFlags > 1 { + throw ValidationError("--net-bridged, --net-softnet and --net-host are mutually exclusive") } if graphics { @@ -184,7 +193,7 @@ struct Run: AsyncParsableCommand { } } - if netSoftnet && isInteractiveSession() { + if (netSoftnet || netHost) && isInteractiveSession() { try Softnet.configureSUIDBitIfNeeded() } @@ -365,10 +374,14 @@ struct Run: AsyncParsableCommand { func userSpecifiedNetwork(vmDir: VMDirectory) throws -> Network? { if netSoftnet { let config = try VMConfig.init(fromURL: vmDir.configURL) - return try Softnet(vmMACAddress: config.macAddress.string) } + if netHost { + let config = try VMConfig.init(fromURL: vmDir.configURL) + return try Softnet(vmMACAddress: config.macAddress.string, extraArguments: ["--vm-net-type", "host"]) + } + if netBridged.count > 0 { func findBridgedInterface(_ name: String) throws -> VZBridgedNetworkInterface { let interface = VZBridgedNetworkInterface.networkInterfaces.first { interface in diff --git a/Sources/tart/Network/Softnet.swift b/Sources/tart/Network/Softnet.swift index 7f1d6ad..8b288dd 100644 --- a/Sources/tart/Network/Softnet.swift +++ b/Sources/tart/Network/Softnet.swift @@ -15,7 +15,7 @@ class Softnet: Network { let vmFD: Int32 - init(vmMACAddress: String) throws { + init(vmMACAddress: String, extraArguments: [String] = []) throws { let fds = UnsafeMutablePointer.allocate(capacity: MemoryLayout.stride * 2) let ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) @@ -30,7 +30,7 @@ class Softnet: Network { try setSocketBuffers(softnetFD, 1 * 1024 * 1024); process.executableURL = try Self.softnetExecutableURL() - process.arguments = ["--vm-fd", String(STDIN_FILENO), "--vm-mac-address", vmMACAddress] + process.arguments = ["--vm-fd", String(STDIN_FILENO), "--vm-mac-address", vmMACAddress] + extraArguments process.standardInput = FileHandle(fileDescriptor: softnetFD, closeOnDealloc: false) }