27.6. Заголовок маршрутизации IPv6

27.6. Заголовок маршрутизации IPv6

Заголовок маршрутизации IPv6 используется для маршрутизации от отправителя в IPv6. Первые два байта заголовка маршрутизации такие же, как показанные на рис. 27.3: поле следующего заголовка (next header) и поле длины заголовка расширения (header extension length). Следующие два байта задают тип маршрутизации (routing type) и количество оставшихся сегментов (number of segments left) (то есть сколько из перечисленных узлов еще нужно пройти). Определен только один тип заголовка маршрутизации, обозначаемый как тип 0. Формат заголовка маршрутизации показан на рис. 27.7.

Рис. 27.7. Заголовок маршрутизации IPv6

В заголовке маршрутизации IPv6 может появиться неограниченное количество адресов (реальное ограничение накладывается длиной пакета), а количество оставшихся сегментов не должно превышать количество адресов в заголовке. Документ RFC 2460 [27] описывает подробности обработки этого заголовка при пересылке его в направлении получателя. Там же вы можете найти подробно рассмотренный пример.

Заголовок маршрутизации обычно задается как вспомогательные данные в функции sendmsg и возвращается в виде вспомогательных данных функцией recvmsg. Для отправки заголовка приложению не требуется выполнять какие-либо специальные действия — достаточно просто указать его при вызове функции sendmsg. Но для получения заголовка маршрутизации требуется, чтобы был включен параметр IPV6_RECVRTHDR:

const int on = 1;

setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVRTHDR, &on, sizeof(on));

На рис. 27.8 показан формат объекта вспомогательных данных, используемый для отправки и получения заголовка маршрутизации. Для создания и обработки заголовка маршрутизации определены шесть функций. Следующие три функции используются для создания отправляемого параметра.

#include <netinet/in.h>

socklen_t inet6_rth_space(int type, int segments);

Возвращает: положительное число, равное количеству байтов в случае успешного выполнения, 0 в случае ошибки

void *inet6_rth_init(void *rthbuf, socklen_t rthlen, int type, int segments);

Возвращает: непустой указатель в случае успешного выполнения, NULL в случае ошибки

int inet6_rth_add(void *rthbuf, const struct in6_addr *addr);

Возвращает: 0 в случае успешного выполнения, -1 в случае ошибки

Рис. 27.8. Объект вспомогательных данных для заголовка маршрутизации IPv6

Функция inet6_rth_space возвращает количество байтов, необходимое для размещения объекта вспомогательных данных, содержащего заголовок маршрутизации указанного типа (обычно это IPV6_RTHDR_TYPE_0) с заданным количеством сегментов.

Функция inet6_rth_init инициализирует буфер, на который указывает аргумент rthbuf, для помещения заголовка маршрутизации типа type и заданного количества сегментов. Возвращаемое значение этой функции — указатель на буфер. Этот указатель используется как аргумент при вызове следующей функции. Функция inet6_rth_init возвращает NULL в случае возникновения ошибок (например, при недостаточном размере предоставленного буфера).

Функция inet6_rth_add добавляет адрес IPv6, на который указывает аргумент addr, к концу составляемого заголовка маршрутизации. В случае успешного выполнения обновляется значение элемента segleft заголовка маршрутизации, чтобы учесть добавленный новый адрес.

Следующие три функции манипулируют полученным заголовком маршрутизации:

#include <netinet/in.h>

int inet6_rth_reverse(const void *in, void *out);

Возвращает: 0 в случае успешного выполнения, -1 в случае ошибки

int inet6_rth_segments(const void *rthbuf);

Возвращает: количество сегментов в заголовке маршрутизации в случае успешного выполнения, -1 в случае ошибки

struct in6_addr *inet6_rth_getaddr(const void *rthbuf, int index);

Возвращает: непустой указатель в случае успешного выполнения, NULL в случае ошибки

Функция inet6_rth_reverse принимает в качестве аргумента заголовок маршрутизации, полученный в виде объекта вспомогательных данных (на который указывает аргумент in), и создает новый заголовок маршрутизации (в буфере, на который указывает аргумент out), отправляющий дейтаграммы по обратному маршруту. Указатели in и out могут указывать на один и тот же буфер.

Функция inet6_rth_segments возвращает количество сегментов в заголовке маршрутизации, на который указывает rthbuf. В случае успешного выполнения функции возвращаемое значение оказывается больше 0.

Функция inet6_rth_getaddr возвращает указатель на адрес IPv6, заданный через index в заголовке маршрутизации rthbuf. Аргумент index должен лежать в пределах от 1 до значения, возвращенного функцией inet6_rth_segments, включительно.

Чтобы продемонстрировать использование этих параметров, мы создали UDP-клиент и UDP-сервер. Клиент представлен в листинге 27.5. Он принимает маршрут от отправителя в командной строке подобно TCP-клиенту IPv4, представленному в листинге 27.4. Сервер печатает маршрут полученного сообщения и обращает этот маршрут для отправки сообщения в обратном направлении.

Листинг 27.5. UDP-клиент, использующий маршрутизацию от отправителя

 1 #include "unp.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int с, sockfd, len = 0;

 6  u_char *ptr = NULL;

 7  void *rth;

 8  struct addrinfo *ai;

 9  if (argc < 2)

10   err_quit("usage: udpcli01 [ <hostname> ... ] <hostname>");

11  if (argc > 2) {

12   int i;

13   len = Inet6_rth_space(IPV6_RTHDR_TYPE_0, argc-2);

14   ptr = Malloc(len);

15   Inet6_rth_init(ptr, len, IPV6_RTHDR_TYPE_0, argc-2);

16   for (i = 1; i < argc-1; i++) {

17    ai = Host_serv(argv[i], NULL, AF_INET6, 0);

18    Inet6_rth_add(ptr,

19     &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr);

20   }

21  }

22  ai = Host_serv(argv[argc-1], SERV_PORT_STR, AF_INET6, SOCK_DGRAM);

23  sockfd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);

24  if (ptr) {

25   Setsockopt(sockfd, IPPROTO_IPV6, IPV6_RTHDR, ptr, len);

26   free(ptr);

27  }

28  dg_cli(stdin, sockfd, ai->ai_addr, ai->ai_addrlen); /* do it all */

29  exit(0);

30 }

Создание маршрута

11-21 Если при вызове программы было указано более одного аргумента, все параметры командной строки, за исключением последнего, формируют маршрут от отправителя. Сначала мы определяем, какой объем памяти займет заголовок маршрутизации, при помощи функции inet6_rth_space, затем выделяем буфер соответствующего размера вызовом malloc. После этого каждый адрес маршрута преобразуется в числовую форму функцией host_serv и добавляется к маршруту функцией inet6_rth_add. Примерно то же самое выполнял и TCP-клиент IPv4, за тем исключением, что здесь мы используем библиотечные функции, а не свои собственные.

Поиск адресата и создание сокета

22-23 Мы определяем адрес назначения при помощи host_serv и создаем сокет для отправки пакетов.

Установка «закрепленного» параметра IPV6_RTHDR и вызов рабочей функции

24-27 В разделе 27.7 будет показано, что не обязательно отправлять одни и те же вспомогательные данные с каждым пакетом. Вместо этого можно вызвать setsockopt таким образом, что один и тот же заголовок будет добавляться ко всем пакетам в рамках одного сеанса. Этот параметр устанавливается только в том случае, если указатель ptr не нулевой, то есть мы уже должны были выделить буфер под заголовок маршрутизации. На последнем этапе мы вызываем рабочую функцию dg_cli, которая не меняется с листинга 8.4.

Программа UDP-сервера не изменилась по сравнению с предыдущими примерами. Сервер открывает сокет и вызывает функцию dg_echo. В листинге 27.6 представлена функция dg_echo, печатающая информацию о маршруте от источника (если таковой был получен) и обращающая этот маршрут для отправки сообщения в обратном направлении.

Листинг 27.6. Функция dg_echo, печатающая маршрут

//ipopts/dgechoprintroute.c

 1 #include "unp.h"

 2 void

 3 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

 4 {

 5  int n;

 6  char mesg[MAXLINE];

 7  int on;

 8  char control[MAXLINE];

 9  struct msghdr msg;

10  struct cmsghdr *cmsg;

11  struct iovec iov[1];

12  on = 1;

13  Setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVRTHDR, &on, sizeof(on));

14  bzero(&msg, sizeof(msg));

15  iov[0].iov_base = mesg;

16  msg.msg_name = pcliaddr;

17  msg.msg_iov = iov;

18  msg.msg_iovlen = 1;

19  msg.msg_control = control;

20  for (;;) {

21   msg.msg_namelen = clilen;

22   msg.msg_controllen = sizeof(control);

23   iov[0].iov_len = MAXLINE;

24   n = Recvmsg(sockfd, &msg, 0);

25   for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;

26    cmsg = CMSG_NXTHDR(&msg, cmsg)) {

27    if (cmsg->cmsg_level == IPPROTO_IPV6 &&

28     cmsg->cmsg_type == IPV6_RTHDR) {

29     inet6_srcrt_print(CMSG_DATA(cmsg));

30     Inet6_rth_reverse(CMSG_DATA(cmsg), CMSG_DATA(cmsg));

31    }

32   }

33   iov[0].iov_len = n;

34   Sendmsg(sockfd, &msg, 0);

35  }

36 }

Включение параметра IPV6_RECVRTHDR и подготовка структуры msghdr

12-13 Чтобы получить информацию о маршруте, мы должны установить параметр сокета IPV6_RECVRTHDR. Кроме того, мы должны использовать функцию recvmsg, поэтому мы настраиваем поля структуры msghdr, которые не требуют изменения.

Настройка изменяемых полей и вызов recvmsg

21-24 Мы устанавливаем размер полей длины и вызываем recvmsg.

Поиск и обработка маршрута от отправителя

25-32 Мы перебираем вспомогательные данные, используя CMSG_FIRSTHDR и CMSG_NXTHDR. Несмотря на то, что мы ожидаем получить только один объект вспомогательных данных, выполнить такой перебор всегда полезно. Если мы обнаруживаем заголовок маршрутизации, он распечатывается функцией inet6_srcrt_print (листинг 27.7). Затем маршрут обращается функцией inet6_rth_reverse для последующего использования при возвращении пакета клиенту. В данном случае обращение производится без копирования в новый буфер, так что можно использовать старый объект вспомогательных данных для отправки пакета клиенту.

Отправка эхо-пакета

33-34 Мы устанавливаем длину пакета и передаем его клиенту вызовом sendmsg.

Благодаря наличию вспомогательных библиотечных функций IPv6 наша функция inet6_srcrt_print становится почти тривиальной.

Листинг 27.7. Функция inet6_srcrt_print: вывод маршрута

 1 #include "unp.h"

 2 void

 3 inet6_srcrt_print(void *ptr)

 4 {

 5  int i, segments;

 6  char str[INET6_ADDRSTRLEN];

 7  segments = Inet6_rth_segments(ptr);

 8  printf("received source route: ");

 9  for (i = 0; i < segments; i++)

10   printf("%s ", Inet_ntop(AF_INET6, Inet6_rth_getaddr(ptr, i),

11    str, sizeof(str)));

12  printf(" ");

13 }

Определение количества сегментов маршрута

7 Количество сегментов маршрута определяется функцией inet6_rth_segments.

Перебор сегментов

9-11 Мы перебираем сегменты маршрута, вызывая для каждого из них inet6_rth_getaddr и преобразуя адреса в формат представления функцией inet_ntop.

Клиенту и серверу, работающим с маршрутами IPv6, не нужно ничего знать о формате этих маршрутов внутри пакета. Библиотечные функции интерфейса скрывают детали форматирования, но не мешают нам программировать с той же гибкостью, которая была в IPv4, где параметры нужно было строить вручную.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Заголовок REG-файла

Из книги Тонкости реестра Windows Vista. Трюки и эффекты автора Клименко Роман Александрович

Заголовок REG-файла Важным элементом любого REG-файла системного реестра Windows является его первая строка, текст которой нельзя изменять ни при каких обстоятельствах. Только по ней система сможет распознать, что текущий файл содержит именно данные реестра. Вот как выглядит


Заголовок Win32

Из книги Язык программирования С# 2005 и платформа .NET 2.0. [3-е издание] автора Троелсен Эндрю

Заголовок Win32 Заголовок Win32 декларирует, что компоновочный блок может загружаться и управляться средствами операционных систем семейства Windows. Данные этого заголовка также идентифицируют тип приложения (консольное, с графическим интерфейсом или библиотека


Заголовок CLR

Из книги Основы AS/400 автора Солтис Фрэнк

Заголовок CLR Заголовок CLR- это блок данных, который должны поддерживать все файлы .NET (и действительно поддерживают, благодаря компилятору C#), чтобы среда CLR имела возможность обрабатывать их. По сути, этот заголовок определяет множество флагов, позволяющих среде


Заголовок сегмента

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

Заголовок сегмента Заголовок сегмента содержит следующую информацию:байт типа;биты флагов:существования (постоянный или временный);авторасширения;наличия в сегменте тегов;другие;число выделенных для него страниц;адрес базового сегмента объекта;адрес


Заголовок EPA

Из книги HTML 5, CSS 3 и Web 2.0. Разработка современных Web-сайтов. автора Дронов Владимир

Заголовок EPA Заголовок ЕРА содержится в базовом сегменте всякого системного объекта и содержит следующую информацию об объекте:байт атрибутов:постоянный ли;подвешенный ли;поврежден ли;присутствует ли группа доступа;трассируется ли;участвует ли в


6.13.1 Заголовок датаграммы

Из книги Инфобизнес на полную мощность [Удвоение продаж] автора Парабеллум Андрей Алексеевич

6.13.1 Заголовок датаграммы Заголовок датаграммы организован как 5 или более 32-разрядных слов. Максимальная длина заголовка — 15 слов (т.е. 60 октетов), но на практике большинство заголовков датаграмм имеют минимально возможную длину в 5 слов (20 октетов).Поля заголовка показаны


9.4.1 Заголовок UDP

Из книги Социальные сети [Источники новых клиентов для бизнеса] автора Парабеллум Андрей Алексеевич

9.4.1 Заголовок UDP На рис. 9.3 представлен формат заголовка UDP. Заголовок содержит 16-разрядные номера портов источника и назначения, определяющие конечные точки коммуникации. Поле длины определяет общее количество октетов в заголовке и части для данных сообщения UDP. Поле


10.8 Заголовок TCP

Из книги UNIX: разработка сетевых приложений автора Стивенс Уильям Ричард

10.8 Заголовок TCP На рис. 10.14 показан формат сегмента (заголовок TCP и данные). Заголовок начинается с идентификаторов портов источника и назначения. Следующее далее поле порядкового номера (sequence number) указывает позицию в исходящем потоке данных, которую занимает данный


22.7.3 Дополнительный заголовок Hop-by-Hop

Из книги автора

22.7.3 Дополнительный заголовок Hop-by-Hop Заголовок Hop-by-Hop переносит информацию, которая должна проверяться на каждом участке попадания по пути следования пакета. Формат этого заголовка показан на рис. 22.5. Рис. 22.5. Заголовок Hop-by-HopЗаголовок Hop-by-Hop может выполнять различные


Заголовок и секции таблицы

Из книги автора

Заголовок и секции таблицы Теперь рассмотрим дополнительные возможности HTML по созданию таблиц. На практике они применяются нечасто, но иногда могут пригодиться.Прежде всего, с помощью парного тега <CAPTION> мы можем дать таблице заголовок. Текст заголовка помещают


Заголовок

Из книги автора

Заголовок Раньше в интернет-магазинах было модно писать в заголовке под шапкой сайта фразы типа «Добро пожаловать в наш интернет-магазин!», «Мы любим и ценим наших покупателей», «У нас лучшее соотношение цены и качества» и т. п.Однако вскоре их владельцы поняли (к


Заголовок

Из книги автора

Заголовок Необходимо придумать интересный заголовок и/или слоган, например, как это реализовано у нас: «25-й час. Экстремальный тайм-менеджмент. У вас больше времени, чем вы думаете».Что важно в заголовке? Во-первых, он должен быть конкретным, чтобы люди сразу понимали, о чем


А.2. Заголовок IPv4

Из книги автора

А.2. Заголовок IPv4 Уровень IP обеспечивает не ориентированную на установление соединения (connectionless) и ненадежную службу доставки дейтаграмм (RFC 791 [94]). Уровень IP делает все возможное для доставки IP-дейтаграммы определенному адресату, но не гарантирует, что дейтаграмма будет


А.3. Заголовок IPv6

Из книги автора

А.3. Заголовок IPv6 На рис. А.2 показан формат заголовка IPv6 (RFC 2460 [27]). Рис. А.2. Формат заголовка IPv6? Значение 4-разрядного поля номера версии (version) равно 6. Данное поле занимает первые 4 бита первого байта заголовка (так же как и в версии IPv4, см. рис. А.1), поэтому если получающий стек