title: Binding UDP Socket to Cellular IP
body: <p>I am trying to create an iOS client that sends data to a server on a UDP socket over the device's cellular communication.</p>
  
  <p>Following <a href="https://stackoverflow.com/questions/18755051/does-ios-support-simultaneous-wifi-and-3g-4g-connections">Does IOS support simultaneous wifi and 3g/4g connections?
  </a> link to <a href="https://gist.github.com/avesus/fdb465b60a4f5204845c" rel="nofollow noreferrer">iOS Multipath BSD Sockets Test</a>, I've tried implementing the solution in Swift 3, that is enumerate network interfaces in the device, identifying the Cellular interface (as suggested in <a href="https://stackoverflow.com/questions/30748480/swift-get-devices-ip-address">Swift - Get device's IP Address</a>), create a UDP socket and bind it to the <strong>sockaddr</strong> retrieved from the interface.</p>
  
  <p>Implementation of socket programming in Swift was done by following examples from <a href="http://swiftrien.blogspot.co.il/2015/10/socket-programming-in-swift-part-1.html" rel="nofollow noreferrer">Socket Programming in Swift: Part 1 - getaddrinfo</a> and following posts.</p>
  
  <p>Unfortunately I received <strong>Operation not permitted</strong> when trying to send data on the socket, so instead I've tried creating the socket and binding it to the data from <strong>getaddrinfo</strong> called on a designated port (5555).</p>
  
  <p>That too didn't do the trick.
  The interesting thing is that while trying to understand what's wrong, I created a test application for both methods, and when tested for 1000 consecutive create->bind->send->close, about 3-5 of the attempts actually <strong>did</strong> send the data without the error on either method.</p>
  
  <p>Needless to say this was tested on an actual iPhone.</p>
  
  <p>Quite at a loss, I'd appreciate any advice regarding this.</p>
  
  <p>Code implemented in a static "SocketManager" class (<strong>edit:</strong> fixed sockaddr allocation size)</p>
  
  <pre><code>// Return IP address String, port String &amp; sockaddr of WWAN interface (pdp_ip0), or `nil`
  public static func getInterface() -&gt; (String?, String?, UnsafeMutablePointer&lt;sockaddr&gt;?) {
      var host : String?
      var service : String?
  
      // Get list of all interfaces on the local machine:
      var ifaddr : UnsafeMutablePointer&lt;ifaddrs&gt;?
      var clt : UnsafeMutablePointer&lt;sockaddr&gt;?
  
      guard getifaddrs(&amp;ifaddr) == 0 else {
          return (nil, nil, clt)
      }
      guard let firstAddr = ifaddr else {
          return (nil, nil, clt)
      }
  
      // For each interface ...
      for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
          let interface = ifptr.pointee
          let flags = Int32(ifptr.pointee.ifa_flags)
  
          /// Check for running IPv4 interfaces. Skip the loopback interface.
          if (flags &amp; (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
              let addrFamily = interface.ifa_addr.pointee.sa_family
              if addrFamily == UInt8(AF_INET) { //Interested in IPv4 for in particular case
  
                  // Check interface name:
                  let name = String(cString: interface.ifa_name)
                  print("interface name: \(name)")
                  if  name.hasPrefix("pdp_ip") { //cellular interface
  
                      // Convert interface address to a human readable string:
                      let ifa_addr_Value = interface.ifa_addr.pointee
                      clt = UnsafeMutablePointer&lt;sockaddr&gt;.allocate(capacity: 1)
                      clt?.initialize(to: ifa_addr_Value, count: 1)
  
                      var hostnameBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                      var serviceBuffer = [CChar](repeating: 0, count: Int(NI_MAXSERV))
                      getnameinfo(interface.ifa_addr, socklen_t(ifa_addr_Value.sa_len),
                              &amp;hostnameBuffer, socklen_t(hostnameBuffer.count),
                              &amp;serviceBuffer,
                              socklen_t(serviceBuffer.count),
                              NI_NUMERICHOST | NI_NUMERICSERV)
                      host = String(cString: hostnameBuffer)
                      if let host = host {
                        print("found host \(String(describing: host))")
                      }
  
                      service = String(cString: serviceBuffer)
                      if let service = service {
                          print("found service \(String(describing: service))")
                      }
                      break;
                  }
              }
          }
      }
      freeifaddrs(ifaddr)
  
      return (host, service, clt)
  }
  
  public static func bindSocket(ip: String, port : String, clt : UnsafeMutablePointer&lt;sockaddr&gt;, useCltAddr : Bool = false) -&gt; Int32 {
  
      print("binding socket for IP: \(ip):\(port) withCltAddr=\(useCltAddr)")
      var hints = addrinfo(ai_flags: 0,
                              ai_family: AF_INET,
                              ai_socktype: SOCK_DGRAM,
                              ai_protocol: IPPROTO_UDP,
                              ai_addrlen: 0,
                              ai_canonname: nil,
                              ai_addr: nil,
                              ai_next: nil)
  
      var connectionInfo : UnsafeMutablePointer&lt;addrinfo&gt;? = nil
  
      let status = getaddrinfo(
              ip,
              port,
              &amp;hints,
              &amp;connectionInfo)
      if status != 0 {
          var strError: String
          if status == EAI_SYSTEM {
              strError = String(validatingUTF8: strerror(errno)) ?? "Unknown error code"
          } else {
              strError = String(validatingUTF8: gai_strerror(status)) ?? "Unknown error code"
          }
          print(strError)
          return -1
      }
  
      let socketDescriptor = useCltAddr ? socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) : socket(connectionInfo!.pointee.ai_family, connectionInfo!.pointee.ai_socktype, connectionInfo!.pointee.ai_protocol)
  
      if socketDescriptor == -1 {
          let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
          let message = "Socket creation error \(errno) (\(strError))"
          freeaddrinfo(connectionInfo)
          print(message)
          return -1
      }
      let res = useCltAddr ? bind(socketDescriptor, clt, socklen_t(clt.pointee.sa_len)) : bind(socketDescriptor, connectionInfo?.pointee.ai_addr, socklen_t((connectionInfo?.pointee.ai_addrlen)!))
  
      if res != 0 {
          let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
          let message = "Socket bind error \(errno) (\(strError))"
          freeaddrinfo(connectionInfo)
          close(socketDescriptor)
          print(message)
          return -1
      }
      freeaddrinfo(connectionInfo)
      print("returned socket descriptor \(socketDescriptor)")
      return socketDescriptor   
  }
  
  //returns 0 for failure, 1 for success
  public static func sendData(toIP: String, onPort : String, withSocketDescriptor : Int32, data : Data) -&gt; Int{
  
      print("sendData called for targetIP: \(toIP):\(onPort) with socket descriptor: \(withSocketDescriptor)")
      var target = UnsafeMutablePointer&lt;sockaddr_in&gt;.allocate(capacity: MemoryLayout&lt;sockaddr_in&gt;.size)
  
      target.pointee.sin_family = sa_family_t(AF_INET)
      target.pointee.sin_addr.s_addr = inet_addr(toIP)
      target.pointee.sin_port = in_port_t(onPort)!
  
      var res = 0
      data.withUnsafeBytes { (u8Ptr: UnsafePointer&lt;UInt8&gt;) in
          let rawPtr = UnsafeRawPointer(u8Ptr)
          withUnsafeMutablePointer(to: &amp;target) {
              $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                  let bytesSent = sendto(withSocketDescriptor, rawPtr, data.count, 0, $0, socklen_t(MemoryLayout.size(ofValue: target)))
                  if bytesSent &gt; 0 {
                      print(" Sent \(bytesSent) bytes ")
                      res = 1
                  }
                  if bytesSent == -1 {
                      let strError = String(utf8String: strerror(errno)) ?? "Unknown error code"
                      let message = "Socket sendto error \(errno) (\(strError))"
                      print(message)
                  }
              }
          }
      }
      return res
  }
  
  public static func closeSocket(socketDescriptor : Int32, clt : UnsafeMutablePointer&lt;sockaddr&gt;) {
      print("closing socket descriptor \(socketDescriptor)")
      close(socketDescriptor)
      clt.deinitialize()
      clt.deallocate(capacity: 1) 
  }
  </code></pre>
  
  <p>On ViewController:</p>
  
  <pre><code>override func viewDidLoad() {
      super.viewDidLoad()
      var i = 0
      for _ in 0..&lt;1000 {
          i += connectSendClose(withDescriptor: false) // change withDescriptor to switch socket create/bind method
      }
      print("Sent \(i) packets")   
  }
  
  private func connectSendClose(withDescriptor : Bool) -&gt; Int {
      let interface = SocketManager.getInterface()
      guard let ip = interface.0 else {
          print("no relevant interface")
          return 0
      }
      guard let clt = interface.2 else {
          print("no addr")
          return 0
      }
      let socketDescriptor = SocketManager.bindSocket(ip: ip, port: "5555", clt: clt, useCltAddr: withDescriptor)
      if socketDescriptor == -1 {
          print("faild to configure socket")
          return 0
      }
      let serverIP = "59.122.442.9" //dummy IP, test was preformed on actual server 
      let serverPort = "10025" //dummy port, test was preformed on actual server
      let input = 42.13
      var value = input
      let data = withUnsafePointer(to: &amp;value) {
          Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: input))
      }
  
      let res = SocketManager.sendData(toIP: serverIP, onPort: serverPort, withSocketDescriptor: socketDescriptor, data: data)
  
      SocketManager.closeSocket(socketDescriptor: socketDescriptor, clt: clt)
      return res
  
  }
  </code></pre>