Skip to content

Commit 3d7ddbb

Browse files
Add support for custom non-default routes on interfaces
1 parent df8b97a commit 3d7ddbb

File tree

13 files changed

+95
-7
lines changed

13 files changed

+95
-7
lines changed

Sources/Containerization/ContainerManager.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public struct ContainerManager: Sendable {
9494
public let address: String
9595
public let gateway: String?
9696
public let macAddress: String?
97+
public let routes: [Route]
9798

9899
// `reference` isn't used concurrently.
99100
nonisolated(unsafe) private let reference: vmnet_network_ref
@@ -102,12 +103,14 @@ public struct ContainerManager: Sendable {
102103
reference: vmnet_network_ref,
103104
address: String,
104105
gateway: String,
105-
macAddress: String? = nil
106+
macAddress: String? = nil,
107+
routes: [Route] = []
106108
) {
107109
self.address = address
108110
self.gateway = gateway
109111
self.macAddress = macAddress
110112
self.reference = reference
113+
self.routes = routes
111114
}
112115

113116
/// Returns the underlying `VZVirtioNetworkDeviceConfiguration`.

Sources/Containerization/Interface.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
/// A custom route for an interface.
18+
public struct Route: Sendable {
19+
/// Destination network in CIDR notation (e.g., "192.168.1.0/24")
20+
public let destination: String
21+
22+
/// Gateway IP address for this route
23+
public let gateway: String
24+
25+
public init(destination: String, gateway: String) {
26+
self.destination = destination
27+
self.gateway = gateway
28+
}
29+
}
1730
/// A network interface.
1831
public protocol Interface: Sendable {
1932
/// The interface IPv4 address and subnet prefix length, as a CIDR address.
@@ -25,4 +38,6 @@ public protocol Interface: Sendable {
2538

2639
/// The interface MAC address, or nil to auto-configure the address.
2740
var macAddress: String? { get }
41+
42+
var routes: [Route] { get }
2843
}

Sources/Containerization/LinuxContainer.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,15 @@ extension LinuxContainer {
348348
if let gateway = i.gateway {
349349
try await agent.routeAddDefault(name: name, gateway: gateway)
350350
}
351+
352+
for route in i.routes {
353+
try await agent.routeAddLink(
354+
name: name,
355+
address: route.destination,
356+
gateway: route.gateway,
357+
srcAddr: ""
358+
)
359+
}
351360
}
352361

353362
// Setup /etc/resolv.conf and /etc/hosts if asked for.

Sources/Containerization/LinuxPod.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,15 @@ extension LinuxPod {
339339
if let gateway = i.gateway {
340340
try await agent.routeAddDefault(name: name, gateway: gateway)
341341
}
342+
343+
for route in i.routes {
344+
try await agent.routeAddLink(
345+
name: name,
346+
address: route.destination,
347+
gateway: route.gateway,
348+
srcAddr: ""
349+
)
350+
}
342351
}
343352

344353
// Setup /etc/resolv.conf if asked for

Sources/Containerization/NATInterface.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public struct NATInterface: Interface {
1818
public var address: String
1919
public var gateway: String?
2020
public var macAddress: String?
21+
public var routes: [Route] { [] }
2122

2223
public init(address: String, gateway: String?, macAddress: String? = nil) {
2324
self.address = address

Sources/Containerization/NATNetworkInterface.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public final class NATNetworkInterface: Interface, Sendable {
2929
public let address: String
3030
public let gateway: String?
3131
public let macAddress: String?
32+
public var routes: [Route] { [] }
3233

3334
@available(macOS 26, *)
3435
// `reference` isn't used concurrently.

Sources/Containerization/SandboxContext/SandboxContext.pb.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,9 +816,20 @@ public struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: Senda
816816

817817
public var srcAddr: String = String()
818818

819+
public var gateway: String {
820+
get {return _gateway ?? String()}
821+
set {_gateway = newValue}
822+
}
823+
/// Returns true if `gateway` has been explicitly set.
824+
public var hasGateway: Bool {return self._gateway != nil}
825+
/// Clears the value of `gateway`. Subsequent reads from it will return its default value.
826+
public mutating func clearGateway() {self._gateway = nil}
827+
819828
public var unknownFields = SwiftProtobuf.UnknownStorage()
820829

821830
public init() {}
831+
832+
fileprivate var _gateway: String? = nil
822833
}
823834

824835
public struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse: Sendable {
@@ -2679,6 +2690,7 @@ extension Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: SwiftProt
26792690
1: .same(proto: "interface"),
26802691
2: .same(proto: "address"),
26812692
3: .same(proto: "srcAddr"),
2693+
4: .same(proto: "gateway"),
26822694
]
26832695

26842696
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@@ -2690,12 +2702,17 @@ extension Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: SwiftProt
26902702
case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()
26912703
case 2: try { try decoder.decodeSingularStringField(value: &self.address) }()
26922704
case 3: try { try decoder.decodeSingularStringField(value: &self.srcAddr) }()
2705+
case 4: try { try decoder.decodeSingularStringField(value: &self._gateway) }()
26932706
default: break
26942707
}
26952708
}
26962709
}
26972710

26982711
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
2712+
// The use of inline closures is to circumvent an issue where the compiler
2713+
// allocates stack space for every if/case branch local when no optimizations
2714+
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
2715+
// https://github.com/apple/swift-protobuf/issues/1182
26992716
if !self.interface.isEmpty {
27002717
try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)
27012718
}
@@ -2705,13 +2722,17 @@ extension Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: SwiftProt
27052722
if !self.srcAddr.isEmpty {
27062723
try visitor.visitSingularStringField(value: self.srcAddr, fieldNumber: 3)
27072724
}
2725+
try { if let v = self._gateway {
2726+
try visitor.visitSingularStringField(value: v, fieldNumber: 4)
2727+
} }()
27082728
try unknownFields.traverse(visitor: &visitor)
27092729
}
27102730

27112731
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest) -> Bool {
27122732
if lhs.interface != rhs.interface {return false}
27132733
if lhs.address != rhs.address {return false}
27142734
if lhs.srcAddr != rhs.srcAddr {return false}
2735+
if lhs._gateway != rhs._gateway {return false}
27152736
if lhs.unknownFields != rhs.unknownFields {return false}
27162737
return true
27172738
}

Sources/Containerization/SandboxContext/SandboxContext.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ message IpRouteAddLinkRequest {
243243
string interface = 1;
244244
string address = 2;
245245
string srcAddr = 3;
246+
optional string gateway = 4;
246247
}
247248

248249
message IpRouteAddLinkResponse {}

Sources/Containerization/VirtualMachineAgent.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public protocol VirtualMachineAgent: Sendable {
6666
func down(name: String) async throws
6767
func addressAdd(name: String, address: String) async throws
6868
func routeAddDefault(name: String, gateway: String) async throws
69+
func routeAddLink(name: String, address: String, gateway: String, srcAddr: String) async throws
6970
func configureDNS(config: DNS, location: String) async throws
7071
func configureHosts(config: Hosts, location: String) async throws
7172

Sources/Containerization/Vminitd.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,17 @@ extension Vminitd {
382382
})
383383
}
384384

385+
public func routeAddLink(name: String, address: String, gateway: String, srcAddr: String = "") async throws {
386+
_ = try await client.ipRouteAddLink(
387+
.with {
388+
$0.interface = name
389+
$0.address = address
390+
$0.gateway = gateway
391+
$0.srcAddr = srcAddr
392+
}
393+
)
394+
}
395+
385396
/// Configure DNS within the sandbox's environment.
386397
public func configureDNS(config: DNS, location: String) async throws {
387398
_ = try await client.configureDns(

0 commit comments

Comments
 (0)