Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import PackageDescription
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
let builderShimVersion = "0.7.0"
let scVersion = "0.17.0"
let scVersion = "0.18.0"

let package = Package(
name: "container",
Expand Down
10 changes: 7 additions & 3 deletions Sources/ContainerCommands/Network/NetworkCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ extension Application {
var labels: [String] = []

@Option(name: .customLong("subnet"), help: "Set subnet for a network")
var subnet: String? = nil
var ipv4Subnet: String? = nil

@Option(name: .customLong("subnet-v6"), help: "Set the IPv6 prefix for a network")
var ipv6Subnet: String? = nil

@OptionGroup
var global: Flags.Global
Expand All @@ -44,8 +47,9 @@ extension Application {

public func run() async throws {
let parsedLabels = Utility.parseKeyValuePairs(labels)
let ipv4Subnet = try subnet.map { try CIDRv4($0) }
let config = try NetworkConfiguration(id: self.name, mode: .nat, ipv4Subnet: ipv4Subnet, labels: parsedLabels)
let ipv4Subnet = try ipv4Subnet.map { try CIDRv4($0) }
let ipv6Subnet = try ipv6Subnet.map { try CIDRv6($0) }
let config = try NetworkConfiguration(id: self.name, mode: .nat, ipv4Subnet: ipv4Subnet, ipv6Subnet: ipv6Subnet, labels: parsedLabels)
let state = try await ClientNetwork.create(configuration: config)
print(state.id)
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/ContainerCommands/System/Property/PropertySet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ extension Application {
throw ContainerizationError(.invalidArgument, message: "invalid CIDRv4 address: \(value)")
}
DefaultsStore.set(value: value, key: key)
case .defaultIPv6Subnet:
guard (try? CIDRv6(value)) != nil else {
throw ContainerizationError(.invalidArgument, message: "invalid CIDRv6 address: \(value)")
}
DefaultsStore.set(value: value, key: key)
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion Sources/ContainerPersistence/DefaultsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum DefaultsStore {
case defaultKernelBinaryPath = "kernel.binaryPath"
case defaultKernelURL = "kernel.url"
case defaultSubnet = "network.subnet"
case defaultIPv6Subnet = "network.subnetv6"
case defaultRegistryDomain = "registry.domain"
}

Expand Down Expand Up @@ -73,6 +74,7 @@ public enum DefaultsStore {
(.defaultKernelBinaryPath, { Self.get(key: $0) }),
(.defaultKernelURL, { Self.get(key: $0) }),
(.defaultSubnet, { Self.getOptional(key: $0) }),
(.defaultIPv6Subnet, { Self.getOptional(key: $0) }),
(.defaultDNSDomain, { Self.getOptional(key: $0) }),
(.defaultRegistryDomain, { Self.get(key: $0) }),
]
Expand Down Expand Up @@ -131,7 +133,9 @@ extension DefaultsStore.Keys {
case .defaultKernelURL:
return "The URL for the kernel file to install, or the URL for an archive containing the kernel file."
case .defaultSubnet:
return "Default subnet for IP allocation (used on macOS 15 only)."
return "Default subnet for IPv4 allocation."
case .defaultIPv6Subnet:
return "Default IPv6 network prefix."
case .defaultRegistryDomain:
return "The default registry to use for image references that do not specify a registry."
}
Expand All @@ -153,6 +157,8 @@ extension DefaultsStore.Keys {
return String.self
case .defaultSubnet:
return String.self
case .defaultIPv6Subnet:
return String.self
case .defaultRegistryDomain:
return String.self
}
Expand Down Expand Up @@ -180,6 +186,8 @@ extension DefaultsStore.Keys {
return "https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz"
case .defaultSubnet:
return "192.168.64.1/24"
case .defaultIPv6Subnet:
return "fd00::/64"
case .defaultRegistryDomain:
return "docker.io"
}
Expand Down
17 changes: 13 additions & 4 deletions Sources/Helpers/NetworkVmnet/NetworkVmnetHelper+Start.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ extension NetworkVmnetHelper {
@Option(name: .shortAndLong, help: "Network identifier")
var id: String

@Option(name: .shortAndLong, help: "CIDR address for the subnet")
var subnet: String?
@Option(name: .customLong("subnet"), help: "CIDR address for the IPv4 subnet")
var ipv4Subnet: String?

@Option(name: .customLong("subnet-v6"), help: "CIDR address for the IPv6 prefix")
var ipv6Subnet: String?

func run() async throws {
let commandName = NetworkVmnetHelper._commandName
Expand All @@ -50,8 +53,14 @@ extension NetworkVmnetHelper {

do {
log.info("configuring XPC server")
let ipv4Subnet = try self.subnet.map { try CIDRv4($0) }
let configuration = try NetworkConfiguration(id: id, mode: .nat, ipv4Subnet: ipv4Subnet)
let ipv4Subnet = try self.ipv4Subnet.map { try CIDRv4($0) }
let ipv6Subnet = try self.ipv6Subnet.map { try CIDRv6($0) }
let configuration = try NetworkConfiguration(
id: id,
mode: .nat,
ipv4Subnet: ipv4Subnet,
ipv6Subnet: ipv6Subnet,
)
let network = try Self.createNetwork(configuration: configuration, log: log)
try await network.start()
let server = try await NetworkService(network: network, log: log)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public actor NetworksService {
serviceIdentifier,
]

if let ipv4Subnet = (configuration.ipv4Subnet.map { $0 }) {
if let ipv4Subnet = configuration.ipv4Subnet {
var existingCidrs: [CIDRv4] = []
for networkState in networkStates.values {
if case .running(_, let status) = networkState {
Expand All @@ -303,6 +303,26 @@ public actor NetworksService {
args += ["--subnet", ipv4Subnet.description]
}

if let ipv6Subnet = configuration.ipv6Subnet {
var existingCidrs: [CIDRv6] = []
for networkState in networkStates.values {
if case .running(_, let status) = networkState, let otherIPv6Subnet = status.ipv6Subnet {
existingCidrs.append(otherIPv6Subnet)
}
}
let overlap = existingCidrs.first {
$0.contains(ipv6Subnet.lower)
|| $0.contains(ipv6Subnet.upper)
|| ipv6Subnet.contains($0.lower)
|| ipv6Subnet.contains($0.upper)
}
if let overlap {
throw ContainerizationError(.exists, message: "IPv6 subnet \(ipv6Subnet) overlaps an existing network with subnet \(overlap)")
}

args += ["--subnet-v6", ipv6Subnet.description]
}

try await pluginLoader.registerWithLaunchd(
plugin: networkPlugin,
pluginStateRoot: store.entityUrl(configuration.id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ public actor AllocationOnlyVmnetNetwork: Network {
let subnet = DefaultsStore.get(key: .defaultSubnet)
let subnetCIDR = try CIDRv4(subnet)
let gateway = IPv4Address(subnetCIDR.lower.value + 1)
self._state = .running(configuration, NetworkStatus(ipv4Subnet: subnetCIDR, ipv4Gateway: gateway))
let status = NetworkStatus(
ipv4Subnet: subnetCIDR,
ipv4Gateway: gateway,
ipv6Subnet: nil,
)
self._state = .running(configuration, status)
log.info(
"started allocation-only network",
metadata: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
/// The preferred CIDR address for the IPv4 subnet, if specified
public let ipv4Subnet: CIDRv4?

/// The preferred CIDR address for the IPv6 subnet, if specified
public let ipv6Subnet: CIDRv6?

/// Key-value labels for the network.
public var labels: [String: String] = [:]

Expand All @@ -40,12 +43,14 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
id: String,
mode: NetworkMode,
ipv4Subnet: CIDRv4? = nil,
ipv6Subnet: CIDRv6? = nil,
labels: [String: String] = [:]
) throws {
self.id = id
self.creationDate = Date()
self.mode = mode
self.ipv4Subnet = ipv4Subnet
self.ipv6Subnet = ipv6Subnet
self.labels = labels
try validate()
}
Expand All @@ -55,6 +60,7 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
case creationDate
case mode
case ipv4Subnet
case ipv6Subnet
case labels
// TODO: retain for deserialization compatability for now, remove later
case subnet
Expand All @@ -72,6 +78,8 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
try container.decodeIfPresent(String.self, forKey: .ipv4Subnet)
?? container.decodeIfPresent(String.self, forKey: .subnet)
ipv4Subnet = try subnetText.map { try CIDRv4($0) }
ipv6Subnet = try container.decodeIfPresent(String.self, forKey: .ipv6Subnet)
.map { try CIDRv6($0) }
labels = try container.decodeIfPresent([String: String].self, forKey: .labels) ?? [:]
try validate()
}
Expand All @@ -83,7 +91,8 @@ public struct NetworkConfiguration: Codable, Sendable, Identifiable {
try container.encode(id, forKey: .id)
try container.encode(creationDate, forKey: .creationDate)
try container.encode(mode, forKey: .mode)
try container.encodeIfPresent(ipv4Subnet?.description, forKey: .ipv4Subnet)
try container.encodeIfPresent(ipv4Subnet, forKey: .ipv4Subnet)
try container.encodeIfPresent(ipv6Subnet, forKey: .ipv6Subnet)
try container.encode(labels, forKey: .labels)
}

Expand Down
33 changes: 8 additions & 25 deletions Sources/Services/ContainerNetworkService/NetworkState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,23 @@ public struct NetworkStatus: Codable, Sendable {
/// The address allocated for the network if no subnet was specified at
/// creation time; otherwise, the subnet from the configuration.
public let ipv4Subnet: CIDRv4

/// The gateway IPv4 address.
public let ipv4Gateway: IPv4Address

/// The address allocated for the IPv6 network if no subnet was specified at
/// creation time; otherwise, the IPv6 subnet from the configuration.
public let ipv6Subnet: CIDRv6?

public init(
ipv4Subnet: CIDRv4,
ipv4Gateway: IPv4Address
ipv4Gateway: IPv4Address,
ipv6Subnet: CIDRv6?,
) {
self.ipv4Subnet = ipv4Subnet
self.ipv4Gateway = ipv4Gateway
self.ipv6Subnet = ipv6Subnet
}

enum CodingKeys: String, CodingKey {
case ipv4Subnet
case ipv4Gateway
}

/// Create a network status from the supplied Decoder.
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let addressText = try container.decode(String.self, forKey: .ipv4Subnet)
ipv4Subnet = try CIDRv4(addressText)
let gatewayText = try container.decode(String.self, forKey: .ipv4Gateway)
ipv4Gateway = try IPv4Address(gatewayText)
}

/// Encode the network status to the supplied Encoder.
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(ipv4Subnet.description, forKey: .ipv4Subnet)
try container.encode(ipv4Gateway.description, forKey: .ipv4Gateway)
}

}

/// The configuration and runtime attributes for a network.
Expand Down
Loading