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
8 changes: 4 additions & 4 deletions Sources/Containerization/ContainerManager.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -93,7 +93,7 @@ public struct ContainerManager: Sendable {
public struct Interface: Containerization.Interface, VZInterface, Sendable {
public let ipv4Address: CIDRv4
public let ipv4Gateway: IPv4Address?
public let macAddress: String?
public let macAddress: MACAddress?

// `reference` isn't used concurrently.
nonisolated(unsafe) private let reference: vmnet_network_ref
Expand All @@ -102,7 +102,7 @@ public struct ContainerManager: Sendable {
reference: vmnet_network_ref,
ipv4Address: CIDRv4,
ipv4Gateway: IPv4Address,
macAddress: String? = nil
macAddress: MACAddress? = nil
) {
self.ipv4Address = ipv4Address
self.ipv4Gateway = ipv4Gateway
Expand All @@ -114,7 +114,7 @@ public struct ContainerManager: Sendable {
public func device() throws -> VZVirtioNetworkDeviceConfiguration {
let config = VZVirtioNetworkDeviceConfiguration()
if let macAddress = self.macAddress {
guard let mac = VZMACAddress(string: macAddress) else {
guard let mac = VZMACAddress(string: macAddress.description) else {
throw ContainerizationError(.invalidArgument, message: "invalid mac address \(macAddress)")
}
config.macAddress = mac
Expand Down
4 changes: 2 additions & 2 deletions Sources/Containerization/Interface.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -26,5 +26,5 @@ public protocol Interface: Sendable {
var ipv4Gateway: IPv4Address? { get }

/// The interface MAC address, or nil to auto-configure the address.
var macAddress: String? { get }
var macAddress: MACAddress? { get }
}
6 changes: 3 additions & 3 deletions Sources/Containerization/NATInterface.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -19,9 +19,9 @@ import ContainerizationExtras
public struct NATInterface: Interface {
public var ipv4Address: CIDRv4
public var ipv4Gateway: IPv4Address?
public var macAddress: String?
public var macAddress: MACAddress?

public init(ipv4Address: CIDRv4, ipv4Gateway: IPv4Address?, macAddress: String? = nil) {
public init(ipv4Address: CIDRv4, ipv4Gateway: IPv4Address?, macAddress: MACAddress? = nil) {
self.ipv4Address = ipv4Address
self.ipv4Gateway = ipv4Gateway
self.macAddress = macAddress
Expand Down
10 changes: 5 additions & 5 deletions Sources/Containerization/NATNetworkInterface.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,7 +29,7 @@ import Synchronization
public final class NATNetworkInterface: Interface, Sendable {
public let ipv4Address: CIDRv4
public let ipv4Gateway: IPv4Address?
public let macAddress: String?
public let macAddress: MACAddress?

@available(macOS 26, *)
// `reference` isn't used concurrently.
Expand All @@ -40,7 +40,7 @@ public final class NATNetworkInterface: Interface, Sendable {
ipv4Address: CIDRv4,
ipv4Gateway: IPv4Address?,
reference: sending vmnet_network_ref,
macAddress: String? = nil
macAddress: MACAddress? = nil
) {
self.ipv4Address = ipv4Address
self.ipv4Gateway = ipv4Gateway
Expand All @@ -52,7 +52,7 @@ public final class NATNetworkInterface: Interface, Sendable {
public init(
ipv4Address: CIDRv4,
ipv4Gateway: IPv4Address?,
macAddress: String? = nil
macAddress: MACAddress? = nil
) {
self.ipv4Address = ipv4Address
self.ipv4Gateway = ipv4Gateway
Expand All @@ -66,7 +66,7 @@ extension NATNetworkInterface: VZInterface {
public func device() throws -> VZVirtioNetworkDeviceConfiguration {
let config = VZVirtioNetworkDeviceConfiguration()
if let macAddress = self.macAddress {
guard let mac = VZMACAddress(string: macAddress) else {
guard let mac = VZMACAddress(string: macAddress.description) else {
throw ContainerizationError(.invalidArgument, message: "invalid mac address \(macAddress)")
}
config.macAddress = mac
Expand Down
4 changes: 2 additions & 2 deletions Sources/Containerization/VZVirtualMachineInstance.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -465,7 +465,7 @@ extension NATInterface: VZInterface {
public func device() throws -> VZVirtioNetworkDeviceConfiguration {
let config = VZVirtioNetworkDeviceConfiguration()
if let macAddress = self.macAddress {
guard let mac = VZMACAddress(string: macAddress) else {
guard let mac = VZMACAddress(string: macAddress.description) else {
throw ContainerizationError(.invalidArgument, message: "invalid mac address \(macAddress)")
}
config.macAddress = mac
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -14,7 +14,7 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

public struct IPAddressError: Error, Equatable, Hashable, CustomStringConvertible {
public struct AddressError: Error, Equatable, Hashable, CustomStringConvertible {
public var description: String {
String(describing: self.base)
}
Expand Down
15 changes: 14 additions & 1 deletion Sources/ContainerizationExtras/CIDR.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -133,3 +133,16 @@ extension CIDR {
case invalidAddressRange(lower: String, upper: String)
}
}

extension CIDR: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
try self.init(string)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(description)
}
}
19 changes: 16 additions & 3 deletions Sources/ContainerizationExtras/IPAddress.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,7 +27,7 @@ public enum IPAddress: Sendable, Hashable, CustomStringConvertible, Equatable {
///
/// - Parameter string: IP address string to parse
/// - Returns: An `IPAddress` containing either an IPv4 or IPv6 address
/// - Throws: `IPAddressError.unableToParse` if invalid
/// - Throws: `AddressError.unableToParse` if invalid
public init(_ string: String) throws {
let utf8 = string.utf8
var hasColon = false
Expand All @@ -50,7 +50,7 @@ public enum IPAddress: Sendable, Hashable, CustomStringConvertible, Equatable {
let ipv4 = try IPv4Address(string)
self = .v4(ipv4)
} else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}
}

Expand Down Expand Up @@ -133,3 +133,16 @@ public enum IPAddress: Sendable, Hashable, CustomStringConvertible, Equatable {
}
}
}

extension IPAddress: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
try self.init(string)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(description)
}
}
18 changes: 9 additions & 9 deletions Sources/ContainerizationExtras/IPv4Address.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Containerization project authors.
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -26,7 +26,7 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl
/// Creates an IPv4Address from a string representation.
///
/// - Parameter string: The IPv4 address string in dotted decimal notation (e.g., "192.168.1.1")
/// - Throws: `IPAddressError.unableToParse` if the string is not a valid IPv4 address
/// - Throws: `AddressError.unableToParse` if the string is not a valid IPv4 address
@inlinable
public init(_ string: String) throws {
self.value = try Self.parse(string)
Expand Down Expand Up @@ -94,15 +94,15 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl
@usableFromInline
internal static func parse(_ s: String) throws -> UInt32 {
guard !s.isEmpty, s.count >= 7, s.count <= 15 else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}

// IP addresses should only contain ASCII digits and dots
let utf8 = s.utf8
for byte in utf8 {
// ASCII whitespace: space(32), tab(9), newline(10), return(13)
if byte == 32 || byte == 9 || byte == 10 || byte == 13 {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}
}

Expand All @@ -120,7 +120,7 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl
if byte == 46 { // ASCII '.'
// Validate octet before processing
guard octetCount < 3, digitCount > 0, digitCount <= 3, currentOctet <= 255 else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}

// Shift result and add current octet
Expand All @@ -143,25 +143,25 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl
currentOctet = 0
} else if digitCount > 1 && currentOctet == 0 {
// We had a leading zero and now have more digits - invalid
throw IPAddressError.unableToParse
throw AddressError.unableToParse
} else {
// Normal case: build the octet value
currentOctet = currentOctet * 10 + digit
}

// Early termination if octet becomes too large
guard currentOctet <= 255, digitCount <= 3 else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}

} else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}
}

// Validate final octet
guard octetCount == 3, digitCount > 0, digitCount <= 3, currentOctet <= 255 else {
throw IPAddressError.unableToParse
throw AddressError.unableToParse
}

return (result << 8) | UInt32(currentOctet)
Expand Down
Loading