[IPv6] Linux 和 Windows中如何获取sockaddr_in6的sin6_scope_id

C++ Socket编程中,
如果客户端要向服务端进行IPv6通信,
如果使用的是global 地址(2409::开头的),直接设置sin6_addr就可以,
如果使用的是链路本地地址(fe80::开头的),就需要设置sin6_scope_id才能通信。

sin6_scope_id实际上是网卡的索引号码。

命令行查看网口索引

Linux

可以用ip addr来查看网口索引
在这里插入图片描述

Windows

可以用netsh interface ipv6 show route来查看网口索引
在这里插入图片描述

通过代码获取网口索引

使用getaddrinfo

首先用gethostname() 获取本地hostname,然后用getaddrinfo() 获取hostname的IP地址信息,这里面的IPv6信息会包括网口索引。

    //get local host name
    char errmsg[256] = { 0x00 };
    struct in_addr addr;

    char local_host_name[256] = { 0x00 };
    if (gethostname(local_host_name, 256))
    {
        std::cout << "Get local host name failed, err:" << 
            strerror_s(errmsg, 256, WSAGetLastError()) << std::endl;
        return false;
    }
    std::cout << "local_host_name:" << local_host_name << std::endl;
    std::cout << std::endl;

    struct addrinfo hints, * res = NULL, * p = NULL;
    memset(&hints, 0, sizeof(hints));
    int e;

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    e = getaddrinfo(local_host_name, NULL, &hints, &res);

    if (e != 0)
    {
        std::cout << "get local addrinfo error:" << WSAGetLastError() << std::endl;
        return false;
    }

    p = res;
    char bufff[512] = { 0x00 };
    while (p != NULL)
    {
        if (p->ai_family == AF_INET)
        {
            std::cout << "support IPv4" << std::endl;
            memset(bufff, 0x00, sizeof(bufff));
            inet_ntop(AF_INET, &((sockaddr_in*)p->ai_addr)->sin_addr, bufff, sizeof(bufff));
            std::cout << "local support IPv4 addr:" << bufff << std::endl;
        }
        else if (p->ai_family == AF_INET6)
        {
            std::cout << "support IPv6" << std::endl;
            memset(bufff, 0x00, sizeof(bufff));
            inet_ntop(AF_INET6, &((sockaddr_in6*)p->ai_addr)->sin6_addr, bufff, sizeof(bufff));
            sockaddr_in6* pv6 = (sockaddr_in6*)p->ai_addr;
            ULONG scopeid = pv6->sin6_scope_id;


            std::cout << "local support IPv6 addr:" << bufff << std::endl;
            std::cout << "ipv6 socopeid:" <<  ((sockaddr_in6*)p->ai_addr)->sin6_scope_id << std::endl;
            this->local_ipv6_scope_id = ((sockaddr_in6*)p->ai_addr)->sin6_scope_id;
            ret = true;
            
        }
        p = p->ai_next;
    }
    freeaddrinfo(res);

输出结果:
是可以返回非0的网口索引的。(sin6_scope_id 设置为0 ,是无法进行链路本地地址通信的)
在这里插入图片描述
这里有一点要注意,如果getaddrinfo设置了hints.ai_flags = AI_PASSIVE;,那获取的是用于bind的任意地址,这个地址信息中是没有网口索引的。

    struct addrinfo hints, * res = NULL, * p = NULL;
    memset(&hints, 0, sizeof(hints));
    int e;

    hints.ai_flags = AI_PASSIVE;
    e = getaddrinfo(NULL, "8080", &hints, &res);

    if (e != 0)
    {
        std::cout << "get local addrinfo error:" << WSAGetLastError() << std::endl;
        return false;
    }

    p = res;
    char bufff[512] = { 0x00 };
    while (p != NULL)
    {
        if (p->ai_family == AF_INET)
        {
            std::cout << "support IPv4" << std::endl;
            memset(bufff, 0x00, sizeof(bufff));
            inet_ntop(AF_INET, &((sockaddr_in*)p->ai_addr)->sin_addr, bufff, sizeof(bufff));
            std::cout << "local support IPv4 addr:" << bufff << std::endl;
        }
        else if (p->ai_family == AF_INET6)
        {
            std::cout << "support IPv6" << std::endl;
            memset(bufff, 0x00, sizeof(bufff));
            inet_ntop(AF_INET6, &((sockaddr_in6*)p->ai_addr)->sin6_addr, bufff, sizeof(bufff));
            sockaddr_in6* pv6 = (sockaddr_in6*)p->ai_addr;
            ULONG scopeid = pv6->sin6_scope_id;


            std::cout << "local support IPv6 addr:" << bufff << std::endl;
            std::cout << "ipv6 socopeid:" <<  ((sockaddr_in6*)p->ai_addr)->sin6_scope_id << std::endl;
            this->local_ipv6_scope_id = ((sockaddr_in6*)p->ai_addr)->sin6_scope_id;
            ret = true;
            
        }
        p = p->ai_next;
    }
    freeaddrinfo(res);

	return ret;

结果:网口索引是0,不能用于链路本地地址通信
在这里插入图片描述

其余可以达成目的的API

Linux

使用if_nameindex()或者getifaddrs() 遍历网口,获取网口名字。-> C语言getifaddrs()通过网口IP获取网口名
然后使用if_nametoindex() 根据网口名,获取网口名的索引。->[IPv6] socket IPv6编程 connect()连接不上的问题

windows

可能是我菜鸡,没找到。