BSD 套接字 ip6 inet_pton 以及如何检索范围 ID

mok*_*oka 1 c c++ sockets bsd

我目前正在研究 IPv6 类,并使用 inet_pton 从字符串中检索 IP 的实际二进制表示形式,即:

    AdressV6::AdressV6(const String & _ip)
    {
        int result = inet_pton(AF_INET6, _ip.c_str(), &(m_nativeAdress));

        if(result <= 0)
            //throw...

        //How can I retrieve the sope ID from that?
    }
Run Code Online (Sandbox Code Playgroud)

有一个通用的方法可以做到这一点吗?您是否只是手动解析字符串并查找听起来不太安全的“%”:(

谢谢你!

我暂时尝试了手动解析,这似乎有效。不过,如果有更好的方法请告诉我:

        //retrieve scope ID
        uint32 scopeId = 0;
        size_t pos = _ip.find("%");
        if(pos != String::npos)
        {
            String theId = _ip.substr(pos+1);
            scopeId = atoi(theId.c_str());
        }
        m_scopeId = scopeId;
Run Code Online (Sandbox Code Playgroud)

Mec*_*cki 5

在 BSD 和基于 BSD 的系统(例如,包括 MacOS X)上,作用域 ID 作为第二个 16 位字嵌入到链接本地地址的地址本身中。请参阅FreeBSD 手册并搜索“8.1.1.3 Scope Index”(不带引号)。

因此,假设 intf1 的作用域 ID 为 1,intf2 的作用域 ID 为 2,inet_pton()则将在这些平台上按如下方式转换字符串:

"fe80::1234%intf1" -> fe80:1::1234
"fe80::1234%intf2" -> fe80:2::1234
"fe80::1234"       -> fe80::1234
Run Code Online (Sandbox Code Playgroud)

最后一个地址根本没有作用域,因此不能真正用于发送数据。

请注意,这是非标准的;inet_pton()在基于 Linux 或 Windows 的系统上则不能以这种方式工作。然而,我认为即使在基于 Linux 和 Windows 的系统上,inet_pton()允许在末尾添加作用域 ID,它也会简单地忽略它。

当然,对于非链接本地地址,这个技巧不起作用,但这些地址通常没有作用域。它们可以限定范围,但通常每个接口都有一个自己的、唯一的接口 IPv6 地址,基于其接口标识符(即使您使用 DHCPv6,在这种情况下,它具有由 DHCP 服务器分配的 DHCP 地址,以及自动生成的地址) IPv6 接口地址,除非此自动生成已被禁止)。

struct sockaddr_in6结构有一个范围 ID 字段,但定义该字段的 RFC(RFC 2553 - 第 3.3 节)并没有真正给出如何解释该字段的详细信息。它只说:

sin6_scope_id 到一个接口或一组接口的映射留给有关站点标识符主题的实现和未来规范。

所以这个字段完全是特定于实现的。

如果您希望正确填写此字段,并且您的代码应尽可能跨平台,则应使用getaddrinfo()

struct addrinfo hints;
struct addrinfo * result;

memset(&hints, 0, sizeof(hints));
// AI_NUMERICHOST prevents usage of DNS servers,
// it tells getaddrinfo that the input string is a numeric IP address.
hints.flags = AI_NUMERICHOST;
if (getaddrinfo("fe80::1234%intf1", NULL, &hints, &result) == 0) {
    // result->ai_addr claims to be a pointer to struct sockaddr,
    // in fact it will be a pointer to a struct sockaddr_in6 in our case.
    struct sockaddr_in6 * so = (struct sockaddr_in6 *)result->ai_addr;

    // It will be prefilled like this:
    //
    // so->sin6_family   ==> AF_INET6;
    // so->sin6_port     ==> 0
    // so->sin6_flowinfo ==> 0
    // so->sin6_addr     ==> fe80::1234
    // so->sin6_scope_id ==> "intf1" as scope ID
    
    // Do something with that sockaddr,
    // e.g. set a port number and connect a socket to that address.

    freeaddrinfo(result);
}
Run Code Online (Sandbox Code Playgroud)

一个额外的提示:如果您想使用返回getaddrinfo()的服务器套接字(您想要在本地绑定然后调用accept()它的套接字),您还应该设置被动标志:

hints.flags = AI_NUMERICHOST | AI_PASSIVE;
Run Code Online (Sandbox Code Playgroud)

并不是说它在大多数情况下都会发挥作用,但这是正确的使用方法getaddrinfo()