1

我目前正在研究 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?
    }

有没有一种通用的方法可以做到这一点?您是否只是手动解析字符串并查找听起来不是很防弹的“%”:(

谢谢!

我现在尝试了手动解析,这似乎可行。不过,如果有更好的方法,请告诉我:

        //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;
4

3 回答 3

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

最后一个地址只是无范围的,因此不能真正用于发送数据。

请注意,这是非标准的;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);
}

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

hints.flags = AI_NUMERICHOST | AI_PASSIVE;

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

于 2012-12-11T18:55:31.910 回答
1

inet_pton()不支持范围 ID。我不知道其他平台,但在 Windows 上你可以使用RtlIpv6StringToAddressEx()

于 2012-04-28T00:51:01.393 回答
0

inet_pton()semi-支持作用域标识符,作用域是用一个解析地址时不会报错。主要限制是调用的参数是struct in6_addr不包含范围标识符字段的,因此struct sockaddr_in6需要上层结构。

为了方便起见,简单的方法是包装getnameinfo()getaddrinfo()使用struct sockaddr参数。例如,

socklen_t
sockaddr_len (
        const struct sockaddr*  sa
        )
{
        socklen_t sa_len;
        switch (sa->sa_family) {
        case AF_INET:   sa_len = sizeof(struct sockaddr_in); break;
        case AF_INET6:  sa_len = sizeof(struct sockaddr_in6); break;
        default:        sa_len = 0; break;
        }
        return sa_len;
}

int
sockaddr_ntop (
        const struct sockaddr* restrict sa,
        char*                  restrict host,
        size_t                          hostlen
        )
{
        return getnameinfo (sa, sockaddr_len (sa),
                            host, hostlen,
                            NULL, 0,
                            NI_NUMERICHOST);
}

int
sockaddr_pton (
        const char*      restrict src,
        struct sockaddr* restrict dst           /* will error on wrong size */
        )
{
        struct addrinfo hints = {
                .ai_family      = AF_UNSPEC,
                .ai_socktype    = SOCK_STREAM,          /* not really */
                .ai_protocol    = IPPROTO_TCP,          /* not really */
                .ai_flags       = AI_NUMERICHOST
        }, *result = NULL;
        const int status = getaddrinfo (src, NULL, &hints, &result);
        if (0 == status) {
                memcpy (dst, result->ai_addr, result->ai_addrlen);
                freeaddrinfo (result);
                return 1;
        }
        return 0;
}

为了回答最初的前提但给定 a struct sockaddr,可能需要额外的 API,例如:

uint32_t
sockaddr_scope_id (
        const struct sockaddr*  sa
        )
{
        uint32_t scope_id;
        if (AF_INET6 == sa->sa_family) {
                struct sockaddr_in6 s6;
                memcpy (&s6, sa, sizeof(s6));
                scope_id = s6.sin6_scope_id;
        } else
                scope_id = 0;
        return scope_id;
}
于 2012-12-11T20:07:58.637 回答