10 optr = Malloc(44); /* NOP, код параметра. len, указатель + до 10
адресов */
11 bzero(optr, 44); /* гарантирует наличие EOL на конце */
12 ocnt = 0;
13 *optr++ = IPOPT_NOP; /* выравнивающие NOP */
14 *optr++ = type ? IPOPT_SSRR : IPOPT_LSRR;
15 lenptr = optr++; /*
поле длины заполняется позже */
16 *optr++ = 4; /* сдвиг на первый адрес */
17 return(optr - 4); /* указатель для setsockopt */
18 }
Инициализация
10-17
Мы выделяем в памяти буфер, максимальный размер которого — 44 байт, и обнуляем его содержимое. Значение параметра EOL равно нулю, так что тем самым параметр инициализируется байтами EOL. Затем мы подготавливаем заголовок для маршрутизации от источника. Как показано на рис. 27.1, сначала мы обеспечиваем выравнивание при помощи параметра NOP, после чего указываем тип маршрута (гибкий, жесткий), длину и значение указателя. Мы сохраняем указатель в поле
len
. Это значение мы будем записывать при поочередном добавлении адресов к списку. Указатель на параметр возвращается вызывающему процессу, а затем передается как четвертый аргумент функции
setsockopt
.
Следующая функция,
inet_srcrt_add
, добавляет один IPv4-адрес к создаваемому маршруту от отправителя.
Листинг 27.2. Функция inet_srcrt_add: добавление одного IPv4-адреса к маршруту от отправителя
//ipopts/sourceroute.с
19 int
20 inet_srcrt_add(char *hostptr)
21 {
22 int len;
23 struct addrinfo *ai;
24 struct sockaddr_in *sin;
25 if (ocnt > 9)
26 err_quit("too many source routes with: %s", hostptr);
Аргумент функции указывает либо на имя узла, либо на адрес IP в точечно- десятичной записи.
Проверка переполнения
25-26
Мы проверяем количество переданных адресов и выполняем инициализацию,
если обрабатывается первый адрес.
Получение двоичного IP-адреса и запись маршрута
29-37
Функция
host_serv
обрабатывает имя узла или его IP-адрес, а возвращаемый ей адрес в двоичной форме мы помещаем в список. Мы обновляем поле
len
и возвращаем полный размер буфера (с учетом параметров NOP), который вызывающий процесс затем передаст функции
setsockopt
.
Когда полученный маршрут от отправителя возвращается приложению функцией
getsockopt
, формат этого параметра отличается от того, что было показано на рис. 27.1. Формат полученного параметра маршрута от отправителя показан на рис. 27.2.
Рис. 27.2. Формат параметра маршрута от отправителя, возвращаемого функцией getsockopt
В первую очередь, мы можем отметить, что порядок следования адресов изменен ядром на противоположный относительно полученного маршрута от отправителя. Имеется в виду следующее: если в полученном маршруте содержались адреса А, В, С и D в указанном порядке, то под противоположным порядком подразумевается следующий: D, С, В, А. Первые 4 байта содержат первый IP-адрес из списка, затем следует однобайтовый параметр NOP (для выравнивания), затем — 3-байтовый заголовок параметра маршрута от отправителя, и далее остальные IP-адреса. После 3-байтового заголовка может следовать до 9 IP-адресов, и максимальное значение поля
len
в возвращенном заголовке равно 39. Поскольку параметр NOP всегда присутствует, длина буфера, возвращаемая функцией
getsockopt
, всегда будет равна значению, кратному 4 байтам.
ПРИМЕЧАНИЕ
Формат, приведенный на рис. 27.2, определен в заголовочном файле <netinet/ip_var.h> в виде следующей структуры:
#define MAX_IPOPTLEN 40
struct ipoption {
struct in_addr ipopt_dst; /* адрес первого получателя */
char ipopt_list[MAX_IPOPTLEN]; /* соответствующие параметры */
};
В листинге 27.3 мы анализируем эти данные, не используя указанную структуру.
Возвращаемый формат отличается от того, который был передан функции
setsockopt
. Если нам было бы нужно преобразовать формат, показанный на рис. 27.2, к формату, показанному на рис. 27.1, нам следовало бы поменять местами первые и вторые 4 байта и изменить значение поля
len
, добавив к имеющемуся значению 4. К счастью, нам не нужно этого делать, так как Беркли-реализации автоматически используют обращенный маршрут от получателя для сокета TCP. Иными словами, данные, возвращаемые функцией
getsockopt
(представленные на рис. 27.2), носят чисто информативный характер. Нам не нужно вызывать функцию
setsockopt
, чтобы указать ядру на необходимость использования данного маршрута для дейтаграмм IP, отсылаемых по соединению TCP, — ядро сделает это само. Подобный пример с нашим сервером TCP мы вскоре увидим.