Преобразование приложений C ++ TCP / IP из IPv4 в IPv6. Сложно? Стоит хлопот? - PullRequest
20 голосов
/ 26 февраля 2010

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

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

Вопросы:

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


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

Ответы [ 4 ]

18 голосов
/ 03 марта 2010

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 ... Это нереально как техническое решение, поскольку в ближайшем будущем оба протокола необходимо будет использовать одновременно, и люди могут заранее не знать, какой из них использовать. Интерфейсы сокетов являются абстрактными, и легко определить поддержку двух стеков или IPv6, пытаясь создать сокет IPv6 или установить параметр сокета IPv6 для двух стеков для слушателей. Нет причин, по которым полученное приложение не будет работать в системе, которая не поддерживает или не знает о IPv6.

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

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

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

12 голосов
/ 26 февраля 2010

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

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

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

Для IPv6 вам необходимо сохранить их как sockaddr_in6 (или некоторую эквивалентную 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, которые со временем станут более распространенными.

6 голосов
/ 31 июля 2010

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

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

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

2 голосов
/ 26 февраля 2010

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

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

...