mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-13 17:35:31 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbd8d57a78 | ||
|
|
dd0b519137 |
@@ -187,6 +187,17 @@ struct Run: AsyncParsableCommand {
|
||||
""", valueName: "interface name"))
|
||||
var netBridged: [String] = []
|
||||
|
||||
@Option(help: ArgumentHelp("Use a connected datagram Unix domain socket inherited from the parent process for networking (e.g. --net-fd=3)",
|
||||
discussion: """
|
||||
The socket carries raw Ethernet frames, one per datagram.
|
||||
|
||||
The simplest use-case is composing Tart and Softnet invocations that are orchestrated by another program, such as Orchard.
|
||||
|
||||
Another way to use this argument is to connect a VM to any other networking implementation such as gVisor or software-defined networking implementation.
|
||||
""",
|
||||
valueName: "file descriptor"))
|
||||
var netFd: Int32?
|
||||
|
||||
@Flag(help: ArgumentHelp("Use software networking provided by Softnet instead of the default shared (NAT) networking",
|
||||
discussion: """
|
||||
Softnet provides better network isolation and alleviates DHCP shortage on production systems. Tart invokes Softnet when this option is specified as a sub-process and communicates with it over socketpair(2).
|
||||
@@ -332,9 +343,10 @@ struct Run: AsyncParsableCommand {
|
||||
if netBridged.count > 0 { netFlags += 1 }
|
||||
if netSoftnet { netFlags += 1 }
|
||||
if netHost { netFlags += 1 }
|
||||
if netFd != nil { netFlags += 1 }
|
||||
|
||||
if netFlags > 1 {
|
||||
throw ValidationError("--net-bridged, --net-softnet and --net-host are mutually exclusive")
|
||||
throw ValidationError("--net-bridged, --net-softnet, --net-host and --net-fd are mutually exclusive")
|
||||
}
|
||||
|
||||
if graphics && noGraphics {
|
||||
@@ -725,6 +737,10 @@ struct Run: AsyncParsableCommand {
|
||||
return NetworkBridged(interfaces: try netBridged.map { try findBridgedInterface($0) })
|
||||
}
|
||||
|
||||
if let netFd = netFd {
|
||||
return try NetworkFD(fd: netFd)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import Darwin
|
||||
import Foundation
|
||||
import Semaphore
|
||||
import System
|
||||
import Virtualization
|
||||
|
||||
class NetworkFD: Network {
|
||||
private let attachment: VZFileHandleNetworkDeviceAttachment
|
||||
|
||||
init(fd: Int32) throws {
|
||||
do {
|
||||
try Softnet.validateControlFD(fd, type: SOCK_DGRAM)
|
||||
} catch {
|
||||
throw RuntimeError.Generic("--net-fd \(error)")
|
||||
}
|
||||
|
||||
let flags = fcntl(fd, F_GETFD)
|
||||
guard flags != -1 && fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1 else {
|
||||
throw RuntimeError.Generic("failed to set close-on-exec on --net-fd: \(Errno(rawValue: errno))")
|
||||
}
|
||||
|
||||
let fileHandle = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
|
||||
attachment = VZFileHandleNetworkDeviceAttachment(fileHandle: fileHandle)
|
||||
}
|
||||
|
||||
func attachments() -> [VZNetworkDeviceAttachment] {
|
||||
[attachment]
|
||||
}
|
||||
|
||||
func run(_ sema: AsyncSemaphore) throws {
|
||||
// no-op, only used for Softnet
|
||||
}
|
||||
|
||||
func stop() async throws {
|
||||
// no-op, only used for Softnet
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,11 @@ class Softnet: Network {
|
||||
}
|
||||
|
||||
controlFileHandle = FileHandle(fileDescriptor: controlFD, closeOnDealloc: true)
|
||||
try Self.validateControlFD(controlFD)
|
||||
do {
|
||||
try Self.validateControlFD(controlFD)
|
||||
} catch {
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
let fds = UnsafeMutablePointer<Int32>.allocate(capacity: MemoryLayout<Int>.stride * 2)
|
||||
@@ -50,20 +54,16 @@ class Softnet: Network {
|
||||
}
|
||||
}
|
||||
|
||||
static func validateControlFD(_ fd: Int32) throws {
|
||||
guard fd > STDERR_FILENO else {
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor must be greater than 2")
|
||||
}
|
||||
|
||||
static func validateControlFD(_ fd: Int32, type: Int32 = SOCK_STREAM) throws {
|
||||
var socketType: Int32 = 0
|
||||
var socketTypeLength = socklen_t(MemoryLayout<Int32>.size)
|
||||
guard getsockopt(fd, SOL_SOCKET, SO_TYPE, &socketType, &socketTypeLength) == 0 else {
|
||||
let details = Errno(rawValue: CInt(errno))
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor is not a socket: \(details)")
|
||||
throw RuntimeError.Generic("is not a socket: \(details)")
|
||||
}
|
||||
|
||||
guard socketType == SOCK_STREAM else {
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor must be a Unix stream socket")
|
||||
guard socketType == type else {
|
||||
throw RuntimeError.Generic("must be a \(type == SOCK_STREAM ? "stream" : "datagram") socket")
|
||||
}
|
||||
|
||||
var peerAddress = sockaddr_storage()
|
||||
@@ -75,11 +75,11 @@ class Softnet: Network {
|
||||
}
|
||||
guard result == 0 else {
|
||||
let details = Errno(rawValue: CInt(errno))
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor is not connected: \(details)")
|
||||
throw RuntimeError.Generic("is not connected: \(details)")
|
||||
}
|
||||
|
||||
guard peerAddress.ss_family == sa_family_t(AF_UNIX) else {
|
||||
throw SoftnetError.InitializationFailed(why: "Softnet control file descriptor must be a Unix stream socket")
|
||||
throw RuntimeError.Generic("must be a Unix socket")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user