getaddrinfo returning ipv6 for ipv4 string on new iPad Pro only

Greetings Dev Forums,


I'm working on an iOS application written in Swift that includes UDP communications. I am using the IBM BlueSocket library. The code in my application sends out a UDP message on the broadcast address of the Wifi and (if connected) Ethernet network. This is used to allow various instances of the app to discover each other. The code has been in place and working for several months.


Two or three weeks ago I purchased a new 11" iPad Pro. When I connected it to my test network it was not discovered by the other systems running my application. The log for the app is filled with messages like this:


15:31:52.502 - unable to send sendUDPBroadcast: Error code: -9980(0x-26FC), Operation not permitted


The code that results in those messages looks like this:


private func sendUDPBroadcast(address: String, message: String) {
       
        guard let addr = Socket.createAddress(for: address, on: 64068) else { return }
       
        let data = encodeOutput(message: message)
       
        do {
            let socket = try Socket.create(family: .inet, type: .datagram, proto: .udp)
            try socket.udpBroadcast(enable: true)
            try socket.write(from: data, to: addr)
            socket.close()
        } catch let error {
            guard let socketError = error as? Socket.Error else {
                print("Unexpected error in sendUDPBroadcast")
                return
            }
            print("unable to send sendUDPBroadcast: \(socketError.description)")
        }
    }


I checked and this code is working fine on all of my other clients - all iPads running iOS 12.1 (16B92), which is the same version as is running on the new iPad Pro.


In attempting to figure out what is going on with the Pro, I noticed that the addr value created by the call on line 3 was incorrectly flagged as an ipv6 address. Digging in, the BlueSocket library uses the following code to convert a string address in dotted-quad format into an address object:



  ///
  /// Creates an Address for a given host and port.
  ///
  /// - Parameters:
  /// - hostname: Hostname for this signature.
  /// - port: Port for this signature.
  ///
  /// - Returns: An Address instance, or nil if the hostname and port are not valid.
  ///
  public class func createAddress(for host: String, on port: Int32) -> Address? {

       var info: UnsafeMutablePointer?

       // Retrieve the info on our target...
       var status: Int32 = getaddrinfo(host, String(port), nil, &info)
       if status != 0 {
            return nil
       }

       // Defer cleanup of our target info...
       defer {
            if info != nil {
                 freeaddrinfo(info)
            }
       }
       
       var address: Address
       if info!.pointee.ai_family == Int32(AF_INET) {

            var addr = sockaddr_in()
            memcpy(&addr, info!.pointee.ai_addr, Int(MemoryLayout<sockaddr_in>.size))
            address = .ipv4(addr)

       } else if info!.pointee.ai_family == Int32(AF_INET6) {

            var addr = sockaddr_in6()
            memcpy(&addr, info!.pointee.ai_addr, Int(MemoryLayout<sockaddr_in6>.size))
            address = .ipv6(addr)

       } else {
            return nil
       }

       return address
  }


For reasons unknown, the call to getaddrinfo() with the string value "192.168.10.255" and the port value of 64068 results in the pointee.ai_family being set to AF_INET6, but only on this one iPad. The call works as expected on all the other test clients.


I've filed a bug with the BlueSocket project on Github, but it looks more like this is something in the networking layer than in IBM's code.


Anyone have any ideas?


FWIW - I'm based in Cupertino and would be happy to let somebody from the core networking team take a look at this if it would help.

Replies

The code in my application sends out a UDP message on the broadcast address of the Wifi and (if connected) Ethernet network. This is used to allow various instances of the app to discover each other.

Before we start on your actual question, I have to ask why you’re doing that? Most folks use Bonjour [1] for this sort of thing, and:

  • That allows you to use nice, high-level APIs, rather than lots of low-level BSD Sockets goo.

  • The resulting on-the-wire behaviour is likely to be much better than rolling your own code.

Is there some specific reason that Bonjour doesn’t work for you?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Bonjour being an Apple term for

Hi Quinn,


In this case I'm using a non-standard approach to discovery because the network also includes a number of non-iOS devices (legacy embedded sensor systems) which don't have an implementation of MDNS / Avahi / Bonjour. UDP broadcast is light weigh and works pretty well for our specific implementation.


To be honest, I can work around this specific issue in other ways. I was just surprised to find a bit of code that worked fine on all but on specific model of iOS device.


Cheers,


-S

In this case I'm using a non-standard approach to discovery because the network also includes a number of non-iOS devices

Fair enough. Your initial question said “This is used to allow various instances of the app to discover each other”, which is why I asked.

With regards your IPv6 issue, it seems very likely that this specific device is triggering iOS’s RFC 7050 support. That is:

  • The device thinks it’s on an IPv6-only network.

  • getaddrinfo
    has not recognised your destination IP address as local and thus it thinks it needs to be routed over the wider Internet.
  • To accomplish that it returns a mapped IPv6 address

Exactly why that’s happening is unclear. If you’re building this app for some specific environment then it’s probably OK to just apply your workaround. If your app is going to be deployed to a lot of users in a lot of different environments, you should get to the bottom of this because it’s likely that it will show up in other environments, and there’s no guarantee that your workaround will work equally well there.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

That sounds like a strong possibility. The Wifi interface on the iPad is receiving its address assignment from a DHCP server running on our flight data computer, which is definitely providing IPv4 addresses. However, the LTE radio link (T-Mobile) on this one unit is IPv6. The other test units, while LTE capable, do not have SIMs installed and are relying on Wifi or Wifi + Ethernet (over Lightning, also connecting to the same IPv4 DHCP server on the FDC) so are 100% IPv4.


This is admittedly an edge case. Think it's worthy of filing a bug report?

Think it's worthy of filing a bug report?

Probably. Before I recommend that I’d like to confirm some things:

  • The IP address you quoted above, 192.168.10.255, is your Wi-Fi’s network broadcast address, right? If so, please re-test with a specific address on that network, like 192.168.10.123.

  • Also double check that this address is on the network assigned to your Wi-Fi. You can get the networks for all your interfaces using

    getaddrinfo
    . I expect that your Wi-Fi’s network is 192.168.10/24, right?
  • Is any other interface using that network? Or a network that conflicts with that? It seems unlikely, but it’s possible that your WWAN is using a conflicting address, like 192.168.10/16, which would be quite confusing.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Quinn,


Yes, 192.168.10.255 is the broadcast address. I've tested with a valid non-broadcast address (192.168.10.12) and I get the same error.


Yes, the network assigned to the iPad Pro is indeed 192.168.10/24.


These iPads are sometimes connected via both wireless and wired (Lightning to Ethernet for older iPads, USB-C to Ethernet for the new Pro models) connections, but the wired network uses a completely different address scheme (10.25.35/24). For whatever it is worth, neither of these networks has a default gateway - they are boths simply local networks for aircraft systems. The only other network is the T-Mobile LTE which is IPv6.


New evidence:


On a suggestion from the lead of the BlueSocket project, I pulled the SIM card out of the iPad Pro and restarted the app. The error disappeared and the disco service worked as expected. It appears that having the LTE interface bound to an IPv6 address causes getaddrinfo to assume all addresses will be IPv6.


Thanks,


-S

Thanks for all the extra info.

It appears that having the LTE interface bound to an IPv6 address causes

getaddrinfo
to assume all addresses will be IPv6.

Yeah, and I’d argue that that’s a bug. IMO

getaddrinfo
should detect IP addresses on local networks and not do the RFC 7050 thing for them. I’d appreciate you filing a bug about this, then posting your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Here's the bug number: 46628991


Question for you - I'm trying to generate a sysdiagnose report per their request. The instructions don't seem to work for this iPad Pro. Pressing both volume buttons plus the top button simply caues the device to display the power-off slider. Any suggestions?

Here's the bug number: 46628991

Thanks for that.

I'm trying to generate a sysdiagnose report per their request. The instructions don't seem to work for this iPad Pro. Pressing both volume buttons plus the top button simply caues the device to display the power-off slider. Any suggestions?

I just happen to have an iPad Pro to hand (a 9.7-inch, but I think this process is the same on all iPads), so I tried this out (using the instructions from our Bug Reporting > Profiles and Logs page) and it worked as expected.

I have two hints that might help you out here:

  • I removed my iPad from its case; it’s much easier to press all of those buttons when you have direct access.

  • While testing this out I attached my iPad to my Mac and monitored the system log with the search term “sysdiagnose”. When you trigger the sysdiagnose you’ll see two entries. The first shows that you triggered a stack shot via Sleep/Wake and Volume Up:

    default
    09:43:54.137166 +0000
    sysdiagnose
    13285
    request: sysdiagnose (stackshot only) keychord: Power + Volume Up

    The second indicates that you got the full sysdiagnose:

    default
    09:43:55.925376 +0000
    sysdiagnose
    i
    13285
    request: sysdiagnose (full) keychord: ?

    .

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"