Manage ifaddrs to return MAC addresses as well in

2020-02-01 02:56发布

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]

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条回答
等我变得足够好
2楼-- · 2020-02-01 03:06

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
查看更多
地球回转人心会变
3楼-- · 2020-02-01 03:14

(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
}
查看更多
登录 后发表回答