@@ -62,6 +62,14 @@ public struct Utility {
6262 }
6363 }
6464
65+ public static func validMACAddress( _ macAddress: String ) throws {
66+ let pattern = #"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"#
67+ let regex = try Regex ( pattern)
68+ if try regex. firstMatch ( in: macAddress) == nil {
69+ throw ContainerizationError ( . invalidArgument, message: " invalid MAC address format \( macAddress) , expected format: XX:XX:XX:XX:XX:XX " )
70+ }
71+ }
72+
6573 public static func containerConfigFromFlags(
6674 id: String ,
6775 image: String ,
@@ -176,13 +184,15 @@ public struct Utility {
176184
177185 config. virtualization = management. virtualization
178186
187+ // Parse network specifications with properties
188+ let parsedNetworks = try management. networks. map { try Parser . network ( $0) }
179189 if management. networks. contains ( ClientNetwork . noNetworkName) {
180190 guard management. networks. count == 1 else {
181191 throw ContainerizationError ( . unsupported, message: " no other networks may be created along with network \( ClientNetwork . noNetworkName) " )
182192 }
183193 config. networks = [ ]
184194 } else {
185- config. networks = try getAttachmentConfigurations ( containerId: config. id, networkIds : management . networks )
195+ config. networks = try getAttachmentConfigurations ( containerId: config. id, networks : parsedNetworks )
186196 for attachmentConfiguration in config. networks {
187197 let network : NetworkState = try await ClientNetwork . get ( id: attachmentConfiguration. network)
188198 guard case . running( _, _) = network else {
@@ -220,7 +230,14 @@ public struct Utility {
220230 return ( config, kernel)
221231 }
222232
223- static func getAttachmentConfigurations( containerId: String , networkIds: [ String ] ) throws -> [ AttachmentConfiguration ] {
233+ static func getAttachmentConfigurations( containerId: String , networks: [ Parser . ParsedNetwork ] ) throws -> [ AttachmentConfiguration ] {
234+ // Validate MAC addresses if provided
235+ for network in networks {
236+ if let mac = network. macAddress {
237+ try validMACAddress ( mac)
238+ }
239+ }
240+
224241 // make an FQDN for the first interface
225242 let fqdn : String ?
226243 if !containerId. contains ( " . " ) {
@@ -235,22 +252,33 @@ public struct Utility {
235252 fqdn = " \( containerId) . "
236253 }
237254
238- guard networkIds. isEmpty else {
239- // networks may only be specified for macOS 26+
240- guard #available( macOS 26 , * ) else {
241- throw ContainerizationError ( . invalidArgument, message: " non-default network configuration requires macOS 26 or newer " )
255+ guard networks. isEmpty else {
256+ // Check if this is only the default network with properties (e.g., MAC address)
257+ let isOnlyDefaultNetwork = networks. count == 1 && networks [ 0 ] . name == ClientNetwork . defaultNetworkName
258+
259+ // networks may only be specified for macOS 26+ (except for default network with properties)
260+ if !isOnlyDefaultNetwork {
261+ guard #available( macOS 26 , * ) else {
262+ throw ContainerizationError ( . invalidArgument, message: " non-default network configuration requires macOS 26 or newer " )
263+ }
242264 }
243265
244266 // attach the first network using the fqdn, and the rest using just the container ID
245- return networkIds . enumerated ( ) . map { item in
267+ return networks . enumerated ( ) . map { item in
246268 guard item. offset == 0 else {
247- return AttachmentConfiguration ( network: item. element, options: AttachmentOptions ( hostname: containerId) )
269+ return AttachmentConfiguration (
270+ network: item. element. name,
271+ options: AttachmentOptions ( hostname: containerId, macAddress: item. element. macAddress)
272+ )
248273 }
249- return AttachmentConfiguration ( network: item. element, options: AttachmentOptions ( hostname: fqdn ?? containerId) )
274+ return AttachmentConfiguration (
275+ network: item. element. name,
276+ options: AttachmentOptions ( hostname: fqdn ?? containerId, macAddress: item. element. macAddress)
277+ )
250278 }
251279 }
252280 // if no networks specified, attach to the default network
253- return [ AttachmentConfiguration ( network: ClientNetwork . defaultNetworkName, options: AttachmentOptions ( hostname: fqdn ?? containerId) ) ]
281+ return [ AttachmentConfiguration ( network: ClientNetwork . defaultNetworkName, options: AttachmentOptions ( hostname: fqdn ?? containerId, macAddress : nil ) ) ]
254282 }
255283
256284 private static func getKernel( management: Flags . Management ) async throws -> Kernel {
0 commit comments