1

I'm trying to get all of my Mac's IP addresses (IPv4 + IPv6) but am not seeing the results I expect. I'm using code from this Stack Overflow post to get an array of all IP addresses, but there's an IP address missing.

I'm using another Mac to share it's internet connection and create a NAT64 network per Apple's documentation for testing/supporting IPv6 networks.

For example here's what the System Preferences → Network pane says my IP address is:

enter image description here

... but I see that I actually have two IPv6 addresses upon further inspection:

enter image description here

... but only one of these is returned:

"169.254.38.213",
"2001:2:0:aab1:d0ef:646d:f22a:5d83",
"127.0.0.1"

... when using this:

#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>

@interface AppDelegate ()

#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

@end

@implementation AppDelegate

- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
    NSMutableArray *ipAddressesArray = [[NSMutableArray alloc] init];

    // retrieve the current interfaces - returns 0 on success
    struct ifaddrs *interfaces;

    if (!getifaddrs(&interfaces))
    {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;

        for (interface=interfaces; interface; interface=interface->ifa_next)
        {
            if (!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */)
            {
                continue; // deeply nested code harder to read
            }

            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];

            if (addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6))
            {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;

                if (addr->sin_family == AF_INET)
                {
                    if (inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN))
                    {
                        type = IP_ADDR_IPv4;
                    }
                }
                else
                {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;

                    if (inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN))
                    {
                        type = IP_ADDR_IPv6;
                    }
                }

                if (type)
                {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }

        for (id key in addresses)
        {
            if (![[addresses valueForKey:key] hasPrefix:@"fe80"])
            {
                [ipAddressesArray addObject:[addresses valueForKey:key]];
            }
        }

        // Free memory
        freeifaddrs(interfaces);
    }

    NSLog(@"%@", ipAddressesArray);
}

Any idea what is going on here? Does it matter? I'm trying to execute some other code conditionally based on IP address matching. It'd be one thing if the only IPv6 address returned was the one displayed to the user in System Preferences when first opening the Network pane, but instead the only IPv6 address returned is the "hidden" one that you have to dig into the Advanced... section to find. Thank you in advance.

  • 1
    I suggest single-stepping through this code and seeing if `getifaddrs` is returning this address and you're filtering it, or if `getifaddrs` isn't returning it at all. – Rob Napier Feb 04 '20 at 15:21
  • You are probably running into the IPv6 privacy extensions where you end up having temporary addresses, and you change addresses every so often. Also, you want to get the Link-Local IPv6 address. It may be better to look via CLI to see what is really on that interface. I know Windows CLI give a better explanation than the GUI tools. – Ron Maupin Feb 04 '20 at 15:57
  • Thank you both for your suggestions and for taking the time to review this question. I feel like a real dummy as I was indeed essentially filtering out the values I thought were missing. I've posted an answer to this questions. – William Gustafson Feb 10 '20 at 17:12

1 Answers1

1

The answer here is that I am an idiot. As suggested by Rob Napier in their comment on my question, I was essentially filtering out the missing IP addresses:

NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];

Each interface can have more than one IP address, but I since I was using the interface type as the unique key in my addresses dictionary, only one IP address per interface was present in the dictionary. I fixed this by returning an array of dictionaries rather than a single dictionary:

if (![[NSString stringWithFormat:@"%s", addrBuf] hasPrefix:@"fe80"])
{
      NSDictionary *addressDict = [NSDictionary dictionaryWithObjectsAndKeys :
                                                   [NSString stringWithFormat:@"%@/%@", name, type], @"Interface",
                                                   [NSString stringWithFormat:@"%s", addrBuf], @"Address",
                                                    nil];

     [addresses addObject:addressDict];
}

return addresses;