UNIX: разработка сетевых приложений - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
int inet6_opt_next(const void *extbuf, socklen_t extlen,
int offset, uint8_t *typep, socklen_t *lenp, void **databufp);
Возвращает: смещение следующего параметра, -1 в случае достижения конца списка параметров или в случае ошибки
int inet6_opt_find(const void *extbuf, socklen_t extlen,
int offset, uint8_t type, socklen_t *lenp, void **databufp);
Возвращает: смещение следующего параметра, -1 в случае достижения конца списка параметров или в случае ошибки
int inet6_opt_get_val(const void *databuf, int offset, void *val, socklen_t vallen);
Возвращает: новое значение смещения внутри буфера databuf
Функция inet6_opt_next обрабатывает следующий параметр в буфере. Аргументы extbuf и extlen определяют буфер, в котором содержится заголовок. Как и у inet6_opt_append, аргумент offset представляет собой текущее смещение внутри буфера. При первом вызове inet6_opt_next значение этого аргумента должно быть равно нулю, а при всех последующих — значению, возвращенному при предыдущем вызове функции. Аргументы typep, lenp и databufp предназначены для возвращения функцией типа, длины и значения параметра соответственно. Функция inet6_opt_next возвращает -1 в случае обработки заголовка с нарушенной структурой или в случае достижения конца буфера.
Функция inet6_opt_find аналогична предыдущей функции, но позволяет вызывающему процессу задать тип параметра, который следует искать (аргумент type), вместо того чтобы каждый раз возвращать следующий параметр.
Функция inet6_opt_get_val предназначена для извлечения значений из параметра по указателю databuf, возвращаемому предшествующим вызовом inet6_opt_next или inet6_opt_find. Как и для inet6_opt_set_val, аргумент offset должен начинаться с 0 для каждого параметра, а затем должен приравниваться значению, возвращаемому предшествующим вызовом inet6_opt_get_val.
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, за тем исключением, что здесь мы используем библиотечные функции, а не свои собственные.