2012-04-26 17 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; 
}