Manage ifaddrs to return MAC addresses as well in Swift

I’ve got the following code that successfully retrieves all the IPs connected to my router. But I need to a get the MAC Addresses for each IP.

So instead of addresses being returned as an array with [ips], be returned as a dictionary [ips:0000000, mac: 000000]

  • Fetching IP address of router to which iPhone is connected
  • How to get Domain Name of ipAddress and ipAddress from Domain Name in objective-c
  • How can I get a real IP address from DNS query in Swift?
  • iOS - Get ARP table
  • Is MAC address of iPhone unique and will it change?
  • Getting Device ID or Mac Address in iOS
  • Is it possible to be achieved with changes to the following code (from How to get Ip address in swift)?

    func getIFAddresses() -> [String] {
    
    
    print("GET IF ADDRESSSE")
    
    var addresses = [String]()
    
    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
    if getifaddrs(&ifaddr) == 0 {
    
        print("getifaddrs\(getifaddrs)")
    
        // For each interface ...
        for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) {
            let flags = Int32(ptr.memory.ifa_flags)
            var addr = ptr.memory.ifa_addr.memory
    
            print("flags\(flags)")
            print("addr\(addr)")
    
            // Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
            if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
    
    
    
                if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
    
                    print("addr.sa_family\(addr.sa_family)")
    
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
    
                    print("hostname\(hostname)")
    
                    if (getnameinfo(
                        &addr, socklen_t(addr.sa_len),
                        &hostname,
                        socklen_t(hostname.count),
                        nil,
                        socklen_t(0), NI_NUMERICHOST) == 0) {
    
    
                            if let address = String.fromCString(hostname) {
                                addresses.append(address)
                            }
                    }
                }
            }
        }
        freeifaddrs(ifaddr)
        print("freeifaddrs\(freeifaddrs)")
    }
    print("ADDRESSES \(addresses)")
    return addresses
    

    }

    2 Solutions Collect From Internet About “Manage ifaddrs to return MAC addresses as well in Swift”

    (Remark/clarification: This is an answer to the question “Manage ifaddrs to return MAC addresses as well in Swift” and “Is it possible to modify the code from How to get Ip address in swift to return the MAC addresses as well”.
    This is not a solution to “retrieve all the IPs connected to my router”
    which is also mentioned in the question body.)

    Here is an extension of the referenced code which returns
    the local (up and running) interfaces as
    an array of (interface name, ip address, MAC address) tuples.
    The MAC address is retrieved from the interfaces of type AF_LINK
    which are stored as sockaddr_dl structure in the interface list.
    This is a variable length structure, and Swift’s strict type system makes some pointer juggling necessary.

    Important: This code is meant to run on Mac computers.
    It does not work to get the MAC addresses on iOS devices.
    iOS intentionally returns “02:00:00:00:00:00” as hardware address
    for all interfaces for privacy reasons, see for example
    Trouble with MAC address in iOS 7.0.2.)

    func getInterfaces() -> [(name : String, addr: String, mac : String)] {
    
        var addresses = [(name : String, addr: String, mac : String)]()
        var nameToMac = [ String : String ]()
    
        // Get list of all interfaces on the local machine:
        var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
        if getifaddrs(&ifaddr) == 0 {
    
            // For each interface ...
            var ptr = ifaddr
            while ptr != nil {
                defer { ptr = ptr.memory.ifa_next }
    
                let flags = Int32(ptr.memory.ifa_flags)
                let addr = ptr.memory.ifa_addr
    
                if let name = String.fromCString(ptr.memory.ifa_name)  {
    
                    // Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
                    if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
    
                        if addr.memory.sa_family == UInt8(AF_LINK) {
                            // Get MAC address from sockaddr_dl structure and store in nameToMac dictionary:
                            let dl = UnsafePointer<sockaddr_dl>(ptr.memory.ifa_addr)
                            let lladdr = UnsafeBufferPointer(start: UnsafePointer<Int8>(dl) + 8 + Int(dl.memory.sdl_nlen),
                                                             count: Int(dl.memory.sdl_alen))
                            if lladdr.count == 6 {
                                nameToMac[name] = lladdr.map { String(format:"%02hhx", $0)}.joinWithSeparator(":")
                            }
                        }
    
                        if addr.memory.sa_family == UInt8(AF_INET) || addr.memory.sa_family == UInt8(AF_INET6) {
                            // Convert interface address to a human readable string:
                            var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
                            if (getnameinfo(addr, socklen_t(addr.memory.sa_len), &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST) == 0) {
                                if let address = String.fromCString(hostname) {
                                    addresses.append( (name: name, addr: address, mac : "") )
                                }
                            }
                        }
                    }
                }
            }
            freeifaddrs(ifaddr)
        }
    
        // Now add the mac address to the tuples:
        for (i, addr) in addresses.enumerate() {
            if let mac = nameToMac[addr.name] {
                addresses[i] = (name: addr.name, addr: addr.addr, mac : mac)
            }
        }
    
        return addresses
    }
    

    You have to add

    #include <ifaddrs.h>
    #include <net/if_dl.h>
    

    to the bridging header file to make this compile.

    Example usage:

    for addr in getInterfaces() {
       print(addr)
    }
    // ("en0", "fe80::1234:7fff:fe2e:8765%en0", "a9:55:6f:2e:57:78")
    // ("en0", "192.168.2.108", "a9:55:6f:2e:57:78")
    // ...
    

    Update for Swift 3 (Xcode 8):

    func getInterfaces() -> [(name : String, addr: String, mac : String)] {
    
        var addresses = [(name : String, addr: String, mac : String)]()
        var nameToMac = [ String: String ]()
    
        // Get list of all interfaces on the local machine:
        var ifaddr : UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0 else { return [] }
        guard let firstAddr = ifaddr else { return [] }
    
        // For each interface ...
        for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
            let flags = Int32(ptr.pointee.ifa_flags)
            if let addr = ptr.pointee.ifa_addr {
                let name = String(cString: ptr.pointee.ifa_name)
    
                // Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
                if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
                    switch Int32(addr.pointee.sa_family) {
                    case AF_LINK:
                        // Get MAC address from sockaddr_dl structure and store in nameToMac dictionary:
                        addr.withMemoryRebound(to: sockaddr_dl.self, capacity: 1) { dl in
                            dl.withMemoryRebound(to: Int8.self, capacity: 8 + Int(dl.pointee.sdl_nlen + dl.pointee.sdl_alen)) {
                                let lladdr = UnsafeBufferPointer(start: $0 + 8 + Int(dl.pointee.sdl_nlen),
                                                                 count: Int(dl.pointee.sdl_alen))
                                if lladdr.count == 6 {
                                    nameToMac[name] = lladdr.map { String(format:"%02hhx", $0)}.joined(separator: ":")
                                }
                            }
                        }
                    case AF_INET, AF_INET6:
                        // Convert interface address to a human readable string:
                        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                        if (getnameinfo(addr, socklen_t(addr.pointee.sa_len),
                                        &hostname, socklen_t(hostname.count),
                                        nil, socklen_t(0), NI_NUMERICHOST) == 0) {
                            let address = String(cString: hostname)
                            addresses.append( (name: name, addr: address, mac : "") )
                        }
                    default:
                        break
                    }
                }
            }
        }
    
        freeifaddrs(ifaddr)
    
        // Now add the mac address to the tuples:
        for (i, addr) in addresses.enumerated() {
            if let mac = nameToMac[addr.name] {
                addresses[i] = (name: addr.name, addr: addr.addr, mac : mac)
            }
        }
    
        return addresses
    }
    

    If you will use this inside a framework, you have to add a .modelmap file with this configuration inside it

    module ifaddrs [system]  [extern_c] {
        header
        "/Applications/Xcode_7.3.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/ifaddrs.h"
        export *
    }
    
    module net [system]  [extern_c] {
    
         module types {
             header "/usr/include/sys/types.h"
             export *
         }
    
         module if_dl {
    
             header
             "/usr/include/net/if_dl.h"
             export *
         }
    
     }
    

    and then in your .swift file

    import ifaddrs
    import net.if_dl