11 September 2010

Getting the Link-Local Address from an interface

Recently I had to bind a socket to a link-local address and one of the issues was getting the IP of that interface. Since ioctl calls don't really work with IPv6 addresses, the solution I found was the getifaddrs() function. This function returns an array of 'ifaddrs' structs with all the IPs on all interfaces. After the list is returned, I only had to look in the list for my IP. I posted the code to my function. Feel free to ask questions or bring suggestions.
The code itself is a method to return the link-local address of an interface, however with simple modifications it can be used to retrieve any address needed.

So I defined a function which receives 3 parameters, the interface name, as a string of chars, the interface name length, which will be helpful later on when comparing the interface names with those we found, and the ip address structure where we want the IP address returned. Like Brandon mentioned, sys/types.h and ifaddrs.h are needed :).
#include "sys/types.h"
#include "ifaddrs.h"
int get_link_local_addr(char* if_name, int if_name_length, struct sockaddr_in6 *ip)
{
Two variables will be declared. The first one will be the return value for the getifaddrs function, consisting in the list of addresses returned. The second one will be used as an iterator to go through the list, once it is returned. An additional int will be declared, to store the return value.
    struct ifaddrs *ifaddr, *ifa;
    int ret = -2;
Call the getifaddrs function to populate the address list.
    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        ret = -1;
        goto end;
    }
Now we have to walk through the list to find the IP we are looking for. In this example, I am looking for the link-local address, so I am looking for the ipv6 addres for my local interface which has the net address FE80::/10. This means, the first byte will be FE and the second one will be between 80 and BF.
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
    {
Since I am looking for IPv6 only, everything else is discarded
        if (ifa->ifa_addr->sa_family != AF_INET6) continue;
For each IPv6 address a check is made to see if the address is set on the right interface or not.
        if (strncmp(ifa->ifa_name, if_name, if_name_length)) continue;
        struct sockaddr_in6 *current_addr = (struct sockaddr_in6 *) ifa->ifa_addr;
If the address is set on the right interface, it is checked to see if it is a link-local address or not. If yes, it is returned.
        memcpy(ip, current_addr, sizeof(*current_addr));
        ret = 0;
        goto end;
    }
end:
    freeifaddrs(ifaddr);
    return ret;
}
This is the code needed to retrieve the link-local address on an interface.

3 comments:

  1. Great tutorial! Just what I was looking for to get the currently configured IPv6 address(es) and/or link local addresses. Also found, from the man page to include sys/types.h and ifaddrs.h.

    ReplyDelete
  2. In your code how are you determining whether a particular ipv6 is link-local addresss but not global??

    ReplyDelete
  3. if (ifa->ifa_addr->sa_family != AF_INET6) continue;
    ->
    if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) continue;

    ReplyDelete