Как обеспечить, чтобы сервер прослушивал IPv6, если это возможно, и IPv4 в противном случае? - PullRequest
3 голосов
/ 24 августа 2010

Я пытаюсь написать серверное приложение, которое прослушивает соединения как IPv6, так и IPv4. Кажется, правильным способом для этого является прослушивание IPv6-адреса, который также будет принимать соединения IPv4.

Соответствующий фрагмент кода:

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, MYPORT, &hints, &res);

(в значительной степени скопировано из руководства Биджа)

Проблема в том, что, по крайней мере, в моей системе getaddrinfo возвращает запись с AF_INET first и AF_INET6 second - тогда как getaddrinfo клиента возвращает AF_INET6 first, согласно спецификации. При моем наивном подходе сервер выбирает IPv4, клиент выбирает IPv6, и соединение не устанавливается.

Я попытался исправить это, установив hints.ai_family = AF_INET6, но это не работает в системах, где IPv6 недоступен.

Я вижу два очевидных решения:
а) попробуйте сначала запросить IPv6 и в случае сбоя вернитесь к IPv4, либо
б) просмотреть результаты getaddrinfo, найти IPv6 и, если нет, выбрать первую запись
но мне не нравится ни один слишком;) Я чувствую, что должен быть способ убедить getaddrinfo сделать правильные вещи или, возможно, другой способ достичь моей цели.

Ответы [ 2 ]

3 голосов
/ 24 августа 2010

Порядок адреса, который возвращает getaddrinfo(), не указан, поэтому вы должны быть готовы обработать любой случай.Это, вероятно, означает обход списка, отслеживание «лучшего адреса, замеченного до сих пор».

В качестве альтернативы, вы можете попробовать bind() и listen() на все адреса, возвращаемыеgetaddrinfo().Это, вероятно, лучший вариант, поскольку некоторые ОС не принимают подключения IPv4 к сокетам IPv6, прослушивая 0::0.

1 голос
/ 10 июля 2014

Ваш код должен работать так, как вы описали.К сожалению, в glibc есть ошибка, описанная в ошибка панели запуска № 673708 , из-за которой он сначала выбирает IPv4.

Решение для конфигурации компьютера

Существует обходной путьэто можно сделать на каждом компьютере Linux, на котором вы запускаете серверную программу: отредактируйте /etc/gai.conf, включите все правила по умолчанию (раскомментируйте их):

label ::1/128       0
label ::/0          1
label 2002::/16     2
label ::/96         3
label ::ffff:0:0/96 4
label fec0::/10     5
label fc00::/7      6
label 2001:0::/32   7

, затем добавьте:

label ::ffff:7f00:1/128 8

Тогда ваш код должен открывать IPv6, если он поддерживается, и также принимать подключения IPv4.

Решение кода

Если вышеприведенное нецелесообразно (это практично, если вы готовыизмените конфигурацию на каждом компьютере, на котором вы работаете), затем измените свой код, чтобы предпочитать IPv6.Например, я сделал это:

  • Выполните три прохода через результаты getaddrinfo().
  • Первый проход, предпочтительнее IPv6.Попытайтесь очистить опцию сокета IPV6_V6ONLY для поддержки IPv6 и IPv4.
  • Второй проход, предпочтительнее IPv4.
  • Третий проход, возьмите все, что доступно.
...