2012-04-26 16 views
1

我目前的工作在IPv6类,并使用inet_pton从字符串中获取IP的实际二进制表示即:BSD套接字IP6 inet_pton以及如何检索范围ID

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; 
+0

您的扫描码被打破,它假定范围ID是一个数字。虽然%1或%2是有效的范围ID,但它们的含义完全取决于实施。范围ID也可以是接口名称,如%dc0,%eth0或%en0(取决于操作系统使用的名称界面)。有关详细信息,请参阅我的回复,以了解如何获取正确的作用域ID,即使是那些具有接口名称 – Mecki 2012-12-11 18:58:41

回答

1

inet_pton()不支持范围的ID,我不知道其他的平台,但在Windows中可以使用RtlIpv6StringToAddressEx()代替

+0

ty,我不在窗户上。尽管它需要使用特定的ipv6表示法,但手动解析似乎现在可行。 – moka 2012-04-30 10:27:14

3

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

所以假设INTF1具有范围编号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 - Section 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()

+0

关于嵌入式作用域ID:它们是内部表示,并不意味着在BSD/Darwin内核之外可见。很少有API暴露它们,它们最终将被修复。 ioctls和sysctl是通过嵌入式scope-id获取地址的唯一常用方法;否则,用户域函数和代码应尽可能正确使用sockaddr_in6。 – 2013-03-27 13:51:47

+0

@NicholasWilson它们可能不是在内核外部可见的,但它们在当时是可见的;我甚至可以确认OS X的最新版本。尽管在OS X甚至UI上都会显示带有嵌入式范围ID的地址(例如,如果您只有链接本地IPv6地址,网络首选项也会这样做)。因此,对这些地址一无所知的用户可能会意外地从那里复制并粘贴一个地址,因此,应该处理IPv6地址字符串的所有功能都可以在OS X上正确处理嵌入式作用域。 – Mecki 2013-03-28 01:09:38

+0

这是网络首选项应用程序的相当差。它可能只是调用getifaddrs,它是少数仍然公开嵌入式作用域id的libc函数之一(它在Darwin [或者]上用ioctl实现;它不会重新格式化复制到它的地址的内核确实是一个bug)。这可能会更糟糕:在某些损坏的系统上,getaddrinfo打印地址时不附加范围标识(错误的坏错误),并且SunOS与范围标识混合在别名接口中。 – 2013-03-28 09:56:32

0

inet_pton()半支持范围标识符,范围是当它解析一个地址时不会引发错误。主要限制是该调用的参数是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; 
} 

要回答原来的前提,但给予了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; 
}