mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
Added ability to get ip for AppleVirtualization machines (#7643)
* added ability to get ip for AppleVirt machines * fixed comments
This commit is contained in:
@@ -280,7 +280,21 @@ class UTMScriptingVirtualMachineImpl: NSObject, UTMScriptable {
|
||||
|
||||
@objc func queryIp(_ command: NSScriptCommand) {
|
||||
withScriptCommand(command) { [self] in
|
||||
try await withGuestAgent { guestAgent in
|
||||
// Apple Virtualization backend: no guest agent available
|
||||
if let appleVM = vm as? UTMAppleVirtualMachine {
|
||||
guard appleVM.state == .started else {
|
||||
throw ScriptingError.notRunning
|
||||
}
|
||||
|
||||
guard let network = appleVM.config.networks.first else {
|
||||
return []
|
||||
}
|
||||
let macAddress = network.macAddress.lowercased()
|
||||
return Self.ipFromARP(macAddress: macAddress)
|
||||
}
|
||||
|
||||
// Non-Apple backend (QEMU): use guest agent
|
||||
return try await withGuestAgent { guestAgent in
|
||||
let interfaces = try await guestAgent.guestNetworkGetInterfaces()
|
||||
var ipv4: [String] = []
|
||||
var ipv6: [String] = []
|
||||
@@ -303,6 +317,72 @@ class UTMScriptingVirtualMachineImpl: NSObject, UTMScriptable {
|
||||
}
|
||||
}
|
||||
|
||||
extension UTMScriptingVirtualMachineImpl {
|
||||
|
||||
/// Normalizes a colon-separated MAC address by stripping leading zeros from each
|
||||
/// octet so that `%x`-formatted bytes compare equal to stored representations.
|
||||
///
|
||||
/// Example: `"ce:09:f1:ce:7f:f2"` → `"ce:9:f1:ce:7f:f2"`
|
||||
private static func normalizeMac(_ mac: String) -> String {
|
||||
mac.split(separator: ":").map { octet in
|
||||
let stripped = octet.drop(while: { $0 == "0" })
|
||||
return stripped.isEmpty ? "0" : String(stripped)
|
||||
}.joined(separator: ":")
|
||||
}
|
||||
|
||||
/// Find the IP address for the given MAC by querying the kernel ARP cache via
|
||||
/// `sysctl(CTL_NET, PF_ROUTE, …, NET_RT_FLAGS, RTF_LLINFO)`.
|
||||
///
|
||||
/// - Parameter macAddress: Lowercase colon-separated MAC, e.g. `"ce:09:f1:ce:7f:f2"`.
|
||||
/// - Returns: A single-element array with the IP, or empty if not found.
|
||||
static func ipFromARP(macAddress: String) -> [String] {
|
||||
var mib: [Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO]
|
||||
var needed = 0
|
||||
guard sysctl(&mib, 6, nil, &needed, nil, 0) == 0, needed > 0 else { return [] }
|
||||
|
||||
var buf = [UInt8](repeating: 0, count: needed)
|
||||
guard sysctl(&mib, 6, &buf, &needed, nil, 0) == 0 else { return [] }
|
||||
|
||||
let normalizedTarget = normalizeMac(macAddress)
|
||||
var offset = 0
|
||||
|
||||
while offset + MemoryLayout<rt_msghdr>.stride <= needed {
|
||||
let msglen = Int(buf.withUnsafeBytes {
|
||||
$0.load(fromByteOffset: offset, as: rt_msghdr.self).rtm_msglen
|
||||
})
|
||||
guard msglen > 0, offset + msglen <= needed else { break }
|
||||
defer { offset += msglen }
|
||||
|
||||
// Sockaddrs start immediately after rt_msghdr.
|
||||
// First: sockaddr_in (destination IP). Layout: len(1) family(1) port(2) addr(4) …
|
||||
let sinStart = offset + MemoryLayout<rt_msghdr>.stride
|
||||
guard sinStart + 8 <= needed else { continue }
|
||||
let sinLen = Int(buf[sinStart])
|
||||
let sinFamily = buf[sinStart + 1]
|
||||
guard sinFamily == UInt8(AF_INET), sinLen >= 8 else { continue }
|
||||
let ipStr = buf[(sinStart + 4)..<(sinStart + 8)].map { String($0) }.joined(separator: ".")
|
||||
|
||||
// Second: sockaddr_dl (link-layer MAC). Padded to sizeof(long) = 8.
|
||||
// Layout: len(1) family(1) index(2) type(1) nlen(1) alen(1) slen(1) data[nlen+alen…]
|
||||
let sdlStart = sinStart + ((sinLen + 7) & ~7)
|
||||
guard sdlStart + 8 <= needed else { continue }
|
||||
let sdlFamily = buf[sdlStart + 1]
|
||||
let sdlNlen = Int(buf[sdlStart + 5])
|
||||
let sdlAlen = Int(buf[sdlStart + 6])
|
||||
guard sdlFamily == UInt8(AF_LINK), sdlAlen == 6 else { continue }
|
||||
|
||||
let macStart = sdlStart + 8 + sdlNlen
|
||||
guard macStart + 6 <= needed else { continue }
|
||||
let mac = buf[macStart..<(macStart + 6)].map { String(format: "%x", $0) }.joined(separator: ":")
|
||||
if normalizeMac(mac) == normalizedTarget {
|
||||
return [ipStr]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Errors
|
||||
extension UTMScriptingVirtualMachineImpl {
|
||||
enum ScriptingError: Error, LocalizedError {
|
||||
|
||||
Reference in New Issue
Block a user