2014/05/11

[Linux] 使用getnameinfo()來get local IPv6 address及用mask比較subnet

IPv6會是未來使用IP address的趨勢,所以嘗試使用getnameinfo來拿到本機的IPv6位址,並且跟其他的IPv6位址(fe80::19fb:8e1d:e1c:947e)比較,看使否在同一個子網域之下。比較的方式採用IPv6的位址與mask做完AND運算,得到Network ID後與另外一個IPv6的Network ID位址比較,因為無法一次比較128bytes,所以依次8個bytes一組,總共比較16次,16的結果都相同,就判定在同一個subnet之下。

IPv6為長度128 bytes的位址,表示的方式會以每16個bytes以符號分號":"隔開的方式來表示,譬如例子的"fe80:0000:0000:0000:19fb:8e1d:e1c:947e",總個128bytes,然後連續0的地方可以用連續兩個分號"::"來表示,所以就變成"fe80::19fb:8e1d:e1c:947e"。


以下為範例
get_ipv6.c
#include <arpa/inet.h>
#include <sys/socket.h>


#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>


char *GetInterfaceIPv6(char *interface, int mode);
int subnet_compare_v6(char *srcIPv6, char *myIPv6, char *myNetmaskv6);

int main(int argc, char *argv[])
{
    printf("address: %s\n",GetInterfaceIPv6("eth0", 1));
    printf("mask: %s\n",GetInterfaceIPv6("eth0", 0));
    if(subnet_compare_v6("fe80::19fb:8e1d:e1c:947e", GetInterfaceIPv6("eth0", 1), GetInterfaceIPv6("eth0", 0)))
        printf("Our IPv6 and fe80::19fb:8e1d:e1c:947e is under the same subnet!\n");
    else
        printf("Our IPv6 and fe80::19fb:8e1d:e1c:947e is not under the same subnet!\n");
    return 0;
}


char *GetInterfaceIPv6(char *interface, int mode) {
    struct ifaddrs *ifaddr, *ifa;

    int family, s;
    char *host, *host_tmp;
    host = (char *) malloc(sizeof(char) * NI_MAXHOST);
    host_tmp = (char *) malloc(sizeof(char) * NI_MAXHOST);


    if (getifaddrs(&ifaddr) == -1) {
        return NULL;
    }


    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {

        family = ifa->ifa_addr->sa_family;


        if ( (family == AF_INET6)&& !(strcmp(interface, ifa->ifa_name) )) {//if (family == AF_INET || family == AF_INET6) {
#if 0
            printf("%s address family: %d%s\n", ifa->ifa_name, family,
                (family == AF_PACKET) ? " (AF_PACKET)" :
                (family == AF_INET) ? " (AF_INET)" :
                (family == AF_INET6) ? " (AF_INET6)" : "");
#endif
            // mode = 0, Netmask ; mode = 1 Address
            s = getnameinfo( (mode==0) ? ifa->ifa_netmask : ifa->ifa_addr, sizeof(struct sockaddr_in6), host_tmp, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);

            char *v6_tok;
            v6_tok = strtok(host_tmp, "%");
            sprintf(host, "%s", v6_tok);

            if (s != 0) {
                //printf("getnameinfo() failed: %s\n", gai_strerror(s));
                return NULL;
            }
            //printf("address: <%s>\n", host);
        }
    }

    freeifaddrs(ifaddr);
    return host;
}


int subnet_compare_v6(char *srcIPv6, char *myIPv6, char *myNetmaskv6)
{
    struct in6_addr src_ip_v6_hex, my_ip_v6_hex, my_netmask_v6_hex;
    int i;
    int same_subnet=0;

    inet_pton(AF_INET6, srcIPv6, (void *) &src_ip_v6_hex);
    inet_pton(AF_INET6, myIPv6, (void *) &my_ip_v6_hex);
    inet_pton(AF_INET6, myNetmaskv6, (void *) &my_netmask_v6_hex);
#if 0
    printf("Sr ip v6= ");
    for(i=0;i<16;i++)
        printf("%02x", src_ip_v6_hex.s6_addr[i]);
    printf("\n");
    printf("My ip v6= ");
    for(i=0;i<16;i++)
        printf("%02x", my_ip_v6_hex.s6_addr[i]);
    printf("\n");
    printf("My netmask v6= ");
    for(i=0;i<16;i++)
        printf("%02x", my_netmask_v6_hex.s6_addr[i]);
    printf("\n");
    printf("My network ID v6= ");
    for(i=0;i<16;i++)
        printf("%02x", (my_ip_v6_hex.s6_addr[i] & my_netmask_v6_hex.s6_addr[i]));
    printf("\n");
#endif
    // Compare srcIPv6 & myIPv6 with myNetmaskv6
    for(i=0;i<16;i++) {
        if((my_ip_v6_hex.s6_addr[i] & my_netmask_v6_hex.s6_addr[i]) == (src_ip_v6_hex.s6_addr[i] & my_netmask_v6_hex.s6_addr[i]))
        same_subnet++;
    }
    //printf("same_subnet:%d\n",same_subnet);
    if(same_subnet==16)
        return 1;
    else
        return 0;
}

執行結果



參考資料
http://www.beej.us/guide/bgnet/output/html/multipage/inet_ntopman.html


沒有留言:

張貼留言