Skip to content

Commit 9ba8267

Browse files
jgloganagamdua
andauthored
Use typesafe IP/CIDR parameters everywhere. (#448)
- Closes #445. - Adopts refined IPv4 and IPv6 types developed by Agam Dua <[email protected]>. - For type safety and clarity, use IP and CIDR types where we were previously using String. Co-authored-by: Agam Dua <[email protected]>
1 parent 6869494 commit 9ba8267

36 files changed

+3679
-658
lines changed

Sources/Containerization/ContainerManager.swift

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,19 @@ public struct ContainerManager: Sendable {
4949
nonisolated(unsafe) private let reference: vmnet_network_ref
5050

5151
/// The IPv4 subnet of this network.
52-
public let subnet: CIDRAddress
52+
public let subnet: CIDRv4
5353

54-
/// The gateway address of this network.
55-
public var gateway: IPv4Address {
54+
/// The IPv4 gateway address of this network.
55+
public var ipv4Gateway: IPv4Address {
5656
subnet.gateway
5757
}
5858

5959
struct Allocator: Sendable {
6060
private let addressAllocator: any AddressAllocator<UInt32>
61-
private let cidr: CIDRAddress
61+
private let cidr: CIDRv4
6262
private var allocations: [String: UInt32]
6363

64-
init(cidr: CIDRAddress) throws {
64+
init(cidr: CIDRv4) throws {
6565
self.cidr = cidr
6666
self.allocations = .init()
6767
let size = Int(cidr.upper.value - cidr.lower.value - 3)
@@ -71,14 +71,14 @@ public struct ContainerManager: Sendable {
7171
)
7272
}
7373

74-
mutating func allocate(_ id: String) throws -> String {
74+
mutating func allocate(_ id: String) throws -> CIDRv4 {
7575
if allocations[id] != nil {
7676
throw ContainerizationError(.exists, message: "allocation with id \(id) already exists")
7777
}
7878
let index = try addressAllocator.allocate()
7979
allocations[id] = index
80-
let ip = IPv4Address(fromValue: index)
81-
return try CIDRAddress(ip, prefixLength: cidr.prefixLength).description
80+
let ip = IPv4Address(index)
81+
return try CIDRv4(ip, prefix: cidr.prefix)
8282
}
8383

8484
mutating func release(_ id: String) throws {
@@ -91,21 +91,21 @@ public struct ContainerManager: Sendable {
9191

9292
/// A network interface supporting the vmnet_network_ref.
9393
public struct Interface: Containerization.Interface, VZInterface, Sendable {
94-
public let address: String
95-
public let gateway: String?
94+
public let ipv4Address: CIDRv4
95+
public let ipv4Gateway: IPv4Address?
9696
public let macAddress: String?
9797

9898
// `reference` isn't used concurrently.
9999
nonisolated(unsafe) private let reference: vmnet_network_ref
100100

101101
public init(
102102
reference: vmnet_network_ref,
103-
address: String,
104-
gateway: String,
103+
ipv4Address: CIDRv4,
104+
ipv4Gateway: IPv4Address,
105105
macAddress: String? = nil
106106
) {
107-
self.address = address
108-
self.gateway = gateway
107+
self.ipv4Address = ipv4Address
108+
self.ipv4Gateway = ipv4Gateway
109109
self.macAddress = macAddress
110110
self.reference = reference
111111
}
@@ -126,7 +126,7 @@ public struct ContainerManager: Sendable {
126126

127127
/// Creates a new network.
128128
/// - Parameter subnet: The subnet to use for this network.
129-
public init(subnet: String? = nil) throws {
129+
public init(subnet: CIDRv4? = nil) throws {
130130
var status: vmnet_return_t = .VMNET_FAILURE
131131
guard let config = vmnet_network_configuration_create(.VMNET_SHARED_MODE, &status) else {
132132
throw ContainerizationError(.unsupported, message: "failed to create vmnet config with status \(status)")
@@ -135,7 +135,7 @@ public struct ContainerManager: Sendable {
135135
vmnet_network_configuration_disable_dhcp(config)
136136

137137
if let subnet {
138-
try Self.configureSubnet(config, subnet: try CIDRAddress(subnet))
138+
try Self.configureSubnet(config, subnet: subnet)
139139
}
140140

141141
guard let ref = vmnet_network_create(config, &status), status == .VMNET_SUCCESS else {
@@ -152,11 +152,11 @@ public struct ContainerManager: Sendable {
152152
/// Returns a new interface for use with a container.
153153
/// - Parameter id: The container ID.
154154
public mutating func create(_ id: String) throws -> Containerization.Interface? {
155-
let address = try allocator.allocate(id)
155+
let ipv4Address = try allocator.allocate(id)
156156
return Self.Interface(
157157
reference: self.reference,
158-
address: address,
159-
gateway: self.gateway.description,
158+
ipv4Address: ipv4Address,
159+
ipv4Gateway: self.ipv4Gateway,
160160
)
161161
}
162162

@@ -166,27 +166,27 @@ public struct ContainerManager: Sendable {
166166
try allocator.release(id)
167167
}
168168

169-
private static func getSubnet(_ ref: vmnet_network_ref) throws -> CIDRAddress {
169+
private static func getSubnet(_ ref: vmnet_network_ref) throws -> CIDRv4 {
170170
var subnet = in_addr()
171171
var mask = in_addr()
172172
vmnet_network_get_ipv4_subnet(ref, &subnet, &mask)
173173

174174
let sa = UInt32(bigEndian: subnet.s_addr)
175175
let mv = UInt32(bigEndian: mask.s_addr)
176176

177-
let lower = IPv4Address(fromValue: sa & mv)
178-
let upper = IPv4Address(fromValue: lower.value + ~mv)
177+
let lower = IPv4Address(sa & mv)
178+
let upper = IPv4Address(lower.value + ~mv)
179179

180-
return try CIDRAddress(lower: lower, upper: upper)
180+
return try CIDRv4(lower: lower, upper: upper)
181181
}
182182

183-
private static func configureSubnet(_ config: vmnet_network_configuration_ref, subnet: CIDRAddress) throws {
183+
private static func configureSubnet(_ config: vmnet_network_configuration_ref, subnet: CIDRv4) throws {
184184
let gateway = subnet.gateway
185185

186186
var ga = in_addr()
187187
inet_pton(AF_INET, gateway.description, &ga)
188188

189-
let mask = IPv4Address(fromValue: subnet.prefixLength.prefixMask32)
189+
let mask = IPv4Address(subnet.prefix.prefixMask32)
190190
var ma = in_addr()
191191
inet_pton(AF_INET, mask.description, &ma)
192192

@@ -415,7 +415,8 @@ public struct ContainerManager: Sendable {
415415
}
416416
if let interface = try self.network?.create(id) {
417417
config.interfaces = [interface]
418-
config.dns = .init(nameservers: [interface.gateway!])
418+
// FIXME: throw instead of crash here if we can't unwrap?
419+
config.dns = .init(nameservers: [interface.ipv4Gateway!.description])
419420
}
420421
config.bootLog = BootLog.file(path: self.containerRoot.appendingPathComponent(id).appendingPathComponent("bootlog.log"))
421422
try configuration(&config)
@@ -454,10 +455,10 @@ public struct ContainerManager: Sendable {
454455
}
455456
}
456457

457-
extension CIDRAddress {
458+
extension CIDRv4 {
458459
/// The gateway address of the network.
459460
public var gateway: IPv4Address {
460-
IPv4Address(fromValue: self.lower.value + 1)
461+
IPv4Address(self.lower.value + 1)
461462
}
462463
}
463464

Sources/Containerization/Interface.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ContainerizationExtras
18+
1719
/// A network interface.
1820
public protocol Interface: Sendable {
1921
/// The interface IPv4 address and subnet prefix length, as a CIDR address.
2022
/// Example: `192.168.64.3/24`
21-
var address: String { get }
23+
var ipv4Address: CIDRv4 { get }
2224

2325
/// The IP address for the default route, or nil for no default route.
24-
var gateway: String? { get }
26+
var ipv4Gateway: IPv4Address? { get }
2527

2628
/// The interface MAC address, or nil to auto-configure the address.
2729
var macAddress: String? { get }

Sources/Containerization/LinuxContainer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,10 @@ extension LinuxContainer {
434434
// 3. If a gateway IP address is present, add the default route.
435435
for (index, i) in self.interfaces.enumerated() {
436436
let name = "eth\(index)"
437-
try await agent.addressAdd(name: name, address: i.address)
437+
try await agent.addressAdd(name: name, ipv4Address: i.ipv4Address)
438438
try await agent.up(name: name, mtu: 1280)
439-
if let gateway = i.gateway {
440-
try await agent.routeAddDefault(name: name, gateway: gateway)
439+
if let ipv4Gateway = i.ipv4Gateway {
440+
try await agent.routeAddDefault(name: name, ipv4Gateway: ipv4Gateway)
441441
}
442442
}
443443

Sources/Containerization/LinuxPod.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,10 +393,10 @@ extension LinuxPod {
393393
// 3. If a gateway IP address is present, add the default route.
394394
for (index, i) in self.interfaces.enumerated() {
395395
let name = "eth\(index)"
396-
try await agent.addressAdd(name: name, address: i.address)
396+
try await agent.addressAdd(name: name, ipv4Address: i.ipv4Address)
397397
try await agent.up(name: name, mtu: 1280)
398-
if let gateway = i.gateway {
399-
try await agent.routeAddDefault(name: name, gateway: gateway)
398+
if let ipv4Gateway = i.ipv4Gateway {
399+
try await agent.routeAddDefault(name: name, ipv4Gateway: ipv4Gateway)
400400
}
401401
}
402402

Sources/Containerization/NATInterface.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ContainerizationExtras
18+
1719
public struct NATInterface: Interface {
18-
public var address: String
19-
public var gateway: String?
20+
public var ipv4Address: CIDRv4
21+
public var ipv4Gateway: IPv4Address?
2022
public var macAddress: String?
2123

22-
public init(address: String, gateway: String?, macAddress: String? = nil) {
23-
self.address = address
24-
self.gateway = gateway
24+
public init(ipv4Address: CIDRv4, ipv4Gateway: IPv4Address?, macAddress: String? = nil) {
25+
self.ipv4Address = ipv4Address
26+
self.ipv4Gateway = ipv4Gateway
2527
self.macAddress = macAddress
2628
}
2729
}

Sources/Containerization/NATNetworkInterface.swift

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
import vmnet
2020
import Virtualization
2121
import ContainerizationError
22+
import ContainerizationExtras
2223
import Foundation
2324
import Synchronization
2425

2526
/// An interface that uses NAT to provide an IP address for a given
2627
/// container/virtual machine.
2728
@available(macOS 26, *)
2829
public final class NATNetworkInterface: Interface, Sendable {
29-
public let address: String
30-
public let gateway: String?
30+
public let ipv4Address: CIDRv4
31+
public let ipv4Gateway: IPv4Address?
3132
public let macAddress: String?
3233

3334
@available(macOS 26, *)
@@ -36,25 +37,25 @@ public final class NATNetworkInterface: Interface, Sendable {
3637

3738
@available(macOS 26, *)
3839
public init(
39-
address: String,
40-
gateway: String?,
40+
ipv4Address: CIDRv4,
41+
ipv4Gateway: IPv4Address?,
4142
reference: sending vmnet_network_ref,
4243
macAddress: String? = nil
4344
) {
44-
self.address = address
45-
self.gateway = gateway
45+
self.ipv4Address = ipv4Address
46+
self.ipv4Gateway = ipv4Gateway
4647
self.macAddress = macAddress
4748
self.reference = reference
4849
}
4950

50-
@available(macOS, obsoleted: 26, message: "Use init(address:gateway:reference:macAddress:) instead")
51+
@available(macOS, obsoleted: 26, message: "Use init(ipv4Address:ipv4Gateway:reference:macAddress:) instead")
5152
public init(
52-
address: String,
53-
gateway: String?,
53+
ipv4Address: CIDRv4,
54+
ipv4Gateway: IPv4Address?,
5455
macAddress: String? = nil
5556
) {
56-
self.address = address
57-
self.gateway = gateway
57+
self.ipv4Address = ipv4Address
58+
self.ipv4Gateway = ipv4Gateway
5859
self.macAddress = macAddress
5960
self.reference = nil
6061
}

0 commit comments

Comments
 (0)