5 Replies
      Latest reply on Mar 18, 2019 2:08 PM by eskimo
      improb.worker.team Level 1 Level 1 (0 points)

        Hello,

         

        I am in the process of trying to make an application work correctly in an IPv6 only environment on iOS as required for App Store certification. It works by sending a request to some service which returns an IP address to establish a UDP connection with. Currently, this returns an IPv4 address to connect to (via a low level socket), and it's a large amount of work to get this to support IPv6 correctly.

         

        For the time being, I am trying to use getaddrinfo to synthesize an IPv6 address using the code example provided in https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW23


        however, it just returns the IPv4 address - rather than the synthesized IPv6 address - when running on an iPhone XS (running iOS 12.1.4) with no sim card, connected via WiFi to a MacBook Pro wifi hotspot (running macOS 10.14.3) with the NAT64 proxy enabled.

        Looking at the wifi information on the iPhone itself, it currently has a link-local IPv4 address (169.254.0.0/24) and 2 IPv6 addresses (starting 2001:2::aab1:...), so I believe the setup is correct.

         

        I created an Xcode iOS project with the following code in main.mm:

        #import 
        #import "AppDelegate.h"
        
        #include 
        #include 
        #include 
        #include 
        
        #include 
        
        void test_getaddrinfo(int family) {
            uint8_t ipv4[4] = {192, 0, 2, 1};
            struct addrinfo hints, *res, *res0;
            int error;
            
            char ipv4_str_buf[INET_ADDRSTRLEN] = { 0 };
            const char *ipv4_str = inet_ntop(AF_INET, &ipv4, ipv4_str_buf, sizeof(ipv4_str_buf));
            
            memset(&hints, 0, sizeof(hints));
            hints.ai_family = family;
            hints.ai_socktype = SOCK_STREAM;
            hints.ai_flags = AI_DEFAULT;
            error = getaddrinfo(ipv4_str, "http", &hints, &res0);
            if (error) {
                printf("%s", gai_strerror(error));
            }
            
            for (res = res0; res; res = res->ai_next) {
                printf("ai_flags:%d ai_family:%d ai_socktype:%d ai_protocol:%d ai_addrlen:%d ai_canonname:%s\n",
                       res->ai_flags, res->ai_family, res->ai_socktype, res->ai_protocol, res->ai_addrlen,
                       res->ai_canonname == NULL ? "" : res->ai_canonname);
                
                char* s = NULL;
                switch (res->ai_addr->sa_family) {
                    case AF_INET: {
                        struct sockaddr_in* addr_in = (struct sockaddr_in*)res->ai_addr;
                        s = (char*)malloc(INET_ADDRSTRLEN);
                        inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
                        break;
                    }
                    case AF_INET6: {
                        struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)res->ai_addr;
                        s = (char*)malloc(INET6_ADDRSTRLEN);
                        inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
                        break;
                    }
                    default:
                        break;
                }
                printf("IP address: %s\n", s);
                free(s);
            }
            
            freeaddrinfo(res0);
        }
        
        int main(int argc, char * argv[]) {
            
            printf("--unspec--\n");
            test_getaddrinfo(PF_UNSPEC);
            printf("--inet6--\n");
            test_getaddrinfo(PF_INET6);
            
            @autoreleasepool {
                return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
            }
        }

         

        and this gives me the following output:

         

        --unspec--

        ai_flags:0 ai_family:2 ai_socktype:1 ai_protocol:6 ai_addrlen:16 ai_canonname:

        IP address: 192.0.2.1

        --inet6--

        ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

        IP address: ::ffff:192.0.2.1

         

        when I would expect this to print "64:ff9b::192.0.2.1" in both PF_UNSPEC and PF_INET6 cases, as specified both in the Apple Developer documentation and the manpage for getaddrinfo.

         

        Can anybody advise as to what I may have done wrong during setup, or is this a regression in either macOS or iOS?

         

        Thanks in advance.

        • Re: iOS NAT64 address synthesis not working as expected with Mac NAT64 proxy
          eskimo Apple Staff Apple Staff (10,875 points)

          I’d expect this to work, and I vaguely remember it having worked for me in the past.  Alas, I’m not in a position to try it for myself today.  Maybe when I get back into the office next week.

          In the meantime, I do have one suggestion: Change your test IP address to something that’s not a private IP address.  My favourite address for this sort of thing is 93.184.216.34, the IP address for example.com.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: iOS NAT64 address synthesis not working as expected with Mac NAT64 proxy
              improb.worker.team Level 1 Level 1 (0 points)

              Thanks for your prompt response!

              I tried both 93.184.216.34 and example.com (without and with a DNS lookup), and got the following output, which matches what I saw above with the other IP address, and the expected behaviour when doing a DNS lookup:

              93.184.216.34:

              --unspec--

              ai_flags:0 ai_family:2 ai_socktype:1 ai_protocol:6 ai_addrlen:16 ai_canonname:

              IP address: 93.184.216.34

              --inet6--

              ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

              IP address: ::ffff:93.184.216.34

               

               

              example.com:

              --unspec--

              ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

              IP address: 2001:2:0:1baa::5db8:d822

              --inet6--

              ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

              IP address: 2001:2:0:1baa::5db8:d822



              • Re: iOS NAT64 address synthesis not working as expected with Mac NAT64 proxy
                improb.worker.team Level 1 Level 1 (0 points)

                @eskimo

                Any chance that you were able to try it out on your side this week?


                  • Re: iOS NAT64 address synthesis not working as expected with Mac NAT64 proxy
                    eskimo Apple Staff Apple Staff (10,875 points)

                    Sadly no.  I have the URL of this thread sitting on my desktop, waiting for me to get a free moment.  Alas, I have a backlog of tricky DTS incidents to work through before I’ll get to that.

                    Share and Enjoy

                    Quinn “The Eskimo!”
                    Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                    let myEmail = "eskimo" + "1" + "@apple.com"

                      • Re: iOS NAT64 address synthesis not working as expected with Mac NAT64 proxy
                        eskimo Apple Staff Apple Staff (10,875 points)

                        OK, I cleared off my desk at home, making space for a ‘victim’ Mac to run Internet Sharing, and ran your test there.  Specifically, I’m testing with iOS 12.1.4 on the client and macOS 10.14.3 on the Mac running Internet Sharing (MacNAT64).

                        The result was what I expected.  I started out with your code verbatim.  Putting it into a test project it produced results like this:

                        --unspec--
                        ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:
                        IP address: 2001:2:0:1baa::5db8:d822
                        --inet6--
                        ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:
                        IP address: 2001:2:0:1baa::5db8:d822

                        Note that 0x5d is 93, 0xb8 is 184, and so on, meaning that the IPv6 address has the IPv4 address (93.184.216.34) embedded within it, using a custom prefix (2001:2:0:1baa) chosen at random by the MacNAT64.

                        I then tweaked the code to make it simpler.  My tweaked version is below.  Hint: Any time you’re tempted to write code that’s specific to an IP version, reach for getaddrinfo or getnameinfo (-:

                        This tweaked code produces exactly the same results.

                        I’m not sure what’s going at your end, but this is working for me.

                        Share and Enjoy

                        Quinn “The Eskimo!”
                        Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                        let myEmail = "eskimo" + "1" + "@apple.com"


                        void test_getaddrinfo(int family) {
                            const char *ipv4_str = "93.184.216.34";
                            struct addrinfo hints;
                            memset(&hints, 0, sizeof(hints));
                            hints.ai_family = family;
                            hints.ai_socktype = SOCK_STREAM;
                            hints.ai_flags = AI_DEFAULT;
                            struct addrinfo * addrList;
                            int error = getaddrinfo(ipv4_str, "http", &hints, &addrList);
                            if (error) {
                                printf("%s", gai_strerror(error));
                                return;
                            }
                        
                            for (struct addrinfo * addr = addrList; addr; addr = addr->ai_next) {
                                printf("ai_flags:%d ai_family:%d ai_socktype:%d ai_protocol:%d ai_addrlen:%d ai_canonname:%s\n",
                                       addr->ai_flags, addr->ai_family, addr->ai_socktype, addr->ai_protocol, addr->ai_addrlen,
                                       addr->ai_canonname == NULL ? "" : addr->ai_canonname);
                        
                                char host[NI_MAXHOST];
                                int niErr = getnameinfo(addr->ai_addr, addr->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV);
                                if (niErr != 0) {
                                    snprintf(host, sizeof(host), "error: %d", niErr);
                                }
                                printf("IP address: %s\n", host);
                            }
                        
                            freeaddrinfo(addrList);
                        }