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
ifa_netmaskin theifaddrsstruct