Swift - Get Device'S Wifi Ip Address

Swift - Get device's WIFI IP Address

According to several SO threads (e.g. What exactly means iOS networking interface name? what's pdp_ip ? what's ap?), the WiFi interface on an iOS device always has then name "en0".

Your code (which seems to be what I answered at How to get Ip address in swift :) retrieves a list of the IP addresses of all running network interfaces. It can easily be modified to return only the IP address
of the "en0" interface, and actually that is what I originally had
answered at that thread (and this is just a Swift translation of the
answer to how to get ip address of iphone programmatically):

// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? {
var address : 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 interface = ptr.memory

// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.memory.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

// Check interface name:
if let name = String.fromCString(interface.ifa_name) where name == "en0" {

// Convert interface address to a human readable string:
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.memory.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String.fromCString(hostname)
}
}
}
freeifaddrs(ifaddr)
}

return address
}

Usage:

if let addr = getWiFiAddress() {
print(addr)
} else {
print("No WiFi address")
}

Update for Swift 3: In addition to adopting the code to the
many changes in Swift 3,
iterating over all interfaces can now use the new generalized
sequence() function:

Do NOT forget to add #include <ifaddrs.h> in your bridging header

// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? {
var address : String?

// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }

// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee

// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {

// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)

return address
}

For those of you who came looking for more than the WIFI IP you could modify this code a little

func getAddress(for network: Network) -> String? {
var address: String?

// Get list of all interfaces on the local machine:
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }

// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee

// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

// Check interface name:
let name = String(cString: interface.ifa_name)
if name == network.rawValue {

// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)

return address
}
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
//... case ipv4 = "ipv4"
//... case ipv6 = "ipv6"
}

Then we have access to the cellular IP as well.

guard let wifiIp = getAddress(for: .wifi) else { return }

&

guard let cellularIp = getAddress(for: .cellular) else { return }

Get IPAddress of iPhone or iPad device Using Swift 3

Add #include<ifaddrs.h> in your bridging header.

This is the framework needed to get IP address.

Also you can refer the following link:

Swift - Get device's IP Address

Get Network (Not device) IP address iOS swift

It seems that you are looking for your external IP, while your local IP is 192.168.4.1. Without the help of some external service, it is impossible ...

UPDATE
see rfc1533 rfc2131 for details, try in Playground :-)

import Foundation

func pack<T:FixedWidthInteger>(_ fi: T)->Data {
var nfi = fi
if 1 == 1.littleEndian {
nfi = fi.bigEndian
}
return withUnsafeBytes(of: nfi) { un -> Data in
var data = Data()
un.forEach({ (byte) in
data.append(byte)
})
return data
}
}

enum StringPack {
case ipv4, ipv6, mac
}

func pack(_ txt: String, type: StringPack)->Data {
var data = Data()
switch type {
case .ipv4:
txt.split(separator: ".", omittingEmptySubsequences: false).forEach { s in
data.append(UInt8(s) ?? 0)
}
while data.count < 4 { // padding with 0
data.append(0)
}
case .ipv6:
txt.split(separator: ":", omittingEmptySubsequences: false).forEach { (s) in
data.append(pack(UInt16(s, radix: 16) ?? 0))
}
while data.count < 8 { // padding with 0
data.append(0)
}
case .mac:
txt.split(separator: ":", omittingEmptySubsequences: false).forEach { s in
s
UInt8(s, radix:16)
data.append(UInt8(s, radix:16) ?? 0)
}
while data.count < 16 { // padding with 0
data.append(0)
}
}
return data
}

func dhcp_packet(
// all parameters have default value (https://www.ietf.org/rfc/rfc2131.txt) for client using wifi interface
op: UInt8 = 1, htype: UInt8 = 1, hlen: UInt8 = 6, hops: UInt8 = 0,
xid: UInt32 = UInt32.random(in: UInt32.min...UInt32.max),
secs: UInt16 = 0, flags: UInt16 = 0,
ciaddr: String = "...",
yiaddr: String = "...",
siaddr: String = "...",
giaddr: String = "...",
chaddr: String = ":::::",
sname: String = "",
file: String = ""
)->Data {
// data represents dhcp_packet
var data = Data()
// sd tuple represents predefined fixed size data
var sd:(Data, Int)

data.append(op)
data.append(htype)
data.append(hlen)
data.append(hops)
data.append(pack(xid))
data.append(pack(secs))
data.append(pack(flags))
data.append(pack(ciaddr, type: .ipv4))
data.append(pack(yiaddr, type: .ipv4))
data.append(pack(siaddr, type: .ipv4))
data.append(pack(giaddr, type: .ipv4))
data.append(pack(chaddr, type: .mac))
sd = (Data(count: 64), min(sname.utf8.count, 64))
sd.0.replaceSubrange(0..<sd.1, with: Data(sname.utf8)[0..<sd.1])
data.append(sd.0)
sd = (Data(count: 128), min(file.utf8.count, 128))
sd.0.replaceSubrange(0..<sd.1, with: Data(sname.utf8)[0..<sd.1])
data.append(sd.0)

return data
}


import Darwin

func getWiFiAddress() -> (ip4: String, mac: String, addr: sockaddr_in) {

var address : String = "..."
var mac: String = ":::::"
var success: Bool
var addr_in = sockaddr_in()
var ifaddr : UnsafeMutablePointer<ifaddrs>?
success = getifaddrs(&ifaddr) == 0
assert(success)
assert(ifaddr != nil)
let firstAddr = ifaddr!

for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
let addrFamily = interface.ifa_addr.pointee.sa_family
let name = String(cString: interface.ifa_name)
if name == "en0" {
if addrFamily == UInt8(AF_INET) {
var addr = interface.ifa_addr.pointee
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
success = getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0
assert(success)
addr_in = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}
address = String(cString: hostname)
}
if addrFamily == UInt8(AF_LINK) {
interface.ifa_addr.withMemoryRebound(to: sockaddr_dl.self, capacity: 1) { (sdl) -> Void in
var hw = sdl.pointee.sdl_data
withUnsafeBytes(of: &hw, { (p) -> Void in
mac = p[Int(sdl.pointee.sdl_nlen)..<Int(sdl.pointee.sdl_alen + sdl.pointee.sdl_nlen)].map({ (u) -> String in
var s = String(u, radix:16)
if s.count < 2 {
s.append("0")
s = String(s.reversed())
}
return s
}).joined(separator: ":")
})
}
}
}
}
freeifaddrs(ifaddr)


return (address, mac, addr_in)
}

func sendBroadcast(data: Data, toPort: UInt16, waitForReplayOn: sockaddr_in)->Data {

var wifiInterface: UInt32
var fd: Int32
var success: Bool
var destAddr = sockaddr_in()
var response = Data()

wifiInterface = if_nametoindex("en0")
assert(wifiInterface != 0)

fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
assert(fd >= 0)

var kOne = 1
success = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
assert(success)

success = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
assert(success)

success = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kOne, socklen_t(MemoryLayout.size(ofValue: kOne))) == 0
assert(success)

var wait = timeval(tv_sec: 0, tv_usec: 64000)
success = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &wait, socklen_t(MemoryLayout.size(ofValue: wait))) == 0
assert(success)

success = setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &wifiInterface, socklen_t(MemoryLayout.size(ofValue: wifiInterface))) == 0
assert(success)

var addr_in = waitForReplayOn
success = bindresvport(fd, &addr_in) == 0
assert(success)

destAddr.sin_family = sa_family_t(AF_INET)
destAddr.sin_len = __uint8_t(MemoryLayout.size(ofValue: destAddr))
destAddr.sin_addr.s_addr = INADDR_BROADCAST
destAddr.sin_port = in_port_t(toPort.bigEndian)

let bytesSent = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Int in
let destAddrSize = socklen_t(MemoryLayout.size(ofValue: destAddr))
return withUnsafePointer(to: &destAddr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
sendto(fd, bytes, data.count, 0, $0, destAddrSize)
}
}
}

if (bytesSent >= 0) {
print("DHCP packet with \(bytesSent) bytes broadcasted to UDP port \(toPort)")
var receiveBuffer = [UInt8](repeating: 0, count: 1024)
let bytes = recv(fd, &receiveBuffer, receiveBuffer.count, 0)
response.append(contentsOf: receiveBuffer[0..<bytes])
} else {
print("error", errno)
}

success = close(fd) == 0
assert(success)
return response
}


var en0info = getWiFiAddress()
var packet = dhcp_packet(/*ciaddr: en0info.ip4,*/ chaddr: en0info.mac)

let dhcp_MAGIC_COOKIE: [UInt8] = [0x63, 0x82, 0x53, 0x63]
// DHCP_OPTIONS [code, length, value]
let dhcp_DHCPINFORM : [UInt8] = [53, 1, 8]
// we request router(s) address (it is standart report, but ... :-)
// see https://www.ietf.org/rfc/rfc1533.txt
let dhcp_PARAMETER_REQUEST_LIST: [UInt8] = [55, 1, 3]
let dhcp_OPTIONS_END: UInt8 = 0xFF

packet.append(contentsOf: dhcp_MAGIC_COOKIE)
packet.append(contentsOf: dhcp_DHCPINFORM)
packet.append(contentsOf: dhcp_PARAMETER_REQUEST_LIST)
packet.append(dhcp_OPTIONS_END)

en0info.addr.sin_len = __uint8_t(MemoryLayout.size(ofValue: sockaddr_in()))
en0info.addr.sin_port = in_port_t(UInt16(68).bigEndian)
en0info.addr.sin_addr.s_addr = INADDR_ANY

var success = false
var attempt = 5
var response = Data()

repeat {
response = sendBroadcast(data: packet, toPort: 67, waitForReplayOn: en0info.addr)
// if succes is false, response is not for us, or invalid
success = response[1..<240] == packet[1..<240]
attempt -= 1
} while success == false && attempt > 0

if success == true {

success = false
var index = 240
let maxIndex = response.count
var option = (code: UInt8, length: UInt8, value: [UInt8])(0,0,[])
var options = [UInt8: [UInt8]]()

repeat {
option.code = response[index]
index += 1
if option.code == 0 {
continue
}
if option.code == 255 {
success = true
break
}
option.length = response[index]
index += 1
let nexti = index + Int(option.length)
if nexti <= maxIndex {
option.value = Array(response[index..<nexti])
options[option.code] = option.value
}
index = nexti
} while index < maxIndex

print(options, success ? "OK" : "incoplete")
} else {
print("DHCPINFORM failed")
}

On my environment it prints

DHCP packet with 244 bytes broadcasted to UDP port 67
[3: [192, 168, 8, 1], 6: [192, 168, 8, 1, 192, 168, 8, 1], 53: [5], 54: [192, 168, 8, 1], 1: [255, 255, 255, 0]]

where:

option 3 represents a list of the routers

option 6 represents a list of DNS servers

option 53 means DHCP Message Type DHCPACK

option 54 means DHCP Server Identifier (where is this particular response from)

option 1 represents Subnet Mask



Related Topics



Leave a reply



Submit