Преобразование приложений TCP/IP С++ с IPv4 на IPv6. Сложно? Стоит беда?
На протяжении многих лет я разработал небольшую массу серверных/клиентских приложений С++ для Windows с использованием WinSock (маршрутизаторы, серверы Web/Mail/FTP и т.д. и т.д.).
Я начинаю все больше думать о создании IPv6-версии этих приложений (хотя, конечно же, сохраняя исходную версию IPv4).
Вопросы:
- Какие проблемы могут возникнуть у меня?
- Не слишком ли затруднено перенос/преобразование?
- Соответствует ли стоимость преобразования?
Для справки (или для удовольствия) вы можете очистить пик кода IPv4 в основе моих приложений.
Ответы
Ответ 1
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 для слушателей. Нет причин, по которым результирующее приложение не будет работать в системе, которая не поддерживает или не знает о IPv6.
Для исходящих соединений используйте PF_UNSPEC
в getaddrinfo
, чтобы семейство адресов выбрано для вас при выполнении исходящих подключений. Это, ИМХО, лучше, чем подход с двойным стеклом, потому что он позволяет платформам, которые не поддерживают двойную сборку, работать.
Для входящих подключений вы можете либо подключать сокеты IPv4/IPv6 отдельно, если это разумно, учитывая дизайн, либо использовать двойную стек, если вы не можете делать отдельные прослушиватели. При использовании dualstack getnameinfo
возвращает IPv6-адрес для адресов IPv4, который ИМХО оказывается совершенно бесполезным. Небольшая утилита может преобразовать строку в обычный IPv4-адрес.
Из моего опыта, когда все сделано правильно, вы удалили зависимости от определенных версий IP и закончили с меньшим кодом управления сокетами, чем вы начали.
Ответ 2
Я добавил поддержку IPv6 в ранее созданную 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, которое станет более распространенным с течением времени.
Ответ 3
Ульрих Дреппер, сторонник glibc, имеет хорошую статью по этой теме,
http://people.redhat.com/drepper/userapi-ipv6.html
Но не забудьте книгу Ричарда Стивена, Unix Network Programming, том 1: Сетевой интерфейс Sockets для хорошей практики.
Ответ 4
Посмотрите журналы изменений некоторых проектов с открытым исходным кодом, которые внедрили IPv6. Большинство из них - это код Unix, но Winsock очень похож на сокеты BSD.
Exim, Courier, Squid, Apache, BIND DNS - это некоторые места для поиска.