Add mtu option for network attachments (#1267)

This commit is contained in:
Raj
2026-03-03 17:57:16 -08:00
committed by GitHub
parent ad7f843751
commit e0bedba81c
9 changed files with 64 additions and 14 deletions
@@ -31,6 +31,8 @@ public struct Attachment: Codable, Sendable {
public let ipv6Address: CIDRv6?
/// The MAC address associated with the attachment (optional).
public let macAddress: MACAddress?
/// The MTU for the network interface.
public let mtu: UInt32?
public init(
network: String,
@@ -38,7 +40,8 @@ public struct Attachment: Codable, Sendable {
ipv4Address: CIDRv4,
ipv4Gateway: IPv4Address,
ipv6Address: CIDRv6?,
macAddress: MACAddress?
macAddress: MACAddress?,
mtu: UInt32? = nil
) {
self.network = network
self.hostname = hostname
@@ -46,5 +49,6 @@ public struct Attachment: Codable, Sendable {
self.ipv4Gateway = ipv4Gateway
self.ipv6Address = ipv6Address
self.macAddress = macAddress
self.mtu = mtu
}
}
@@ -38,8 +38,12 @@ public struct AttachmentOptions: Codable, Sendable {
/// The MAC address associated with the attachment (optional).
public let macAddress: MACAddress?
public init(hostname: String, macAddress: MACAddress? = nil) {
/// The MTU for the network interface.
public let mtu: UInt32?
public init(hostname: String, macAddress: MACAddress? = nil, mtu: UInt32? = nil) {
self.hostname = hostname
self.macAddress = macAddress
self.mtu = mtu
}
}
@@ -30,7 +30,7 @@ struct IsolatedInterfaceStrategy: InterfaceStrategy {
ipv4Gateway: ipv4Gateway,
macAddress: attachment.macAddress,
// https://github.com/apple/containerization/pull/38
mtu: 1280
mtu: attachment.mtu ?? 1280
)
}
}
@@ -50,7 +50,7 @@ struct NonisolatedInterfaceStrategy: InterfaceStrategy {
reference: networkRef,
macAddress: attachment.macAddress,
// https://github.com/apple/containerization/pull/38
mtu: 1280
mtu: attachment.mtu ?? 1280
)
}
}
@@ -268,7 +268,7 @@ public struct Flags {
@Option(name: .long, help: "Use the specified name as the container ID")
public var name: String?
@Option(name: [.customLong("network")], help: "Attach the container to a network (format: <name>[,mac=XX:XX:XX:XX:XX:XX])")
@Option(name: [.customLong("network")], help: "Attach the container to a network (format: <name>[,mac=XX:XX:XX:XX:XX:XX][,mtu=VALUE])")
public var networks: [String] = []
@Flag(name: [.customLong("no-dns")], help: "Do not configure DNS in the container")
@@ -795,16 +795,18 @@ public struct Parser {
public struct ParsedNetwork {
public let name: String
public let macAddress: String?
public let mtu: UInt32?
public init(name: String, macAddress: String? = nil) {
public init(name: String, macAddress: String? = nil, mtu: UInt32? = nil) {
self.name = name
self.macAddress = macAddress
self.mtu = mtu
}
}
/// Parse network attachment with optional properties
/// Format: network_name[,mac=XX:XX:XX:XX:XX:XX]
/// Example: "backend,mac=02:42:ac:11:00:02"
/// Format: network_name[,mac=XX:XX:XX:XX:XX:XX][,mtu=VALUE]
/// Example: "backend,mac=02:42:ac:11:00:02,mtu=1500"
public static func network(_ networkSpec: String) throws -> ParsedNetwork {
guard !networkSpec.isEmpty else {
throw ContainerizationError(.invalidArgument, message: "network specification cannot be empty")
@@ -822,6 +824,7 @@ public struct Parser {
}
var macAddress: String?
var mtu: UInt32?
// Parse properties if any
for part in parts.dropFirst() {
@@ -848,15 +851,23 @@ public struct Parser {
)
}
macAddress = value
case "mtu":
guard let mtuValue = UInt32(value), mtuValue >= 1280, mtuValue <= 65535 else {
throw ContainerizationError(
.invalidArgument,
message: "invalid mtu value '\(value)': must be between 1280 and 65535"
)
}
mtu = mtuValue
default:
throw ContainerizationError(
.invalidArgument,
message: "unknown network property '\(key)'. Available properties: mac"
message: "unknown network property '\(key)'. Available properties: mac, mtu"
)
}
}
return ParsedNetwork(name: networkName, macAddress: macAddress)
return ParsedNetwork(name: networkName, macAddress: macAddress, mtu: mtu)
}
// MARK: DNS
@@ -297,15 +297,16 @@ public struct Utility {
// attach the first network using the fqdn, and the rest using just the container ID
return try networks.enumerated().map { item in
let macAddress = try item.element.macAddress.map { try MACAddress($0) }
let mtu = item.element.mtu ?? 1280
guard item.offset == 0 else {
return AttachmentConfiguration(
network: item.element.name,
options: AttachmentOptions(hostname: containerId, macAddress: macAddress)
options: AttachmentOptions(hostname: containerId, macAddress: macAddress, mtu: mtu)
)
}
return AttachmentConfiguration(
network: item.element.name,
options: AttachmentOptions(hostname: fqdn ?? containerId, macAddress: macAddress)
options: AttachmentOptions(hostname: fqdn ?? containerId, macAddress: macAddress, mtu: mtu)
)
}
}
@@ -314,7 +315,7 @@ public struct Utility {
guard let builtinNetworkId else {
throw ContainerizationError(.invalidState, message: "builtin network is not present")
}
return [AttachmentConfiguration(network: builtinNetworkId, options: AttachmentOptions(hostname: fqdn ?? containerId, macAddress: nil))]
return [AttachmentConfiguration(network: builtinNetworkId, options: AttachmentOptions(hostname: fqdn ?? containerId, macAddress: nil, mtu: 1280))]
}
private static func getKernel(management: Flags.Management) async throws -> Kernel {
@@ -417,9 +417,26 @@ public actor ContainersService {
hostname: n.options.hostname,
macAddress: n.options.macAddress
)
guard let allocatedAttach = allocatedAttach else {
guard var allocatedAttach = allocatedAttach else {
throw ContainerizationError(.internalError, message: "failed to allocate a network")
}
if let mtu = n.options.mtu {
let a = allocatedAttach.attachment
allocatedAttach = AllocatedAttachment(
attachment: Attachment(
network: a.network,
hostname: a.hostname,
ipv4Address: a.ipv4Address,
ipv4Gateway: a.ipv4Gateway,
ipv6Address: a.ipv6Address,
macAddress: a.macAddress,
mtu: mtu
),
additionalData: allocatedAttach.additionalData,
pluginInfo: allocatedAttach.pluginInfo
)
}
allocatedAttachments.append(allocatedAttach)
}
@@ -191,6 +191,19 @@ class TestCLINetwork: CLITest {
}
}
@Test func testNetworkMTU() async throws {
let name = getLowercasedTestName()
try? doStop(name: name)
try? doRemove(name: name)
try doLongRun(name: name, args: ["--network", "default,mtu=1500"])
defer { try? doStop(name: name) }
try waitForContainerRunning(name)
let output = try doExec(name: name, cmd: ["ip", "link", "show", "eth0"])
#expect(output.contains("mtu 1500"), "expected mtu 1500 in ip link output: \(output)")
}
@available(macOS 26, *)
@Test func testIsolatedNetwork() async throws {
do {