Skip to content
Open
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
Add IPv6 gateway support for container networking
Add ipv6Gateway field to network attachments and status to enable
proper IPv6 routing in containers. This complements the existing
ipv6Address/ipv6Subnet fields.

Changes:
- Attachment: Add ipv6Gateway field
- NetworkState: Add ipv6Gateway to NetworkStatus
- IsolatedInterfaceStrategy: Pass IPv6 gateway to NATInterface
- NonisolatedInterfaceStrategy: Pass IPv6 gateway to NATNetworkInterface
- NetworkService: Include ipv6Gateway in attachment creation
- ReservedVmnetNetwork: Calculate and store ipv6Gateway

Depends on: apple/containerization IPv6 support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
  • Loading branch information
yordsel and claude committed Feb 6, 2026
commit 40abf11b6a891e7de2a5c35fdb22c36f5c727cfb
5 changes: 5 additions & 0 deletions Sources/ContainerResource/Network/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public struct Attachment: Codable, Sendable {
/// The CIDR address describing the interface IPv6 address, with the prefix length of the subnet.
/// The address is nil if the IPv6 subnet could not be determined at network creation time.
public let ipv6Address: CIDRv6?
/// The IPv6 gateway address.
/// The value is nil if the IPv6 subnet could not be determined at network creation time.
public let ipv6Gateway: IPv6Address?
/// The MAC address associated with the attachment (optional).
public let macAddress: MACAddress?

Expand All @@ -38,13 +41,15 @@ public struct Attachment: Codable, Sendable {
ipv4Address: CIDRv4,
ipv4Gateway: IPv4Address,
ipv6Address: CIDRv6?,
ipv6Gateway: IPv6Address? = nil,
macAddress: MACAddress?
) {
self.network = network
self.hostname = hostname
self.ipv4Address = ipv4Address
self.ipv4Gateway = ipv4Gateway
self.ipv6Address = ipv6Address
self.ipv6Gateway = ipv6Gateway
self.macAddress = macAddress
}
}
6 changes: 6 additions & 0 deletions Sources/ContainerResource/Network/NetworkState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,20 @@ public struct NetworkStatus: Codable, Sendable {
/// The value is nil if the IPv6 subnet cannot be determined at creation time.
public let ipv6Subnet: CIDRv6?

/// The gateway IPv6 address.
/// The value is nil if the IPv6 subnet cannot be determined at creation time.
public let ipv6Gateway: IPv6Address?

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

Expand Down
3 changes: 3 additions & 0 deletions Sources/Helpers/RuntimeLinux/IsolatedInterfaceStrategy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ import Containerization
struct IsolatedInterfaceStrategy: InterfaceStrategy {
public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) -> Interface {
let ipv4Gateway = interfaceIndex == 0 ? attachment.ipv4Gateway : nil
let ipv6Gateway = interfaceIndex == 0 ? attachment.ipv6Gateway : nil
return NATInterface(
ipv4Address: attachment.ipv4Address,
ipv4Gateway: ipv4Gateway,
ipv6Address: attachment.ipv6Address,
ipv6Gateway: ipv6Gateway,
macAddress: attachment.macAddress,
// https://github.com/apple/containerization/pull/38
mtu: 1280
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ struct NonisolatedInterfaceStrategy: InterfaceStrategy {

log.info("creating NATNetworkInterface with network reference")
let ipv4Gateway = interfaceIndex == 0 ? attachment.ipv4Gateway : nil
let ipv6Gateway = interfaceIndex == 0 ? attachment.ipv6Gateway : nil
return NATNetworkInterface(
ipv4Address: attachment.ipv4Address,
ipv4Gateway: ipv4Gateway,
ipv6Address: attachment.ipv6Address,
ipv6Gateway: ipv6Gateway,
reference: networkRef,
macAddress: attachment.macAddress,
// https://github.com/apple/containerization/pull/38
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public actor NetworkService: Sendable {
ipv4Address: try CIDRv4(ip, prefix: status.ipv4Subnet.prefix),
ipv4Gateway: status.ipv4Gateway,
ipv6Address: ipv6Address,
ipv6Gateway: status.ipv6Gateway,
macAddress: macAddress
)
log?.info(
Expand All @@ -86,6 +87,7 @@ public actor NetworkService: Sendable {
"ipv4Address": "\(attachment.ipv4Address)",
"ipv4Gateway": "\(attachment.ipv4Gateway)",
"ipv6Address": "\(attachment.ipv6Address?.description ?? "unavailable")",
"ipv6Gateway": "\(attachment.ipv6Gateway?.description ?? "unavailable")",
"macAddress": "\(attachment.macAddress?.description ?? "unspecified")",
])
let reply = message.reply()
Expand Down Expand Up @@ -136,6 +138,7 @@ public actor NetworkService: Sendable {
ipv4Address: ipv4Address,
ipv4Gateway: status.ipv4Gateway,
ipv6Address: ipv6Address,
ipv6Gateway: status.ipv6Gateway,
macAddress: macAddress
)
log?.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public final class ReservedVmnetNetwork: Network {
let ipv4Subnet: CIDRv4
let ipv4Gateway: IPv4Address
let ipv6Subnet: CIDRv6
let ipv6Gateway: IPv6Address
}

private let stateMutex: Mutex<State>
Expand Down Expand Up @@ -85,6 +86,7 @@ public final class ReservedVmnetNetwork: Network {
ipv4Subnet: networkInfo.ipv4Subnet,
ipv4Gateway: networkInfo.ipv4Gateway,
ipv6Subnet: networkInfo.ipv6Subnet,
ipv6Gateway: networkInfo.ipv6Gateway
)
state.networkState = NetworkState.running(configuration, networkStatus)
state.network = networkInfo.network
Expand Down Expand Up @@ -183,6 +185,7 @@ public final class ReservedVmnetNetwork: Network {
}
let prefixIpv6Addr = try IPv6Address(prefixIpv6Bytes)
let runningV6Subnet = try CIDRv6(prefixIpv6Addr, prefix: prefix)
let runningV6Gateway = IPv6Address(runningV6Subnet.lower.value + 1)

log.info(
"started vmnet network",
Expand All @@ -199,6 +202,7 @@ public final class ReservedVmnetNetwork: Network {
ipv4Subnet: runningSubnet,
ipv4Gateway: runningGateway,
ipv6Subnet: runningV6Subnet,
ipv6Gateway: runningV6Gateway
)
}
}