Преобразование C++ приложения TCP/IP от IPv4 до IPv6. Трудный? Стоящий проблемы?

За эти годы я разработал маленькую массу сервера/клиентских приложений C++ для Windows с помощью WinSock (Маршрутизаторы, СЕТЬ/ПОЧТА/FTP-СЕРВЕРЫ, и т.д.... и т.д....).

Я начинаю думать все больше создания версии IPv6 этих приложений (При поддержании исходной IPv4 версии также, конечно).

Вопросы:

  1. С какими ловушками я мог бы столкнуться?
  2. Действительно ли портирование/преобразование является трудным?
  3. Действительно ли преобразование стоит того?


Для ссылки (или для забавы), Вы можете Снек пик кода IPv4 в ядре моих приложений.

21
задан NTDLS 26 February 2010 в 12:44
поделиться

4 ответа

getaddrinfo и getnameinfo - ваши друзья... Насколько это возможно, я предлагаю сделать их вашими лучшими друзьями в вашем стремлении обеспечить поддержку IPv4 и IPv6 в существующем приложении.

Если все сделано правильно, добавляя поддержку IPv6, вы также абстрагируете систему до такой степени, что неизвестный будущий IP-протокол может работать без модификации кода.

Обычно при подключении вы заполняете структуру сокета, порт, семейство адресов, IP-адрес, преобразовываете адреса/порты в сетевой порядок байтов и т.д.

С помощью getaddrinfo вы посылаете IP-адрес или имя хоста и порт или имя порта, и он возвращает связанный список со структурами и всем, что готово для передачи непосредственно в socket() и connect().

getaddrinfo критически важен для работы с обоими IP-протоколами, поскольку он знает, есть ли у хоста подключение IPv6 или IPv4, и знает, есть ли оно и у пира, просматривая записи DNS AAAA vs A и динамически определяя, какой протокол (протоколы) доступен для обслуживания конкретного запроса на подключение.

Я настоятельно не рекомендую использовать inet_pton(), inet_addr() или аналогичные устройства, которые зависят от версии IP. На платформе Windows, в частности, inet_pton() не совместим с более ранними версиями MS Windows (XP, 2003 и др.), если только вы не создадите свою собственную. Также не рекомендуется использовать отдельные версии для IPv4 и IPv6... Это неработоспособное техническое решение, потому что в ближайшем будущем оба протокола должны будут использоваться одновременно, и люди могут не знать заранее, какой из них использовать. Интерфейсы сокетов абстрактны, и легко обнаружить поддержку dualstack или IPv6, попытавшись создать сокет IPv6 или установить опцию сокета IPv6 dualstack для слушателей. Нет причин, по которым полученное приложение не будет работать на системе, не поддерживающей IPv6 или не знающей о нем.

Для исходящих соединений используйте PF_UNSPEC в getaddrinfo, чтобы семейство адресов выбиралось за вас при создании исходящих соединений. Это, IMHO, лучше, чем подход dualstack, поскольку позволяет работать платформам, не поддерживающим dualstack.

Для входящих соединений вы можете либо привязать IPv4/IPv6 сокеты отдельно, если это разумно с учетом дизайна, либо использовать dualstack, если вы не можете сделать отдельные слушатели. При использовании dualstack getnameinfo возвращает IPv6-адрес для IPv4-адресов, что IMHO оказывается довольно бесполезным. Небольшая утилита может преобразовать строку в обычный IPv4-адрес.

По моему опыту, если все сделано правильно, вы устранили зависимость от конкретных версий IP и в итоге получили меньше кода управления сокетами, чем начинали.

19
ответ дан 29 November 2019 в 20:43
поделиться

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

Единственная большая разница заключается в способе хранения IP-адресов :

В IPv4 вы храните их как sockaddr_in (или, если вы непослушный, как я, как uint32_t).

Для IPv6 вам нужно хранить их как sockaddr_in6 (или как эквивалентную 128-битную структуру).

Хорошим шагом перед преобразованием будет просмотреть ваш код и найти все места, где в настоящее время хранятся IPv4 адреса, и абстрагировать их в общий класс IP Address, который впоследствии может быть реализован внутри, чтобы быть либо IPv4 адресом, либо IPv6 адресом.
Затем повторно протестируйте, чтобы убедиться, что ничего не нарушено в режиме IPv4... После проверки вы сможете перейти на IPv6, внеся еще несколько изменений (в основном, изменив PF_INET на PF_INET6, inet_aton() на inet_pton() и т.д.).

Моя библиотека по-прежнему поставляется как IPv4-only по умолчанию, но с возможностью определения макроса препроцессора (-DMUSCLE_USE_IPV6) для перекомпиляции в IPv6-aware режим.
Таким образом, его можно будет компилировать на системах, не поддерживающих IPv6. Одна очень полезная функция, которую я обнаружил на этом пути, - IPv4-mapped IPv6-адреса: Указав один из них (по сути, адрес IPv4 с 0xFFFF, добавленной к нему), вы получаете сокет, который может работать как с IPv4, так и с IPv6, а значит, и сервер, который может работать с клиентами IPv4 и IPv6 одновременно, без необходимости писать отдельные пути кода IPv4 и IPv6 для всего.

Что касается того, стоит ли это усилий, то это действительно зависит от того, что вы собираетесь делать с кодом. Я бы сказал, что это хороший образовательный опыт, если не больше, и это позволит использовать ваше программное обеспечение в средах IPv6, которые со временем станут более распространенными.

12
ответ дан 29 November 2019 в 20:43
поделиться

Посмотрите журналы изменений некоторых проектов с открытым исходным кодом, в которых реализован IPv6. По большей части это код Unix, но Winsock очень похож на сокеты BSD.

Exim, Courier, Squid, Apache, BIND DNS - это то, с чего стоит начать поиски.

2
ответ дан 29 November 2019 в 20:43
поделиться

У Ульриха Дреппера, сопровождающего glibc, есть хорошая статья на эту тему,

http://people.redhat.com/drepper/userapi-ipv6.html

Но не забудьте книгу Ричарда Стивена, Unix Network Programming, Volume 1: The Sockets Networking API для хорошей практики.

6
ответ дан 29 November 2019 в 20:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: