Про IPv6 и IPv4 и проблемы с Dual Stack

 
+
-
edit
 

Mishka

модератор
★★☆
Тут народ помнит про мучения на Форточках с подключением IPv6. На самом деле в Лине и Фряхе тоже есть различия, которые вызывают головную боль. Как раз разбирался на днях и нашёл старую, но хорошую статью на эту тему:

Sample Code for Dual Stack Applications Using Non-Blocking Sockets | What Are My IP Addresses?

Home Sample Code for Dual Stack Applications Using Non-Blocking Sockets Lawrence E. HughesCTO, InfoWeapons Corp / DualStak Networks Sdn. Bhd. There are several sources of sample code for making connections from a dual stack client, and additional ones for accepting connections on a dual stack server. Many of these are suboptimal for various reasons, but a lot of current applications have used the code (or at least the techniques illustrated by them) from these samples. Because of this, some major content providers (e.g. // Дальше — www.v6address.com
 

http://www.v6address.com/sites/default/files/nbsdemo.zip — можно сгрузить и код.
В общем разница такова, что на фряхе в режиме поддержки обоих стеков, каждый стек используется только для своих адресов. А на лине IPv6 автоматом идёт, как поддерживающий IPv4 в устаревшем режиме: “IPv4-mapped IPv6 addresses” (e.g. ::ffff:123.45.67.89).

RFC, которые расписывают расширения для работы с IPv6 и адресацию таковы:


Замечу, что
code text
  1. 2.1.  Well-Known Prefix
  2.  
  3.    This document reserves a "Well-Known Prefix" for use in an
  4.    algorithmic mapping.  The value of this IPv6 prefix is:
  5.  
  6.       64:ff9b::/96
  7.  
  8. 2.2.  IPv4-Embedded IPv6 Address Format
  9.  
  10.    IPv4-converted IPv6 addresses and IPv4-translatable IPv6 addresses
  11.    follow the same format, described here as the IPv4-embedded IPv6
  12.    address Format.  IPv4-embedded IPv6 addresses are composed of a
  13.    variable-length prefix, the embedded IPv4 address, and a variable-
  14.    length suffix, as presented in the following diagram, in which PL
  15.    designates the prefix length:
  16.  
  17.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  18.     |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
  19.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  20.     |32|     prefix    |v4(32)         | u | suffix                    |
  21.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  22.     |40|     prefix        |v4(24)     | u |(8)| suffix                |
  23.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  24.     |48|     prefix            |v4(16) | u | (16)  | suffix            |
  25.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  26.     |56|     prefix                |(8)| u |  v4(24)   | suffix        |
  27.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  28.     |64|     prefix                    | u |   v4(32)      | suffix    |
  29.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  30.     |96|     prefix                                    |    v4(32)     |
  31.     +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+


Что префикса ::ffff:0:0.96 не осталось. Может в новых ядрах уже и поменяли, но на текущих RHEL 5 ещё нет.

Как это проявляется?
В моём случае, когда открывался адрес IPv6, то он сразу открывался и для IPv4, а последущее открытие, как IPv4 приводило к ошибке. В частности пришлось подправить код для gnutls-serv сервера:
code text
  1. static int
  2. listen_socket (const char *name, int listen_port)
  3. {
  4.   struct addrinfo hints, *res, *ptr;
  5.   char portname[6];
  6.   int s;
  7.   int yes;
  8.   listener_item *j = NULL;
  9.  
  10.   snprintf (portname, sizeof (portname), "%d", listen_port);
  11.   memset (&hints, 0, sizeof (hints));
  12.   hints.ai_socktype = SOCK_STREAM;
  13.   hints.ai_flags = AI_PASSIVE;
  14.  
  15.   if ((s = getaddrinfo (NULL, portname, &hints, &res)) != 0)
  16.     {
  17.       fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (s));
  18.       return -1;
  19.     }
  20.  
  21.   for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
  22.     {
  23.       /* Print what we are doing. */
  24.       {
  25.         char topbuf[512];
  26.  
  27.         fprintf (stderr, "%s listening on %s...",
  28.                  name, human_addr (ptr->ai_addr, ptr->ai_addrlen,
  29.                                    topbuf, sizeof (topbuf)));
  30.       }
  31.  
  32.       if ((s = socket (ptr->ai_family, ptr->ai_socktype,
  33.                        ptr->ai_protocol)) < 0)
  34.         {
  35.           perror ("socket() failed");
  36.           continue;
  37.         }
  38.  
  39.       yes = 1;
  40.       if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
  41.                       (const void *) &yes, sizeof (yes)) < 0)
  42.         {
  43.           perror ("setsockopt() failed");
  44.         failed:
  45.           close (s);
  46.           continue;
  47.         }
  48.  
  49.       if ( ptr->ai_family == PF_INET6 )
  50.         {
  51.           yes = 1;
  52.           if ( setsockeopt( s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof( yes ) ) )
  53.             {
  54.               perror( "cannot set IPV6_V6ONLY" );
  55.               goto failed;
  56.             }
  57.         }
  58.  
  59.       if (bind (s, ptr->ai_addr, ptr->ai_addrlen) < 0)
  60.         {
  61.           perror ("bind() failed");
  62.           goto failed;
  63.         }
  64.  
  65.       if (listen (s, 10) < 0)
  66.         {
  67.           perror ("listen() failed");
  68.           goto failed;
  69.         }
  70.  
  71.       /* new list entry for the connection */
  72.       lappend (listener_list);
  73.       j = listener_list.tail;
  74.       j->listen_socket = 1;
  75.       j->fd = s;
  76.  
  77.       /* Complete earlier message. */
  78.       fprintf (stderr, "done\n");
  79.     }
  80.  
  81.   fflush (stderr);
  82.  
  83.   freeaddrinfo (res);
  84.   if (!j)
  85.     return -1;
  86.  
  87.   return 0;
  88. }


Пришлось вставить такое:
code text
  1.       if ( ptr->ai_family == PF_INET6 )
  2.         {
  3.           yes = 1;
  4.           if ( setsockeopt( s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof( yes ) ) )
  5.             {
  6.               perror( "cannot set IPV6_V6ONLY" );
  7.               goto failed;
  8.             }
  9.         }


Но суть проблемы описана в статье и для варианта с DNS:
In a dual stack client (IPv4 and IPv6), when you query DNS, you may get back only one IPv4 address, only one IPv6 address, or if the server is dual stack, you may get back both an IPv4 and an IPv6 address (or possibly even more than one of each). The current code samples (assuming IPv6 connectivity is available) will try IPv6 first, and if that doesn’t work, fail back to IPv4. In the simple case of one IPv6 and one IPv4 address, the client will first try the IPv6 address. If it works, all is fine. If for some reason that fails, there may be a 30 to 70 second timeout, then it will try the IPv4 address, which usually works. The user doesn’t understand that the fault lies in his client software and thinks there is a problem with the content provider’s website (and then complain to the content provider or worse yet, use another content provider).

В системе с двойным стеком на клиенте, когда вы запрашиваете DNS, вы можете получить адрес:
1. только формата IPv4,
2. только формата IPv6,
3. или же, если сервер тоже поддерживает двойной стек, то можете получить адрес и в IPv4, и в IPv6 (они могут быть разными, их может быть много). Текущий пример кода (при предположении, что IPv6 работает) сначала попытается использовать его, и, если он не работает, то перейдёт к IPv4. В простейшем случае, одного IPv6 и одного IPv4, клиент попробует IPv6 первым. Если получилось, то хорошо. Если нет, то может последовать задержка на 30-70 секунд. Только после этой задержки IPv4 пойдёт в дело. И последний, обычно соеденится. Пользователи не понимают, что проблема лежит в клиентском коде, и они думают, что провайдер — или интернет, или контент провайдере. И они жалуюстя им на проблемы.
 

Понятно, что описаны проблемы с текущими примерами, но ... эти проблемы характерны для кучи софта, в то числи и для макросовфтовского DNS клиента, который даже в версиях форточек 2003 и 2008 часто умудрялся опросить IPv6 первым.

Такие дела. :)

Если кому чего непонятно — справшивайте.
 5.05.0
+
-
edit
 

HolyBoy

аксакал


Трудности перехода.

Кстати, хорошо показывает, зачем нужна такая забюрократизированная система создания и приёма обновлений.
 

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru