Преобразование приложений C++ TCP/IP из IPv4 в IPv6. Сложно? Стоит хлопот?

За прошедшие годы я разработал небольшую массу серверных / клиентских приложений на C++ для Windows с использованием WinSock (маршрутизаторы, веб-серверы, почтовые /FTP-серверы и т. Д. И т. Д.).

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

Вопросы:

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


Для справки (или для удовольствия) вы можете подсмотреть пик кода IPv4 в основе моих приложений.

4 ответа

Решение

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

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

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

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

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

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

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

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

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

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

Единственная большая разница в том, как вы храните IP-адреса:

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

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

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

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

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

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

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

Но не забывайте книгу Ричарда Стивена " Сетевое программирование Unix", том 1: API-интерфейс для работы с сокетами.

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

Exim, Courier, Squid, Apache, BIND DNS - вот несколько мест, где можно начать поиск.

Другие вопросы по тегам