Address resolution protocol – что это?

Arp: протокол определения адреса

Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?

Глава 4 ARP: протокол определения

адреса

Введение

Проблема, которую мы будем обсуждать в этой

главе, заключается в том, что IP адреса имеют

какое-либо значение только в семействе

протоколов TCP/IP. Канальные уровни, такие как Ethernet

или Token ring, имеют собственную схему адресации (в

основном 48-битные адреса); сетевые уровни, в свою

очередь, используют эти канальные уровни. Сеть

Ethernet, может быть использована различными

сетевыми уровнями в одно и то же время.

Компьютеры использующие разные сетевые

протоколы могут находиться на одном и том же

физическом кабеле.

Когда фрейм Ethernet отправляется от одного

хоста по локальной сети к другому, по его

48-битному Ethernet адресу определяется, к какому

интерфейсу он должен быть доставлен. Драйвер

сетевой платы никогда не смотрит на IP адрес

назначения в IP датаграмме.

Другими словами возникает необходимость

установить соответствие между двумя различными

формами адресов: 32-битными IP адресами и

каким-либо типом адресов канального уровня. RFC 826 [Plummer 1982] – официальная спецификация ARP.

На рисунке 4.1 показаны два протокола,

которые мы рассмотрим в этой и следующей главах: протокол определения адреса (ARP – address

resolution protocol) и обратный протокол определения

адреса (RARP – reverse address resolution protocol).

Address resolution protocol - что это?

Рисунок 4.1 Протоколы определения

адреса: ARP и RARP.

ARP предоставляет динамическое

сопоставление IP адресов и соответствующих

аппаратных адресов. Мы используем термин

динамическое, так как это происходит

автоматически и обычно не зависит от

используемых прикладных программ или воли

системного администратора.

RARP, в основном, используется системами без

жестких дисков (бездисковые рабочие станции или X

терминалы), однако здесь требуется ручная

конфигурация с участием системного

администратора. Мы рассмотрим RARP в главе

5.

Пример

Если мы введем команду

% ftp bsdi

будет выполнена следующая

последовательность действий. (См. рисунок 4.2.)

  1. Приложение, FTP клиент, вызывает функцию gethostbyname(3), чтобы конвертировать имя

    хоста (bsdi) в 32-битный IP адрес. Эта функция в DNS (Domain

    Name System) называется разборщиком (resolver)

    , мы опишем это подробно в главе 14.

    Подобное преобразование осуществляется с

    использованием DNS или, если существует маленькая

    сеть, то с помощью статического файла хостов (/etc/hosts).

  2. FTP клиент требует установить TCP соединение с

    указанным IP адресом.

  3. TCP посылает запрос на установление

    соединения удаленному хосту, посылая IP

    датаграммы по указанному IP адресу. (Мы рассмотрим

    как это делается более подробно в главе

    18.)

  4. Если хост назначения подключен к сети (Ethernet,

    Token ring, или к другому концу канала точка-точка), IP

    датаграмма может быть послана непосредственно

    хосту. Если хост назначения находится в

    удаленной сети, IP маршрутизатор определяет Internet

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

    следующей пересылки, чтобы послать туда IP

    датаграмму. В обоих случаях IP датаграмма

    посылается либо хосту, либо маршрутизатору,

    подключенные непосредственно к данной сети.

  5. Если используется Ethernet, посылающий хост

    должен конвертировать 32-битный адрес в 48-битный

    Ethernet адрес. Или другими словами, осуществить

    преобразование из логического Internet адреса в

    соответствующий физический аппаратный адрес.

    Этим занимается ARP. ARP работает в

    широковещательных сетях, где много хостов или

    маршрутизаторов подключено к одной и той же сети.

  6. ARP посылает фрейм Ethernet, который называется ARP

    запрос (ARP request), каждому хосту в сети. Подобный

    метод рассылки называется широковещательным

    запросом (broadcast). На рисунке 4.2 широковещательный

    запрос показан пунктирными линиями. ARP запрос

    содержит IP адрес хоста назначения (имя которого

    bsdi) и запрос “если Вы владелец этого IP адреса,

    пожалуйста сообщите мне Ваш аппаратный адрес”.

Address resolution protocol - что это?

Рисунок 4.2 Реакция ARP на

ввод пользователя: ftp hostname.

  • Хост назначения на ARP уровне получает этот

    широковещательный запрос, определяет, что

    отправитель спрашивает именно его IP адрес, и

    отвечает на него ARP откликом (ARP reply). Этот отклик

    содержит IP адрес и соответствующий аппаратный

    адрес.

  • ARP отклик принимается, и IP датаграмма, из-за

    которой начался обмен ARP запрос – ARP отклик, может

    быть послана.

  • IP датаграмма отправляется на хост

    назначения.

  • Фундаментальная концепция, заложенная в ARP,

    заключается в следующем. Сетевой интерфейс имеет

    аппаратный адрес (48-битное значение для Ethernet или

    Token ring). Фреймы, которыми обмениваются на

    аппаратном уровне, должны адресоваться к

    корректному интерфейсу. Однако TCP/IP испоьзует

    собственную схему адрессации: 32-битные IP адреса.

    Знание IP адреса хоста не позволяет ядру послать

    датаграмму этому хосту. Драйвер Ethernet должен

    знать аппаратный адрес пункта назначения, чтобы

    послать туда данные. В задачу ARP входит

    обеспечение динамического соответствия между

    32-битными IP адресами и аппаратными адресами,

    используемыми различными сетевыми технологиями.

    Каналы точка-точка не используют ARP. Когда

    эти каналы конфигурируются (обычно во время

    загрузки), ядру необходимо сказать IP адрес для

    каждого конца канала. Аппаратные адреса, такие

    как Ethernet адреса, в данном случае не используются.

    ARP Кэш

    Эффективность функционирования ARP

    во многом зависит от ARP кэша (ARP cache),

    который присутствует на каждом хосте. В кэше

    содержатся Internet адреса и соответствующие им

    аппаратные адреса. Стандартное время жизни

    каждой записи в кэше составляет 20 минут с момента

    создания записи.

    Содержимое ARP кэша можно увидеть с

    использованием команды arp(8). Опция -a показывает все записи, содержащиеся

    в кэше:

    bsdi % arp -a
    sun (140.252.13.33) at 8:0:20:3:f6:42
    svr4 (140.252.13.34) at 0:0:c0:c2:9b:26

    48-битные Ethernet адреса приведены в виде шести

    шестнадцатиричных чисел, разделенных

    двоеточиями. Дополнительные функции команды arp

    обсуждаются в разделе “Команда arp”

    главы 4.

    Формат пакета ARP

    На рисунке 4.3 показан формат ARP запроса и

    формат ARP отклика, в случае использования Ethernet и IP

    адресов. (ARP можно использовать в других сетей,

    при этом он способен устанавливать соответствие

    не только для IP адресов. Первые четыре поля,

    следующие за полем типа фрейма, указывают на типы

    и размеры заключительных четырех полей.)

    Address resolution protocol - что это?

    Рисунок 4.3 Формат ARP запроса или

    отклика при работе с Ethernet.

    Два первых поля в Ethernet заголовке – поля

    источника и назначения Ethernet. Специальный адрес

    назначения Ethernet, состоящий из всех единиц,

    означает широковещательный адрес. Фреймы с таким

    адресом будут получены всеми Ethernet интерфейсами

    на кабеле.

    Двухбайтовый тип фрейма (frame type)

    Ethernet указывает, данные какого типа, пойдут

    следом. Для ARP запроса или ARP отклика это поле

    содержит 0x0806.

    Выражения аппаратный (hardware) и протокол

    (protocol) используются для описания полей в пакетах

    ARP. Например, ARP запрос запрашивает аппаратный

    адрес (в данном случае Ethernet адрес)

    соответствующий адресу протокола (в данном

    случае IP адрес).

    Поле hard type указывает на тип аппаратного

    адреса. Для Ethernet это значение равно единице. Prot type

    указывает тип адреса протокола, к которому будет

    приведено соответствие. Для IP адресов

    используется значение 0x0800. По своему целевому

    назначению это значение соответствует полю типа

    во фрейме Ethernet, который содержит IP датаграмму.

    (См. рисунок 2.1.)

    Два следующих однобайтных поля, hard size и prot

    size, указывают на размеры в байтах аппаратного

    адреса и адреса протокола. В ARP запросах и

    откликах они составляют 6 для Ethernet и 4 для IP

    адреса.

    Поле op указывает на тип

    операции: ARP запрос (значение устанавливается в 1),

    ARP отклик (2), RARP запрос (3) и RARP отклик (4). (Мы

    поговорим о RARP в главе 5.) Это поле

    необходимо, так как поля типа фрейма

    (frame type) одинаковы для ARP запроса и ARP отклика.

    Следующие четыре поля: аппаратный

    адрес отправителя (Ethernet адрес в данном примере), адрес протокола (IP адрес), аппаратный

    адрес назначения и адрес протокола назначения.

    Обратите внимание, что в данном случае

    происходит некоторое дублирование информации:

    аппаратный адрес отправителя может быть получен

    как из Ethernet заголовка, так и из ARP запроса.

    Для ARP запроса все поля заполнены, за

    исключением аппаратного адреса назначения.

    Когда система получает ARP запрос, который

    предназначается ей, она вставляет свой

    аппаратный адрес, меняет местами адреса

    источника и назначения, устанавливает поле op в

    значение 2 и отправляет отклик.

    Примеры ARP

    В этом разделе мы воспользуемся командой tcpdump, чтобы посмотреть, как в

    действительности работает ARP при запуске

    обычного TCP приложения, например, Telnet. В приложении А содержится

    дополнительная информация о работе программы

    tcpdump.

    Типичный пример

    Чтобы посмотреть как функционирует ARP, мы

    запустим команду telnet, чтобы

    подсоединиться к discard (discard server – сервер, не

    предоставляющий пользователю никаких услуг)

    серверу.

    bsdi% arp -a

                     проверяем,

    что ARP кэш пуст
    bsdi% telnet svr4 discard     подсоединяемся к

    серверу
    Trying 140.252.13.34 …
    Connected to svr4.
    Escape character is ‘^]’ .
    ^]

                               нажимаем

    Control и правую квадратную скобку,
    telnet> quit

                     чтобы

    получить приглашение Telnet и закрыть сессию
    Connection closed.

    Пока осуществляются эти действия, мы

    запускаем команду tcpdump с опцией -e на

    другом хосте (sun). Это позволит нам посмотреть

    аппаратные адреса (48-битные адреса Ethernet).

    1 0.0

                    0:0:c0:6f:2d:40

    ff:ff:ff:ff:ff:ff arp 60:
    arp

    who-has svr4 tell bsdi
    2 0.002174 (0.0022)  0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 arp 60:
    arp

    reply svr4 is-at 0:0:c0:c2:9b:26
    3 0.002831 (0.0007)  0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip 60:
    bsdi.1030>svr4.discard:

    S 596459521:596459521 (0)
    win

    4096 <mss 1024> [tos 0x10]4 0.007834 (0.0050)  0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 ip 60:
    svr4.discard>bsdi.1030:

    S 3562228252:3562228252 (0)
    ack

    596459522 win 4096 <mss 1024>
    5 0.009615 (0.0018)  0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip 60:
    bsdi.1030>svr4.discard:

    . ack 1 win 4096 [tos 0x10]

    Рисунок 4.4 ARP запрос и ARP отклик,

    сгенерированные при запросе на Telnet соединение.

    На рисунке А.3 в

    приложении А показан реальный вывод команды tcpdump, которую мы запустили на рисунке

    4.4. Так как это первый пример вывода tcpdump в тексте,

    Вам стоит посмотреть приложение, чтобы увидеть

    как мы преобразовали вывод, чтобы он стал более

    красивым и читаемым.

    Мы удалили 4 заключительные строки из

    вывода tcpdump, которые соответствуют разрыву

    соединения (более подробно рассматривается в главе 18), так как они не имеют

    отношения к нашему обсуждению.

    В строке 1 приводится аппаратный адрес

    источника (bsdi), в данном случае – 0:0:c0:6f:2d:40.

    Аппаратный адрес назначения ff:ff:ff:ff:ff:ff,

    являющийся широковещательным адресом Ethernet.

    Каждый Ethernet интерфейс на кабеле получит фрейм и

    обработает его, как показано на рисунке 4.2.

    Следующее поле вывода в строке 1, arp,

    означает, что тип фрейма (frame type) установлен в 0x0806,

    что означает либо ARP запрос, либо ARP отклик.

    Значение 60, напечатанное после слов arp и ip, в

    каждой из 5 строк означает длину фрейма Ethernet. Так

    как размер ARP запроса и ARP отклика составляет 42

    байта (28 байт – ARP сообщение, 14 байт – Ethernet

    заголовок), каждый фрейм дополняется до минимума

    Ethernet: 60 байт.

    Если обратиться к рисунку 1.7, то можно

    увидеть, что минимальный размер (60 байт) включает

    в себя 14-байтный Ethernet заголовок, однако не

    включает 4-байтный Ethernet завершитель. В некоторых

    книгах минимум приводится как 64 байта, что

    включает в себя и Ethernet завершитель. Мы

    целенаправленно не включили 14 байт заголовка

    Ethernet в минимум из 46 байт, показанных на рисунке 1.7.

    Максимальный размер составляет 1500 байт. Обычно

    эта величина называется максимальный блок

    передачи (MTU – maximum transmission unit) (См. рисунок 2.5). Мы

    часто используем понятие MTU, потому что оно

    ограничивает размер IP датаграммы, однако оно

    никак не связано с минимальным размером.

    Большинство драйверов устройств или

    интерфейсных плат автоматически дополняют Ethernet

    фреймы до минимального размера. IP датаграммы в

    строках 3, 4 и 5 (содержащие TCP сегменты) меньше чем

    минимум и также будут дополнены до 60 байт.

    Следующее поле в строке 1, “arp кто имеет”

    (arp who-has), идентифицирует фрейм как ARP запрос с IP

    адресом svr4 в качестве адреса назначения и IP

    адресом bsdi в качестве адреса отправителя. tcpdump по

    умолчанию приводит имена хостов соответствующие

    IP адресам. (В разделе “Беспричинный

    ARP” мы воспользуемся опцией -n,

    чтобы посмотреть реальные IP адреса в ARP запросе.)

    В строке 2 мы видим, что ARP запрос

    распространяется как широковещательный, тогда

    как адрес назначения ARP отклика это адрес bsdi

    (0:0:c0:6f:2d:40). ARP отклик посылается непосредственно

    запрашивающему хосту; он не является

    широковещательным.

    tcpdump печатает для этого фрейма

    arp reply вместе с именем хоста и аппаратным адресом

    отвечающего.

    В строке 3 отправляется первый TCP сегмент,

    содержащий требование об установлении

    соединения. Аппаратный адрес назначения это

    адрес хоста назначения (svr4). Мы рассмотрим этот

    сегмент более подробно в главе 18.

    Число, которое печатается в каждой строке,

    после номера строки – это время (в секундах) когда

    пакет был принят программой tcpdump. В каждой строке

    после первой содержится разница во времени (в

    секундах) с предыдущей строкой. Это значение

    приводится в скобках. Как видно из рисунка, время

    между отправкой ARP запроса и получением ARP

    отклика составляет 2,2 мс. Первый TCP сегмент послан

    через 0,7 мс после этого. Таким образом, для

    динамического определения адреса с

    использованием ARP, в данном примере,

    потребовалось менее чем 3 мс.

    И последнее на что следует обратить

    внимание в выводе tcpdump: мы не увидим ARP запрос от

    svr4, когда он посылает свой первый TCP сегмент

    (строка 4). Дело в том, что svr4 уже имеет данные о

    bsdi в своем ARP кэше, так как, когда система получает

    ARP запрос, помимо того что она посылает ARP отклик,

    она также сохраняет аппаратный адрес и IP адрес

    запросившего в своем ARP кэше. Это логично, так как

    если запросивший собирается послать IP

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

    отправить ответ на эту датаграмму.

    ARP запрос на несуществующий хост

    Что произойдет, если запрашиваемый хост

    выключен или не существует вообще? Попробуем

    указать несуществующий Internet адрес –

    идентификатор сети и идентификатор подсети

    будет от нашего локального Ethernet, однако

    указанного идентификатора хоста не существует.

    На рисунке 3.10 мы видели, что идентификаторов

    хостов с 36-го по 62-ой не существуют (идентификатор

    хоста 63 – широковещательный адрес). В данном

    примере мы будем использовать идентификатор

    хоста 36.

    в этот раз telnet на IP адрес, а не на имя хоста (hostname)
    bsdi % date ; telnet 140.252.13.36 ; date
    Sat Jan 30 06:46:33 MST 1993
    Trying 140.252.13.36 …
    telnet: Unable to connect to remote host : Connection timed out
    Sat Jan 30 06:47:49 MST 1993

                прошло 76

    секунд

    bsdi % arp -a

                              проверяем

    ARP кэш
    ? (140.252.13.36) at (incomplete)

    На рисунке 4.5 мы видим вывод tcpdump.

    1  0.0

                     arp

    who-has 140.252.13.36 tell bsdi
    2  5.509069 ( 5.5091)  arp who-has 140.252.13.36 tell bsdi
    3 29.509745 (24.0007)  arp who-has 140.252.13.36 tell bsdi

    Рисунок 4.5 ARP запрос на несуществующий хост.

    Сейчас мы не указываем опцию -e, так как мы

    уже знаем, что ARP запрос широковещательный.

    Здесь интересно посмотреть, с какой

    частотой рассылаются ARP запросы: 5,5 секунд после

    первого запроса и снова через 24 секунды. (Мы

    рассмотрим тайм-ауты TCP и алгоритм повторных

    передач более подробно в главе 21.)

    Полное время, показанное в выводе tcpdump,

    составляет 29,5 секунды. Однако вывод от команды date перед и после команды telnet

    показывает, что запрос на соединение от Telnet

    клиента длился в течении 75 секунд. И

    действительно, мы увидим позже, что большинство

    BSD реализаций устанавливают ограничение в 75

    секунд для завершения запроса на установление TCP

    соединения.

    В главе 18, при рассмотрении

    последовательности TCP сегментов, которые

    посылаются в процессе установления соединения,

    мы увидим, что моменты отправки ARP запросов

    полностью совпадают с отправкой сегментов TCP SYN.

    Обратите внимание на то, что в кабеле мы

    никогда не увидим TCP сегменты. Все что мы можем

    увидеть это ARP запросы. Пока не получен ARP отклик,

    TCP сегменты не могут быть отправлены, так как

    неизвестен аппаратный адрес назначения. Если

    запустить tcpdump

    в

    фильтрующем режиме, чтобы увидеть только данные

    TCP, вывода не будет вообще.

    Тайм-аут ARP кэша

    Для записей, вводимых в ARP кэш, обычно

    устанавливается тайм-аут. (В разделе “Команда

    arp” мы увидим, что команда arp

    позволяет системному администратору

    поместить в кэш определенную запись, и на нее

    тайм-аут распространяться не будет.) Реализации,

    произошедшие от Berkeley, обычно установливают

    тайм-аут, в 20 минут для завершенной записи и 3

    минуты для незавершенной записи. (Мы видели

    незавершенную запись в предыдущем примере, когда

    заставили отправить ARP запрос на несуществующий

    хост.) Эти реализации обычно перестартовывают

    20-минутный тайм-аут для записи каждый раз, когда

    эта запись используется.Требования к хостам Host Requirements RFC говорит, что запись должна

    удаляться по тайм-ауту, даже если данная запись

    используется, однако большинство реализаций,

    произошедших от Berkeley, не делают этого – они

    перестартовывают тайм-аут каждый раз, когда

    происходит обращение к записи.

    Уполномоченный агент ARP

    Уполномоченный агент ARP позволяет

    маршрутизатору отвечать на ARP запросы в одну

    сеть, в то время как запрашиваемый хост находится

    в другой сети. С помощью этого средства

    происходит обман отправителя, который отправил

    ARP запрос, после чего он думает, что маршрутизатор

    является хостом назначения, тогда как в

    действительности хост назначения находится

    “на другой стороне” маршрутизатора.

    Маршрутизатор выступает в роли уполномоченного

    агента хоста назначения, перекладывая пакеты от

    другого хоста.

    Для того чтобы лучше описать работу

    уполномоченных агентов ARP, мы рассмотрим пример.

    Из рисунка 3.10 видно, что система sun подключена к

    двум сетям Ethernet. Однако в действительности это не

    так, в чем можно убедиться, если сравнить этот

    рисунок с рисунком, который приведен на

    внутренней стороне обложки. Между sun и подсетью

    140.252.1 находится маршрутизатор, который выступает

    в роли уполномоченного агента ARP, при этом все

    выглядело так, как будто sun находится в подсети

    140.252.1. На рисунке 4.6 показано, что Telebit NetBlazer,

    названный netb, находится между подсетью и хостом

    sun.

    Address resolution protocol - что это?

    Рисунок 4.6 Пример уполномоченного

    ARP.

    Когда какой-либо другой хост в подсети 140.252.1

    (скажем, gemini) хочет послать IP датаграмму хосту sun

    на адрес 140.252.1.29, gemini сравнивает идентификатор

    сети (140.252) и идентификатор подсети (1), и если они

    идентичны, отправляет ARP запрос в верхний Ethernet (на

    рисунке 4.6) на IP адрес 140.252.1.29. Маршрутизатор netb

    распознает этот IP адрес как принадлежащий одному

    из dialup хостов и отвечает, отправив аппаратный

    адрес этого Ethernet интерфейса в кабель 140.252.1. Хост

    gemini

    посылает IP датаграмму в netb по

    Ethernet, а netb направляет датаграмму в sun по SLIP

    каналам с дозвоном (dialup). Это делает его

    прозрачным для всех хостов подсети 140.252.1, так как

    хост sun действительно находится “позади”

    маршрутизатора netb.

    Если мы запустим команду arp на хосте gemini

    после общения с хостом sun, то увидим, что оба эти

    адреса принадлежат подсети 140.252.1 (netb и sun) и что им

    соответствует один аппаратный адрес. Как

    правило, это основная причина, по которой

    используется уполномоченный агент ARP.

    gemini % arp -a
    появится много строк про хосты из подсети 140.252.1
    netb (140.252.1.183) at 0:80:ad:3:6a:80
    sun (140.252.1.29) at 0:80:ad:3:6a:80

    Еще одна деталь на рисунке 4.6, которую

    необходимо объяснить, это отсутствие IP адреса

    под квадратиком, который обозначает

    маршрутизатор netb (SLIP канал). Почему на обоих

    концах SLIP канала нет IP адреса, как между bsdi и slip? В

    разделе “Команда ifconfig”

    главы 3, из вывода команды ifconfig, мы

    заметили, что адрес назначения SLIP канала 140.252.1.183.

    NetBlazer не требует наличия IP адресов на каждом

    конце SLIP канала. (Это позволяет сэкономить

    несколько столь ценных в настоящее время IP

    адресов.) Он определяет какой хост посылает пакет

    в зависимости от того по какому

    последовательному интерфейсу прибыл пакет,

    поэтому нет необходимости каждому хосту на SLIP

    канале использовать уникальный IP адрес для

    своего канала с маршрутизатором. Все dialup хосты

    используют адрес 140.252.1.183 в качестве адреса

    назначения для своих SLIP каналов.

    Уполномоченный агент ARP

    обеспечивает доставку датаграмм к

    маршрутизатору sun, однако как это делают другие

    хосты из подсети 140.252.13? Для направления

    датаграмм в другие хосты должна использоваться

    маршрутизация. Где-либо в сети 140.252 должны быть

    сделаны записи в таблице маршрутизации, поэтому

    все датаграммы, направляющиеся в подсеть 140.252.13

    или в указанные хосты этой подсети, будут

    направляться на маршрутизатор netb. Этот

    маршрутизатор знает, как доставить датаграммы в

    их конечный пункт назначения, отправляя их через

    маршрутизатор sun.

    Уполномоченный агент ARP также называется смешанным (promiscuous ARP) или расщепленным

    (ARP hack). Эти имена появились благодаря другому

    использованию уполномоченных агентов ARP: они

    применялись для того, чтобы спрятать друг от

    друга две физические сети между которыми

    находился маршрутизатор. В этом случае обе

    физические сети использовали один и тот же

    идентификатор сети, так как маршрутизатор,

    находящийся между ними, был сконфигурирован как

    уполномоченный ARP агент, чтобы отвечать на ARP

    запросы из одной сети к хостам в другой сети. Эта

    техника использовалась в прошлом, чтобы спрятать

    группу хостов с более старой версией TCP/IP на

    отдельном физическом кабеле. Две причины, по

    которым приходилось отделять эти

    “устаревшие” хосты, заключались в том, что,

    во-первых, они не могли поддерживать разделение

    на подсети и, во-вторых, использовали старые

    широковещательные адреса (идентификатор хоста

    состоял из всех нулевых бит вместо современного

    стандарта, при котором идентификатор хоста

    состоит из единичных битов).

    “Беспричинный” ARP

    Другая характеристика ARP, которую стоит

    рассмотреть – “беспричинный” ARP (gratuitous ARP). Он

    проявляется, когда хост посылает ARP запрос,

    основываясь на собственном IP адресе. Обычно это

    делается, когда интерфейс конфигурируется во

    время загрузки.

    Если мы запустим tcpdump на хосте sun

    при загрузке хоста bsdi, то увидим пакет,

    показанный на рисунке 4.7.

    1  0.0

                    0:0:c0:6f:2d:40

    ff:ff:ff:ff:ff:ff arp 60:
    arp

    who-has 140.252.13.35 tell 140.252.13.35

    Рисунок 4.7 Пример “беспричинного” ARP.

    (Мы использовали флаг -n

    программы tcpdump, чтобы напечатать адреса в

    цифровом десятичном виде вместо имен хостов.) В

    терминах полей ARP запроса, адрес протокола

    отправителя и адрес протокола назначения

    идентичны: 140.252.13.35 (что соответствует хосту bsdi).

    Адрес источника в заголовке Ethernet, 0:0:c0:6f:2d:40 как

    показано программой tcpdump, эквивалентен

    аппаратному адресу отправителя (из рисунка 4.4).

    “Беспричинный” ARP предоставляет две

    характеристики.

    1. Он позволяет хосту определить, существует

      ли другой хост с тем же самым IP адресом. Хост bsdi не

      ожидает отклика на свой запрос, однако если

      отклик принят, на консоли возникает сообщение об

      ошибке “обнаружен дублирующий IP адрес с Ethernet

      адресом: a:b:c:d:e:f”. Это предупреждение

      системному администратору о том, что одна из

      систем неправильно сконфигурирована.

    2. Если хост, посылающий “беспричинный” ARP,

      только что изменил свой аппаратный адрес (может

      быть потому, что хост был выключен, удалена

      интерфейсная плата и затем хост был

      перезагружен), этот пакет заставляет другой хост

      на кабеле, который имеет запись в своем кэше для

      старого аппаратного адреса, обновить ARP кэш

      соответствующим образом. Малоизвестный факт о

      протоколе ARP [Plummer 1982] заключается в том, что если

      хост получает ARP запрос для IP адреса, который он

      уже имеет в кэше, содержимое кэша обновляется

      аппаратным адресом отправителя (Ethernet адресом) из

      запроса ARP. Это делается для любого запроса ARP,

      полученного хостом. (Повторим, что ARP запросы

      широковещательные, поэтому такие действия

      осуществляются всеми хостами в сети каждый раз

      при появлении ARP запроса.) [Bhide, Elnozahy, and

      Morgan 1991] описывает приложения, которые используют

      эту характеристику ARP. Она позволяет запасному

      (backup) файл-серверу занять место вышедшего из

      строя сервера с использованием

      “беспричинного” ARP запроса с запасным

      аппаратным адресом, однако с тем же IP адресом,

      который имел вышедший из строя хост. При этом все

      пакеты, направляемые серверу, вышедшему из строя,

      будут посланы на запасной сервер, а

      пользовательские приложения не будут знать о

      том, что основной сервер вышел из строя.

    К сожалению, авторы

    затем отказались от этого подхода, так как он

    зависит от корректности реализации ARP на всех

    типах клиентов. Существуют различные типы ARP,

    которые не поддерживают эту спецификацию.

    Наблюдения за всеми системами в

    подсети, используемой в этой книге, показывает,

    что SunOS 4.1.3 и 4.4BSD

    используют “беспричинный” ARP при загрузке, а SVR4 не поддерживает эту характеристику.

    Команда arp

    Мы использовали эту команду с флагом -a, чтобы отобразить все записи ARP кэша.

    Существуют и другие опции.

    Суперпользователь может использовать

    опцию -d, чтобы удалить запись из ARP

    кэша. (Это было сделано перед запуском некоторых

    примеров, чтобы показать изменения ARP.)

    Записи могут быть добавлены с

    использованием опции -s. При

    использовании этой опции необходимо указать имя

    хоста и Ethernet адрес, IP адрес, соответствующий

    имени хоста, и Ethernet адрес добавляются в кэш.

    Подобная запись делается на постоянной основе

    (она не будет удалена из кэша по тайм-ауту), если

    только в конце командной строки не будет

    использовано ключевое слово temp.

    Ключевое слово pub

    в конце

    командной строки с опцией -s приведет к тому, что

    система будет функционировать как ARP агент для

    этого хоста. Система будет отвечать на ARP запросы

    для IP адресов, соответствующих имени хоста, при

    этом ответ будет содержать указанный Ethernet адрес.

    Если объявленный адрес это адрес самой

    отвечающей системы, это означает, что система

    работает как уполномоченный агент ARP для

    указанного имени хоста.

    Краткие выводы

    ARP это основной протокол, который

    используется практически во всех реализациях

    TCP/IP. Обычно его функционирование не зависит от

    используемых приложений или воли системного

    администратора. ARP кэш является

    фундаментом этой работы. Мы использовали команду

    arp, чтобы просмотреть или модифицировать кэш.

    Каждая запись в кэше имеет таймер, который

    используется для удаления незавершенных или

    завершенных записей. Команда arp отображает

    модифицированные записи в ARP кэше.

    Мы посмотрели обычное функционирование ARP и

    специализированные версии: уполномоченный агент

    ARP (когда маршрутизатор отвечает на ARP запросы для

    хостов, находящихся на другом интерфейсе

    маршрутизатора) и “беспричинный” ARP

    (посылающий ARP запросы для своего собственного IP

    адреса, обычно во время загрузки).

    Упражнения

    Вернемся к команде, которую

    мы исполнили, чтобы получить вывод, показанный на

    рисунке 4.4. Что произойдет, если после того как мы

    проверили локальный ARP кэш и он оказался пустым,

    мы введем команду

    bsdi % rsh svr4 arp -a

    чтобы проверить, что ARP кэш

    также пуст на хосте назначения? (Эта команда

    исполнит команду arp -a на хосте svr4.)

  • Опишите тест, который позволит определить,

    корректно ли обрабатывает определенный хост

    “беспричинные” ARP запросы.

  • Шаг номер 7 в разделе “Пример”

    может занять определенное время (миллисекунды),

    потому что пакет отправлен и ARP ожидает ответа.

    Как Вы думаете, обработает ли ARP несколько

    датаграмм, которые прибыли от IP на тот же адрес

    назначения в течение этого периода времени?

  • В конце раздела “Примеры ARP” мы

    упомянули, что RFC Host Requirements и Berkeley

    реализации отличаются с точки зрения обработки

    тайм-аутов для активных записей ARP. Что

    произойдет, если клиент Berkeley постарается

    установить контакт с сервером, который был

    выключен и из него была удалена плата Ethernet?

    Изменится ли что-нибудь, если сервер выдаст

    “беспричинный” ARP запрос при загрузке?

  • Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?Address resolution protocol - что это?

    Tcp/ip крупным планом – arp: протокол определения адреса

    Введение

    Проблема, которую мы будем обсуждать в этой главе, заключается в том,
    что IP адреса имеют какое-либо значение только в семействе протоколов TCP/IP.
    Канальные уровни, такие как Ethernet или Token ring, имеют собственную схему
    адресации (в основном 48-битные адреса); сетевые уровни, в свою очередь,
    используют эти канальные уровни. Сеть Ethernet, может быть использована
    различными сетевыми уровнями в одно и то же время. Компьютеры использующие
    разные сетевые протоколы могут находиться на одном и том же физическом кабеле.

    Когда фрейм Ethernet отправляется от одного хоста по локальной сети к
    другому, по его 48-битному Ethernet адресу определяется, к какому интерфейсу он
    должен быть доставлен. Драйвер сетевой платы никогда не смотрит на IP адрес
    назначения в IP датаграмме.

    Другими словами возникает необходимость установить соответствие между
    двумя различными формами адресов: 32-битными IP адресами и каким-либо типом
    адресов канального уровня. RFC 826 [Plummer 1982] –
    официальная спецификация ARP.

    На рисунке 4.1 показаны два протокола, которые мы рассмотрим в этой и
    следующей главах: протокол определения адреса (ARP – address
    resolution protocol) и обратный протокол определения адреса (RARP – reverse
    address resolution protocol).

    Address resolution protocol - что это?

    Рисунок 4.1 Протоколы определения адреса: ARP и
    RARP.

    ARP предоставляет динамическое сопоставление IP адресов и
    соответствующих аппаратных адресов. Мы используем термин динамическое, так как
    это происходит автоматически и обычно не зависит от используемых прикладных
    программ или воли системного администратора.

    RARP, в основном, используется системами без жестких дисков
    (бездисковые рабочие станции или X терминалы), однако здесь требуется ручная
    конфигурация с участием системного администратора. Мы рассмотрим RARP в главе
    5
    .

    Пример

    Если мы введем команду

    % ftp bsdi

    будет выполнена следующая последовательность действий. (См. рисунок
    4.2.)

    1. Приложение, FTP клиент, вызывает функцию gethostbyname(3), чтобы конвертировать имя хоста (bsdi) в
      32-битный IP адрес. Эта функция в DNS (Domain Name System) называется разборщиком (resolver) , мы опишем это подробно в главе 14.
      Подобное преобразование осуществляется с использованием DNS или, если существует
      маленькая сеть, то с помощью статического файла хостов (/etc/hosts).
    2. FTP клиент требует установить TCP соединение с указанным IP
      адресом.
    3. TCP посылает запрос на установление соединения удаленному хосту,
      посылая IP датаграммы по указанному IP адресу. (Мы рассмотрим как это делается
      более подробно в главе 18.)
    4. Если хост назначения подключен к сети (Ethernet, Token ring, или к
      другому концу канала точка-точка), IP датаграмма может быть послана
      непосредственно хосту. Если хост назначения находится в удаленной сети, IP
      маршрутизатор определяет Internet адрес непосредственно подключенного маршрутизатора следующей пересылки, чтобы послать туда IP
      датаграмму. В обоих случаях IP датаграмма посылается либо хосту, либо
      маршрутизатору, подключенные непосредственно к данной сети.
    5. Если используется Ethernet, посылающий хост должен конвертировать
      32-битный адрес в 48-битный Ethernet адрес. Или другими словами, осуществить
      преобразование из логического Internet адреса в соответствующий физический
      аппаратный адрес. Этим занимается ARP. ARP работает в широковещательных сетях,
      где много хостов или маршрутизаторов подключено к одной и той же сети.
    6. ARP посылает фрейм Ethernet, который называется ARP запрос (ARP
      request), каждому хосту в сети. Подобный метод рассылки называется широковещательным запросом (broadcast). На рисунке 4.2
      широковещательный запрос показан пунктирными линиями. ARP запрос содержит IP
      адрес хоста назначения (имя которого bsdi) и запрос “если Вы владелец этого
      IP адреса, пожалуйста сообщите мне Ваш аппаратный адрес”.

    Address resolution protocol - что это?

    Рисунок 4.2 Реакция ARP на ввод пользователя: ftp
    hostname.

  • Хост назначения на ARP уровне получает этот широковещательный запрос,
    определяет, что отправитель спрашивает именно его IP адрес, и отвечает на него
    ARP откликом (ARP reply). Этот отклик содержит IP адрес и соответствующий
    аппаратный адрес.
  • ARP отклик принимается, и IP датаграмма, из-за которой начался обмен
    ARP запрос – ARP отклик, может быть послана.
  • IP датаграмма отправляется на хост назначения.
  • Фундаментальная концепция, заложенная в ARP, заключается в следующем.
    Сетевой интерфейс имеет аппаратный адрес (48-битное значение для Ethernet или
    Token ring). Фреймы, которыми обмениваются на аппаратном уровне, должны
    адресоваться к корректному интерфейсу. Однако TCP/IP испоьзует собственную схему
    адрессации: 32-битные IP адреса. Знание IP адреса хоста не позволяет ядру
    послать датаграмму этому хосту. Драйвер Ethernet должен знать аппаратный адрес
    пункта назначения, чтобы послать туда данные. В задачу ARP входит обеспечение
    динамического соответствия между 32-битными IP адресами и аппаратными адресами,
    используемыми различными сетевыми технологиями.

    Каналы точка-точка не используют ARP. Когда эти каналы конфигурируются
    (обычно во время загрузки), ядру необходимо сказать IP адрес для каждого конца
    канала. Аппаратные адреса, такие как Ethernet адреса, в данном случае не
    используются.

    ARP Кэш

    Эффективность функционирования ARP во многом
    зависит от ARP кэша (ARP cache), который присутствует на
    каждом хосте. В кэше содержатся Internet адреса и соответствующие им аппаратные
    адреса. Стандартное время жизни каждой записи в кэше составляет 20 минут с
    момента создания записи.

    Содержимое ARP кэша можно увидеть с использованием команды arp(8). Опция -a показывает все записи,
    содержащиеся в кэше:

    bsdi % arp -a
    sun
    (140.252.13.33) at 8:0:20:3:f6:42
    svr4 (140.252.13.34) at
    0:0:c0:c2:9b:26

    48-битные Ethernet адреса приведены в виде шести шестнадцатиричных
    чисел, разделенных двоеточиями. Дополнительные функции команды arp обсуждаются в
    разделе “Команда arp” главы
    4.

    Формат пакета ARP

    На рисунке 4.3 показан формат ARP запроса и формат ARP отклика, в
    случае использования Ethernet и IP адресов. (ARP можно использовать в других
    сетей, при этом он способен устанавливать соответствие не только для IP адресов.
    Первые четыре поля, следующие за полем типа фрейма, указывают на типы и размеры
    заключительных четырех полей.)

    Address resolution protocol - что это?

    Рисунок 4.3 Формат ARP запроса или отклика при работе с
    Ethernet.

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

    Двухбайтовый тип фрейма (frame type) Ethernet
    указывает, данные какого типа, пойдут следом. Для ARP запроса или ARP отклика
    это поле содержит 0x0806.

    Выражения аппаратный (hardware) и протокол (protocol) используются для
    описания полей в пакетах ARP. Например, ARP запрос запрашивает аппаратный адрес
    (в данном случае Ethernet адрес) соответствующий адресу протокола (в данном
    случае IP адрес).

    Поле hard type указывает на тип аппаратного адреса. Для Ethernet это
    значение равно единице. Prot type указывает тип адреса протокола, к которому
    будет приведено соответствие. Для IP адресов используется значение 0x0800. По
    своему целевому назначению это значение соответствует полю типа во фрейме
    Ethernet, который содержит IP датаграмму. (См. рисунок 2.1.)

    Два следующих однобайтных поля, hard size и prot size, указывают на
    размеры в байтах аппаратного адреса и адреса протокола. В ARP запросах и
    откликах они составляют 6 для Ethernet и 4 для IP адреса.

    Поле op указывает на тип операции: ARP запрос
    (значение устанавливается в 1), ARP отклик (2), RARP запрос (3) и RARP отклик
    (4). (Мы поговорим о RARP в главе 5.) Это поле необходимо, так как
    поля типа фрейма (frame type) одинаковы для ARP запроса и
    ARP отклика.

    Следующие четыре поля: аппаратный адрес
    отправителя (Ethernet адрес в данном примере), адрес
    протокола (IP адрес), аппаратный адрес назначения и адрес протокола назначения.
    Обратите внимание, что в данном случае происходит некоторое дублирование
    информации: аппаратный адрес отправителя может быть получен как из Ethernet
    заголовка, так и из ARP запроса.

    Для ARP запроса все поля заполнены, за исключением аппаратного адреса
    назначения. Когда система получает ARP запрос, который предназначается ей, она
    вставляет свой аппаратный адрес, меняет местами адреса источника и назначения,
    устанавливает поле op в значение 2 и отправляет отклик.

    Примеры ARP

    В этом разделе мы воспользуемся командой tcpdump,
    чтобы посмотреть, как в действительности работает ARP при запуске обычного TCP
    приложения, например, Telnet. В приложении А содержится дополнительная
    информация о работе программы tcpdump.

    Типичный пример

    Чтобы посмотреть как функционирует ARP, мы запустим команду telnet, чтобы подсоединиться к discard (discard server –
    сервер, не предоставляющий пользователю никаких услуг) серверу.

    bsdi% arp -a                  проверяем, что ARP кэш пуст
    bsdi%
    telnet svr4 discard     подсоединяемся к серверу
    Trying 140.252.13.34

    Connected to svr4.
    Escape character is ‘^]’ .
    ^]                            нажимаем Control и правую квадратную
    скобку,
    telnet> quit                  чтобы получить приглашение
    Telnet и закрыть сессию
    Connection closed.

    Пока осуществляются эти действия, мы запускаем команду tcpdump с
    опцией -e на другом хосте (sun). Это позволит нам посмотреть
    аппаратные адреса (48-битные адреса Ethernet).

    1 0.0                 0:0:c0:6f:2d:40 ff:ff:ff:ff:ff:ff arp 60:

    arp who-has svr4 tell bsdi
    2 0.002174 (0.0022) 
    0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 arp 60:
    arp reply svr4
    is-at 0:0:c0:c2:9b:26
    3 0.002831 (0.0007)  0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip
    60:
    bsdi.1030>svr4.discard: S 596459521:596459521
    (0)
    win 4096 <mss 1024> [tos 0x10]4 0.007834
    (0.0050)  0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 ip 60:
    svr4.discard>bsdi.1030: S 3562228252:3562228252 (0)
    ack 596459522 win 4096 <mss 1024>
    5 0.009615 (0.0018)  0:0:c0:6f:2d:40
    0:0:c0:c2:9b:26 ip 60:
    bsdi.1030>svr4.discard: . ack
    1 win 4096 [tos 0x10]

    Рисунок 4.4 ARP запрос и ARP отклик, сгенерированные при запросе на
    Telnet соединение.

    На рисунке А.3 в приложении А
    показан реальный вывод команды tcpdump, которую мы запустили
    на рисунке 4.4. Так как это первый пример вывода tcpdump в тексте, Вам стоит
    посмотреть приложение, чтобы увидеть как мы преобразовали вывод, чтобы он стал
    более красивым и читаемым.

    Мы удалили 4 заключительные строки из вывода tcpdump, которые
    соответствуют разрыву соединения (более подробно рассматривается в главе 18),
    так как они не имеют отношения к нашему обсуждению.

    В строке 1 приводится аппаратный адрес источника (bsdi), в данном
    случае – 0:0:c0:6f:2d:40. Аппаратный адрес назначения ff:ff:ff:ff:ff:ff,
    являющийся широковещательным адресом Ethernet. Каждый Ethernet интерфейс на
    кабеле получит фрейм и обработает его, как показано на рисунке 4.2.

    Следующее поле вывода в строке 1, arp, означает, что тип фрейма (frame
    type) установлен в 0x0806, что означает либо ARP запрос, либо ARP отклик.

    Значение 60, напечатанное после слов arp и ip, в каждой из 5 строк
    означает длину фрейма Ethernet. Так как размер ARP запроса и ARP отклика
    составляет 42 байта (28 байт – ARP сообщение, 14 байт – Ethernet заголовок),
    каждый фрейм дополняется до минимума Ethernet: 60 байт.

    Если обратиться к рисунку 1.7, то можно увидеть, что минимальный
    размер (60 байт) включает в себя 14-байтный Ethernet заголовок, однако не
    включает 4-байтный Ethernet завершитель. В некоторых книгах минимум приводится
    как 64 байта, что включает в себя и Ethernet завершитель. Мы целенаправленно не
    включили 14 байт заголовка Ethernet в минимум из 46 байт, показанных на рисунке
    1.7. Максимальный размер составляет 1500 байт. Обычно эта величина называется
    максимальный блок передачи (MTU – maximum transmission unit) (См. рисунок 2.5).
    Мы часто используем понятие MTU, потому что оно ограничивает размер IP
    датаграммы, однако оно никак не связано с минимальным размером. Большинство
    драйверов устройств или интерфейсных плат автоматически дополняют Ethernet
    фреймы до минимального размера. IP датаграммы в строках 3, 4 и 5 (содержащие TCP
    сегменты) меньше чем минимум и также будут дополнены до 60 байт.

    Следующее поле в строке 1, “arp кто имеет” (arp who-has),
    идентифицирует фрейм как ARP запрос с IP адресом svr4 в качестве адреса
    назначения и IP адресом bsdi в качестве адреса отправителя. tcpdump по умолчанию
    приводит имена хостов соответствующие IP адресам. (В разделе “Беспричинный ARP” мы воспользуемся
    опцией -n, чтобы посмотреть реальные IP адреса в ARP
    запросе.)

    В строке 2 мы видим, что ARP запрос распространяется как
    широковещательный, тогда как адрес назначения ARP отклика это адрес bsdi
    (0:0:c0:6f:2d:40). ARP отклик посылается непосредственно запрашивающему хосту;
    он не является широковещательным.

    tcpdump печатает для этого фрейма arp reply вместе
    с именем хоста и аппаратным адресом отвечающего.

    В строке 3 отправляется первый TCP сегмент, содержащий требование об
    установлении соединения. Аппаратный адрес назначения это адрес хоста назначения
    (svr4). Мы рассмотрим этот сегмент более подробно в главе 18.

    Число, которое печатается в каждой строке, после номера строки – это
    время (в секундах) когда пакет был принят программой tcpdump. В каждой строке
    после первой содержится разница во времени (в секундах) с предыдущей строкой.
    Это значение приводится в скобках. Как видно из рисунка, время между отправкой
    ARP запроса и получением ARP отклика составляет 2,2 мс. Первый TCP сегмент
    послан через 0,7 мс после этого. Таким образом, для динамического определения
    адреса с использованием ARP, в данном примере, потребовалось менее чем 3 мс.

    И последнее на что следует обратить внимание в выводе tcpdump: мы не
    увидим ARP запрос от svr4, когда он посылает свой первый TCP сегмент (строка 4).
    Дело в том, что svr4 уже имеет данные о bsdi в своем ARP кэше, так как, когда
    система получает ARP запрос, помимо того что она посылает ARP отклик, она также
    сохраняет аппаратный адрес и IP адрес запросившего в своем ARP кэше. Это
    логично, так как если запросивший собирается послать IP датаграмму, то
    получившему скорее всего придется отправить ответ на эту
    датаграмму.

    ARP запрос на несуществующий хост

    Что произойдет, если запрашиваемый хост выключен или не существует
    вообще? Попробуем указать несуществующий Internet адрес – идентификатор сети и
    идентификатор подсети будет от нашего локального Ethernet, однако указанного
    идентификатора хоста не существует. На рисунке 3.10 мы видели, что
    идентификаторов хостов с 36-го по 62-ой не существуют (идентификатор хоста 63 –
    широковещательный адрес). В данном примере мы будем использовать идентификатор
    хоста 36.

    в этот раз telnet на IP адрес, а не на имя хоста (hostname)
    bsdi % date
    ; telnet 140.252.13.36 ; date

    Sat Jan 30 06:46:33 MST 1993
    Trying
    140.252.13.36 …
    telnet: Unable to connect to remote host : Connection timed
    out
    Sat Jan 30 06:47:49 MST 1993             прошло 76 секунд

    bsdi %
    arp -a                           проверяем ARP кэш
    ? (140.252.13.36)
    at (incomplete)

    На рисунке 4.5 мы видим вывод tcpdump.

    1  0.0                  arp who-has 140.252.13.36 tell bsdi
    2  5.509069 (
    5.5091)  arp who-has 140.252.13.36 tell bsdi
    3 29.509745 (24.0007)  arp
    who-has 140.252.13.36 tell bsdi

    Рисунок 4.5 ARP запрос на несуществующий хост.

    Сейчас мы не указываем опцию -e, так как мы уже знаем, что ARP запрос
    широковещательный.

    Здесь интересно посмотреть, с какой частотой рассылаются ARP запросы:
    5,5 секунд после первого запроса и снова через 24 секунды. (Мы рассмотрим
    тайм-ауты TCP и алгоритм повторных передач более подробно в главе 21.)
    Полное время, показанное в выводе tcpdump, составляет 29,5 секунды. Однако вывод
    от команды date перед и после команды telnet показывает, что запрос на соединение от Telnet клиента
    длился в течении 75 секунд. И действительно, мы увидим позже, что большинство
    BSD реализаций устанавливают ограничение в 75 секунд для завершения запроса на
    установление TCP соединения.

    В главе 18, при рассмотрении
    последовательности TCP сегментов, которые посылаются в процессе установления
    соединения, мы увидим, что моменты отправки ARP запросов полностью совпадают с
    отправкой сегментов TCP SYN.

    Обратите внимание на то, что в кабеле мы никогда не увидим TCP
    сегменты. Все что мы можем увидеть это ARP запросы. Пока не получен ARP отклик,
    TCP сегменты не могут быть отправлены, так как неизвестен аппаратный адрес
    назначения. Если запустить tcpdump в
    фильтрующем режиме, чтобы увидеть только данные TCP, вывода не будет вообще.

    Тайм-аут ARP кэша

    Для записей, вводимых в ARP кэш, обычно устанавливается тайм-аут. (В
    разделе “Команда arp” мы увидим,
    что команда arp позволяет системному
    администратору поместить в кэш определенную запись, и на нее тайм-аут
    распространяться не будет.) Реализации, произошедшие от Berkeley, обычно
    установливают тайм-аут, в 20 минут для завершенной записи и 3 минуты для
    незавершенной записи. (Мы видели незавершенную запись в предыдущем примере,
    когда заставили отправить ARP запрос на несуществующий хост.) Эти реализации
    обычно перестартовывают 20-минутный тайм-аут для записи каждый раз, когда эта
    запись используется.

    Требования к хостам Host Requirements RFC говорит, что запись должна удаляться по
    тайм-ауту, даже если данная запись используется, однако большинство реализаций,
    произошедших от Berkeley, не делают этого – они перестартовывают тайм-аут каждый
    раз, когда происходит обращение к записи.

    Уполномоченный агент ARP

    Уполномоченный агент ARP позволяет маршрутизатору отвечать на ARP
    запросы в одну сеть, в то время как запрашиваемый хост находится в другой сети.
    С помощью этого средства происходит обман отправителя, который отправил ARP
    запрос, после чего он думает, что маршрутизатор является хостом назначения,
    тогда как в действительности хост назначения находится “на другой
    стороне” маршрутизатора. Маршрутизатор выступает в роли уполномоченного
    агента хоста назначения, перекладывая пакеты от другого хоста.

    Для того чтобы лучше описать работу уполномоченных агентов ARP, мы
    рассмотрим пример. Из рисунка 3.10 видно, что система sun подключена к двум
    сетям Ethernet. Однако в действительности это не так, в чем можно убедиться,
    если сравнить этот рисунок с рисунком, который приведен на внутренней стороне
    обложки. Между sun и подсетью 140.252.1 находится маршрутизатор, который
    выступает в роли уполномоченного агента ARP, при этом все выглядело так, как
    будто sun находится в подсети 140.252.1. На рисунке 4.6 показано, что Telebit
    NetBlazer, названный netb, находится между подсетью и хостом sun.

    Address resolution protocol - что это?

    Рисунок 4.6 Пример уполномоченного ARP.

    Когда какой-либо другой хост в подсети 140.252.1 (скажем, gemini)
    хочет послать IP датаграмму хосту sun на адрес 140.252.1.29, gemini сравнивает
    идентификатор сети (140.252) и идентификатор подсети (1), и если они идентичны,
    отправляет ARP запрос в верхний Ethernet (на рисунке 4.6) на IP адрес
    140.252.1.29. Маршрутизатор netb распознает этот IP адрес как принадлежащий
    одному из dialup хостов и отвечает, отправив аппаратный адрес этого Ethernet
    интерфейса в кабель 140.252.1. Хост gemini посылает IP
    датаграмму в netb по Ethernet, а netb направляет датаграмму в sun по SLIP
    каналам с дозвоном (dialup). Это делает его прозрачным для всех хостов подсети
    140.252.1, так как хост sun действительно находится “позади”
    маршрутизатора netb.

    Если мы запустим команду arp на хосте gemini после общения с хостом
    sun, то увидим, что оба эти адреса принадлежат подсети 140.252.1 (netb и sun) и
    что им соответствует один аппаратный адрес. Как правило, это основная причина,
    по которой используется уполномоченный агент ARP.

    gemini % arp -a
    появится много строк про хосты из подсети
    140.252.1
    netb (140.252.1.183) at 0:80:ad:3:6a:80
    sun (140.252.1.29) at
    0:80:ad:3:6a:80

    Еще одна деталь на рисунке 4.6, которую необходимо объяснить, это
    отсутствие IP адреса под квадратиком, который обозначает маршрутизатор netb
    (SLIP канал). Почему на обоих концах SLIP канала нет IP адреса, как между bsdi и
    slip? В разделе “Команда ifconfig”
    главы 3, из вывода команды ifconfig, мы заметили, что адрес
    назначения SLIP канала 140.252.1.183. NetBlazer не требует наличия IP адресов на
    каждом конце SLIP канала. (Это позволяет сэкономить несколько столь ценных в
    настоящее время IP адресов.) Он определяет какой хост посылает пакет в
    зависимости от того по какому последовательному интерфейсу прибыл пакет, поэтому
    нет необходимости каждому хосту на SLIP канале использовать уникальный IP адрес
    для своего канала с маршрутизатором. Все dialup хосты используют адрес
    140.252.1.183 в качестве адреса назначения для своих SLIP каналов.

    Уполномоченный агент ARP обеспечивает доставку
    датаграмм к маршрутизатору sun, однако как это делают другие хосты из подсети
    140.252.13? Для направления датаграмм в другие хосты должна использоваться
    маршрутизация. Где-либо в сети 140.252 должны быть сделаны записи в таблице
    маршрутизации, поэтому все датаграммы, направляющиеся в подсеть 140.252.13 или в
    указанные хосты этой подсети, будут направляться на маршрутизатор netb. Этот
    маршрутизатор знает, как доставить датаграммы в их конечный пункт назначения,
    отправляя их через маршрутизатор sun.

    Уполномоченный агент ARP также называется смешанным (promiscuous ARP) или расщепленным (ARP hack). Эти имена появились благодаря другому
    использованию уполномоченных агентов ARP: они применялись для того, чтобы
    спрятать друг от друга две физические сети между которыми находился
    маршрутизатор. В этом случае обе физические сети использовали один и тот же
    идентификатор сети, так как маршрутизатор, находящийся между ними, был
    сконфигурирован как уполномоченный ARP агент, чтобы отвечать на ARP запросы из
    одной сети к хостам в другой сети. Эта техника использовалась в прошлом, чтобы
    спрятать группу хостов с более старой версией TCP/IP на отдельном физическом
    кабеле. Две причины, по которым приходилось отделять эти “устаревшие”
    хосты, заключались в том, что, во-первых, они не могли поддерживать разделение
    на подсети и, во-вторых, использовали старые широковещательные адреса
    (идентификатор хоста состоял из всех нулевых бит вместо современного стандарта,
    при котором идентификатор хоста состоит из единичных битов).

    “Беспричинный” ARP

    Другая характеристика ARP, которую стоит рассмотреть –
    “беспричинный” ARP (gratuitous ARP). Он проявляется, когда хост
    посылает ARP запрос, основываясь на собственном IP адресе. Обычно это делается,
    когда интерфейс конфигурируется во время загрузки.

    Если мы запустим tcpdump на хосте sun при загрузке
    хоста bsdi, то увидим пакет, показанный на рисунке 4.7.

    1  0.0                 0:0:c0:6f:2d:40 ff:ff:ff:ff:ff:ff arp 60:

    arp who-has 140.252.13.35 tell
    140.252.13.35

    Рисунок 4.7 Пример “беспричинного” ARP.

    (Мы использовали флаг -n программы tcpdump, чтобы
    напечатать адреса в цифровом десятичном виде вместо имен хостов.) В терминах
    полей ARP запроса, адрес протокола отправителя и адрес протокола назначения
    идентичны: 140.252.13.35 (что соответствует хосту bsdi). Адрес источника в
    заголовке Ethernet, 0:0:c0:6f:2d:40 как показано программой tcpdump,
    эквивалентен аппаратному адресу отправителя (из рисунка 4.4).

    “Беспричинный” ARP предоставляет две характеристики.

    1. Он позволяет хосту определить, существует ли другой хост с тем же
      самым IP адресом. Хост bsdi не ожидает отклика на свой запрос, однако если
      отклик принят, на консоли возникает сообщение об ошибке “обнаружен
      дублирующий IP адрес с Ethernet адресом: a:b:c:d:e:f”. Это предупреждение
      системному администратору о том, что одна из систем неправильно
      сконфигурирована.
    2. Если хост, посылающий “беспричинный” ARP, только что
      изменил свой аппаратный адрес (может быть потому, что хост был выключен, удалена
      интерфейсная плата и затем хост был перезагружен), этот пакет заставляет другой
      хост на кабеле, который имеет запись в своем кэше для старого аппаратного
      адреса, обновить ARP кэш соответствующим образом. Малоизвестный факт о протоколе
      ARP [Plummer 1982] заключается в том, что если хост получает ARP запрос для IP
      адреса, который он уже имеет в кэше, содержимое кэша обновляется аппаратным
      адресом отправителя (Ethernet адресом) из запроса ARP. Это делается для любого
      запроса ARP, полученного хостом. (Повторим, что ARP запросы широковещательные,
      поэтому такие действия осуществляются всеми хостами в сети каждый раз при
      появлении ARP запроса.) [Bhide, Elnozahy, and Morgan 1991] описывает приложения, которые используют эту характеристику ARP. Она позволяет
      запасному (backup) файл-серверу занять место вышедшего из строя сервера с
      использованием “беспричинного” ARP запроса с запасным аппаратным
      адресом, однако с тем же IP адресом, который имел вышедший из строя хост. При
      этом все пакеты, направляемые серверу, вышедшему из строя, будут посланы на
      запасной сервер, а пользовательские приложения не будут знать о том, что
      основной сервер вышел из строя.

    К сожалению, авторы затем отказались
    от этого подхода, так как он зависит от корректности реализации ARP на всех
    типах клиентов. Существуют различные типы ARP, которые не поддерживают эту
    спецификацию.

    Наблюдения за всеми системами в подсети, используемой в
    этой книге, показывает, что SunOS 4.1.3 и 4.4BSD используют “беспричинный” ARP при загрузке, а
    SVR4 не поддерживает эту характеристику.

    Команда arp

    Мы использовали эту команду с флагом -a, чтобы
    отобразить все записи ARP кэша. Существуют и другие опции.

    Суперпользователь может использовать опцию -d,
    чтобы удалить запись из ARP кэша. (Это было сделано перед запуском некоторых
    примеров, чтобы показать изменения ARP.)

    Записи могут быть добавлены с использованием опции -s. При использовании этой опции необходимо указать имя хоста и
    Ethernet адрес, IP адрес, соответствующий имени хоста, и Ethernet адрес
    добавляются в кэш. Подобная запись делается на постоянной основе (она не будет
    удалена из кэша по тайм-ауту), если только в конце командной строки не будет
    использовано ключевое слово temp.

    Ключевое слово pub в конце командной строки с
    опцией -s приведет к тому, что система будет функционировать как ARP агент для
    этого хоста. Система будет отвечать на ARP запросы для IP адресов,
    соответствующих имени хоста, при этом ответ будет содержать указанный Ethernet
    адрес. Если объявленный адрес это адрес самой отвечающей системы, это означает,
    что система работает как уполномоченный агент ARP для указанного имени
    хоста.

    Краткие выводы

    ARP это основной протокол, который используется практически во всех
    реализациях TCP/IP. Обычно его функционирование не зависит от используемых
    приложений или воли системного администратора. ARP кэш
    является фундаментом этой работы. Мы использовали команду arp, чтобы просмотреть
    или модифицировать кэш. Каждая запись в кэше имеет таймер, который используется
    для удаления незавершенных или завершенных записей. Команда arp отображает
    модифицированные записи в ARP кэше.

    Мы посмотрели обычное функционирование ARP и специализированные
    версии: уполномоченный агент ARP (когда маршрутизатор отвечает на ARP запросы
    для хостов, находящихся на другом интерфейсе маршрутизатора) и
    “беспричинный” ARP (посылающий ARP запросы для своего собственного IP
    адреса, обычно во время загрузки).

    Упражнения

    1. Вернемся к команде, которую мы исполнили,
      чтобы получить вывод, показанный на рисунке 4.4. Что произойдет, если после того
      как мы проверили локальный ARP кэш и он оказался пустым, мы введем
      команду

      bsdi % rsh svr4 arp -a

      чтобы проверить, что ARP кэш также пуст на хосте назначения? (Эта команда
      исполнит команду arp -a на хосте svr4.)

    2. Опишите тест, который позволит определить, корректно ли обрабатывает
      определенный хост “беспричинные” ARP запросы.
    3. Шаг номер 7 в разделе “Пример”
      может занять определенное время (миллисекунды), потому что пакет отправлен и ARP
      ожидает ответа. Как Вы думаете, обработает ли ARP несколько датаграмм, которые
      прибыли от IP на тот же адрес назначения в течение этого периода времени?
    4. В конце раздела “Примеры ARP”
      мы упомянули, что RFC Host Requirements и Berkeley
      реализации отличаются с точки зрения обработки тайм-аутов для активных записей
      ARP. Что произойдет, если клиент Berkeley постарается установить контакт с
      сервером, который был выключен и из него была удалена плата Ethernet? Изменится
      ли что-нибудь, если сервер выдаст “беспричинный” ARP запрос при
      загрузке?

    §

    Введение

    TCP это протокол, ориентированный на соединение.
    Перед тем как какая-либо сторона может послать данные другой, между ними должно
    быть установлено соединение. В этой главе мы подробно рассмотрим, как
    устанавливается TCP соединение и как оно разрывается.

    Так как для работы TCP необходимо установить соединение между двумя
    концами он отличается от протоколов без соединения, таких
    как UDP. В главе 11 мы видели, что при
    использовании UDP каждая сторона просто отсылает датаграммы другой, не установив
    перед этим соединения.

    Установление и разрыв соединения

    Для того чтобы посмотреть, что происходит при установлении и разрыве
    TCP соединения, мы исполнили на системе svr4 следующую команду:

    svr4 % telnet bsdi discard
    Trying 192.82.148.3 …
    Connected
    to bsdi.
    Escape character is ‘^]’.
    ^]                          
    вводим Control, правую квадратную скобку,
    telnet> quit
                    чтобы Telnet клиент разорвал соединение
    Connection
    closed.

    Команда telnet устанавливает TCP соединение с
    хостом bsdi на порт, соответствующий discard сервису (глава 1, раздел “Стандартные простые
    сервисы”
    ). Это как раз тот тип сервиса, который нам необходим, чтобы
    посмотреть, что происходит при установлении и разрыве соединения, но без обмена
    данными.

    Вывод tcpdump

    На рисунке 18.1 показан вывод tcpdump для
    сегментов, сгенерированных этой командой.

    1  0.0                         svr4.1037 > bsdi.discard: S
    1415531521:1415531521
    (0)
    win 4096
    <mss 1024>
    2  0.002402 (0.0024)           bsdi.discard > svr4.1037:
    S 1823083521:1823083521
    (0)
    ack 1415531522
    win 4096
    <mss
    1024>
    3  0.007224 (0.0048)          svr4.1037 > bsdi.discard: . ack
    1823083522 win 4096
    4  4.155441 (4.1482)          svr4.1037 >
    bsdi.discard: F 1415531522:1415531522
    (0)
    ack 1823083522
    win 4096
    5  4.156747 (0.0013)           bsdi.discard > svr4.1037: . ack
    1415531523 win 4096
    6  4.158144 (0.0014)           bsdi.discard >
    svr4.1037: F 1823083522:1823083522
    (0)
    ack 1415531523
    win 4096
    7  4.180662 (0.0225)          svr4.1037 > bsdi.discard: . ack
    1823083523 win 4096

    Рисунок 18.1 Вывод tcpdump для установления и разрыва TCP
    соединения.

    Эти семь TCP сегментов содержат только TCP заголовки. Обмен данными не
    осуществлялся.

    Для TCP сегментов каждая выходная строка начинается с

    source > destination: flags (источник > назначение:
    флаги)

    где флаги (flags) представляют собой четыре из шести флаговых битов
    TCP заголовка (рисунок 17.2). На рисунке 18.2
    показаны пять различных символов, которые соответствуют флагам и могут появиться
    в выводе.

    флаг

    3-символьное сокращение

    Описание

    S

    SYN

    синхронизирующие номера
    последовательности

    F

    FIN

    отправитель закончил передачу
    данных

    R

    RST

    сброс
    соединения

    P

    PSH

    отправка данных принимающему
    процессу настолько быстро, насколько это возможно

    .

     ни один из четырех флагов не
    установлен

    Рисунок 18.2 Символы флагов, выведенные командой tcpdump для флаговых
    битов в TCP заголовке.

    В данном примере мы видим флаги S, F и точку. Еще два флага (R и P)
    появятся позже. Два других бита флагов в TCP заголовке – ACK и URG – напечатаны
    командой tcpdump.

    В одном сегменте может присутствовать больше чем один из четырех
    флаговых битов, показанных на рисунке 18.2, однако, обычно взведен бывает только
    один флаг.

    RFC 1025 [Postel
    1987] называет сегмент, в котором максимальная комбинация всех доступных
    флаговых битов взведена одновременно (SYN, URG, PSH, FIN и 1 байт данных)
    пакетом Камикадзе (в английском языке существует еще
    несколько определений подобного пакета, а именно – “грязный пакет”,
    “пакет Новогодней елки” и т.п.).

    В строке 1 поле 1415531521:1415531521 (0) означает, что номер
    последовательности пакета равен 1415531521, а количество байт данных в сегменте
    равно 0. Команда tcpdump печатает начальный номер
    последовательности, двоеточие, предполагаемый заключительный номер
    последовательности и затем в скобках количество байт данных. При этом существует
    возможность просмотреть предполагаемый окончательный номер последовательности,
    когда количество байтов больше чем 0. В поле появляется (1), если сегмент
    содержит один или несколько байт пользовательских данных, или (2) если взведены
    флаг SYN, FIN или RST. В строках 1, 2, 4 и 6 на рисунке 18.1 это поле
    появляется, потому что взведены флаговые биты – обмен какими-либо данными в этом
    примере не производился.

    В строке 2 поле ack 1415531522 содержит номер подтверждения. Оно
    печатается только в том случае, если флаг ACK взведен. Поле win 4096 в каждой
    строке вывода показывает размер окна, который был объявлен отправителем. В этом
    примере, где не осуществлялся обмен данными, размер окна оставался неизменным и
    использовалась величина по умолчанию – 4096. (Мы рассмотрим размер окна TCP в
    разделе “Размер окна” главы
    20.)

    И последнее поле в выводе на рисунке 18.1, <mss 1024> показывает
    максимальный размер сегмента (MSS – maximum segment size) ,
    опция, которую устанавливает отправитель. Отправитель не хочет получать TCP
    сегменты больше чем это значение. Это делается обычно для того, чтобы избежать
    фрагментации (глава 11, раздел “Фрагментация IP”). Мы
    рассмотрим максимальный размер сегмента в разделе “Максимальный размер сегмента” этой
    главы, а формат различных опций TCP покажем в разделе “Опции TCP” этой
    главы.

    Временные диаграммы

    На рисунке 18.3 показана временная диаграмма соответствующая этому
    обмену пакетами. (Мы описали некоторые основные характеристики временных
    диаграмм, когда первый раз обратились к рисунку 6.11.) На этом рисунке
    показано, какая сторона отправляет пакеты. Также приведен вывод команды tcpdump
    (на печать выводилось SYN вместо S). В этой временной диаграмме удалено значение
    размера окна, так как это не существенно для нашего обсуждения.

    Протокол установления соединения

    А теперь давайте вернемся к деталям TCP протокола, которые показаны на
    рисунке 18.3. Чтобы установить TCP соединение, необходимо:

    1. Запрашивающая сторона (которая, как правило, называется клиент)
      отправляет SYN сегмент, указывая номер порта сервера, к которому клиент хочет
      подсоединиться, и исходный номер последовательности клиента (в данном примере ISN, 1415531521). Это сегмент номер 1.
    2. Сервер отвечает своим сегментом SYN, содержащим исходный номер
      последовательности сервера (сегмент 2). Сервер также подтверждает приход SYN
      клиента с использованием ACK (ISN клиента плюс один). На SYN используется один
      номер последовательности.
    3. Клиент должен подтвердить приход SYN от сервера с использованием ACK
      (ISN сервера плюс один, сегмент 3).

    Этих трех сегментов достаточно для установления соединения. Часто это
    называется трехразовым рукопожатием (three-way handshake).

    Address resolution protocol - что это?

    Рисунок 18.3 Временная диаграмма установления и разрыва
    соединения.

    Считается, что сторона, которая посылает первый SYN, активизирует
    соединение (активное открытие). Другая сторона, которая
    получает первый SYN и отправляет следующий SYN, принимает пассивное участие в
    открытии соединения (пассивное открытие). (В разделе “Одновременное открытие” этой главы мы
    подробно опишем процедуру открытия соединения, где обе стороны считаются
    активными при установлении соединения.)

    Когда каждая сторона отправила свой SYN чтобы установить соединение,
    она выбирает исходный номер последовательности (ISN) для этого соединения. ISN
    должен меняться каждый раз, поэтому каждое соединение имеет свой, отличный от
    других ISN. RFC 793 [Postel 1981c] указывает, что ISN
    является 32-битным счетчиком, который увеличивается на единицу каждые 4
    микросекунды. Благодаря номерам последовательностей, пакеты, задержавшиеся в
    сети и доставленные позже, не воспринимаются как часть существующего
    соединения.

    Как выбирается номер
    последовательности? В 4.4BSD (и в большинстве Berkeley
    реализаций) при инициализации системы исходный номер последовательности
    устанавливается в 1. Подобная практика осуждается требованием к хостам Host Requirements RFC. Затем эта величина увеличивается на
    64000 каждые полсекунды и возвращается в значение 0 через каждые 9,5 часов. (Это
    соответствует счетчику, который увеличивается на единицу каждые 8 микросекунд, а
    не каждые 4 микросекунды.) Кроме того, каждый раз, когда устанавливается
    соединение, эта переменная увеличивается на 64000.

    Промежуток в 4,1 секунды между сегментами 3 и 4 соответствует времени
    между установлением соединения и вводом команды quit для telnet, чтобы разорвать соединение.

    Протокол разрыва соединения

    Для того чтобы установить соединение, необходимо 3 сегмента, а для
    того чтобы разорвать – 4. Это объясняется тем, что TCP соединение может быть в
    наполовину закрытом состоянии. Так как TCP соединение полнодуплексное (данные могут передвигаться в каждом
    направлении независимо от другого направления), каждое направление должно быть
    закрыто независимо от другого. Правило заключается в том, что каждая сторона
    должна послать FIN, когда передача данных завершена. Когда TCP принимает FIN, он
    должен уведомить приложение, что удаленная сторона разрывает соединение и
    прекращает передачу данных в этом направлении. FIN обычно отправляется в
    результате того, что приложение было закрыто.

    Получение FIN означает только, что в этом направлении прекращается
    движение потока данных. TCP, получивший FIN, может все еще посылать данные.
    Несмотря на то, что приложение все еще может посылать данные при наполовину
    закрытом TCP соединении, на практике только некоторые (совсем немного) TCP
    приложения используют это. Обычным является тот сценарий, который показан на
    рисунке 18.3. Мы опишем наполовину закрытый режим более подробно в разделе “Наполовину закрытый TCP” этой главы.

    Можно сказать, что та сторона, которая первой закрывает соединение
    (отправляет первый FIN), осуществляет активное закрытие, а
    другая сторона (которая приняла этот FIN) осуществляет пассивное закрытие. Обычно, одна сторона осуществляет активное
    закрытие, а другая пассивное, однако в разделе “Одновременное закрытие” этой главы мы
    увидим, что обе стороны могут осуществить активное закрытие.

    Сегмент номер 4 на рисунке 18.3 приводит к закрытию соединения и
    посылается, когда Telnet клиент прекращает работу. Это происходит, когда мы
    вводим quit. При этом TCP клиент вынужден послать FIN, закрывая поток данных от
    клиента к серверу.

    Когда сервер получает FIN, он отправляет назад ACK с принятым номером
    последовательности плюс один (сегмент 5). На FIN тратится один номер
    последовательности, так же как на SYN. В этот момент TCP сервер также доставляет
    приложению признак конца файла (end-of-file) (чтобы выключить сервер). Затем
    сервер закрывает свое соединение, что заставляет его TCP послать FIN (сегмент
    6), который клиент должен подтвердить (ACK), увеличив на единицу номер принятой
    последовательности (сегмент 7).

    На рисунке 18.4 показан типичный обмен сегментами при закрытии
    соединения. Номера последовательности опушены. На этом рисунке FIN посылаются
    из-за того, что приложения закрывают свои соединения, тогда как ACK для этих FIN
    генерируется автоматически программным обеспечением TCP.

    Соединения обычно устанавливаются клиентом, то есть первый SYN
    двигается от клиента к серверу. Однако любая сторона может активно закрыть
    соединение (послать первый FIN). Часто, однако, именно клиент определяет, когда
    соединение должно быть разорвано, так как процесс клиента в основном управляется
    пользователем, который вводит что-нибудь подобное “quit”, чтобы
    закрыть соединение. На рисунке 18.4 мы можем поменять местами метки, приведенные
    наверху рисунка, назвав левую сторону сервером, а правую сторону клиентом.
    Однако даже в этом случае все будет работать именно так, как показано на
    рисунке. (Первый пример в разделе “Простой пример” главы
    14, например, показывал, как сервер времени закрывает соединение.)

    Address resolution protocol - что это?

    Рисунок 18.4 Обычный обмен сегментами при закрытии
    соединения.

    Обычный вывод tcpdump

    Так как задача отсортировать огромное количество номеров
    последовательности довольно сложна, в выводе программы tcpdump содержатся полные
    номера последовательности только для SYN сегментов, а все следующие номера
    последовательностей показаны как относительное смещение от исходных номеров
    последовательности. (Для того чтобы получить вывод, приведенный на рисунке 18.1,
    мы должны были указать опцию -S.) Обычный вывод tcpdump, соответствующий рисунку 18.1, показан на рисунке
    18.5.

    1  0.0                     svr4.1037 > bsdi.discard: S
    1415531521:1415531521(0)
    win 4096 <mss 1024>
    2  0.002402 (0.0024)      bsdi.discard >
    svr4.1037: S
    1823083521:1823083521(0)
    ack 1415531522
    win 4096
    <mss 1024>
    3  0.007224 (0.0048)      svr4.1037 > bsdi.discard: . ack
    1 win 4096
    4  4.155441 (4.1482)      svr4.1037 > bsdi.discard: F 1:1 (0)
    ack 1 win 4096
    5  4.156747 (0.0013)      bsdi.discard > svr4.1037: . ack 2
    win 4096
    6  4.158144 (0.0014)      bsdi.discard > svr4.1037: F 1:1 (0) ack
    2 win 4096
    7  4.180662 (0.0225)      svr4.1037 > bsdi.discard: . ack 2 win
    4096

    Рисунок 18.5 Обычный вывод команды tcpdump, соответствующий
    установлению и разрыву соединения.

    Если у нас не будет возникать необходимости показывать полные номера
    последовательности, мы будем использовать эту форму вывода во всех следующих
    примерах.

    Тайм-аут при установлении соединения

    Существует несколько причин, по которым не может быть установлено
    соединение. Например, хост (сервер) выключен. Чтобы сымитировать подобную
    ситуацию, мы исполнили команду telnet, после того как
    отсоединили Ethernet кабель от сервера. На рисунке 18.6 показан вывод команды
    tcpdump.

    1   0.0                     bsdi.1024 > svr4.discard: S
    291008001:291008001(0)
    win 4096 <mss
    1024>
    [tos
    0x10]2   5.814797 ( 5.8148)     bsdi.1024 > svr4.discard: S
    291008001:291008001(0)
    win 4096 <mss
    1024>
    [tos
    0x10]3  29.815436 (24.0006)     bsdi.1024 > svr4.discard: S
    291008001:291008001(0)
    win 4096 <mss
    1024>
    [tos
    0x10]

    Рисунок 18.6 Вывод команды tcpdump для установления соединения,
    которое было прекращено по тайм-ауту.

    В этом выводе необходимо обратить внимание на то, как часто TCP клиент
    отправляет SYN, стараясь установить соединение. Второй сегмент посылается через
    5,8 секунды после первого, а третий посылается через 24 секунды после
    второго.

    Необходимо заметить, что этот пример
    был запущен примерно через 38 минут после того, как клиент был перезагружен.
    Поэтому соответствующий исходный номер последовательности равен 291008001
    (примерно 38х60х6400х2). В начале главы мы сказали, что типичные системы
    Berkeley устанавливают исходный номер последовательности в 1, а затем
    увеличивают его на 64000 каждые полсекунды.

    Также необходимо отметить, что это первое TCP соединение
    с того момента, как система была перезагружена, так как номер порта клиента
    равен 1024.

    Однако на рисунке 18.6 не показано, сколько времени TCP клиент
    осуществлял повторные передачи, перед тем как отказаться от своей попытки. Для
    того чтобы посмотреть это временные значения, мы должны исполнить команду telnet
    следующим образом:

    bsdi % date ; telnet svr4 discard ;
    date
    Thu Sep 24 16:24:11 MST 1992
    Trying 192.82.148.2…
    telnet:
    Unable to connect to remote host: Connection timed out
    Thu Sep 24 16:25:27
    MST 1992 

    Время составляет 76 секунд. Большинство систем Berkeley устанавливают
    предел времени в 75 секунд, за это время должно быть установлено новое
    соединение. В разделе “Пример RTT” главы 21
    мы увидим, что третий пакет, посланный клиентом, будет отброшен по тайм-ауту
    примерно в 16:25:29, то есть через 48 секунд после того как он был отправлен,
    при этом клиент не прекратит свои попытки через 75 секунд.

    Первый тайм-аут

    На рисунке 18.6 следует обратить внимание на то, что первый тайм-аут,
    5,8 секунды, близок к 6 секундам, однако не равен 6 секундам, тогда как второй
    тайм-аут практически точно равен 24 секундам. Было исполнено еще десять подобных
    тестов, причем в каждом из них значение первого тайм-аута колебалось в диапазоне
    от 5,59 секунды до 5,93 секунды. Второй тайм-аут, однако, всегда был 24,00
    секунды.

    Это объясняется тем, что BSD реализации TCP запускают таймер каждые
    500 миллисекунд. Этот 500-миллисекундный таймер используется для различных TCP
    тайм-аутов, все они будут описаны в следующих главах. Когда мы вводим команду
    telnet, устанавливается исходный 6-секундный таймер (12 тиков часов), однако он
    может истечь в любом месте между 5,5 и 6 секундами. На рисунке 18.7 показано как
    это происходит.

    Address resolution protocol - что это?

    Рисунок 18.7 500-миллисекундный таймер TCP.

    Так как таймер установлен в 12 тиков, первое уменьшение таймера может
    произойти между 0 и 500 миллисекунд после его установки. С этого момента таймер
    уменьшается примерно каждые 500 миллисекунд, однако первый период времени может
    быть разным. (Мы используем слово “примерно”, потому что время, когда
    TCP получает управление каждые 500 миллисекунд, примерное, так как может пройти
    другое прерывание, которое будет обрабатываться ядром.)

    Когда этот 6-секундный таймер истечет на тике помеченном 0 на рисунке
    18.7, таймер переустанавливается в 24 секунды (48 тиков). Этот следующий таймер
    будет равен 24 секундам, так как он был установлен в тот момент времени, когда
    500-миллисекундный таймер TCP был вызван ядром, а не
    пользователем.

    Поле типа сервиса

    На рисунке 18.6 мы видим выражение [tos 0x10]. Это поле типа сервиса
    (TOS – type-of-service) в IP датаграмме (рисунок 3.2). Telnet клиент в BSD/386 устанавливает это поле таким образом, чтобы получить
    минимальную задержку.

    Максимальный размер сегмента

    Максимальный размер сегмента (MSS) это самая большая порция данных,
    которую TCP пошлет на удаленный конец. Когда соединение устанавливается, каждая
    сторона может объявить свой MSS. Значения, которые мы видели, были 1024. IP
    датаграмма, которая получится в результате, обычно на 40 байт больше: 20 байт
    отводится под TCP заголовок и 20 байт под IP заголовок.

    В некоторых публикациях говорится, что эта опция устанавливается
    “по договоренности”. В действительности, договоренность в данном
    случае не используется. Когда соединение устанавливается, каждая сторона
    объявляет MSS, которой она собирается принимать. (Опция MSS
    может быть использована только в SYN сегменте.) Если одна сторона не принимает
    опцию MSS от другой стороны, используется размер по умолчанию в 536 байт. (В
    этом случае, при 20-байтном IP заголовке и 20-байтном TCP заголовке, размер IP
    датаграммы будет составлять 576 байт.)

    В общем случае, чем больше MSS тем лучше, до тех пор пока не
    происходит фрагментация. (Это не всегда верно. Обратитесь к рисунку 24.3 и рисунку 24.4, чтобы в этом
    убедиться.) Большие размеры сегмента позволяют послать больше данных в каждом
    сегменте, что уменьшает относительную стоимость IP и TCP заголовков. Когда TCP
    отправляет SYN сегмент, либо когда локальное приложение хочет установить
    соединение, или когда принят запрос на соединение от удаленного хоста, может
    быть установлено значение MSS равное MTU исходящего интерфейса минус размер
    фиксированных TCP и IP заголовков. Для Ethernet MSS может
    достигать 1460 байт. При использовании инкапсуляции IEEE
    802.3 (глава 2, раздел “Ethernet и IEEE 802
    инкапсуляция”
    ) MSS может быть до 1452 байт.

    Значение 1024, которое мы видим в этой главе, соответствует
    соединениям, в которых участвуют BSD/386 и SVR4, потому что большинство BSD реализаций требует, чтобы MSS
    было кратно 512. Другие системы, такие как SunOS 4.1.3, Solaris 2.2 и AIX 3.2.2, объявляют MSS
    равный 1460, когда обе стороны находятся на одном Ethernet. Расчеты, приведенные
    в [Mogul 1993], показывают, что MSS равный 1460 обеспечивают
    лучшую производительность на Ethernet, чем MSS равный 1024.

    Если IP адрес назначения “не локальный”,
    MSS обычно устанавливается по умолчанию – 536. Является ли локальным или
    нелокальным конечный пункт назначения можно следующим образом. Пункт назначения,
    IP адрес которого имеет тот же самый идентификатор сети и ту же самую маску
    подсети, что и у отправителя является локальным; пункт назначения, IP адрес
    которого полностью отличается от идентификатора сети, является нелокальным;
    пункт назначения с тем же самым идентификатором сети, однако с другой маской
    подсети, может быть как локальным, так и нелокальным. Большинство реализаций
    предоставляют опцию конфигурации (приложение E и рисунок Е.1), которая позволяет
    системному администратору указать, какие подсети являются локальными, а какие
    нелокальными. Установка этой опции определяет максимальный анонсируемый MSS
    (который по величине может достигать MTU исходящего интерфейса), иначе
    используется значение по умолчанию равное 536.

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

    Представьте наш хост slip, который имеет SLIP канал с MTU равным 296,
    подключенным к маршрутизатору bsdi. На рисунке 18.8 показаны эти системы и хост
    sun.

    Address resolution protocol - что это?

    Рисунок 18.8 TCP соединение от sun к slip и значения
    MSS.

    Мы установили TCP соединение от sun к slip и просмотрели сегменты с
    использованием tcpdump. На рисунке 18.9 показано только установление соединения
    (объявления размера окна удалены).

    1  0.0                 sun.1093 > slip.discard: S
    517312000:517312000(0)
    <mss 1460>
    2  0.10 (0.00)        slip.discard > sun.1093: S
    509556225:509556225(0)
    ack
    517312001 <mss 256>
    3  0.10 (0.00)        sun.1093 > slip.discard: .
    ack 1

    Рисунок 18.9 Вывод tcpdump для установления соединения от sun к
    slip.

    Здесь важно обратить внимание на то, что что sun не может послать
    сегмент с порцией данных больше чем 256 байт, так как он получил MSS равный 256
    (строка 2). Более того, так как slip знает что MTU исходящего интерфейса
    равен 296, даже если sun объявит MSS равный 1460, он никогда не сможет послать
    больше чем 256 байт данных, чтобы избежать фрагментации. Однако, система может
    послать данных меньше, чем MSS объявленный удаленной стороной.

    Избежать фрагментации таким образом можно только если хост
    непосредственно подключен к сети с MTU меньше чем 576. Если оба хоста подключены
    к Ethernet и оба анонсируют MSS равный 536, однако промежуточная сеть имеет MTU
    равный 296, будет осуществлена фрагментация. Единственный способ избежать этого
    – воспользоваться механизмом определения транспортного MTU (глава 24, раздел “Определение транспортного
    MTU”
    ).

    Наполовину закрытый TCP

    TCP предоставляет возможность одному участнику соединения прекратить
    передачу данных, однако все еще получать данные от удаленной стороны. Это
    называется наполовину закрытый TCP. Как мы уже упоминали ранее, немногие
    приложения могут пользоваться этой возможностью.

    Чтобы использовать эту характеристику программного интерфейса,
    необходимо предоставить возможность приложению сказать: “Я закончило
    передачу данных, поэтому посылаю признак конца файла (end-of-file) (FIN) на
    удаленный конец, однако я все еще хочу получать данные с удаленного конца до тех
    пор, пока он мне не пошлет признак конца файла (end-of-file)
    (FIN).”

    Сокеты API
    поддерживают полузакрытый режим, если приложение вызовет shutdown со вторым аргументом равным 1 вместо вызова close. Большинство приложений, однако, разрывают соединения в
    обоих направлениях вызовом close.

    На рисунке 18.10 показан стандартный сценарий для полузакрытого TCP.
    Мы показали клиента с левой стороны, он инициирует полузакрытый режим, однако
    это может сделать любая сторона. Первые два сегмента одинаковы: FIN от
    инициатора, за ним следует ACK и FIN от принимающего. Однако дальше сценарий
    будет отличаться от того, который приведен на рисунке 18.4, потому что сторона,
    которая приняла приказ “полузакрыть”, может все еще посылать данные.
    Мы показали только один сегмент данных, за которым следует ACK, однако в этом
    случае может быть послано любое количество сегментов данных. (Мы расскажем более
    подробно об обмене сегментами данных и подтверждениями в главе 19.) Когда конец, который
    получил приказ “полузакрыть”, осуществил передачу данных, он закрывает
    свою часть соединения, в результате чего посылается FIN, при этом признак конца
    файла доставляется приложению, которое инициировало “полузакрытый”
    режим. Когда второй FIN подтвержден, соединение считается полностью закрытым.

    Address resolution protocol - что это?

    Рисунок 18.10 TCP в полузакрытом режиме.

    Для чего может быть использован полузакрытый режим? Одним из примеров
    может являться команда Unix rsh(1), которая исполняет
    команду на другой системе. Команда

    sun % rsh bsdi sort <
    datafile

    запустит команду sort на хосте bsdi, причем стандартный ввод команды
    rsh будет читаться из файла с именем datafile. Команда rsh создает TCP
    соединения между собой и программой, которая будет исполнена на удаленном хосте.
    Затем rsh функционирует довольно просто: команда копирует стандартный ввод
    (datafile) в соединение и копирует из соединения в стандартный вывод (наш
    терминал). На рисунке 18.11 показано как это происходит. (Мы помним, что TCP
    соединение полнодуплексное.)

    Address resolution protocol - что это?

    Рисунок 18.11 Команда: rsh bsdi sort <
    datafile.

    На удаленном хосте bsdi сервер rshd исполняет программу sort таким
    образом, что ее стандартный ввод и стандартный вывод направлены в TCP
    соединение. В главе 14 [Stevens 1990] приводится подробное описание структуры
    процесса Unix, который участвует здесь, однако нас интересует, как используется
    TCP соединение и полузакрытый режим TCP.

    Программа sort не может начать генерацию вывода до тех пор, пока весь
    ее ввод не будет прочитан. Все исходные данные, поступающие по соединению от
    клиента rsh на сервер sort, посылаются в файл, который должен быть отсортирован.
    Когда достигается метка конца файла во вводе (datafile), клиент rsh осуществляет
    полузакрытие TCP соединения. Затем сервер sort принимает метку конца файла из
    своего стандартного ввода (TCP соединение), сортирует файл и пишет результат в
    свой стандартный вывод (TCP соединение). Клиент rsh продолжает читать TCP
    соединение на своем конце, копируя отсортированный файл в свой стандартный
    вывод.

    Без использования наполовину закрытого режима требуется какая-либо
    дополнительная техника, которая позволит клиенту сообщить серверу, что он
    закончил посылку данных, однако клиенту все еще разрешено получать данные от
    сервера. Альтернативно необходимо использовать два соединения, однако
    предпочительно использование полузакрытого режима.

    Диаграмма состояний передачи TCP

    Мы описали несколько правил установления и разрыва TCP соединения. Эти
    правила собраны в диаграмму состояний передачи, которая приведена на
    рисунке 18.12.

    Необходимо отметить, что эта диаграмма – диаграмма стандартных
    состояний. Мы пометили обычную передачу клиента сплошными жирными стрелками, а
    обычную передачу сервера пунктирными жирными стрелками.

    Две передачи, ведущие к состоянию УСТАНОВЛЕНО (ESTABLISHED), соответствуют открытию соединения, а две
    передачи, ведущие от состояния УСТАНОВЛЕНО (ESTABLISHED), соответствуют разрыву
    соединения. Состояние УСТАНОВЛЕНО (ESTABLISHED) наступает в тот момент, когда
    появляется возможность осуществить передачу данных между двумя сторонами в обоих
    направлениях. В следующих главах будет описано, что происходит в этом состоянии.

    Мы объединили четыре квадратика в левой нижней части диаграммы внутри
    пунктирной рамки и пометили их “активное закрытие” (active close). Два
    других квадратика (ОЖИДАНИЕ_ЗАКРЫТИЯ – CLOSE_WAIT и
    ПОСЛЕДНЕЕ_ПОДТВЕРЖДЕНИЕ – LAST_ACK) объединены пунктирной
    рамкой и помечены как “пассивное закрытие” (passive close).

    Названия 11-ти состояний (ЗАКРЫТО – CLOSED,
    СЛУШАЕТ – LISTEN, SYN_ОТПРАВЛЕН – SYN_SENT, и так далее) на этом рисунке выбраны таким образом,
    чтобы соответствовать состояниям, которые выводит команда netstat. Имена же netstat, в свою очередь, практически
    идентичны именам, описанным в RFC 793. Состояние ЗАКРЫТО (CLOSED) в
    действительности не является состоянием, однако является стартовой и конечной
    точкой для диаграммы.

    Изменение состояния от СЛУШАЕТ (LISTEN) к SYN_ОТПРАВЛЕН (SYN_SENT)
    теоретически возможно, однако не поддерживается в реализациях Berkeley.

    А изменение состояния от ПОЛУЧЕН_SYN (SYN_RCVD)
    назад к СЛУШАЕТ (LISTEN) возможно только в том случае, если в состояние
    ПОЛУЧЕН_SYN (SYN_RCVD) вошли из состояния СЛУШАЕТ (LISTEN) (это обычный
    сценарий), а не из состояния SYN_ОТПРАВЛЕН (SYN_SENT) (одновременное открытие).
    Это означает, что если мы осуществили пассивное открытие (вошли в состояние
    СЛУШАЕТ – LISTEN), получили SYN, послали SYN с ACK (вошли в состояние
    ПОЛУЧЕН_SYN – SYN_RCVD) и затем получили сброс вместо ACK, конечная точка
    возвращается в состояние СЛУШАЕТ (LISTEN) и ожидает прибытия другого запроса на
    соединение.

    Address resolution protocol - что это?

    Рисунок 18.12 Диаграмма изменений состояния
    TCP.

    На рисунке 18.13 показано обычное установление и закрытие TCP
    соединения. Также подробно описаны разные состояния, через которые проходят
    клиент и сервер.

    Address resolution protocol - что это?

    Рисунок 18.13 Состояния TCP, соответствующие обычному
    открытию и разрыву соединения.

    На рисунке 18.13 мы предположили что клиент, находящийся с левой
    стороны, осуществляет активное открытие, а сервер, находящийся справа,
    осуществляет пассивное открытие. Также мы показали, что клиент осуществляет
    активное закрытие, (как мы упоминали ранее, каждая сторона может осуществить
    активное закрытие).

    Вам следует проследить изменения состояний на рисунке 18.13 с
    использованием датаграммы изменения состояний, приведенной на рисунке 18.12, что
    позволит понять, почему осуществляется то или иное изменение
    состояния.

    Состояние ожидания 2MSL

    Состояние ВРЕМЯ_ОЖИДАНИЯ (TIME_WAIT) также иногда называется
    состоянием ожидания 2MSL. В каждой реализации выбирается значение для
    максимального времени жизни сегмента (MSL – maximum segment
    lifetime) . Это максимальное время, в течение которого сегмент может
    существовать в сети, перед тем как он будет отброшен. Мы знаем, что это время
    ограничено, так как TCP сегменты передаются посредством IP датаграмм, а каждая
    IP датаграмма имеет поле TTL, которое ограничивает время ее
    жизни.

    RFC 793 [Postel
    1981c] указывает, что MSL должно быть равно 2 минутам. В разных реализациях эта
    величина имеет значение 30 секунд, 1 минута или 2 минуты.

    В главе 8 говорилось, что время жизни IP
    датаграммы ограничивается количеством пересылок, а не таймером.

    При использовании MSL действуют следующие правила: когда TCP
    осуществляет активное закрытие и посылает последний сегмент содержащий
    подтверждение (ACK), соединение должно остаться в состоянии TIME_WAIT на время
    равное двум MSL. Это позволяет TCP повторно послать последний ACK в том случае,
    если первый ACK потерян (в этом случае удаленная сторона отработает тайм-аут и
    повторно передаст свой конечный FIN).

    Другое назначение ожидания 2MSL заключается в том,
    что пока TCP соединение находится в ожидании 2MSL, пара
    сокетов, выделенная для этого соединения (IP адрес клиента, номер порта клиента,
    IP адрес сервера и номер порта сервера), не может быть повторно использована.
    Это соединение может быть использовано повторно только когда истечет время
    ожидания 2MSL.

    К сожалению, большинство реализаций (Berkeley одна из них) подчиняются
    более жестким требованиям. По умолчанию локальный номер порта не может быть
    повторно использован, до тех пор пока этот номер порта является локальным
    номером порта пары сокетов, который находится в состоянии ожидания 2MSL. Ниже мы
    рассмотрим примеры общих требований.

    Некоторые реализации и API
    предоставляют средства, которые позволяют обойти эти ограничения. С
    использованием API сокет может быть указана опция сокета SO_REUSEADDR. Она позволяет вызывающему назначить себе номер
    локального порта, который находится в состоянии 2MSL, однако мы увидим, что
    правила TCP не позволяют этому номеру порта быть использованным в соединении,
    которое находится в состоянии ожидания 2MSL.

    Каждый задержанный сегмент, прибывающий по соединению, которое
    находится в состоянии ожидания 2MSL, отбрасывается. Так как соединение
    определяется парой сокет в состоянии 2MSL, это соединение не может быть повторно
    использовано до того момента, пока мы не сможем установить новое соединение. Это
    делается для того, чтобы опоздавшие пакеты не были восприняты как часть нового
    соединения. (Соединение определяется парой сокет. Новое соединение называется восстановлением или оживлением данного соединения.)

    Как мы уже показали на рисунке 18.13, обычно клиент осуществляет
    активное закрытие и входит в режим TIME_WAIT. Сервер обычно осуществляет
    пассивное закрытие и не проходит через режим TIME_WAIT. Можно сделать вывод, что
    если мы выключим клиента и немедленно его перестартуем, этот новый клиент не
    сможет использовать тот же самый локальный номер порта. В этом нет никакой
    проблемы, так как клиенты обычно используют динамически назначаемые порты и не
    заботятся, какой динамически назначаемый порт используется в настоящее время.

    Однако, с точки зрения сервера все иначе, так как сервера используют
    заранее известные порты. Если мы выключим сервер, который имеет установленное
    соединение, и постараемся немедленно перестартовать его, сервер не может
    использовать свой заранее известный номер порта в качестве конечной точки
    соединения, так как этот номер порта является частью соединения, находящегося в
    состоянии ожидания 2MSL. Поэтому может потребоваться от 1 до 4 минут, перед тем
    как сервер будет перестартован.

    Пронаблюдать подобный сценарий можно с использованием программы sock. Мы стартовали сервер, подсоединили к нему клиента, а
    затем выключили сервер:

    sun % sock -v -s 6666              стартуем сервер, слушающий порт
    6666
    (запускаем клиента на bsdi, который
    подсоединится к этому порту)
    connection on 140.252.13.33.6666
    from140.252.13.35.1081
    ^?                                 вводим
    символ прерывания, чтобы выключить сервер
    sun % sock -s 6666
                    и стараемся немедленно перестартовать сервер на тот же самый
    порт
    can’t bind local address: Address already in use
    sun % netstat
                         попробуем проверить состояние соединения
    Active
    Internet сonnections
    Proto Recv-Q Send-Q   Local Address    Foreign Address
            (state)
    tcp        0      0    sun.6666         bsdi.1081
                 TIME_WAIT
    множество строк
    удалено

    Когда мы стараемся перестартовать сервер, программа выдает сообщение
    об ошибке, указывающее на то, что она не может захватить свой заранее известный
    номер порта, потому что он уже используется (находится в состоянии ожидания 2MSL).

    Затем мы немедленно исполняем netstat, чтобы
    посмотреть состояние соединения и проверить, что оно действительно находится в
    состоянии TIME_WAIT.

    Если мы будем продолжать попытки
    перестартовать сервер и посмотрим время, когда это удастся, то можем вычислить
    значение 2MSL. Для SunOS 4.1.3, SVR4, BSD/386 и AIX 3.2.2 перестартовка сервера
    займет 1 минуту, что означает, что MSL равно 30 секундам. В Solaris 2.2 эта перестартовка сервера занимает 4 минуты, это
    означает, что MSL равно 2 минутам.

    Мы можем увидеть ту же самую ошибку, сгенерированную клиентом, если
    клиент старается захватить порт, который является частью соединения, находящего
    в режиме ожидания 2MSL (обычно клиент этого не делает):

    sun % sock -v bsdi echo           стартуем клиент, который
    подсоединяется к серверу echo
    connected on 140.252.13.33.1162 to
    140.252.13.35.7
    hello there                       печатаем эту
    строку
    hello there                        она отражается эхом от
    сервера
    ^D                                вводим символ конца файла,
    чтобы выключить клиента
    sun % sock -b1162 bsdi echo
    can’t bind
    local address: Address already in use

    При первом запуске клиента была указана опция -v, которая позволяет
    посмотреть, какой используется локальный номер порта (1162). При втором запуске
    клиента, была указана опция -b, которая сообщает клиенту о
    необходимости назначить самому себе номер локального порта 1162. Как мы и
    ожидали, клиент не может этого сделать, так как этот номер порта является частью
    соединения, которое находится в состоянии 2MSL.

    Здесь необходимо упомянуть об одной особенности состояния ожидания
    2MSL, к которой мы вернемся в главе 27, когда будем рассказывать о
    протоколе передачи файлов (FTP – File Transfer Protocol). Как уже упоминалось
    раньше, в состоянии ожидания 2MSL остается пара сокетов (состоящая из локального
    IP адреса, локального порта, удаленного IP адреса и удаленного порта). Однако,
    множество реализаций позволяют процессу повторно использовать номер порта,
    который является частью соединения, находящегося в режиме 2MSL (обычно с
    использованием опции SO_REUSEADDR) , TCP не может позволить
    создать новое соединение с той же самой парой сокет. Это можно доказать с
    помощью следующего эксперимента:

    sun % sock -v -s 6666              старт сервера, слушающего порт
    6666
    (запускаем клиента на bsdi, который
    подсоединяется к этому порту)
    connection on 140.252.13.33.6666 from
    140.252.13.35.1098
    ^?                                 вводим символ
    прерывания, чтобы выключить сервер
    sun % sock -b6666 bsdi 1098       
    стартуем клиента с локальным портом 6666
    can’t bind local address: Address
    already in use
    sun % sock -A -b6666 bsdi 1098     пытаемся снова, на
    этот раз с опцией -A
    active open error: Address already in
    use

    В первый раз мы запустили нашу программу sock как сервер на порт 6666
    и подсоединили к нему клиента с хоста bsdi. Номер динамически назначаемого порта
    клиента 1098. Мы выключили сервер, таким образом, он осуществил активное
    закрытие. При этом 4 параметра – 140.252.13.33 (локальный IP адрес), 6666
    (локальный номер порта), 140.252.13.35 (удаленный IP адрес) и 1098 (удаленный
    номер порта) на сервере попадают в состояние 2MSL.

    Во второй раз мы запустили эту программу в качестве клиента, указав
    локальный номер порта 6666, при этом была сделана попытка подсоединиться к хосту
    bsdi на порт 1098. При попытке повторно использовать локальный порт 6666 была
    сгенерирована ошибка, так как этот порт находится в состоянии 2MSL.

    Чтобы избежать появления этой ошибки, мы запустили программу снова,
    указав опцию -A, которая активизирует опцию SO_REUSEADDR. Это позволило программе назначить себе номер
    порта 6666, однако была получена ошибка, когда программа попыталась осуществить
    активное открытие. Даже если программа сможет назначить себе номер порта 6666,
    она не сможет создать соединения с портом 1098 на хосте bsdi, потому что пара
    сокетов, определяющая это соединение, находится в состоянии ожидания 2MSL.

    Что если мы попытаемся установить соединение с другого хоста?
    Во-первых, мы должны перестартовать сервер на sun с флагом -A, так как порт,
    который ему необходим (6666), является частью соединения, находящегося в
    состоянии ожидания 2MSL:

    sun % sock -A -s 6666            стартуем сервер, слушающий порт
    6666

    Затем, перед тем как состояние ожидания 2MSL закончится на sun, мы
    стартуем клиента на bsdi:

    bsdi % sock -b1098 sun
    6666
    connected on 140.252.13.35.1098 to 140.252.13.33.6666 

    К сожалению, это работает! Это является недостатком TCP спецификации,
    однако поддерживается большинством реализаций Berkeley. Эти реализации
    воспринимают прибытие запроса на новое соединение для соединения, которое
    находится в состоянии TIME_WAIT, если новый номер последовательности больше чем
    последний номер последовательности, использованный в предыдущем соединении. В
    этом случае ISN для нового соединения устанавливается равным
    последнему номеру последовательности для предыдущего соединения плюс 128000.
    Приложение к RFC 1185 [Jacobson, Braden, and Zhang 1990] показывает возможные недостатки подобной технологии.

    Эта характеристика реализации позволяет клиенту и серверу повторно
    использовать те же самые номера порта для успешного восстановления того же
    самого соединения, в том случае, однако, если сервер не осуществил активное
    закрытие. Мы увидим другой пример состояния ожидания 2MSL на рисунке 27.8, когда будем
    обсуждать FTP. Также обратитесь к упражнению
    5
    этой главы.

    Концепция тихого времени

    Состояние ожидания 2MSL предоставляет защиту от опоздавших пакетов,
    принадлежащих ранним соединениям, при этом они не будут интерпретироваться как
    часть нового соединения, которое использует те же самые локальный и удаленный IP
    адреса и номера портов. Однако это работает только в том случае, если хост с
    соединением в состоянии 2MSL не вышел из строя.

    Что если хост с портами в состоянии 2MSL вышел из строя,
    перезагрузился во время MSL и немедленно установил новые соединения с
    использованием тех же самых локальных и удаленных IP адресов и номеров портов,
    соответствующих локальным портам, которые были в состоянии 2MSL перед поломкой?
    В этом случае опоздавшие сегменты из соединения, которое существовало перед
    поломкой, могут быть ошибочно интерпретированы как принадлежащие новому
    соединению, созданному после перезагрузки. Это может произойти вне зависимости
    от того, какой исходный номер последовательности выбран после перезагрузки.

    Чтобы защититься от подобных нежелательных сценариев, RFC 793
    указывает, что TCP не должен создавать новые соединения до истечения MSL после момента загрузки. Это называется тихое время (quiet
    time).

    В некоторых реализациях хосты ожидают даже дольше, чем
    время MSL после перезагрузки.

    Состояние ОЖИДАНИЕ_И_ПОДТВЕРЖДЕНИЕ_FIN (FIN_WAIT_2)

    В состоянии FIN_WAIT_2 мы посылаем наш FIN, а
    удаленная сторона подтверждает его. Если мы не находимся в состоянии
    полузакрытого соединения, то ожидаем от приложения на удаленном конце, что оно
    опознает прием признака конца файла и закроет свою сторону соединения, причем
    пошлет нам FIN. Только когда процесс на удаленном конце осуществит это закрытие,
    наша сторона перейдет из режима FIN_WAIT_2 в режим TIME_WAIT.

    Это означает, что наша сторона соединения может остаться в этом режиме
    навсегда. Удаленная сторона все еще в состоянии CLOSE_WAIT и
    может оставаться в этом состоянии всегда, до тех пор пока приложение не решит
    осуществить закрытие.

    Большинство Berkeley реализаций
    предотвращают подобное вечное ожидание в состоянии FIN_WAIT_2 следующим образом.
    Если приложение, которое осуществило активное закрытие, осуществило полное
    закрытие, а не полузакрытие, что указывает на то, что оно ожидает приема данных,
    в этом случае устанавливается таймер. Если соединение не используется в течении
    10 минут плюс 75 секунд, TCP переводит соединение в режим ЗАКРЫТО (CLOSED). В комментариях говорится, что подобная характеристика
    противоречит спецификации протокола.

    Сегменты сброса (Reset)

    Мы упоминали, что в TCP заголовке существует бит, называемый RST, что означает “сброс” (reset). В общем случае
    сигнал “сброс” (reset) посылается TCP в том случае, если прибывающие
    сегменты не принадлежат указанному соединению. (Мы используем термин
    “указанное соединение” (referenced connection), который обозначает
    соединение, идентифицируемое IP адресом назначения и номером порта назначения, а
    также IP адресом источника и номером порта источника. В RFC 793 это называется
    “сокет”.)

    Запрос о соединении на несуществующий порт

    Самый общий случай, при котором генерируется сброс (reset), это когда
    запрос о соединении прибывает и при этом не существует процесса, который слушает
    порт назначения. В случае UDP, как мы видели в разделе “ICMP ошибка недоступности
    порта”
    главы 6, если датаграмма прибывает на неиспользуемый порт
    назначения – генерируется ошибка ICMP о недоступности порта.
    TCP вместо этого использует сброс.

    Мы приведем простой пример с использованием Telnet клиента, указав
    номер порта, который не используется на пункте назначения:

    bsdi % telnet svr4 20000                        порт 20000 не
    используется
    Trying 140.252.13.34…
    telnet: Unable to connect to remote
    host: Connection refused

    Сообщение об ошибке выдается клиенту Telnet немедленно. На рисунке
    18.14 показан обмен пакетами, соответствующий этой команде.

    1  0.0                    bsdi.1087 > svr4.20000: S
    297416193:297416193(0)
    win
    4096 <mss 1024>
    [tos
    0x10]2  0.003771 (0.0038)     svr4.20000 > bsdi.1087: R 0:0 (0) ack
    297416194 win 0

    Рисунок 18.14 Генерация сброса при попытке открыть соединение на
    несуществующий порт.

    Значения, которые нам необходимо более подробно рассмотреть на этом
    рисунке, это поле номера последовательности и поле номера подтверждения в
    сбросе. Так как бит подтверждения (ACK) не был установлен в прибывшем сегменте,
    номер последовательности сброса установлен в 0, а номер подтверждения установлен
    во входящий исходный номер последовательности (ISN) плюс
    количество байт данных в сегменте. Несмотря на то, что в прибывшем сегменте не
    присутствует реальных данных, бит SYN логически занимает 1 байт в пространстве
    номера последовательности; таким образом, в этом примере номер подтверждения в
    сбросе устанавливается в ISN плюс длина данных (0) плюс один SYN
    бит.

    Разрыв соединения

    В разделе “Установление и разрыв
    соединения”
    этой главы мы видели, что обычный метод, используемый для
    разрыва соединения, заключается в том, что одна из сторон посылает FIN. Иногда
    это называется правильным освобождением (orderly release),
    так как FIN посылается после того, как все данные, ранее поставленные в очередь,
    были отправлены, и обычно при этом не происходит потеря данных. Однако
    существует возможность прервать соединение, послав сброс (reset) вместо FIN.
    Иногда это называется прерывающим освобождением (abortive
    release).

    Подобный разрыв соединения предоставляет приложению две возможности:
    (1) любые данные, стоящие в очереди – теряются, и сброс отправляется немедленно,
    и (2) сторона, принявшая RST, может сказать, что удаленная сторона разорвала
    соединение, вместо того чтобы закрыть его обычным образом. Программный интерфейс
    (API), который используется приложением, должен предоставлять способ
    сгенерировать подобный сброс вместо нормального закрытия.

    Мы можем посмотреть, что происходит в случае подобного разрыва, с
    использованием нашей программы sock. Сокеты API предоставляют эту возможность с использованием опции
    сокета “задержки закрытия” (linger on close) (SO_LINGER). Мы указали опцию -L со временем
    задержки равным 0. Это означает, что вместо обычного FIN будет послан сброс,
    чтобы закрыть соединение. Мы подключимся к версии программы sock, которая
    выступает в роли сервера, на svr4:

    bsdi % sock -L0 svr4 8888             это клиент; сервер показан
    дальше
    hello, world                          вводим одну строку,
    которая будет отправлена на удаленный конец
    ^D
                                       вводим символ конца файла, чтобы
    выключить клиента

    На рисунке 18.15 показан вывод команды tcpdump для этого примера. (Мы
    удалили все объявления окон в этом рисунке, так как они не влияют на наши
    рассуждения.)

    1  0.0                      bsdi.1099 > svr4.8888: S
    671112193:671112193(0)
    <mss 1024>
    2  0.004975 (0.0050)       svr4.8888 > bsdi.1099: S
    3224959489:3224959489(0)
    ack 671112194 <mss 1024>
    3  0.006656 (0.0017)       bsdi.1099 >
    svr4.8888: . ack 1
    4  4.833073 (4.8264)       bsdi.1099 > svr4.8888: P
    1:14 (13) ack 1
    5  5.026224 (0.1932)       svr4.8888 > bsdi.1099: . ack
    14
    6  9.527634 (4.5014)       bsdi.1099 > svr4.8888: R 14:14 (0) ack
    1

    Рисунок 18.15 Разрыв соединения с использованием сброса (RST) вместо
    FIN.

    В строках 1-3 показано обычное установление соединения. В строке 4
    отправляется строка данных, которую мы напечатали (12 символов плюс Unix символ
    новой строки), и в строке 5 прибывает подтверждение о приеме данных.

    Строка 6 соответствует введенному символу конца файла (Control-D), с
    помощью которого мы выключили клиента. Так как мы указали разрыв вместо обычного
    закрытия (опция командной строки -L0), TCP на bsdi пошлет RST вместо обычного
    FIN. RST сегмент содержит номер последовательности и номер подтверждения. Также
    обратите внимание на то, что RST сегмент не ожидает ответа с удаленного конца –
    он не содержит подтверждения вообще. Получатель сброса прерывает соединение и
    сообщает приложению, что соединение было прервано.

    Мы получим следующую ошибку от сервера при подобном
    обмене:

    svr4 % sock -s 8888             запускаем как сервер, слушаем порт
    8888
    hello, world                     это то, что отправил клиент
    read
    error: Connection reset by peer

    Этот сервер читает из сети и копирует в стандартный вывод все что
    получил. Обычно он завершает свою работу, получив признак конца файла от своего
    TCP, однако здесь мы видим, что он получил ошибку при прибытии RST. Ошибка это
    как раз то, что мы ожидали: соединение было разорвано одним из участников
    соединения.

    Определение полуоткрытого соединения

    Считается, что TCP соединение полуоткрыто, если
    одна сторона закрыла или прервала соединение без уведомления другой стороны. Это
    может произойти в любое время, если один из двух хостов выйдет из строя. Так как
    какое-то время не будет попыток передать данные по полуоткрытому соединению,
    одна из сторон будет работать, до тех пор пока не определит что удаленная
    сторона вышела из строя.

    Еще одна причина, по которой может возникнуть полуоткрытое соединение,
    заключается в том, что на хосте клиента было выключено питание, вместо того
    чтобы погасить приложение клиента, а затем выключить компьютер. Это происходит
    тогда, например, Telnet клиент запускается на PC, и пользователи выключают
    компьютер в конце рабочего дня. Если в момент выключения PC не осуществлялась
    передача данных, сервер никогда не узнает, что клиент исчез. Когда пользователь
    приходит на следующее утро, включает свой PC и стартует новый клиент Telnet, на
    хосте сервера стартует новый сервер. Из-за этого на хосте сервера может
    появиться очень много открытых TCP соединений. (В главе 23 мы увидим способ, с помощью
    которого один конец TCP соединения может определить, что другой исчез. Это
    делается с использованием TCP опции “оставайся в живых” (keepalive)).

    Мы можем легко создать полуоткрытое соединение. Запускаем клиента
    Telnet на bsdi и подсоединяемся к discard серверу на svr4. Вводим одну строку и
    смотрим с использованием tcpdump, как она проходит, а затем
    отсоединяем Ethernet кабель от хоста сервера и перезапускаем его. Этим самым мы
    имитировали выход из строя хоста сервера. (Мы отсоединили Ethernet кабель перед
    перезагрузкой сервера, чтобы тот не послал FIN в открытое соединение, что делают
    некоторые TCP модули при выключении.) После того как сервер перезагрузился, мы
    подсоединили кабель и попробовали послать еще одну строку от клиента на сервер.
    Так как сервер был перезагружен и потерял все данные о соединениях, которые
    существовали до перезагрузки, он ничего не знает о соединениях и не подозревает
    о том, какому соединению принадлежат прибывшие сегменты. В этом случае
    принимающая сторона TCP отвечает сбросом (reset).

    bsdi % telnet svr4 discard               запуск клиента
    Trying
    140.252.13.34…
    Connected to svr4.
    Escape character is ‘^]’.
    hi
    there
                                    эта строка послана
    нормально
    в этом месте мы
    перезагрузили хост сервера
    another line                             в
    этом месте был осуществлен сброс (reset)
    Connection closed by foreign
    host.

    На рисунке 18.16 показан вывод tcpdump для этого примера. (Мы удалили
    из вывода объявления окон, информацию о типе сервиса и объявления MSS, так как
    они не влияют на наши рассуждения.)

    1    0.0                       bsdi.1102 > svr4.discard: S
    1591752193:1591752193(0)
    2    0.004811 (  0.0048)      svr4.discard >
    bsdi.1102: S
    26368001:26368001(0)
    ack 1591752194
    3    0.006516 (  0.0017)      bsdi.1102 > svr4.discard: .
    ack 1

    4    5.167679 (  5.1612)      bsdi.1102 > svr4.discard: P 1:11
    (10) ack 1
    5    5.202262 (  0.0340)      svr4.discard > bsdi.1102: . ack
    11

    6  194.909929 (189.7083)      bsdi.1102 > svr4.discard: P 11:25
    (14) ack 1
    7  194.914957 (  0.0050)      arp who-has bsdi tell svr4

    194.915678 (  0.0007)      arp reply bsdi is-at 0:0:c0:6f:2d:40
    9  194.918225
    (  0.0025)      svr4.discard > bsdi.1102: R
    26368002:26368002(0)

    Рисунок 18.16 Сброс в ответ на приход сегмента данных при полуоткрытом
    соединении.

    В строках 1-3 осуществляется обычное установление соединения. В строке
    4 отправляется строка “hi there” (это можно примерно перевести как
    “эй вы, там”) на discard сервер, в строке 5 приходит подтверждение.

    В этом месте мы отсоединили Ethernet кабель от svr4, перезагрузили его
    и подсоединили кабель снова. Вся процедура заняла примерно 190 секунд. Затем мы
    напечатали следующую строку ввода на клиенте (“another line”), и когда
    мы нажали клавишу Return, строка была отправлена на сервер (строка 6 на рисунке
    18.16). При этом был получен ответ от сервера, однако так как сервер был
    перезагружен, его ARP кэш пуст, поэтому в строках 7 и 8 мы видим ARP запрос и
    отклик. Затем в строке 9 был послан сброс (reset). Клиент получил сброс и выдал,
    что соединение было прервано удаленным хостом. (Последнее сообщение вывода от
    клиента Telnet не столь информативно как могло бы быть.)

    Одновременное открытие

    Для двух приложений существует возможность осуществить активное
    открытие в одно и то же время. С каждой стороны должен быть передан SYN, и эти
    SYN должны пройти по сети навстречу друг другу. Также требуется, чтобы каждая
    сторона имела номер порта, который известен другой стороне. Это называется
    одновременным открытием (simultaneous open).

    Например, приложение на хосте А имеющее локальный порт 7777
    осуществляет активное открытие на порт 8888 хоста В. Приложение на хосте В
    имеющее локальный порт 8888 осуществляет активное открытие на порт 7777 хоста А.

    Это не то же самое что подсоединение Telnet клиента с хоста А на
    Telnet сервер на хосте В, в то время когда Telnet клиент с хоста В
    подсоединяется к Telnet серверу на хосте А. В подобном сценарии оба Telnet
    сервера осуществляют пассивное открытие, а не активное, тогда как Telnet клиенты
    назначают себе динамически назначаемые номера портов, а не порты, которые
    заранее известны для удаленных Telnet серверов.

    TCP специально разработан таким образом, чтобы обрабатывать
    одновременное открытие, при этом результатом является одно соединение, а не два.
    (В других семействах протоколов, например, в транспортном уровне OSI, в подобном случае создается два соединения, а не одно.)

    Когда осуществляется одновременное открытие, изменения состояний
    протокола отличаются от тех, которые показаны на рисунке 18.13. Оба конца
    отправляют SYN в одно и то же время, при этом входят в состояние SYN_ОТПРАВЛЕН
    (SYN_SENT). Когда каждая сторона принимает SYN, состояние
    меняется на SYN_ПРИНЯТ (SYN_RCVD) (см. рисунок 18.12), и
    каждый конец повторно отправляет SYN с подтверждением о том, что SYN принят.
    Когда каждый конец получает SYN плюс ACK, состояние меняется на УСТАНОВЛЕНО (ESTABLISHED). Изменения состояний приведены на рисунке 18.17.

    Address resolution protocol - что это?

    Рисунок 18.17 Обмен сегментами в процессе одновременного
    открытия.

    Одновременное открытие требует обмена четырьмя сегментами, на один
    больше чем при “трехразовом рукопожатии”. Также обратите внимание на
    то, что мы не называем один из концов клиентом, а другой сервером, потому что в
    данном случае оба выступают и как клиент и как сервер.

    Пример

    Осуществить одновременное открытие возможно, однако достаточно сложно.
    Обе стороны должны быть стартованы примерно в одно и то же время, таким образом,
    чтобы SYN пересеклись друг с другом. В данном случае может помочь большое время
    возврата между двумя участниками соединения, что позволяет SYN пересечься. Чтобы
    получить это, мы используем в качестве одного участника соединения хост bsdi, а
    другого хост vangogh.cs.berkeley.edu. Так как между ними находится SLIP канал с
    дозвоном, время возврата должно быть достаточно большим (несколько сотен
    миллисекунд), что позволяет SYN пересечься.

    Один конец (bsdi) назначает себе локальный порт 8888 (опция командной
    строки -b) и осуществляет активное открытие на порт 7777
    другого хоста:

    bsdi % sock -v -b8888 vangogh.cs.berkeley.edu 7777
    connected on
    140.252.13.35.8888 to 128.32.130.2.7777
    TCP_MAXSEG = 512
    hello, world
                                       вводим эту строку
    and hi there
                                        эта строка была напечатана на другом
    конце
    connection closed by peer                        это вывод, когда был
    получен FIN

    Другой конец был стартован примерно в это же самое время, он назначил
    себе номер локального порта 7777 и осуществил активное открытие на порт
    8888:

    vangogh % sock -v -b7777 bsdi.tuc.noao.edu 8888
    connected on
    128.32.130.2.7777 to 140.252.13.35.8888
    TCP_MAXSEG = 512
    hello, world
                                        это введено на другом конце
    and hi
    there
                                       мы напечатали эту строку
    ^D
                                                 и затем ввели символ конца
    файла EOF

    Мы указали флаг -v в командной строке программы sock, чтобы проверить IP адреса и номера портов для каждого
    конца соединений. Этот флаг также печатает MSS, используемый на каждом конце
    соединения. Мы также напечатали в качестве ввода по одной строке на каждом
    конце, которые были отправлены на удаленный конец и напечатаны там, чтобы
    убедиться, что оба хоста “видят” друг друга.

    На рисунке 18.18 показан обмен сегментами для этого соединения. (Мы
    удалили некоторые новые опции TCP, появившиеся в исходных SYN, пришедших от
    vangogh, которая работает под управлением 4.4BSD. Мы опишем
    эти новые опции в разделе “Опции
    TCP”
    этой главы.) Обратите внимание, что за двумя SYN (строки 1 и 2)
    следуют два SYN с ACK (строки 3 и 4). При этом происходит одновременное
    открытие.

    В строке 5 показана введенная строка “hello, world”, которая
    идет от bsdi к vangogh с подтверждением в строке 6. Строки 7 и 8 соответствуют
    строке “and hi there”, которая идет в другом направлении. В строках
    9-12 показано обычное закрытие соединения.

    Большинство реализаций Berkeley не
    поддерживают корректно одновременное открытие. В этих системах, если Вы можете
    добиться того, что SYN пересекутся, все закончится обменом сегментов, каждый с
    SYN и ACK, в обоих направлениях. В большинстве реализаций не всегда
    осуществляется переход от состояния SYN_SENT в состояние SYN_RCVD, показанный на
    рисунке 18.12.

    1   0.0                      bsdi.8888 > vangogh.7777: S
    91904001:91904001(0)
    win 4096 <mss 512>
    2   0.213782 (0.2138)       vangogh.7777 >
    bsdi.8888: S
    1058199041:1058199041(0)
    win 8192 <mss 512>
    3   0.215399 (0.0016)       bsdi.8888 >
    vangogh.7777: S
    91904001:91904001(0)
    ack 1058199042 win
    4096
    <mss
    512>
    4   0.340405 (0.1250)       vangogh.7777 > bsdi.8888: S
    1058199041:1058199041(0)
    ack 91904002 win
    8192
    <mss
    512>
    5   5.633142 (5.2927)       bsdi.8888 > vangogh.7777: P 1:14 (13)
    ack 1 win 4096
    6   6.100366 (0.4672)       vangogh.7777 > bsdi.8888: . ack
    14 win 8192

    7   9.640214 (3.5398)       vangogh.7777 > bsdi.8888: P
    1:14 (13) ack 14 win 8192
    8   9.796417 (0.1562)       bsdi.8888 >
    vangogh.7777: . ack 14 win 4096

    9  13.060395 (3.2640)       vangogh.7777
    > bsdi.8888: F 14:14 (0) ack 14 win 8192
    10  13.061828 (0.0014)      
    bsdi.8888 > vangogh.7777: . ack 15 win 4096
    11  13.079769 (0.0179)      
    bsdi.8888 > vangogh.7777: F 14:14 (0) ack 15 win 4096
    12  13.299940
    (0.2202)       vangogh.7777 > bsdi.8888: . ack 15 win
    8192

    Рисунок 18.18 Обмен сегментами при одновременном открытии.

    Одновременное закрытие

    Как мы сказали ранее, с одной стороны (часто, но не всегда, со стороны
    клиента) осуществляется активное закрытие, при этом посылается первый FIN. Также
    возможно для обеих сторон осуществить активное закрытие, так как протокол TCP
    позволяет осуществить одновременное закрытие (simultaneous close).

    В терминах, приведенных на рисунке 18.12, оба конца переходят от
    состояния УСТАНОВЛЕНО (ESTABLISHED) к состоянию
    ОЖИДАНИЕ_FIN_1 (FIN_WAIT_1) , когда приложение выдает сигнал
    к закрытию. При этом оба посылают FIN, которые возможно встретятся где-нибудь в
    сети. Когда FIN принят, на каждом конце происходит переход из состояния
    FIN_WAIT_1 в состояние ЗАКРЫВАЮ (CLOSING) и с каждой стороны
    посылается завершающий ACK. Когда каждый конец получает завершающий ACK,
    состояние меняется на TIME_WAIT. На рисунке 18.19 показаны
    изменения состояний.

    Address resolution protocol - что это?

    Рисунок 18.19 Обмен сегментами в процессе одновременного
    закрытия.

    При одновременном закрытии происходит обмен таким же количеством
    пакетов, как и при обычном закрытии.

    Опции TCP

    TCP заголовок может содержать опции (рисунок 17.2).
    Единственные опции, которые определены в оригинальной спецификации TCP,
    следующие: конец списка опций, нет операции и максимальный размер сегмента. Мы
    видели в наших примерах, опцию MSS практически в каждом SYN сегменте.

    Более новые RFC, например RFC 1323 [Jacobson,
    Braden, and Borman 1992], определяют дополнительные опции TCP, большинство из
    которых можно обнаружить только в более поздних реализациях. (Мы опишем новые
    опции в главе 24.) На рисунке 18.20 показан
    формат текущих опций TCP – тех, которые описаны в RFC 793 и RFC 1323.

    Address resolution protocol - что это?

    Рисунок 18.20 TCP опции.

    Каждая опция начинается с 1-байтового типа (kind), который указывает
    на тип опции. Опции, тип которых равен 0 и 1, занимают 1 байт. Другие опции
    имеют длину (len) байт, который следует за байтом типа. Длина – это полная
    длина, включающая байты типа и длины.

    Опция “нет операции” (NOP) добавлена,
    чтобы отправитель мог заполнить поля, которые должны быть кратны 4 байтам. Если
    мы установим TCP соединение от системы 4.4BSD, в начальном
    SYN сегменте с помощью tcpdump можно увидеть следующие опции:

    <mss 512, nop, wscale 0, nop, nop,
    timestamp 146647 0>

    Опция MSS установлена в 512, за ней следует NOP,
    за ней следует опция размера окна. Первая опция NOP
    используется для того, чтобы дополнить 3-байтовую опцию размера окна до 4 байт.
    Точно так же, 10-байтовая опция временной марки предваряется
    двумя NOP, чтобы занимать 12 байт.

    Четыре другие опции, которым
    соответствует тип равный 4, 5, 6 и 7, называются опциями селективного ACK и эхо опциями. Мы не показали их на рисунке
    18.20, потому что эхо опции заменены опцией временной марки, а селективные ACK,
    как определено в настоящее время, все еще находятся в обсуждении и не были
    включены в RFC 1323. Надо отметить, что предложение T/TCP для TCP транзакций
    (раздел “T/TCP: расширение TCP для
    транзакций”
    главы 24) указывает еще три опции с типами равными 11, 12 и
    13.

    Реализация TCP сервера

    В разделе “Модель Клиент-Сервер”
    главы 1 мы сказали, что большинство TCP серверов – конкурентные. Когда на сервер прибывает запрос об установлении
    нового соединения, он принимает соединение и запускает новый процесс, который
    будет обслуживать нового клиента. В зависимости от операционной системы
    используются различные способы для создания нового сервера. В Unix системах
    новый процесс создается с использованием функции fork.

    Нам необходимо обсудить, как TCP взаимодействует с конкурентными серверами. Хочется ответить на следующий вопрос:
    как обрабатываются номера портов, когда сервер получает запрос на новое
    соединение от клиента, и что произойдет, если в одно и то же время прибудет
    несколько запросов на соединение?

    Номера портов сервера TCP

    Мы можем сказать, как TCP обрабатывает номера портов, рассмотрев любой
    TCP сервер. Рассмотрим Telnet сервер с использованием команды netstat. Следующий вывод приведен для системы, у которых нет
    активных Telnet соединений. (Мы удалили все строки за исключением одной, которая
    показывает Telnet сервер.)

    sun % netstat -a -n -f inet
    Active Internet connections
    (including servers)
    Proto Recv-Q Send-Q   Local Address          Foreign
    Address           (state)
    tcp        0      0    *.23                   *.*
                          LISTEN

    Флаг -a сообщает о всех конечных точках сети, а не
    только о находящихся в состоянии установлено (ESTABLISHED).
    Флаг -n печатает IP адреса в цифровом десятичном
    представлении, вместо того чтобы использовать DNS для конвертирования адресов в
    имена, и печатает цифровые номера портов (например, 23) вместо печати имен
    сервисов (в данном случае Telnet). Опция -f inet сообщает
    только о конечных точках TCP и UDP.

    Локальный адрес выводится как *.23, где звездочка обычно называется символом подстановки или метасимволом. Это означает что
    входящий запрос о соединении (SYN) будет принят с любого локального интерфейса.
    Если хост имеет несколько интерфейсов, мы могли бы указать
    один конкретный IP адрес в качестве локального IP адреса (один из IP адресов
    хоста), и только запросы на соединение, принятые с этого интерфейса, будут
    обслужены. (Мы увидим, как это делается, позже в этом разделе.) Локальный порт
    равен 23, это заранее известный порт для Telnet.

    Удаленный адрес показан как *.*, это означает, что удаленный IP адрес
    и удаленный номер порта пока не известны, потому что конечная точка находится в
    состоянии СЛУШАЕТ (LISTEN), ожидая прибытия запроса на
    соединение.

    Сейчас мы стартуем Telnet клиента на хосте slip (140.252.13.65),
    который подсоединится к этому серверу. Здесь приведены соответствующие строки
    вывода команды netstat:

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    tcp        0      0    140.252.13.33.23       140.252.13.65.1029
           ESTABLISHED
    tcp        0      0    *.23                    *.*
                          LISTEN

    Первая строка для порта 23 это установленное соединение (ESTABLISHED).
    Для этого соединения заполнены все четыре элемента локального и удаленного
    адресов: локальный IP адрес и номер порта, и удаленный IP адрес и номер порта.
    Локальный IP адрес соответствует интерфейсу, на который прибыл запрос о
    соединении (Ethernet интерфейс, 140.252.13.33).

    Конечная точка осталась в состоянии LISTEN. Это конечная точка,
    которую конкурентный сервер использует, для того чтобы принимать запросы на
    соединение, которые придут в будущем. В данном случае TCP модуль, находящийся в
    ядре, создал новую конечную точку в состоянии ESTABLISHED, в момент, когда
    входящий запрос о соединении прибыл и был принят. Также обратите внимание на то,
    что номер порта для соединения, которое находится в состоянии ESTABLISHED, не
    изменился: он равен 23, как и для конечной точки, которая находится в состоянии
    LISTEN.

    Сейчас мы стартуем еще одного Telnet клиента с того же самого клиента
    (slip) на этот сервер. Соответствующий вывод команды netstat
    будет выглядеть следующим образом:

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    tcp        0      0    140.252.13.33.23       140.252.13.65.1030
           ESTABLISHED
    tcp        0      0    140.252.13.33.23      
    140.252.13.65.1029        ESTABLISHED
    tcp        0      0    *.23
                       *.*                       LISTEN

    Сейчас мы видим два установленных (ESTABLISHED) соединения с того же
    самого хоста на тот же самый сервер. Оба имеют локальный номер порта равный 23.
    Это не проблема для TCP, так как номера удаленных портов различны. Они должны
    быть различны, потому что каждый Telnet клиент использует динамически
    назначаемый порт, а из определения динамически назначаемого порта мы знаем, что
    динамически назначенным может быть только тот порт, который не используется в
    настоящее время на хосте (slip).

    Этот пример показывает, что TCP демультиплексирует
    входящие сегменты с использованием всех четырех значений, которые сравниваются с
    локальным и удаленным адресами: IP адрес назначения, номер порта назначения, IP
    адрес источника и номер порта источника. TCP не может определить, какой процесс
    получил входящий сегмент, просматривая только номер порта назначения. Также
    только одна из трех конечных точек на порту 23, которая находится в состоянии
    LISTEN, принимает входящие запросы на соединения. Конечные точки, находящиеся в
    состоянии ESTABLISHED, не могут принимать сегменты SYN, а конечная точка,
    находящаяся в состоянии LISTEN, не может принимать сегменты данных.

    Сейчас мы стартуем еще одного клиента Telnet с хоста solaris, который
    пройдет через SLIP канал от sun, а не через Ethernet.

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    tcp        0      0    140.252.1.29.23        140.252.1.32.34603
           ESTABLISHED
    tcp        0      0    140.252.13.33.23      
    140.252.13.65.1030        ESTABLISHED
    tcp        0      0   
    140.252.13.33.23       140.252.13.65.1029        ESTABLISHED
    tcp       
    0      0    *.23                    *.*                      
    LISTEN

    Локальный IP адрес для первого установленного (ESTABLISHED) соединения
    теперь соответствует адресу интерфейса SLIP канала на многоинтерфейсном хосте
    sun (140.252.1.29).

    Ограничение локальных IP адресов

    Мы можем посмотреть, что произойдет, когда сервер не использует
    символы подстановки в качестве своих локальных IP адресов, устанавливая вместо
    этого один конкретный адрес локального интерфейса. Если мы укажем IP адрес (или
    имя хоста) нашей программе sock, когда используем ее в качестве сервера, этот IP
    адрес станет локальным IP адресом слушающей конечной точки. Например

    sun % sock -s 140.252.1.29
    8888

    ограничивает этот сервер только для соединений, прибывающих с SLIP
    интерфейса (140.252.1.29). Вывод команды netstat покажет следующее:

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    tcp        0      0    140.252.1.29.8888      *.*
                          LISTEN

    Если мы подсоединимся к этому серверу через SLIP канал с хоста
    solaris, это сработает.

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    tcp        0      0    140.252.1.29.8888      140.252.1.32.34614
           ESTABLISHED
    tcp        0      0    140.252.1.29.8888      *.*
                          LISTEN

    Однако если мы постараемся подсоединиться к этому серверу с хоста
    через Ethernet (140.252.13), запрос на соединение не будет принят TCP модулем.
    Если мы посмотрим с помощью tcpdump, то увидим что на SYN получен отклик RST,
    как показано на рисунке 18.21.

    1  0.0                      bsdi.1026 > sun.8888: S
    3657920001:3657920001(0)
    win 4096 <mss 1024>
    2  0.000859 (0.0009)       sun.8888 > bsdi.1026:
    R 0:0 (0) ack 3657920002 win 0

    Рисунок 18.21 Ограничение запросов на соединения, основанное на
    локальном IP адресе сервера.

    Приложение, работающее на сервере, никогда не увидит запрос на
    соединение – ограничение осуществляется TCP модулем в ядре на основе локального
    IP адреса, указанного приложением.

    Ограничение удаленного IP адреса

    В разделе “Сервер UDP” главы 11
    мы видели, что UDP сервер может определить удаленный IP адрес и номер порта, в
    дополнение к указанным локальному IP адресу и номеру порта. Функции интерфейса,
    приведенные в RFC 793, позволяют серверу осуществлять пассивное открытие на
    основе полностью описанного удаленного сокета (в этом случае ожидается запрос на
    активное открытие от конкретного клиента) или не указанного удаленного сокета (в
    этом случае ожидается запрос на соединение от любого клиента).

    К сожалению, большинство API не предоставляют таких возможностей.
    Сервер должен оставлять удаленный сокет неконкретизированным, ожидая прибытия
    соединения, а затем проверяя IP адрес и номер порта клиента.

    На рисунке 18.22 показаны три типа адресов и взаимосвязи адресов с
    портами, которые TCP сервер может установить для себя. Во всех случаях lport это
    заранее известный порт сервера, а localIP должен быть IP адресом локального
    интерфейса. Порядок, в котором расположены три строки в таблице, соответствует
    порядку, в котором TCP модуль пытается определить, которая локальная конечная
    точка примет входящий запрос на соединение. Сначала осуществляется попытка,
    соответствующая первой строке таблицы (если поддерживается), и затем остальные
    спецификации (последняя строка с IP адресами, указанными в виде символов
    подстановки) пробуются последней.

    Локальный адрес

    Удаленный адрес

    Описание

    localIP.lport

    foreignIP.fport

    ограничено одним клиентом
    (обычно не поддерживается)

    localIP.lport

    *.*

    ограничено соединениями,
    прибывающими с одного локального интерфейса: localIP

    *.lport

    *.*

    принимает все соединения,
    посланные на lport

    Рисунок 18.22 Указание локального и удаленного IP адресов и номеров
    порта для TCP сервера.

    Входящая очередь запросов на
    соединение

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

    Реализации Berkeley используют следующие правила.

    1. Каждая слушающая конечная точка имеет фиксированную длину очереди
      соединений, которые могут быть приняты TCP (“трехразовое рукопожатие”
      завершено), однако еще не принятые приложением. Будьте внимательны, проводя
      различие между принятием соединения TCP и помещением его в очередь, и
      приложением, принимающем соединения из этой очереди.
    2. Приложение указывает ограничение или предел для этой очереди, который
      обычно называется backlog. Это ограничение должно быть в диапазоне от 0 до 5.
      (Большинство приложений указывают максимальное значение равное 5.)
    3. Когда прибывает запрос на соединение (SYN сегмент), TCP просматривает
      текущее количество соединений, поставленных в настоящий момент в очередь для
      этой слушающей конечной точки, при этом он выясняет, можно ли принять
      соединение. Мы ожидаем, что значение backlog, указанное приложением, будет
      максимальным, то есть, разрешено поставить в очередь максимальное количество
      соединений для этой точки, хотя это и не очень просто. На рисунке 18.23 показано
      взаимоотношение между значением backlog и реальным максимальным количеством
      соединений, которые можно поставить в очередь в традиционных Berkeley системах и
      Solaris 2.2.

      значение backlog

      Максимальное количество соединений, поставленных в
      очередь

      Традиционный BSD

      Solaris 2.2

      1

      1

      2

      1

      2

      4

      2

      3

      5

      3

      4

      7

      4

      5

      8

      5

      Рисунок 18.23 Максимальное количество принимаемых соединений для
      слушающей конечной точки.

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

      Значения для Solaris на этом рисунке
      именно такие, как мы и ожидали. Традиционные значения для BSD (по каким-то
      непонятным причинам) равны значению backlog, умноженному на 3, поделенному на 2,
      плюс 1.

    4. Если в очереди для данной слушающей конечной точки есть место для
      нового соединения (см. рисунок 18.23), TCP модуль подтверждает (ACK) пришедший
      SYN и устанавливает соединение. Приложение сервера со слушающей конечной точкой
      не увидит этого нового соединения до тех пор, пока не будет принят третий
      сегмент из “трехразового рукопожатия”. Также клиент может считать, что
      сервер готов принять данные, когда активное открытие клиента завершено успешно,
      перед тем как приложение сервера будет уведомлено о новом соединении. (Если это
      произойдет, сервер TCP просто поставит в очередь входящие данные.)
    5. Если не хватает места, для того чтобы поставить в очередь новое
      соединение, TCP просто игнорирует принятый SYN. В ответ ничего не посылается (не
      посылается даже RST сегмент). Если слушающий сервер не может отказаться от
      приема некоторых уже принятых соединений, которые заполнили собой очередь до
      предела, активное открытие клиента будет прервано по тайм-ауту.

    Мы можем посмотреть этот сценарий с использованием программы sock.
    Запустим ее с новой опцией (-O), которая сообщает о
    необходимости сделать паузу после создания слушающей конечной точки, перед
    приемом любого запроса на соединение. Если затем мы запустим несколько клиентов
    в течение этой паузы, сервер будет вынужден поставить в очередь принятые
    соединения, а то, что произойдет, мы увидим с использованием команды
    tcpdump.

    bsdi % sock -s -v -q1 -O30
    5555

    Опция -q1 устанавливает backlog слушающей конечной
    точки в значение 1, для традиционной BSD системы это будет соответствовать двум
    запросам на соединение (рисунок 18.23). Опция -O30 заставляет программу
    “проспать” 30 секунд перед приемом любого соединения от клиента. Это
    дает нам 30 секунд, чтобы стартовать несколько клиентов, которые заполнят
    очередь. Мы стартуем четырех клиентов на хосте sun.

    На рисунке 18.24 показан вывод программы tcpdump, этот вывод
    начинается с первого SYN от первого клиента. (Мы удалили объявления размера окна
    и объявления MSS. Также мы выделили номера портов клиента жирным шрифтом, когда
    TCP соединение устанавливается – “трехразовое рукопожатие”.)

    Первый запрос на соединение от клиента, пришедший с порта 1090,
    принимается TCP модулем (сегменты 1-3). Второй запрос на соединение от клиента с
    порта 1091 также принимается TCP модулем (сегменты 4-6). Приложение сервера все
    еще “спит” и не приняло ни одного соединения. Все проделанное было
    осуществлено TCP модулем в ядре. Также надо отметить, что два клиента успешно
    осуществили активное открытие, то есть “трехразовое рукопожатие” было
    успешно завершено.

    1   0.0                     sun.1090 > bsdi.7777: S
    1617152000:1617152000(0)
    2   0.002310 ( 0.0023)     bsdi.7777 >
    sun.1090: S 4164096001:4164096001(0)
    3   0.003098 ( 0.0008)    
    sun.1090 > bsdi.7777: . ack
    1617152001
    ack 1
    4  
    4.291007 ( 4.2879)     sun.1091 > bsdi.7777: S
    1617792000:1617792000(0)
    5   4.293349 ( 0.0023)     bsdi.7777 >
    sun.1091: S
    4164672001:4164672001(0)
    ack 1617792001
    6   4.294167 ( 0.0008)     sun.1091 > bsdi.7777: .
    ack 1
    7   7.131981 ( 2.8378)     sun.1092 > bsdi.7777: S
    1618176000:1618176000(0)
    8  10.556787 ( 3.4248)     sun.1093 > bsdi.7777:
    S 1618688000:1618688000(0)
    9  12.695916 ( 2.1391)     sun.1092 >
    bsdi.7777: S 1618176000:1618176000(0)
    10  16.195772 ( 3.4999)     sun.1093
    > bsdi.7777: S 1618688000:1618688000(0)
    11  24.695571 ( 8.4998)    
    sun.1092 > bsdi.7777: S 1618176000:1618176000(0)
    12  28.195454 (
    3.4999)     sun.1093 > bsdi.7777: S 1618688000:1618688000(0)
    13 
    28.197810 ( 0.0024)     bsdi.7777 > sun.1093: S
    4167808001:4167808001(0)
    14  28.198639 ( 0.0008)     sun.1093 >
    bsdi.7777: . ack
    1618688001
    ack 1
    15 
    48.694931 (20.4963)     sun.1092 > bsdi.7777: S
    1618176000:1618176000(0)
    16  48.697292 ( 0.0024)     bsdi.7777 >
    sun.1092: S
    4170496001:4170496001(0)
    ack 1618176001
    17  48.698145 ( 0.0009)     sun.1092 > bsdi.7777: .
    ack 1

    Рисунок 18.24 Вывод программы tcpdump для примера использования
    backlog.

    Мы попытались стартовать третьего клиента в сегменте 7 (порт 1092) и
    четвертого в сегменте 8 (порт 1093). TCP игнорировало оба SYN, так как очередь
    для этой слушающей конечной точки заполнена. Оба клиента повторно передали свои
    SYN в сегментах 9, 10, 11, 12 и 15. Третья повторная передача четвертого клиента
    принята (сегменты 12-14), потому что 30-секундная пауза сервера закончилась, и
    сервер удалил два соединения, которые были приняты, очистив очередь. (Причина,
    по которой это произошло, заключается в том, что это соединение было принято
    сервером в момент времени 28.19, а не в момент времени, который больше чем 30;
    это произошло потому, что потребовалось несколько секунд, чтобы стартовать
    первого клиента [сегмент 1, время старта в выводе] после старта сервера.)
    Четвертая повторная передача третьего клиента также принята (сегменты 15-17).
    Соединение четвертого клиента (порт 1093) принято сервером перед соединением
    третьего клиента (порт 1092) из-за совпадения времени между окончанием
    30-секундной паузы и повторной передачей клиента.

    Мы могли ожидать, что очередь
    принятых соединений будет обработана приложением в соответствии с принципом FIFO (первый вошел, первый вышел). Таким образом, после того
    как TCP принял приложение на порты 1090 и 1091, мы ожидали, что приложение
    получит соединение сначала на порт 1090, а затем соединение на порт 1091.
    Однако, в большинстве реализаций Berkeley существует ошибка (bug), в результате
    чего используется порядок LIFO (последний вошел, первый
    вышел). Производители много раз пытались исправить эту ошибку, однако она до сих
    пор существует в таких системах как SunOS 4.1.3.

    TCP игнорирует входящее SYN, когда очередь заполнена, и не отвечает с
    использованием RST, из-за ошибки. Обычно очередь заполнена, потому что
    приложение или операционная система заняты, поэтому приложение не может
    обработать входящие соединения. Подобное состояние может измениться за короткий
    промежуток времени. Однако, если TCP сервер ответил сбросом (reset), активное
    открытие клиента будет прервано (как раз это произойдет, если сервер не был
    стартован). Так как SYN игнорирован, TCP клиент будет вынужден повторно передать
    SYN позже, в надежде на то, что в очереди появится место для нового соединения.

    В этом месте необходимо обсудить еще одну очень важную деталь, которая
    присутствует практически во всех реализациях TCP/IP. Она заключается в том, что
    TCP принимает входящий запрос на соединение (SYN) в том случае, если в очереди
    есть место. При этом приложение не может посмотреть, от кого пришел запрос (IP
    адрес источника и номер порта источника). Это не требуется TCP, это всего лишь
    общая техника, используемая в реализациях. Если API, такой как TLI (раздел “Интерфейсы прикладного
    программирования”
    главы 1), уведомляет приложение о прибытии запроса на
    соединение и позволяет приложению выбрать, принять это соединение или нет, то
    при использовании TCP получается так, что когда приложению сообщается, что
    соединение только что прибыло, в действительности TCP уже завершил
    “трехразовое рукопожатие”! В других транспортных уровнях существует
    возможность разграничить прибывшее и принятое соединения (OSI транспортный уровень), однако TCP такой возможности не
    предоставляет.

    Solaris 2.2
    предоставляет опцию, которая не позволяет TCP принимать входящий запрос на
    соединение до тех пор, пока ему это не разрешит приложение (tcp_eager_listeners в разделе “Solaris 2.2”
    приложения E).

    Это поведение также означает, что TCP сервер не может сделать так что
    активное открытие клиента будет прервано. Когда соединение от нового клиента
    попадает к приложению сервера, “трехстороннее рукопожатие” TCP уже
    закончено и активное открытие клиента завершено успешно. Если сервер затем
    смотрит на IP адрес клиента и номер порта и решает, что он не хочет обслуживать
    этого клиента, все сервера могут просто закрыть соединение (при этом будет
    послан FIN) или сбросить соединение (будет послан RST). В любом случае клиент
    будет считать, что с сервером все нормально, так как завершилось активное
    открытие, и, вполне возможно, уже послал серверу какой-либо
    запрос.

    Краткие выводы

    Перед тем как два процесса смогут обмениваться данными с
    использованием TCP, они должны установить соединение между собой. Когда работа
    между ними закончена, соединение должно быть разорвано. В этой главе детально
    рассмотрено то, как устанавливается соединение с использованием
    “трехразового рукопожатия” и как оно разрывается с использованием
    четырех сегментов.

    Мы использовали tcpdump, чтобы показать все поля в
    заголовке TCP. Мы также посмотрели, как установленное соединение может быть
    прервано по тайм-ауту, как сбрасывается соединение, что происходит с
    полуоткрытым соединением и как TCP предоставляет полузакрытый режим,
    одновременное открытие и одновременное закрытие.

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

    TCP соединения уникально идентифицируются 4 параметрами: локальным IP
    адресом, локальным номером порта, удаленным IP адресом и удаленным номером
    порта. Если соединение разрывается, одна сторона все равно должна помнить об
    этом соединении, в этом случае мы говорим что работает режим TIME_WAIT. Правило гласит, что эта сторона может осуществить
    активное открытие, войдя в этот режим, после того как истекло удвоенное время
    MSL, принятое для данной реализации.

    Упражнения

    1. В разделе “Установление и разрыв соединения” мы
      сказали, что исходный номер последовательности (ISN) обычно
      устанавливается в 1 и увеличивается на 64000 каждые полсекунды и каждый раз
      когда осуществляется активное открытие. Это означает, что младшие три цифры в
      ISN всегда будут 001. Однако на рисунке 18.3 эти младшие три цифры для каждого
      направления равны 521. Как это произошло?
    2. На рисунке 18.15 мы напечатали 12 символов, но видели что TCP послал 13
      байт. На рисунке 18.16 мы напечатали 8 символов, однако TCP отправил 10 байт.
      Почему в первом случае был добавлен 1 байт, а во втором 2 байта?
    3. В чем заключается отличие между полуоткрытым соединением и полузакрытым
      соединением?
    4. Если мы стартуем программу sock в качестве сервера, а
      затем прервем ее работу (при этом к ней не было подключено ни одного клиента),
      мы можем немедленно перестартовать сервер. Это означает, что он не будет
      находиться в состоянии ожидания 2MSL. Объясните это в
      терминах диаграммы изменения состояний.
    5. В разделе “Диаграмма состояний передачи
      TCP”
      мы показали, что клиент не может повторно использовать тот же
      самый локальный номер порта, пока порт является частью соединения в состоянии
      ожидания 2MSL. Однако, если мы запустим программу sock дважды подряд в качестве
      клиента, подсоединяясь к серверу времени, мы можем использовать тот же самый
      локальный номер порта. В дополнение, мы можем создать новое соединение, которое
      будет в состоянии ожидания 2MSL. Как это происходит?

      sun % sock -v bsdi
      daytime

      connected on 140.252.13.33.1163 to 140.252.13.35.13
      Wed Jul 7
      07:54:51 1993
      connection closed by peer

      sun % sock -v -b1163 bsdi
      daytime
                     повторное использование того же номера локального
      порта
      connected on 140.252.13.33.1163 to 140.252.13.35.13
      Wed Jul 7
      07:55:01 1993
      connection closed by peer

    6. В конце раздела “Диаграмма состояний передачи TCP”,
      когда мы описывали состояние FIN_WAIT_2, мы указали, что
      большинство реализаций переводит соединение из этого состояния в состояние CLOSED, если приложение осуществило полное закрытие (не
      наполовину закрытый) примерно через 11 минут. Если другая сторона (в состоянии
      CLOSE_WAIT) ждет 12 минут перед осуществлением закрытия
      (отправка своего FIN), что его TCP получит в ответ на FIN?
    7. Какая сторона в телефонном разговоре осуществляет активное открытие, а какая
      осуществляет пассивное открытие? Возможно ли одновременное открытие? Возможно ли
      одновременное закрытие?
    8. На рисунке 18.6 мы не видели ARP запрос или ARP отклик. Однако аппаратный
      адрес хоста svr4 должен быть в ARP кэше bsdi. Что изменится на этом рисунке,
      если этот пункт в ARP кэше отсутствует?
    9. Объясните следующий вывод команды tcpdump. Сравните его с рисунком
      18.13.

      1  0.0                    
      solaris.32990 > bsdi.discard: S 40140288:40140288
      (0)
      win 8760 <mss
      1460>
      2  0.003295 (0.0033)      bsdi.discard > solaris.32990: S
      4208081409:4208081409
      (0)
      ack 40140289 win
      4096
      <mss
      1024>
      3  0.419991 (0.4167)      solaris.32990 > bsdi.discard: P 1:257
      (256) ack 1 win 9216
      4  0.449852 (0.0299)      solaris.32990 >
      bsdi.discard: F 257:257 (0) ack 1 win 9216
      5  0.451965 (0.0021)     
      bsdi.discard > solaris.32990: . ack 258 win 3840
      6  0.464569 (0.0126)     
      bsdi.discard > solaris.32990: F 1:1 (0) ack 258 win 4096
      7  0.720031
      (0.2555)      solaris.32990 > bsdi.discard: . ack 2 win
      9216

    10. Почему бы серверу на рисунке 18.4 не
      скомбинировать ACK на FIN клиента со своим собственным FIN, уменьшив тем самым
      количество сегментов до трех?
    11. На рисунке 18.16, почему номер последовательности RST равен 26368002?
    12. Скажите, основан ли запрос TCP к канальному уровню о его MTU на принципе
      разбиения на уровни?
    13. Представьте, что на рисунке 14.16 каждый DNS запрос
      отправляется с использованием TCP вместо UDP. Скажите, сколькими пакетами будет
      осуществлен обмен?
    14. Если MSL равно 120 секунд, через какое максимальное
      время система может инициировать новое соединение, а затем осуществить активное
      закрытие?
    15. Прочитайте RFC 793, чтобы посмотреть что произойдет, когда конечная точка,
      находящаяся в режиме TIME_WAIT, получает повторный FIN,
      который помещает ее в это состояние.
    16. Прочитайте RFC 793, чтобы посмотреть что произойдет, когда конечная точка,
      которая находится в состоянии TIME_WAIT, получает RST.
    17. Прочитайте требования к хостам Host Requirements RFC,
      чтобы получить определение полудуплексного TCP закрытия.
    18. На рисунке 1.8 мы показали, что
      входящие TCP сегменты демультиплексируются на основе номера
      порта назначения TCP. Правильно ли это?

    §

    Введение

    TCP функционирует уже в течение многих лет и по SLIP каналам со
    скоростью 1200 бит в секунду и по Ethernet. В 80-х и начале 90-х годов Ethernet
    был основным типом канального уровня для TCP/IP. Несмотря на то, что TCP
    корректно работает на скоростях больших, чем предоставляемые Ethernet
    (телефонные линии T3, FDDI и гигабитные сети, например), на повышенных скоростях
    начинают сказываться некоторые ограничения TCP.

    В этой главе рассматриваются некоторые предложения посвященные
    модификациям TCP, которые позволяют добиться максимальной пропускной способности
    на высоких скоростях. Во-первых, мы рассмотрим механизм определения
    транспортного MTU, который мы уже упоминали раньше. Сейчас мы подробно
    рассмотрим, как он функционирует с TCP. Этот алгоритм позволяет TCP использовать
    MTU больше чем 536 для соединений по глобальным сетям, что, в свою очередь,
    повышает пропускную способность.

    Затем мы рассмотрим каналы с повышенной пропускной способностью (long
    fat pipes), сети, имеющие большую емкость канала зависящую от полосы пропускания
    (bandwidth-delay product), и ограничения TCP, которые становятся существенными
    для этих сетей. Здесь описаны две новые опции TCP, используемые для работы с
    каналами с повышенной пропускной способностью (long fat pipes): опция
    масштабирования окна (позволяет использовать окна TCP с максимальным размером
    больше чем 65535 байт) и опция временной марки. Опция временной марки позволяет
    TCP осуществлять более аккуратный расчет RTT для сегментов данных, а также
    предоставляет защиту от перехода номеров последовательности через ноль, что
    может возникнуть на высоких скоростях. Эти две опции определены в RFC 1323 [Jacobson, Braden, and Borman 1992].

    Также мы рассмотрим T/TCP – модификацию TCP для транзакций. Режим
    транзакций это характеристики коммуникации, при которых на запрос от клиента
    приходит отклик от сервера. Основная задача T/TCP заключается в том, чтобы
    уменьшить количество сегментов, которыми обмениваются два участника соединения,
    при этом отпадает необходимость в трехразовом рукопожатии (three-way handshake)
    и четырех сегментах, которыми необходимо обменяться, чтобы закрыть соединение.
    При этом клиент получает отклик от сервера через время равное одному RTT плюс
    время, необходимое для обработки запроса.

    И самое замечательное в этих новых опциях – в характеристике
    определения транспортного MTU, опции масштабирования окна, опции временной марки
    и T/TCP – это то, что они совместимы с уже существующими реализациями TCP. Новые
    системы, которые имеют эти опции, могут общаться с более ранними системами. За
    исключением дополнительных полей в ICMP сообщении, которые могут быть
    использованы при определении транспортного MTU, новые опции должны быть
    реализованы только на конечных системах, которые хотят пользоваться их
    преимуществами.

    Мы закончим эту главу рассмотрением публикаций, вышедших в настоящее
    время и имеющих отношение к производительности TCP.

    Определение транспортного MTU

    В разделе “Транспортный MTU”
    главы 2 мы описали концепцию транспортного MTU (path MTU). Это минимальный MTU в
    любой из сетей, по которым проходит маршрут между двумя хостами. При определении
    транспортного MTU в IP заголовке устанавливается бит “не фрагментировать” (DF – don’t fragment), что
    позволяет определить, необходимо ли какому-либо маршрутизатору на текущем
    маршруте фрагментировать IP датаграммы, которые мы посылаем. В разделе “ICMP ошибки о
    недоступности”
    главы 11 мы показали ICMP ошибку о
    недоступности, генерируемую маршрутизатором, которому необходимо перенаправить
    IP датаграмму с установленным битом DF, когда MTU меньше чем размер датаграммы.
    В разделе “Определение транспортного MTU
    с использованием Traceroute”
    главы 11 мы показали версию программы traceroute, которая использует этот механизм, чтобы определить
    транспортный MTU к пункту назначения. В разделе “Определение
    транспортного MTU при использовании UDP”
    главы 11 мы видели, как UDP
    определял транспортный MTU. В этом разделе мы просмотрим, как TCP использует
    этот механизм. Это описано в RFC 1191 [Mogul and Deering
    1990].

    Из всех систем, которые используются
    в этой книге (см. вступление), только Solaris 2.x
    поддерживает определение транспортного MTU.

    Алгоритм определения транспортного MTU TCP работает следующим образом.
    Когда соединение установлено, TCP использует минимальный MTU исходящего
    интерфейса или MSS, объявленный удаленным концом, в качестве
    исходного размера сегмента. Алгоритм определения транспортного MTU не позволяет
    TCP превосходить MSS, объявленный удаленным концом. Если удаленный конец не
    указал MSS, то он устанавливается по умолчанию в значение равное 536. Реализации
    могут сохранить информацию о транспортном MTU определенного канала, как мы
    говорили в разделе “Показатели на
    маршрут”
    главы 21.

    После того как выбран исходный размер сегмента, во всех IP
    датаграммах, отправляемых TCP по этому соединению, установливается бит DF. Если
    промежуточному маршрутизатору необходимо фрагментировать датаграмму, в которой
    установлен бит DF, он отбрасывает датаграмму и генерирует ICMP ошибку “не
    могу фрагментировать” (can’t fragment). Это описано в разделе “ICMP ошибки о
    недоступности”
    главы 11. Если принята такая ICMP ошибка, TCP уменьшает
    размер сегмента и повторяет передачу. Если маршрутизатор сгенерировал новую ICMP
    ошибку, размер сегмента может быть установлен в MTU следующей пересылки минус
    размеры IP и TCP заголовков.

    Если возвратилась старая ICMP ошибка, должно быть использовано
    следующее меньшее значение MTU (рисунок 2.5). Когда осуществляются повторные
    передачи, вызванные этими ICMP ошибками, окно переполнения не должно изменяться,
    вместо этого должен быть использован медленный старт.

    Так как маршруты могут меняться со временем, по
    истечении определенного времени после последнего уменьшения транспортного MTU,
    можно попробовать большее значение (до величины минимального MSS, объявленного
    удаленным концом, или MTU исходящего интерфейса). RFC 1191 рекомендует, чтобы
    этот временной интервал составлял примерно 10 минут. (Мы видели в разделе “Определение транспортного MTU
    при использовании UDP”
    главы 11, что Solaris 2.2 использует для этих
    целей 30-секундный тайм-аут.)

    Используя обычное для работы в глобальных сетях значение MSS по
    умолчанию равное 536, алгоритм определения транспортного MTU избегает
    фрагментации по промежуточным каналам с MTU меньшим чем 576 (что встречается
    довольно редко). Также можно избежать фрагментации в локальных сетях, когда
    промежуточный канал (Ethernet) имеет меньший MTU, чем сеть конечного пункта
    назначения (Token ring). В процессе определения транспортного MTU (при работе в
    глобальных сетях с MTU большим чем 576), системы не должны использовать MSS по
    умолчанию равный 536 байт для нелокальных пунктов назначения. Предпочтительней
    выбирать MSS равный MTU исходящего интерфейса (естественно, минус размер IP и
    TCP заголовков). (В приложении Е мы увидим, что большинство
    реализаций позволяют системным администраторам изменить значение MSS принятое по
    умолчанию.)

    Пример

    Увидеть, как происходит определение транспортного MTU, можно в том
    случае, когда промежуточный маршрутизатор имеет MTU меньше чем MTU интерфейсов
    конечных точек. На рисунке 24.1 показана топология для данного примера.

    Address resolution protocol - что это?

    Рисунок 24.1 Топология для примера транспортного
    MTU.

    Мы установим соединение с хоста solaris (который поддерживает механизм
    определения транспортного MTU) на хост slip. Установки идентичны тем, которые
    использованы для примера определения транспортного MTU в случае UDP (рисунок
    11.13), однако здесь мы установили MTU интерфейса на slip равный 552, вместо его
    обычного значения 296. Это заставляет slip объявить MSS
    равный 512. Мы оставили MTU = 296 на SLIP канале на bsdi, поэтому TCP сегменты,
    размером больше чем 256, должны быть фрагментированы. Посмотрим, как механизм
    определения транспортного MTU на solaris обработает подобную ситуацию.

    Запустим программу sock на хосте solaris и
    осуществим одну запись величиной 512 байт на discard сервис хоста
    slip:

    solaris % sock -i -n1 -w512 slip
    discard

    На рисунке 24.2 мы показали вывод команды tcpdump, полученный на SLIP
    интерфейсе хоста sun.

    Значение MSS в строках 1 и 2 как раз такое, как и ожидалось. Затем мы
    видим, что solaris отправил сегмент размером 512 байт (строка 3), содержащий 512
    байт данных и подтверждение на SYN. (Мы видели эту комбинацию ACK вместе с SYN в
    первом сегменте данных в упражнении 9 главы 18.)

    1  0.0                   solaris.33016 > slip.discard: S
    1171660288:1171660288(0)

    win 8760 <mss
    1460> (DF)
    2  0.101597 (0.1016)    slip.discard > solaris.33016: S
    137984001:137984001(0)
    ack 1171660289 win 4096

    <mss 512>

    0.630609 (0.5290)    solaris.33016 > slip.discard: P 1:513(512)

    ack 1 win 9216
    (DF)
    4  0.634433 (0.0038)    bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu = 296
    (DF)
    5  0.660331 (0.0259)    solaris.33016 > slip.discard: F 513:513(0)

    ack 1 win 9216
    (DF)
    6  0.752664 (0.0923)    slip.discard > solaris.33016: . ack 1 win
    4096
    7  1.110342 (0.3577)    solaris.33016 > slip.discard: P 1:257(256)

    ack 1 win 9216
    (DF)
    8  1.439330 (0.3290)    slip.discard > solaris.33016: . ack 257 win
    3840
    9  1.770154 (0.3308)    solaris.33016 > slip.discard:FP 257:513(256)

    ack 1 win 9216
    (DF)
    10  2.095987 (0.3258)    slip.discard > solaris.33016: . ack 514 win
    3840
    11  2.138193 (0.0422)    slip.discard > solaris.33016: F 1:1(0) ack
    514 win 4096
    12  2.310103 (0.1719)    solaris.33016 > slip.discard: . ack
    2 win 9216 (DF)

    Рисунок 24.2 Вывод команды tcpdump для определения транспортного
    MTU.

    В строке 4 генерируется ICMP ошибка, и мы видим, что маршрутизатор
    bsdi генерирует новую ICMP ошибку, содержащую MTU исходящего интерфейса.

    Так случилось, что перед тем, как эта ошибка вернулась на хост
    solaris, был отправлен FIN (строка 5). Так как slip не получил 512 байт данных,
    отброшенных маршрутизатором bsdi, и не ожидает этого номера последовательности
    (513), он отправляет ожидаемый номером последовательности (1) в строке 6.

    В это время ICMP ошибка вернулась на solaris, и было повторно передано
    512 байт данных в двух сегментах размером 256 байт (строки 7 и 9). Оба сегмента
    отправлены с установленным битом DF, так как дальше за маршрутизатором bsdi
    может быть еще один маршрутизатор, у которого MTU еще меньше.

    Была осуществлена довольно долгая передача (она заняла примерно 15
    минут), и после перехода от исходного размера сегмента равного 512 байт к
    сегментам размером 256 байт, solaris уже больше никогда не пытался отправить
    сегменты большего размера.

    Большие пакеты или маленькие?

    Народная мудрость гласит, что лучше использовать большие пакеты [Mogul 1993, Sec.15.2.8], потому что отправка меньшего
    количества больших пакетов “дешевле”, чем отправка большего количества
    маленьких пакетов. (Подразумевается, что пакеты не настолько велики, чтобы
    вызвать фрагментацию.) Не все согласны с этим положением [Bellovin 1993].

    Представьте себе следующий пример. Мы посылаем 8192 байта через четыре
    маршрутизатора, каждый из которых подключен к телефонной линии T1 (1544000
    бит/сек). Во-первых, мы используем два пакета размером 4096 байт, как показано
    на рисунке 24.3.

    Address resolution protocol - что это?

    Рисунок 24.3 Отправка двух пакетов размером 4096 байт
    через четыре маршрутизатора.

    Основная проблема заключается в том, что маршрутизаторы это
    устройства, которые работают по принципу “сохранить и перенаправить”.
    Они обычно получают входящий пакет целиком, проверяют на правильность IP
    заголовок, включая контрольную сумму IP, принимают решение о
    маршрутизации и затем начинают отправку исходящего пакета. На этом рисунке мы
    предположили идеальный случай, когда на операции, осуществляемые в
    маршрутизаторе, время не тратится, (горизонтальные пунктирные линии). Тем не
    менее, на отправку всех 8192 байт от R1 до R4 будет истрачено четыре отрезка
    времени. Время на каждую пересылку будет составлять

    [(4096 40 байт) x 8 бит/байт]/1544000 бит/сек = 21,4 миллисекунды на
    пересылку

    (IP и TCP заголовки составляют 40 байт.) Полное время, которое
    тратится на отправку данных, состоит из количества пакетов плюс количество
    пересылок минус один и составляет четыре отрезка времени или 85,6 миллисекунды.
    Каждый канал остается неиспользованным в течение двух отрезков времени или 42,8
    миллисекунды.

    На рисунке 24.4 показано что произойдет, если мы пошлем 16 пакетов
    размером 512 байт.

    Address resolution protocol - что это?

    Рисунок 24.4 Отправка 16 пакетов размером 512 байт через
    четыре маршрутизатора.

    Это передача займет больше отрезков времени, однако каждый отрезок
    короче, так как отправляются пакеты меньшего размера.

    [(512 40 байт) x 8 бит/байт]/1544000 бит/сек = 2,9 миллисекунды на
    пересылку

    Сейчас полное время составляет (18 x 2,9) = 52,2 миллисекунды. Каждый
    канал снова не занят в течение двух отрезков времени, что сейчас составляет 5,8
    миллисекунды.

    В этом примере мы игнорировали время, которое необходимо для того,
    чтобы вернулось подтверждение (ACK), также мы проигнорировали время, необходимое
    для установления и разрыва соединения, и не приняли во внимание то, что по
    каналам может двигаться и другой траффик. Тем не менее, расчеты в [Bellovin 1993] указывают, что отправка больших пакетов всегда
    эффективней. Однако, для различных сетей требуются более подробные
    исследования.

    Каналы с повышенной пропускной способностью (Long Fat
    Pipes)

    В разделе “Пропускная способность для
    неинтерактивных данных”
    главы 20 мы сказали, что емкость соединения
    можно рассчитать следующим образом

    емкость (бит) = ширина полосы (бит/сек) x время возврата
    (сек)

    и назвали это емкость канала в зависимости от
    полосы пропускания. Иногда эта величина называется размером канала между двумя
    точками.

    Существующие ограничения TCP начинают влиять на производительность по
    мере увеличения емкости каналов. На рисунке 24.5 показаны некоторые значения для
    различных типов сетей.

    Сеть

    Ширина полосы (бит/сек)

    Время возврата (миллисекунды)

    Емкость канала (байты)

    Локальная сеть на основе
    Ethernet

    10000000

    3

    3750

    Трансконтинентальный канал,
    телефонная линия T1

    1544000

    60

    11580

    Спутниковый канал, телефонная
    линия T1

    1544000

    500

    95500

    Трансконтинентальный канал,
    телефонная линия T3

    45000000

    60

    337500

    Трансконтинентальный гигабитный
    канал

    1000000000

    60

    7500000

    Рисунок 24.5 Емкость канала для различных сетей.

    Мы показали емкость канала в байтах, потому что именно так эта
    величина обычно рассчитывается на каждом конце соединения для определения
    размеров буферов и размеров окон.

    Сети с большой емкостью канала называются сетями с повышенной
    пропускной способностью (LFN – long fat networks,
    произносится как “elephant(s)”, elephant (англ.) – слон), а TCP
    соединения, работающие на LFN, называются каналами с
    повышенной пропускной способностью (long fat pipe). Возвращаясь назад к рисункам
    20.11 и 20.12, можно сказать, что эти каналы могут быть расширены в
    горизонтальном направлении (большие RTT) или в вертикальном
    направлении (большая ширина полосы передачи) или в обоих направлениях. Однако, с
    подобными каналами с повышенной пропускной способностью возникают некоторые
    проблемы.

    1. Размер окна TCP находится в 16-битном поле TCP заголовка, ограничивая
      размер окна величиной равной 65535 байт. Как видно из последней колонки на
      рисунке 24.5, существующие сети уже требуют большего окна, чем 65535, для
      достижения максимальной пропускной способности. Опция масштабирования окна,
      описанная в разделе “Опция масштабирования
      окна”
      этой главы, решает эту проблему.
    2. Пакеты, теряемые в LFN, могут значительно уменьшить пропускную
      способность. Если потерян только один сегмент, алгоритм
      быстрой передачи и быстрого восстановления, который мы описали в разделе “Быстрая повторная передача и
      алгоритм быстрого восстановления”
      главы 21, сделает так, что канал не
      сузится. Однако, даже при использовании этого алгоритма, потеря больше чем
      одного пакета внутри окна обычно приводит к тому, что канал сужается. (Если
      канал сузился, снова используется медленный старт, что в несколько раз
      увеличивает время возврата, прежде чем канал будет снова заполнен.) Чтобы
      обработать потерю нескольких пакетов внутри окна, в RFC 1072 [Jacobson and Braden 1988] было предложено использовать
      cелективные подтверждения (SACK). Однако, начиная с RFC
      1323, от использования этой характеристики отказались, потому что авторы
      обнаружили несколько технических проблем, которые необходимо решить перед
      включением этой опции в TCP.
    3. В разделе “Пример RTT” главы 21
      мы видели, что большинство TCP реализаций измеряют только одно время задержки на
      окно. Они не измеряют RTT для каждого сегмента. Однако для
      функционирования в LFN требуется лучшее измерение RTT. Опция временной марки,
      которая описана в разделе “Опция временной
      марки”
      этой главы, позволяет оценить время передачи нескольких
      сегментов, включая повторно переданные.
    4. TCP идентифицирует каждый байт данных уникальным 32-битным номером
      последовательности. Что произойдет, если сегмент, задержанный в сети, появится
      после того как соединение, которому он принадлежал, уже закрыто, и когда
      установлено новое соединение между теми же двумя хостами и теми же номерами
      портов? Во-первых, вспомним, что поле TTL в IP заголовке
      содержит максимальное время жизни любой IP датаграммы – 255 пересылок или 255
      секунд (что кончится первым). В разделе “Диаграмма состояний передачи
      TCP”
      главы 18 мы определили, что максимальное время жизни сегмента (MSL) это параметр, зависящий от реализации и используемый для
      того, чтобы не возникла подобная ситуация. Рекомендуемое значение для MSL – 2
      минуты (при этом 2MSL будет равно 240 секундам), однако мы
      видели в разделе “Диаграмма состояний передачи
      TCP”
      главы 18, что многие реализации устанавливают MSL в 30 секунд. Еще
      одна проблема с номерами последовательности TCP возникает при использовании LFN.
      Так как величина номера последовательности ограничена, тот же самый номер
      последовательности будет использован повторно после того, как будет передано
      4.294.967.296 байт. Что произойдет, если сегмент, содержащий байт с номером
      последовательности N, будет задержан в сети и появится позже, когда соединение
      все еще открыто? Эта проблема появится только в том случае, если тот же самый
      номер последовательности N повторно используется в течение периода MSL, то есть
      в том случае, если сеть настолько быстрая, что номер последовательности успевает
      повториться за время меньшее чем MSL. Для Ethernet необходимо почти 60 минут,
      чтобы послать такое количество данных, поэтому подобная ситуация не возможна,
      однако время, необходимое на то, чтобы появился номер последовательности,
      который уже существует в сети, уменьшается с ростом ширины пропускания сети: для
      телефонных линий T3 (45 Мбит/сек) требуются 12 минут, для FDDI (100 Мбит/сек) – 5 минут, а для гигабитных сетей (1000
      Мбит/сек) – 34 секунды. В данном случае проблема не связана с емкостью канала, а
      связана с шириной полосы. В разделе “PAWS:
      защита от перехода номеров последовательности через ноль”
      этой главы мы
      описываем способ, с помощью которого можно решить эту проблему: алгоритм PAWS
      (защита от перехода номеров последовательности через ноль), который использует
      опцию временной марки TCP.

    4.4BSD содержит
    все опции и алгоритмы, которые мы описываем в следующих разделах: опцию
    масштабирования окна, опцию временной марки и защиту от перехода номеров
    последовательности через ноль. Некоторые производители также начинают
    поддерживать эти опции.

    Гигабитные сети

    Положение вещей меняется, когда скорости в сетях достигают гигабитов.
    [Partridge 1994] описывает гигабитные сети более подробно.
    Здесь мы рассмотрим различие между задержкой (латенсией) и шириной полосы [Kleinrock 1992].

    Представьте себе процесс отправки файла размером 1 миллион байт через
    Соединенные Штаты, с предполагаемой латенсией равной 30 миллисекундам. На
    рисунке 24.6 показаны два сценария, верхний соответствует использованию
    телефонной линии T1 (1544000 бит/сек), а нижний подразумевает использование сети
    1 гигабит/сек. Время показано по оси ОХ, (отправитель находится слева, а
    получатель справа), а емкость показана по оси OY. Закрашенный прямоугольник на
    обоих рисунках – это 1 миллион байт, который необходимо отправить.

    Address resolution protocol - что это?

    Рисунок 24.6 Отправка файла размером 1 Мбайт по сетям с
    30-миллисекундной латенсией.

    На рисунке 24.6 показано состояние обеих сетей через 30 миллисекунд. В
    обеих сетях первый бит данных достиг удаленного конца через 30 миллисекунд
    (латенсия), однако в случае сети T1 (емкость канала – 5790 байт), 994210 байт
    все еще находятся у отправителя, ожидая того, что они будут отправлены. Емкость
    гигабитной сети, составляет 3750000 байт, поэтому файл целиком занимает всего
    лишь около 25% канала. Последний бит файла достигает получателя через 8
    миллисекунд после первого бита.

    Полное время передачи файла по сети T1 составляет 5,211 секунды. Если
    мы увеличить ширину полосы пропускания, например, с использованием сети T3
    (45000000 бит/сек), полное время уменьшится до 0,208 секунды. Увеличение ширины
    полосы в 29 раз уменьшает полное время в 25 раз.

    В случае гигабитной сети полное время, необходимое на передачу файла,
    составляет 0,038 секунды: 30-миллисекундная латенсия плюс 8 миллисекунд на
    реальную передачу файла. Предположим, мы можем увеличить ширину полосы
    пропускания до 2 гигабит/сек, однако в этом случае мы уменьшим полное время
    передачи до всего лишь 0,034 секунды: та же самая 30-миллисекундная латенсия
    плюс 4 миллисекунды на передачу файла. Таким образом, удвоение полосы передачи,
    уменьшает полное время всего лишь на 10%. В случае гигабитных скоростей мы уже
    ограничены латенсией, а не шириной полосы.

    Латенсия определяется скоростью света, и мы не можем ее уменьшить
    (если, конечно, Эйнштейн был прав). Влияние фиксированной латенсии становится
    еще более ощутимым (в отрицательную сторону), когда мы решаем установить или
    закрыть соединение. В случае гигабитных сетей на некоторые сетевые проблемы
    приходится взглянуть с другой точки зрения.

    Опция масштабирования окна

    Опция масштабирования окна увеличивает определение окна TCP с 16 до 32
    бит. Вместо того чтобы изменять TCP заголовок, для того чтобы поместить в него
    окно большего размера, заголовок все так же содержит 16-битное значение, а опция
    определяет операцию масштабирования этого 16-битного значения. После чего TCP
    использует “реальный” размер окна внутри себя как 32-битное значение.

    Мы видели пример использования этой опции на рисунке 18.20. 1-байтовый
    сдвиговый счетчик находится в диапазоне от 0 (нет масштабирования) до 14.
    Максимальное значение равное 14 соответствует окну размером 1.073.725.440 байт
    (65535 x 214).

    Эта опция может появиться только в сегменте SYN; таким образом,
    коэффициент масштабирования определяется в каждом направлении при установлении
    соединения. Чтобы включить масштабирование окна, оба конца должны активизировать
    опцию в своих сегментах SYN. Сторона, осуществляющая активное открытие, посылает
    опцию в своем SYN, однако сторона, осуществляющая пассивное открытие, может
    послать опцию, только если эта опция установлена в полученном SYN. Коэффициент
    масштабирования может быть различен для каждого направления.

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

    Требования к хостам Host Requirements RFC требуют, чтобы TCP принимал эту опцию в
    любом сегменте. (Единственная заранее определенная опция, максимальный размер
    сегмента, может появиться только в сегментах SYN.) Также этот документ требует,
    чтобы TCP игнорировал любые опции, которые он не понимает. Это легко
    осуществимо, так как все новые опции имеют поле длины (рисунок
    18.20).

    Представьте, что мы используем опцию масштабирования окна со сдвиговым
    счетчиком равным S для отправки и со сдвиговым счетчиком равным R для приема. В
    этом случае каждые 16 бит объявленного окна, которые мы получаем от удаленного
    конца, сдвигаются влево на R бит, чтобы получить реальный размер объявленного
    окна. Каждый раз, когда мы отправляем объявление окна на удаленный конец, мы
    берем реальный 32-битный размер окна, сдвигаем его вправо на S бит, помещаем
    получившийся результат (16-битное значение) в TCP заголовок.

    TCP автоматически выбирает значение сдвигового счетчика, основываясь
    на размере приемного буфера. Размер приемного буфера
    устанавливается системой, однако приложению дается возможность изменить его. (Мы
    обсудили приемный буфер в разделе “Размер окна” главы
    20.)

    Пример

    Если мы инициируем соединение с использованием программы sock с хоста 4.4BSD
    vangogh.cs.berkeley.edu, то можем увидеть, как TCP рассчитывает коэффициент
    масштабирования окна. Приведенный ниже интерактивный вывод показывает два
    последовательных запуска программы, причем в первом случае устанавливается
    приемный буфер размером 128000 байт, а во втором приемный буфер установлен в
    220000 байт:

    vangogh % sock -v -R128000 bsdi.tuc.noao.edu echo
    SO_RCVBUF =
    128000
    connected on 128.32.130.2.4107 to 140.252.13.35.7
    TCP_MAXSEG =
    512
    hello, world                          вводим эту строку
    hello,
    world                           здесь она отражена эхом
    ^D
                                       вводим символ конца файла, чтобы закрыть
    соединение

    vangogh % sock -v -R220000 bsdi.tuc.noao.edu
    echo

    SO_RCVBUF = 220000
    connected on 128.32.130.2.4108 to
    140.252.13.35.7
    TCP_MAXSEG = 512
    bye, bye
                                 вводим эту строку
    bye, bye
                                  здесь она отражена эхом
    ^D
                                       вводим символ конца файла, чтобы закрыть
    соединение

    На рисунке 24.7 показан вывод команды tcpdump для этих двух
    соединений. (Мы удалили последние 8 строк для второго соединения, потому что в
    них нет ничего нового.)

    1   0.0                    vangogh.4107 > bsdi.echo: S
    462402561:462402561(0)
    win 65535

    <mss 512,nop,wscale 1,nop,nop,timestamp 995351
    0>
    2   0.003078 ( 0.0031)    bsdi.echo > vangogh.4107: S
    177032705:177032705(0)
    ack 462402562 win 4096
    <mss 512>
    3   0.300255 ( 0.2972)    vangogh.4107 > bsdi.echo: . ack
    1 win 65535

    4  16.920087 (16.6198)    vangogh.4107 > bsdi.echo: P
    1:14(13) ack 1 win 65535
    5  16.923063 ( 0.0030)    bsdi.echo >
    vangogh.4107: P 1:14(13) ack 14 win 4096
    6  17.220224 ( 0.2971)   
    vangogh.4107 > bsdi.echo: . ack 14 win 65535

    7  26.640335 ( 9.4202)   
    vangogh.4107 > bsdi.echo: F 14:14(0) ack 14 win 65535
    8  26.642688 (
    0.0024)    bsdi.echo > vangogh.4107: . ack 15 win 4096
    9  26.643964 (
    0.0013)    bsdi.echo > vangogh.4107: F 14:14(0) ack 15 win 4096
    10 
    26.880274 ( 0.2363)    vangogh.4107 > bsdi.echo: . ack 15 win
    65535

    11  44.400239 (17.5200)    vangogh.4108 > bsdi.echo: S
    468226561:468226561(0)
    win 65535

    <mss 512,nop,wscale 2,nop,nop,timestamp 995440
    0>
    12  44.403358 ( 0.0031)    bsdi.echo > vangogh.4108: S
    182792705:182792705(0)
    ack 468226562 win 4096
    <mss 512>
    13  44.700027 ( 0.2967)    vangogh.4108 > bsdi.echo: . ack
    1 win 65535
    все остальное для этого соединения
    удалено

    Рисунок 24.7 Пример опции масштабирования окна.

    В строке 1 vangogh объявляет окно размером 65535 и указывает опцию
    масштабирования окна со сдвиговым счетчиком равным 1. Это объявленное окно имеет
    максимально возможное значение, однако оно меньше чем размер приемного буфера
    (128000), потому что поле окна в сегменте SYN никогда не масштабируется.

    Коэффициент масштабирования равный 1 означает, что vangogh хочет
    объявить окна размером до 131070 (65535 x 21). Это соотносимо с
    размером приемного буфера (128000). Так как bsdi не отправлял опцию
    масштабирования окна в своем SYN (строка 2), эта опция не используется. Обратите
    внимание на то, что vangogh и дальше продолжает использовать максимально
    возможный размер окна (65535) для соединения.

    Для второго соединения vangogh устанавливает сдвиговый счетчик в
    значение 2, а это означает, что он собирается отправить объявления окна размером
    до 262140 (65535 x 22), то есть больше чем размер приемного буфера
    (220000).

    Опция временной марки

    Опция временной марки (timestamp) позволяет отправителю поместить
    значение временной марки в каждый сегмент. Получатель возвращает это значение в
    подтверждении, что позволяет отправителю рассчитать RTT при
    получении каждого ACK. (Мы должны сказать ” каждый ACK”, а не
    “каждый сегмент”, так как TCP обычно подтверждает несколько сегментов
    с помощью одного ACK.) Мы сказали, что большинство современных реализаций
    рассчитывают одно RTT на окно, что вполне достаточно, если окно содержит 8
    сегментов. Однако в случае, если окно имеет большие размеры, требуется лучший
    расчет RTT.

    Раздел 3.1 в RFC 1323 объясняет
    причины, по которым требуется лучшая оценка RTT при больших размерах окна.
    Обычно RTT измеряется с помощью сигнала данных (сегмент данных), с небольшой
    частотой (один раз на окно). Когда в окне 8 сегментов, скорость сигналов
    составляет одну восьмую скорости данных, что вполне приемлемо, однако когда в
    окне 100 сегментов, скорость сигналов составляет 1/100 от скорости данных. При
    этом RTT может быть рассчитано некорректно, что, в свою очередь, может вызвать
    повторные передачи, в которых нет необходимости. Если сегмент потерян, все
    становится еще хуже.

    На рисунке 18.20 показан формат опции временной марки. Отправитель
    помещает 32-битное значение в первое поле, а получатель отражает его эхом в поле
    отклика. TCP заголовки, содержащие эту опцию, увеличиваются с обычных 20 байт до
    32-х.

    Временная марка – монотонно увеличивающееся значение. Так как
    получатель отражает то, что он получает, его не интересуют конкретные значения
    временных марок. Эта опция не требует какой-либо формы синхронизации часов между двумя хостами. RFC 1323 рекомендует,
    чтобы значение временной марки увеличивалось на единицу в диапазоне от 1
    миллисекунды до 1 секунды.

    4.4BSD
    увеличивает временную марку каждые 500 миллисекунд, и эта временная марка
    сбрасывается в 0 при перезагрузке.

    Обратитесь к рисунку 24.7. Разница временных марок между
    сегментом 1 и сегментом 11 составляет 89 промежутков размером по 500
    миллисекунд. Что составляет 44,4 секунды.

    Эта опция устанавливается при открытии соединения таким же образом,
    как и опция масштабирования окна, которую мы рассмотрели в предыдущем разделе.
    Сторона, осуществляющая активное открытие, устанавливает опцию в своем SYN.
    Только если опция получена в SYN с удаленного конца, она может быть установлена
    в следующих сегментах.

    Мы видели, что получающий TCP не должен подтверждать каждый полученный
    сегмент данных. Если получатель отправляет ACK, подтверждающий два принятых
    сегмента данных, которая из принятых временных марок отправляются назад в поле
    эха отклика на временную марку?

    Чтобы минимизировать количество состояний, обрабатываемых на каждом
    конце, только одно единственное значение временной марки используется для
    каждого соединения. Алгоритм, по которому выбирается момент, когда необходимо
    обновить это значение, довольно прост.

    TCP всегда знает значение временной марки, которое необходимо послать
    в следующем ACK (переменная с именем tsrecent), и номер последовательности
    подтверждения последнего ACK, который был отправлен (переменная с именем
    lastack). Номер последовательности это следующий номер последовательности,
    который ожидает принять получатель. Когда прибывает сегмент, содержащий байт,
    номер которого хранится в lastack, значение временной марки из этого сегмента
    сохраняется в tsrecent. Когда бы ни была отправлена опция временной марки,
    tsrecent отправляется в поле эха отклика временной марки, а поле номера
    последовательности сохраняется в lastack.

    Этот алгоритм обрабатывает два следующих случая:

    1. Если подтверждения (ACK) задержаны получателем,
      значение временной марки, возвращаемое эхом, будет соответствовать самому
      раннему подтверждаемому сегменту. Например, если прибыло два сегмента содержащие
      байты 1-1024 и 1025-2048, оба с опцией временной марки, а получатель
      подтверждает их обоих с ACK 2049, временная марка в ACK будет иметь значение из
      первого сегмента, содержащего байты 1-1024. Это делается именно так, потому что
      отправитель должен рассчитать свой тайм-аут для повторной передачи, с учетом
      задержанных ACK.
    2. Если полученный сегмент принят в своем окне, но его номер
      последовательности не соответствует ожидаемому, можно сделать предположение, что
      предыдущий сегмент был потерян. Однако когда этот отсутствующий сегмент получен,
      именно его временная марка будет отражена эхом, а не временная марка сегмента,
      пришедшего “вне очереди”. Например, представьте себе три сегмента,
      каждый из которых содержит 1024 байта, они приняты в следующем порядке: сегмент
      1 с байтами 1-1024, сегмент 3 с байтами 2049-3072 и затем сегмент 2 с байтами
      1025-2048. Будут отправлены следующие подтверждения: ACK 1025 с временной маркой
      из сегмента 1 (обычный ACK для ожидаемых данных), ACK 1025 с временной маркой из
      сегмента 1 (дублированный ACK в ответ на сегмент, пришедший “в окне”,
      но “вне последовательности”) и затем ACK 3073 с временной маркой из
      сегмента 2 (но не с последней временной маркой из сегмента 3). В подобных
      случаях RTT может быть оценен несколько раз, что все же лучше чем неверная
      оценка RTT. Также, если последний ACK содержит временную марку из сегмента 3, он
      может включать в себя время, необходимое для возврата дублированного ACK и
      повторной передачи сегмента 2, или он может включать в себя время, выделенное
      отправителем на тайм-аут повторной передачи для сегмента 2. В обоих случаях
      отражение эхом временной марки из сегмента 3 может повлиять на расчет RTT
      отправителем.

    Помимо того, что опция временной марки позволяет лучше рассчитывать
    RTT, она также предоставляет получателю способ избежать получения старых
    сегментов и воспринятия их как части существующих сегментов данных. Это
    описывается в следующем разделе.

    PAWS: защита от перехода номеров последовательности через
    ноль

    Представим TCP соединение, использующее опцию масштабирования окна, с
    максимально возможным окном, 1 гигабайт (230). (Самое большое окно
    даже меньше чем это, 65535 x 214, а не 216 x
    214, однако это не должно влиять на наши рассуждения.) Также
    представьте, что используется опция временной марки, и что значение временной
    марки, назначенное отправителем, увеличивается на единицу для каждого
    отправляемого окна. (Это достаточно устаревший способ. Обычно значение временной
    марки увеличивается значительно быстрее.) На рисунке 24.8 показан поток данных
    между двумя хостами, возникающий при передаче 6 гигабайт. Чтобы избежать
    большого количества десятизначных цифр, мы используем запись G, что означает
    умножение на 1.073.741.824. Мы также используем форму записи из tcpdump, где J:K означает байты от J до K-1, включая байт
    K-1.

    Время

    Отправленные байты

    Отправленный номер
    последова-тельности

    Отправлен-ная временная марка

    Получение

    A

    0G:1G

    0G:1G

    1

    принято
    нормально

    B

    1G:2G

    1G:2G

    2

    принято нормально, но один
    сегмент потерян и передан повторно

    C

    2G:3G

    2G:3G

    3

    принято
    нормально

    D

    3G:4G

    3G:4G

    4

    принято
    нормально

    E

    4G:5G

    0G:1G

    5

    принято
    нормально

    F

    5G:6G

    1G:2G

    6

    принято нормально, но повторно
    переданный сегмент появился в сети
    повторно

    Рисунок 24.8 Передача 6 гигабайт в шести 1-гигабайтных
    окнах.

    32-битный номер последовательности перешел через ноль между моментами
    времени D и E. Мы предположили, что один сегмент потерялся в момент времени B и
    был передан повторно. Также мы предположили, что потерянный сегмент повторно
    появился в сети в момент времени F.

    Разница во времени между моментами, когда сегмент был потерян и
    появился повторно, меньше чем MSL; иначе сегмент должен быть
    отброшен каким-либо маршрутизатором по истечению его TTL.
    Как мы упоминали ранее, такая проблема возникает только на высокоскоростных
    соединениях, где старые сегменты могут повторно появиться и содержать номер
    последовательности, который в настоящее время передается.

    Также мы можем видеть из рисунка 24.8, что использование временной
    марки решает эту проблему. Получатель рассматривает временную марку как
    32-битное расширение к номеру последовательности. Так как потерянный сегмент,
    повторно появившийся в момент времени F, имел временную марку равную 2, что
    меньше чем самая последняя приемлемая временная марка (5 или 6), он
    отбрасывается алгоритмом PAWS.

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

    T/TCP: расширение TCP для транзакций

    TCP предоставляет транспортный сервис виртуальных
    каналов (virtual-circuit). Существуют три определенные фазы в жизни соединения:
    установление соединения, передача данных и разрыв соединения. Приложения,
    осуществляющие удаленный терминальный доступ и передачу файлов, хорошо
    приспособлены для работы с сервисом виртуальных каналов.

    Помимо этого существуют приложения, разработанные для использования
    сервиса транзакций. Транзакция это запрос от клиента, за
    которым следует отклик от сервера, со следующими характеристиками:

    Необходимо избежать лишних действий при установлении и разрыве
    соединения. Когда это возможно, необходимо отправлять один пакет с запросом и
    получать один пакет с откликом. Латенсия должна быть
    уменьшена до RTT плюс SPT, где RTT это время возврата, а SPT это время
    необходимое серверу для обработки запроса. Сервер должен определять
    дублированные запросы и не повторять транзакцию, когда прибывает дублированный
    запрос. (Другими словами, сервер не обрабатывает запрос снова. Он должен послать
    назад сохраненный отклик, соответствующий запросу.)

    Одно из рассмотренных нами приложение, использующее этот тип сервиса –
    система имен доменов (DNS, глава 14). Надо отметить, что DNS сервер
    не осуществляет повторную обработку дублированных запросов.

    В настоящее время разработчики приложений имеют выбор: TCP или UDP.
    TCP предоставляет слишком много характеристик для транзакций, а UDP – слишком
    мало. Обычно приложения строятся с использованием UDP (чтобы избежать
    перегруженности характеристиками свойственной TCP соединениям), при этом
    большинство требуемых характеристик (динамические тайм-ауты и повторные
    передачи, избежание переполнения и так далее) помещаются внутрь приложений, и
    для каждого приложения их приходится делать заново.

    Оптимальное решение – это транспортный уровень, который включает в
    себя эффективную обработку транзакций. Протокол транзакций, который мы описываем
    в этом разделе, называется T/TCP. Протокол определен в RFC 1379 [Braden 1992b] и [Braden 1992c].

    Для большинства TCP реализаций требуются 7 сегментов, чтобы открыть и
    закрыть соединение (см. рисунок 18.13). Здесь добавляются еще три сегмента: один
    с запросом, другой с откликом и подтверждением на запрос и третий с
    подтверждением на отклик. Если в сегменты добавлены дополнительные управляющие
    биты – а именно, первый сегмент содержит SYN, запрос клиента, и FIN – клиенту
    все кажется, что имеют место лишние действия, которые выражается в виде
    удвоенного значения RTT плюс SPT. (Отправка SYN вместе с данными и FIN
    разрешена; сможет ли TCP обработать подобную ситуацию корректно – это уже другой
    вопрос.)

    Еще одна проблема с TCP это состояние TIME_WAIT,
    которое требует ожидания в течение 2MSL. Как показано в упражнении 14 главы 18, это
    ограничивает скорость транзакций между двумя хостами на величине примерно 268 в
    секунду.

    Две модификации, необходимые для TCP, чтобы обрабатывать транзакции,
    заключаются в том, чтобы избежать трехразового рукопожатия и
    сократить состояние TIME_WAIT. T/TCP избегает трехразового рукопожатия с
    использованием ускоренного открытия:

    T/TCP назначает каждому соединению номер в соответствии с 32-битным
    счетчиком соединений (CC – connection count), вне зависимости от того,
    осуществляется ли активное или пассивное открытие. Значение CC хоста назначается
    из общего счетчика, который увеличивается на единицу при каждом его
    использовании. Каждый сегмент между двумя хостами, использующими T/TCP, включает
    новую TCP опцию, которая называется CC. Эта опция имеет длину 6 байт и содержит
    32-битное значение CC отправителя для соединения. Хост имеет кэш для каждого
    хоста, с которым был осуществлен обмен. В кэше содержится значение CC из
    последнего полученного от этого хоста сегмента SYN. Когда опция CC получена в
    исходном SYN, получатель сравнивает значение с сохраненным значением для этого
    отправителя. Если полученное CC больше чем кэшированное CC, SYN новый, и любые
    данные, находящиеся в сегменте, передаются принимающему приложению (серверу).
    Соединение называется наполовину синхронизированным. Если полученное CC не
    больше чем кэшированное CC, или если принимающий хост не имеет кэшированного CC
    для этого клиента, осуществляется стандартное трехразовое рукопожатие TCP. SYN,
    ACK сегмент в отклике на первоначальный SYN, отражает эхом
    полученное значение CC в другой новой опции, которая называется CC эхо или
    CCECHO. С помощью значения CC в сегментах, не содержащих SYN, определяются и
    отбрасываются любые дублированные сегменты от предыдущих воплощений того же
    самого соединения.

    Благодаря ускоренному открытию отпадает необходимость в трехразовом
    рукопожатии, за исключением того случая когда оба, и клиент, и сервер, вышли из
    строя и перезагрузились. Однако мы платим за это тем, что сервер должен помнить
    последний полученный CC от каждого клиента.

    Состояние TIME_WAIT становится короче, потому что расчет задержки
    TIME_WAIT осуществляется динамически, на основании измеренного RTT между двумя
    хостами. Задержка TIME_WAIT устанавливается в RTO умноженное
    на 8 (RTO – значение тайм-аута повторной передачи, глава 21, раздел “Определение времени
    возврата”
    ).

    С использованием этих характеристик, минимальная последовательность
    транзакций заключается в обмене тремя сегментами:

    От клиента к серверу, осуществляется при активном открытии:
    SYN-клиента, данные от клиента (запрос), FIN-клиента и CC-клиента. TCP сервер,
    осуществляющий пассивное открытие, получает эти сегменты и если CC-клиента
    больше чем кэшированный CC для этого клиента, данные клиента передаются
    приложению сервера, которое обрабатывает запрос. От сервера к клиенту:
    SYN-сервера, данные сервера (отклик), FIN-сервера, подтверждение на FIN-клиента,
    CC-сервера и CCECHO на CC-клиента. Так как подтверждения TCP – обобщающие, ACK
    на FIN-клиента подтверждает SYN-клиента, данные и FIN. Когда TCP клиент получает
    этот сегмент, он передает отклик приложению клиента. От клиента к серверу: ACK
    на FIN-сервера, который подтверждает SYN-сервера, данные и FIN.

    Время отклика клиента на его запрос составляет RTT плюс SPT.

    В реализации этой TCP опции существует множество особенностей, которые
    мы кратко рассмотрим:

    • ACK на SYN сервера (второй сегмент) должен быть задержан, чтобы
      позволить отклику передаваться вместе с ним. (Обычно ACK на SYN не
      задерживается.) Он не может быть задержан надолго, иначе клиент отработает
      тайм-аут и осуществит повторную передачу.
    • Запрос может состоять из нескольких сегментов, однако сервер должен
      предусмотреть вариант, когда данные приходят в беспорядке. (Обычно, когда данные
      прибывают перед SYN, они отбрасываются и генерируется сброс. В случае T/TCP
      данные, прибывшие в беспорядке, должны быть поставлены в очередь.)
    • API должен позволять процессу сервера отправлять данные и закрывать
      соединение с помощью одной операции, что позволит FIN во втором сегменте
      передаваться вместе с откликом. (Обычно приложение отправляет отклик, что
      вызывает отправку сегменту данных, а затем закрывает соединение, отправляя FIN.)

    • Клиент отправляет данные в первом сегменте перед получением
      объявления MSS от сервера. Чтобы не ограничивать клиента
      значением MSS равным 536, MSS для данного хоста должно быть кэшировано вместе с
      его значением CC.
    • Клиент отправляет данные серверу без получения объявления окна от
      сервера. T/TCP предоставляет окно по умолчанию равное 4096 байтам, а также
      кэширует порог переполнения для сервера.
    • В случае минимального обмена тремя сегментами может быть измерен
      только один RTT для каждого направления. RTT, измеренный клиентом, включает
      время, необходимое серверу для обработки запроса. Это означает, что хэшированное
      значение RTT и его отклонение также должны быть кэшированы для сервера; это
      напоминает то, что мы описали в разделе “Показатели на
      маршрут”
      главы 21.

    Одна из основных особенностей T/TCP заключается в том, что для его
    реализации требуется минимальный набор изменений к существующему протоколу, что,
    в свою очередь, обеспечивает совместимость с более ранними версиями существующих
    реализаций. Он также использует все преимущества существующих характеристик TCP
    (динамические тайм-ауты и повторные передачи, предотвращение переполнения и так
    далее), вместо того чтобы заставлять приложения заботиться об этом.

    Альтернативный протокол транзакций – VMTP, Versatile Message
    Transaction Protocol. Он описан в RFC 1045 [Cheriton 1988].
    В отличие от T/TCP, который вносит небольшое количество расширений в
    существующий протокол, VMTP это транспортный уровень в целом, который использует
    IP. VMTP занимается определением ошибок, повторными передачами и предотвращением
    дублирования. Он также поддерживает групповые способы рассылки.

    Производительность TCP

    Цифры, которые публиковались в середине 80-х годов, показывали
    пропускную способность TCP по Ethernet где-то в районе 100000-200000 байт в
    секунду. (В разделе 17.5 [Stevens 1990] приводятся эти цифры.) С того времени
    многое изменилось. Современное аппаратное обеспечение (рабочие станции и быстрые
    персональные компьютеры) обеспечивает передачу 800000 байт в секунду и больше.

    Стоит наверное рассчитать максимальную теоретически возможную
    пропускную способность, которую мы можем получить с TCP на Ethernet 10 Мбит/сек
    [Warnock 1991]. На рисунке 24.9 показаны данные, необходимые
    для подобного расчета. На этом рисунке показано полное количество байт,
    необходимое при обмене сегментами данных полного размера, и ACK.

    Поле

    Количество байт данных

    Количество байт
    подтверждения

    преамбула
    Ethernet

    8

    8

    адрес назначения
    Ethernet

    6

    6

    адрес источника
    Ethernet

    6

    6

    поле типа
    Ethernet

    2

    2

    заголовок
    IP

    20

    20

    заголовок
    TCP

    20

    20

    пользовательские
    данные

    1460

    заполнение (до минимального
    размера Ethernet)

    6

    контрольная сумма Ethernet

    4

    4

    промежуток между пакетами (9,6
    микросекунды)

    12

    12

    всего

    1538

    84

    Рисунок 24.9 Размеры полей для Ethernet при расчете максимальной
    теоретически возможной пропускной способности.

    Мы должны сделать расчет для всех составляющих: преамбула, байты
    заполнения, которые добавляются к подтверждению, контрольная
    сумма, и минимальный промежуток между пакетами (9,6 микросекунды, что равно 12
    байтам при скорости 10 Мбит/сек).

    Во-первых, мы предположим, что отправитель передает два полноразмерных
    сегмента данных, после чего получатель отправляет ACK на эти два сегмента.
    Максимальная пропускная (throughput) способность (для пользовательских данных)
    будет равна

    throughput = [(2 x 1460 байт)/(2 x 1538 84 байта)] x [(10.000.000
    бит/сек)/(8 бит/байт)] = 1.155.063 байт/сек

    Если окно TCP открыто на его максимальный размер (65535, опция
    масштабирования окна не используется), это позволяет отправить окно размером в
    44 сегмента, каждый из которых размером 1460 байт. Если получатель отправляет
    ACK на каждый 22-й сегмент, расчет будет следующим

    throughput = [(22 x 1460 байт)/(22 x 1538 84 байта)] x [(10.000.000
    бит/сек)/(8 бит/байт)] = 1.183.667 байт/сек

    Это теоретический предел, при расчете которого сделаны некоторые
    допущения: не произойдет коллизии (столкновения) между ACK, отправленным
    получателем, и одним из сегментов отправителя; отправитель может передать два
    сегмента с минимальным промежутком Ethernet; и получатель может сгенерировать
    ACK внутри минимального промежутка Ethernet. Несмотря на оптимизм, который так и
    пышет из этих цифр, [Warnock 1991] приводит измеренную
    скорость равную 1.075.000 байт/сек по Ethernet, со стандартной
    многопользовательской рабочей станцией (быстрая рабочая станция), что составляет
    примерно 90% от теоретического значения.

    Что касается более быстрых сетей, таких как FDDI
    (100 Мбит/сек), [Schryver 1993] рассказывает, что три
    поставщика демонстрировали TCP поверх FDDI со скоростью в диапазоне от 80 до 98
    Мбит/сек. В случае, когда доступна большая ширина полосы пропускания, [Borman 1992] сообщает, что было получено до 781 Мбит/сек между
    двумя компьютерами Cray Y-MP по каналу HIPPI 800 Мбит/сек, и
    907 Мбит/сек между двумя процессами с использованием loopback интерфейса на компьютере Cray Y-MP.

    Следующие практические ограничения применимы для всех реальных
    сценариев [Borman 1991].

    1. Вы не можете запустить что-либо быстрее, чем скорость самого
      медленного канала.
    2. Вы не можете двигаться быстрее, чем ширина пропускания памяти в самой
      медленной машине. Здесь подразумевается, что ваша реализация обрабатывает данные
      за один раз. Если нет (то есть, ваша реализация делает один шаг, чтобы
      скопировать их из пространства пользователя в ядро, затем еще один, чтобы
      рассчитать TCP контрольную сумму), у вас все будет работать еще медленнее. [Dalton et al. 1993] описывает способы улучшения
      производительности стандартной Berkeley реализации, когда количество шагов было
      уменьшено до одного копирования. [Partridge and Pink 1993] применяет то же улучшение “копирование и контрольная
      сумма” для UDP, вместе с улучшением производительности, что повышает
      производительность UDP примерно на 30%.
    3. Вы не можете двигаться быстрее, чем размер окна, предложенный
      получателем, поделенный на время возврата. (Это наше
      уравнение емкости канала, где размер окна используется как емкость канала и
      используется для расчета ширины полосы.) Если мы используем максимальный
      коэффициент масштабирования окна равный 14 из раздела “Опция масштабирования окна”, то
      получим размер окна равный 1,073 Гбайта; эта величина, поделенная на RTT, будет
      являться пределом ширины полосы пропускания.

    Смысл всех приведенных выше цифр заключается в том, что реальный
    верхний предел того, насколько быстро может работать TCP, определяется размером
    TCP окна и скоростью света. В заключение, можно, ссылаясь на [Partridge and Pink 1993], заявить, что большинство проблем с
    производительностью протокола заключается в основном в реализациях, а не во
    внутренних или наследуемых ограничениях самого протокола.

    Краткие выводы

    В этой главе рассмотрено пять новых характеристик TCP: определение
    транспортного MTU, опция масштабирования окна, опция временной марки, защита от
    перехода через 0 номера последовательности и улучшенная обработка транзакций с
    использованием TCP. Мы видели, что три средние опции требуются для оптимальной
    производительности в каналах с повышенной пропускной способностью – в сетях с
    большой емкостью.

    Определение транспортного MTU позволяет TCP использовать окна больше,
    чем окно по умолчанию равное 536, для нелокальных соединений, когда транспортный
    MTU больше. Это может улучшить производительность.

    Опция масштабирования окна воспринимает максимальный размер TCP окна
    от 65535 байт до сверх 1 гигабайта. Опция временной марки позволяет аккуратно
    засечь время для нескольких сегментов и также позволяет получателю предоставить
    защиту против перехода номера последовательности через 0 (PAWS). Это очень важно
    для высокоскоростных соединений. Эти новые опции TCP обсуждаются хостами при
    установлении соединения и игнорируются более старыми системами, которые не
    понимают их, позволяя более новым системам нормально функционировать с более
    старыми системами.

    Расширение TCP для транзакций, T/TCP, позволяет ограничить общение
    клиент-сервер запрос-отклик всего тремя сегментами. Это позволяет избежать
    трехразового рукопожатия и уменьшить продолжительность состояния TIME_WAIT с помощью кэширования небольшого количества
    информации для каждого хоста, с которым установлено соединение. При этом флаги
    SYN и FIN передаются в сегментах данных.

    Мы завершили главу рассмотрением производительности TCP, так как до
    сих пор существует огромное количество “фольклора”, который далек от
    истины, о том, как может и насколько быстро должен работать TCP. Для хорошо
    настроенных и отлаженных реализаций, которые используют более новые
    характеристики, описанные в этой главе, производительность TCP ограничивается
    только максимальным окном размером в 1 гигабайт и скоростью света (то есть,
    временем возврата).

    Упражнения

    1. Что означает, если система отправляет первоначальный
      сегмент SYN с коэффициентом масштабирования окна равным 0?
    2. Если хост bsdi на рисунке 24.7 поддерживает опцию масштабирования окна,
      какое ожидаемое значение 16-битного поля размера окна в TCP заголовке от хоста
      vangogh в сегменте 3? Точно так же, если опция используется на втором соединении
      на этом рисунке, какое будет объявленное окно в сегменте 13?
    3. Вместо фиксированного коэффициента масштабирования окна, который
      используется при установлении соединения, может ли опция масштабирования окна
      быть определена, а затем изменен коэффициент масштабирования?
    4. При какой скорости передачи данных переход через 0 номера последовательности
      станет реальной проблемой, в том случае, если MSL равно 2
      минутам?
    5. PAWS разработан для того, чтобы работать только с одним соединением. Какие
      модификации должны быть сделаны для TCP, чтобы использовать PAWS как замену для
      ожидания 2MSL (состояние TIME_WAIT)?
    6. В примере в конце раздела “Опция
      масштабирования окна”
      , почему программа sock выдала
      размер приемного буфера перед следующей строкой (с IP
      адресами и номерами портов)?
    7. Повторите расчет пропускной способности из раздела “Производительность TCP”, представив
      себе, что MSS равно 1024.
    8. Как опция временной марки влияет на алгоритм Карна
      (глава 21, раздел “Определение времени
      возврата”
      )?
    9. Если TCP отправляет данные с сегментом SYN, который сгенерирован при
      активном открытии (без использования расширений, которые мы описали в разделе “T/TCP: расширение TCP для
      транзакций”
      ), что получающий TCP будет делать с данными?
    10. В разделе “T/TCP: расширение TCP для
      транзакций”
      мы сказали, что без расширения T/TCP, даже если активное
      открытие посылается с данными и FIN, задержка для клиента при получении отклика
      от сервера будет все еще удвоенное RTT плюс SPT. Покажите сегменты, с помощью
      которых это можно рассчитать.
    11. Сделайте повторно упражнение 14 главы 18,
      представив себе, что поддерживается расширение T/TCP и что минимальное RTO, поддерживаемое Berkeley системами, равно половине секунды.

    12. Если мы применили T/TCP и рассчитали время транзакции между двумя хостами, с
      чем мы можем это сравнить, чтобы определить его эффективность?

    §

    Введение

    UDP простой, ориентированный на передачу
    датаграмм, протокол транспортного уровня: за один раз процесс выдает одну UDP
    датаграмму, в результате чего передается одна IP датаграмма. Это отличается от
    поток-ориентированных протоколов, таких как TCP, где количество данных, которое
    выдается приложением, практически не имеет отношения к количеству отправленных
    IP датаграмм.

    На рисунке 11.1 показана инкапсуляция UDP датаграммы в IP
    датаграмму.

    Address resolution protocol - что это?

    Рисунок 11.1 UDP инкапсуляция.

    Официальная спецификация UDP приведена в RFC 768 [Postel 1980].

    UDP является ненадежным протоколом: он отправляет датаграммы, которые
    приложение пишет в IP уровень, однако не существует гарантии того, что они
    достигнут конечного пункта назначения. С точки зрения надежности может
    возникнуть предположение, что стоит избегать использовать UDP и всегда
    пользоваться надежными протоколами, такими как TCP. После того как TCP будет
    описан в главе 17, мы вернемся к этой теме и
    посмотрим, какие типы приложений могут использовать UDP.

    Приложениям нет необходимости беспокоиться о размере получившейся в
    результате IP датаграммы. Если она по размеру больше, чем MTU для данной сети
    (см. главу 2, раздел “MTU”), IP датаграмма
    будет фрагментирована. Это применимо к каждой сети, через которую пройдет
    датаграмма по пути от источника до пункта назначения, кроме первой сети, к
    которой подключен посылающий хост. (Мы определили понятие транспортного MTU в
    разделе “Транспортный MTU”
    главы 2.) Более подробно IP фрагментация будет рассмотрена в разделе “Фрагментация IP” этой
    главы.

    UDP заголовок

    На рисунке 11.2 показаны поля, присутствующие в UDP заголовке.

    Address resolution protocol - что это?

    Рисунок 11.2 UDP заголовок.

    Номера портов (port numbers) указывают на
    отправляющий и принимающий процессы. На рисунке 1.8 показано, что TCP и
    UDP используют номер порта назначения для демультиплексирования данных,
    поступающих от IP. Так как IP осуществляет демультиплексирование входящих IP
    датаграмм для TCP и UDP (с использованием значения протокола в IP заголовке),
    TCP просматривает номера портов TCP, а UDP – номера портов UDP. Номера портов
    TCP независимы от номеров портов UDP.

    Несмотря на подобную независимость,
    если зарезервированный сервис предоставляется обоими TCP и UDP, обычно
    выбирается один и тот же номер порта для обоих транспортных уровней. Это сделано
    для удобства, а не по требованию протоколов.

    Поле длины UDP содержит длину в байтах UDP
    заголовка и UDP данных. Минимальное значение для этого поля составляет 8 байт.
    (Не произойдет ничего страшного, если будет отправлена UDP датаграмма с нулевой
    длиной данных.) Параметр UDP длины – избыточный. IP датаграмма содержит свою
    полную длину в байтах, поэтому длина UDP датаграммы это полная длина минус длина IP заголовка (которая указана в поле
    длины заголовка на рисунке 3.1).

    Контрольная сумма UDP

    Контрольная сумма UDP охватывает UDP заголовок и
    UDP данные. Вспомним, что контрольная сумма в IP заголовке охватывает только IP
    заголовок – она не охватывает данные, находящиеся в IP датаграмме. И UDP, и TCP
    содержат контрольные суммы в своих заголовках, которые охватывают как заголовок,
    так и данные. Для UDP контрольная сумма необязательна, но для TCP она
    обязательна.

    Контрольная сумма UDP рассчитывается точно так же, как (глава 3,
    раздел “IP заголовок”)
    контрольная сумма IP заголовка (сумма 16-битных слов с переполнением), хотя
    существуют и отличия. Во-первых, UDP датаграмма может состоять из нечетного
    количества байт, тогда как при расчете контрольной суммы складываются 16-битные
    слова. При этом в конец датаграммы добавляются нулевые байты заполнения, если
    это необходимо для расчета контрольной суммы. (Байты заполнения не передаются.)

    В UDP и TCP существуют 12-байтовые псевдозаголовки
    (в UDP датаграммах и TCP сегментах) только для расчета контрольной суммы.
    Псевдозаголовки содержат в себе определенные поля из IP заголовка. Все это
    сделано для двойной проверки того, что данные достигли того пункта назначения,
    которому предназначались (IP не принимает датаграммы, которые не адресованы для
    данного хоста, и не сможет передать UDP датаграммы, предназначенные для другого
    верхнего уровня). На рисунке 11.3 показан псевдозаголовок и
    UDP датаграмма.

    Address resolution protocol - что это?

    Рисунок 11.3 Поля, используемые для расчета контрольной
    суммы UDP.

    На этом рисунке мы специально показали датаграмму с нечетной длиной, в
    этом случае требуется дополнительный байт для расчета контрольной суммы.
    Обратите внимание на то, что длина UDP датаграммы, при расчете контрольной
    суммы, появляется дважды.

    Если рассчитанная контрольная сумма равна 0, она хранится как все
    единичные биты (65535), эти значения эквивалентны в арифметике с поразрядным
    дополнением (дополнение единицы) (ones-complement). Если переданная контрольная
    сумма равна 0, это означает, что отправитель не рассчитал контрольную сумму.

    Если отправитель все же рассчитал контрольную сумму, а получатель
    определил наличие ошибки, UDP датаграмма молча уничтожается, сообщение об ошибке
    не генерируется. (То же самое происходит, если IP уровень обнаружил ошибку в
    контрольной сумме IP заголовка.)

    Контрольная сумма UDP рассчитывается отправителем
    и проверяется получателем. Она позволяет определить любые изменения в UDP
    заголовке или данных, которые произошли на пути между отправителем и
    получателем.

    Несмотря на то, что для контрольная сумма UDP – необязательный
    параметр, она должна рассчитываться всегда. В конце 1980-х годов некоторые
    производители компьютеров стали по умолчанию отключать расчет контрольной суммы
    UDP, чтобы увеличить скорость работы сетевой файловой системы (NFS – Network
    File System), которая использует UDP. Это может быть допустимым в одной
    локальной сети, где рассчитывается циклический избыточный код для фреймов на
    канальном уровне (фреймы Ethernet или Token ring), с помощью которого можно
    определить повреждение фрейма, когда датаграмма проходит через маршрутизаторы.
    Поверите Вы или нет, но существуют маршрутизаторы, у которых есть ошибки в
    программном или аппаратном обеспечении и которые изменяют биты в датаграммах,
    которые они маршрутизируют. Эти ошибки не могут быть выявлены в UDP датаграммах,
    если отключена опция контрольной суммы. Также необходимо отметить, что некоторые
    протоколы канального уровня (например, SLIP) не имеют каких-либо форм расчета
    контрольной суммы для данных в канале.

    Требования к хостам Host Requirements RFC требуют, чтобы расчет контрольной суммы
    UDP был включен по умолчанию. Также они требуют, чтобы принятая контрольная
    сумма обязательно проверялась, если ее рассчитал отправитель (в том случае, если
    принятая контрольная сумма не нулевая). Некоторые реализации игнорируют это и
    проверяют принятую контрольную сумму в том случае, если включена опция расчета
    исходящей контрольной суммы.

    Вывод команды tcpdump

    Довольно сложно определить, включена ли опция расчета контрольной суммы UDP на конкретной системе. Обычно приложение
    не имеет доступа к полю контрольной суммы принятого UDP заголовка. Чтобы решить
    эту проблему, автор добавил еще одну опцию к программе tcpdump, после чего та стала выдавать полученные контрольные
    суммы UDP. Если полученное значение равно 0, это означает, что отправитель не
    рассчитал контрольную сумму.

    На рисунке 11.4 показан вывод к трем системам и от тех же трех
    различных систем в нашей сети. Мы запустили программу sock
    (приложение
    С
    ), послав единственную UDP датаграмму с 9 байтами данных на стандартный эхо
    сервер.

    >

    1   0.0                     sun.1900 > gemini.echo: udp 9 (UDP
    cksum=6e90)
    2   0.303755 ( 0.3038)     gemini.echo > sun.1900: udp 9 (UDP
    cksum=0)

    3  17.392480 (17.0887)     sun.1904 > aix.echo: udp 9 (UDP
    cksum=6e3b)
    4  17.614371 ( 0.2219)     aix.echo > sun.1904: udp 9 (UDP
    cksum=6e3b)

    5  32.092454 (14.4781)     sun.1907 > solaris.echo: udp 9
    (UDP cksum=6e74)
    6  32.314378 ( 0.2219)     solaris.echo > sun.1907: udp 9
    (UDP cksum=6e74)

    Рисунок 11.4 Вывод tcpdump, с помощью которого можно определить,
    включена ли опция контрольной суммы UDP на конкретном хосте.

    Отсюда мы видим, что у двух из трех систем опция контрольной суммы UDP
    включена.

    Также обратите внимание на то, что исходящая датаграмма имеет такую же
    контрольную сумму, как и входящая датаграмма (строки 3 и 4, 5 и 6). На
    рисунке 11.3, можно заметить, что два IP адреса поменяны местами, так же, как и
    два номера порта. Другие поля в псевдозаголовке и заголовке UDP остались те же,
    так как данные были отражены эхом. Это подтверждает, что контрольная сумма UDP
    (а в действительности, и все контрольные суммы в семействе протоколов TCP/IP)
    это простая 16-битовая сумма. С ее помощью невозможно определить ошибку, которая
    заключается в перемене мест двух 16-битных значений.

    Автор также направил DNS запрос на
    каждый из восьми корневых серверов DNS, описанных в разделе “Основы DNS” главы 14.
    DNS использует UDP, и только на двух из восьми была включена опция расчета
    контрольной суммы UDP!

    Немного статистики

    [Mogul 1992] предоставляет некоторую
    статистическую информацию о появлении ошибок контрольных
    сумм на довольно загруженном NFS сервере, который работал в течение 40 дней. На
    рисунке 11.5 приведены статистические данные.

    Уровень

    Количество ошибок в контрольных
    суммах

    Приблизительное количество
    пакетов

    Ethernet

    446

    170.000.000

    IP

    14

    170.000.000

    UDP

    5

    140.000.000

    TCP

    350

    30.000.000

    Рисунок 11.5 Статистика поврежденных пакетов, определенных с
    использованием контрольных сумм.

    В последней колонке приведено приблизительное количество пакетов, так
    как и другие протоколы используют Ethernet и IP уровень. Например, не все
    Ethernet фреймы используются IP датаграммами, ARP также использует Ethernet. Не
    все IP датаграммы используются UDP или TCP, так как ICMP также использует IP.

    Обратите внимание на то, что выявлено значительно больше ошибок контрольных сумм TCP, чем UDP. Это скорее всего вызвано тем,
    что с помощью TCP обычно устанавливаются “дальние” соединения (которые
    проходят через много маршрутизаторов, мостов и так далее), тогда как UDP траффик
    обычно локальный.

    Поэтому ошибки, приведенные в нижней строке, не всегда имеют отношение
    к канальному уровню (Ethernet, Token ring). При передаче данных следует всегда
    включать опцию контрольную сумму в оконечных точках. Однако,
    если передаваемые данные представляют определенную ценность, не стоит полностью
    доверять контрольным суммам UDP или TCP, так как это простые
    контрольные суммы, и они не гарантируют, что защитят данные от всех возможных
    ошибок.

    Простой пример

    Мы воспользуемся программой sock, чтобы сгенерировать несколько UDP
    датаграмм, которые мы просмотрим с использованием tcpdump:

    >bsdi % sock -v -u -i -n4 svr4
    discard
    connected on 140.252.13.35.1108 to 140.252.13.34.9

    bsdi %
    sock -v -u -i -n4 -w0 svr4 discard
    connected on 140.252.13.35.1110 to
    140.252.13.34.9 

    В первом случае запуска программы установлен отладочный режим (-v), при этом можно просмотреть номера динамически назначаемых
    портов, указан UDP (-u) вместо TCP по умолчанию и установлен
    режим источника, опция (-i) , это означает, что мы будем
    посылать данные, а не будем читать из стандартного ввода или писать в
    стандартный вывод. Опция -n4 сообщает о необходимости выдать
    4 датаграммы (вместо того, чтобы выдавать по умолчанию 1024) на хост назначения
    svr4. Сервис discard описан в разделе “Стандартные простые
    сервисы”
    главы 1. Мы используем размер вывода по умолчанию в 1024 байта
    на одну запись.

    Второй раз мы запустили программу, указав -w0, при
    этом будут выдаваться датаграммы нулевой длины. На рисунке 11.6 показан вывод
    команды tcpdump для двух примеров.

    >

    1   0.0                     bsdi.1108 > svr4.discard: udp 1024
    2  
    0.002424 ( 0.0024)     bsdi.1108 > svr4.discard: udp 1024
    3   0.006210 (
    0.0038)     bsdi.1108 > svr4.discard: udp 1024
    4   0.010276 ( 0.0041)    
    bsdi.1108 > svr4.discard: udp 1024

    5  41.720224 (41.7098)    
    bsdi.1110 > svr4.discard: udp 0
    6  41.721072 ( 0.0010)     bsdi.1110 >
    svr4.discard: udp 0
    7  41.722094 ( 0.0010)     bsdi.1110 > svr4.discard:
    udp 0
    8  41.723070 ( 0.0010)     bsdi.1110 > svr4.discard: udp

    Рисунок 11.6 Вывод команды tcpdump для случая, когда UDP датаграммы
    посылаются в одном направлении.

    В выводе показаны четыре датаграммы размером 1024 байта, за которыми
    следуют четыре датаграммы нулевой длинны. Каждая датаграмма следует за
    предыдущей с интервалом в несколько миллисекунд. (Для того чтобы ввести вторую
    команду, потребовалась 41 секунда.)

    До того как была отправлена первая датаграмма, соединения между
    отправителем и получателем не существовало. (В главе 17, где рассказывается о TCP, мы
    покажем, что перед тем как будет отправлен первый байт данных, должно быть
    установлено соединение.) Необходимо отметить, что получатель не выдает
    подтверждение, когда получает данные. Отправитель, в этом примере, не имеет
    представления о том, получены ли данные на удаленном конце.

    И в завершение, обратите внимание, что номер порта источника UDP
    меняется каждый раз при запуске программы. Сначала порт был 1108, затем 1110. В
    разделе “Номера портов” главы
    1 мы показали, что номера динамически назначаемых портов, используемых
    клиентами, обычно находятся в диапазоне от 1024 до 5000.

    Фрагментация IP

    Как мы указали в разделе “MTU” главы 2,
    физический сетевой уровень обычно имеет ограничение максимального размера
    фрейма, который может быть передан. Когда IP уровень получает IP датаграмму,
    которую необходимо отправить, он определяет, на какой локальнй интерфейс
    отправляется датаграмма (или маршрутизируется), и запрашивает интерфейс, чтобы
    тот сообщил размер своего MTU. IP сравнивает MTU с размером датаграммы и, если
    необходимо, осуществляет фрагментацию. Фрагментация может быть осуществлена как
    на отправляющем хосте, так и на промежуточном маршрутизаторе.

    Когда IP датаграмма фрагментирована, она не собирается вновь до тех
    пор, пока не достигнет конечного пункта назначения. (Для некоторых других
    сетевых протоколов процесс повторной сборки отличается от описанного выше, при
    этом повторная сборка осуществляется на маршрутизаторе
    следующей пересылки, а не в конечном пункте назначения.) На уровне IP сборка
    осуществляется в конечном пункте назначения. Это сделано для того, чтобы сделать
    фрагментацию и повторную сборку прозрачной для транспортных уровней (TCP и UDP),
    хотя это может вести к некоторой потере производительности. Существует
    вероятность, что фрагмент датаграммы будет снова фрагментирован (возможно даже
    несколько раз). Информации, которая содержится в IP заголовке вполне достаточно
    для фрагментации и повторной сборки.

    Вернемся снова к IP заголовку (см. рисунок 3.1) и
    рассмотрим, какие поля используются в процессе фрагментации. Поле идентификации содержит значение уникальное для каждой
    отправленной IP датаграммы. Это значение копируется в каждый фрагмент конкретной
    датаграммы. В поле флагов один бит означает, что “дальше следуют еще
    фрагменты” (more fragments). Этот бит устанавливается в единицу для каждого
    фрагмента, кроме последнего. Поле смещения фрагмента
    (fragment offset) содержит смещение этого фрагмента от начала исходной
    датаграммы. Когда датаграмма фрагментируется, поле полной
    длины каждого фрагмента изменяется, так чтобы соответствовать размеру фрагмента.

    И в заключение, один из битов в поле флагов называется “не фрагментировать” (don’t fragment). Если этот бит
    установлен в единицу, IP не будет фрагментировать датаграмму. Вместо этого
    датаграмма уничтожается, а отправителю посылается ICMP ошибка “фрагментация необходима, однако установлен бит не
    фрагментировать” (fragmentation needed but don’t fragment bit set). Мы
    рассмотрим эту ошибку более подробно в следующем разделе.

    Когда IP датаграмма фрагментируется, каждый фрагмент становится
    пакетом, с собственным IP заголовком, и маршрутизируется независимо от других
    пакетов. Поэтому становится возможным, что датаграммы прибудет в конечный пункт
    назначения в другом порядке, нежели они были исходно отправлены и
    фрагментированы. Однако, в IP заголовке хранится достаточно информации для того,
    чтобы датаграмма была собрана корректно.

    Несмотря на то, что процесс фрагментации IP выглядит довольно
    прозрачным, существует одна особенность, которая делает его не всегда
    желательным: если один фрагмент потерялся, датаграмма должна быть целиком повторно передана. Это объясняется тем, что IP не имеет
    тайм-аутов и не осуществляет повторной передачи – за это несут ответственность
    верхние уровни. (TCP осуществляет тайм-аут и повторную передачу, UDP – нет.
    Некоторые UDP приложения осуществляют тайм-аут и повторную передачу
    самостоятельно.) Когда потерялся фрагмент из TCP сегмента, TCP отработает
    тайм-аут и повторно передаст TCP сегмент целиком (IP датаграмма). Не существует
    способа повторно передать только один фрагмент датаграммы. И действительно, если
    фрагментация была осуществлена промежуточным маршрутизатором, а не отправляющей
    системой, отправляющая система не сможет знать как датаграмма была
    фрагментирована в процессе передачи. Именно по этой причине (всего одной)
    фрагментации стараются избежать. [Kent and Mogul 1987] приводят аргументы, которые доказывают необходимость избегать фрагментации.

    С использованием UDP довольно легко добиться IP фрагментации. (Позже
    мы увидим, что TCP старается избежать фрагментации, и для приложения практически
    невозможно заставить TCP отправлять сегменты, которые нуждаются в фрагментации.
    То есть, другими словами, отправить сегмент такого размера, для которого
    потребуется фрагментация, практически невозможно.) Мы воспользуемся программой
    sock, чтобы увеличить размер датаграммы, с таким расчетом,
    чтобы была осуществлена фрагментация. Максимальный размер данных во фрейме
    Ethernet составляет 1500 байт (рисунок 2.1), при этом 1472
    байта предназначены для пользовательских данных, 20 байт отводится под IP
    заголовок и 8 байт под UDP заголовок. Запустим программу sock с размером данных
    1471, 1472, 1473 и 1474 байта. Мы ожидаем, что в двух последних случаях
    произойдет фрагментация:

    >bsdi % sock -u -i -n1 -w1471 svr4
    discard
    bsdi % sock -u -i -n1 -w1472 svr4 discard
    bsdi %
    sock -u -i -n1 -w1473 svr4 discard
    bsdi % sock -u -i -n1 -w1474
    svr4 discard
     

    На рисунке 11.7 показан соответствующий вывод команды tcpdump.

    >

    1   0.0                        bsdi.1112 > svr4.discard: udp 1471

    21.008303 (21.0083)        bsdi.1114 > svr4.discard: udp 1472


    50.449704 (29.4414)        bsdi.1116 > svr4.discard: udp 1473 (frag
    26304:1480@0 )
    4  50.450040 ( 0.0003)        bsdi > svr4: (frag
    26304:1@1480)

    5  75.328650 (24.8786)        bsdi.1118 > svr4.discard:
    udp 1474 (frag 26313:1480@0 )
    6  75.328982 ( 0.0003)        bsdi > svr4:
    (frag 26313:2@1480)

    Рисунок 11.7 Наблюдения за фрагментацией UDP датаграмм.

    Первые две UDP датаграммы (строки 1 и 2) помещаются в Ethernet фреймы
    и не фрагментируются. Однако, длина IP датаграммы, в которую необходимо записать
    1473 байта данных, составляет 1501 байт, поэтому она должна быть фрагментирована
    (строка 3 и 4). Точно так же, длина датаграммы, в которую необходимо записать
    1474 байта данных, будет составлять 1502 байта, и она также должна быть
    фрагментирована (строки 5 и 6).

    Когда IP датаграмма фрагментирована, tcpdump выводит дополнительную
    информацию. Во-первых, выводится флаг frag 26304 (строки 3 и 4) и флаг frag
    26313 (строки 5 и 6), который указывает на значение поля идентификации в IP
    заголовке.

    Число 1480 в строке 3 (между двоеточием и символом @), это размер, за
    исключением IP заголовка. Первые фрагменты обеих датаграмм содержит 1480 байт
    данных: 8 байт UDP заголовока и 1472 байта пользовательских данных. (Если
    добавить 20 байт IP заголовка, получится точно размер пакета – 1500 байт.)
    Второй фрагмент первой датаграммы (строка 4) содержит 1 байт данных – оставшийся
    байт пользовательских данных. Второй фрагмент второй датаграммы (строка 6)
    содержит 2 оставшихся байта пользовательских данных.

    При фрагментации требуется, чтобы порция данных генерируемых
    фрагментов (за исключением IP заголовока) была кратна 8 байтам для всех
    фрагментов за исключением последнего. В этом примере, число 1480 кратно восьми.

    Число, следующее за символом @, это смещение данных во фрагменте от
    начала датаграммы. Первый фрагмент обеих датаграмм начинается с нуля (строки 3 и
    5), а второй фрагмент обеих датаграмм начинается со смещения в 1480 байт (строки
    4 и 6). Символ плюс, который следует за смещением и напечатан для первых
    фрагментов обеих датаграмм, означает, что в данной датаграмме присутствуют еще
    фрагменты. Знак плюс соответствует биту “дальше следуют еще фрагменты”
    (more fragments) в 3-битовом поле флагов IP заголовка. Назначение этого бита
    заключается в том, чтобы сообщить принимающему о том, что сборка всех фрагментов
    датаграммы полностью завершена.

    И в завершение, обратите внимание на то, что в строках 4 и 6 (не
    первые фрагменты) не указан протокол (UDP), а также порты источника и
    назначения. Протокол должен быть напечатан, так как он находится в IP заголовке,
    который копируется в каждый фрагмент. А номера портов находятся в UDP заголовке,
    который присутствует только в первом фрагменте. На рисунке 11.8 показано, что
    произойдет с третьей датаграммой (которая содержит 1473 байта пользовательских
    данных). Здесь мы видим подтверждение того, что заголовок любого транспортного
    уровня присутствует только в первом фрагменте.

    Хочется обратить внимание на терминологию: IP
    датаграмма (IP datagram) это блок, который передается от одного конца IP уровня
    к другому концу IP уровня (перед фрагментацией и после повторной сборки), тогда
    как пакет (packet) это блок данных, который передается между
    IP уровнем и канальным уровнем. Пакет может быть полной IP датаграммой или всего
    лишь фрагментом IP датаграммы.

    Address resolution protocol - что это?

    Рисунок 11.8 Пример UDP фрагментации.

    ICMP ошибки о недоступности (требуется фрагментация)

    Необходимо обсудить еще одну ICMP ошибку о недоступности. Она
    генерируется, когда маршрутизатор принимает датаграмму, которую необходимо
    фрагментировать, однако в IP заголовке установлен флаг “не фрагментировать” (DF). Эта ошибка может быть
    использована программой, которой необходимо определить минимальный MTU в
    маршруте до пункта назначения – что называется механизмом
    определения транспортного MTU (path MTU discovery) (глава 2, раздел “Транспортный MTU”).

    На рисунке 11.9 показан формат ICMP ошибки о недоступности для данного
    случая. Он отличается от формата, приведенного на рисунке 6.10, потому
    что биты 16-31 во втором 32-битном слове могут содержать MTU следующей
    пересылки, вместо того чтобы быть установленными в 0.

    Address resolution protocol - что это?

    Рисунок 11.9 ICMP ошибка о недоступности, когда
    необходима фрагментация, однако установлен бит “не
    фрагментировать”.

    Если маршрутизатор не поддерживает этот новый формат ICMP ошибки, MTU
    следующей пересылки устанавливается в 0.

    Новые требования к маршрутизаторам Router Requirements RFC [Almquist 1993] указывают на то, что маршрутизатор должен генерировать эту новую форму, когда
    выдает ICMP сообщение о недоступности.

    Пример

    Проблема, которую мы обсудим, возникла при получении ICMP ошибки при
    попытке определить MTU SLIP канала с дозвоном между маршрутизатором netb и
    хостом sun. Мы знаем MTU этого канала от sun к netb, так как, во-первых, это
    указывается при конфигурации SLIP на хосте sun, во-вторых, мы видели MTU при
    запуске команды netstat в разделе “Команда netstat”
    главы 3. А сейчас мы хотим определить MTU в другом направлении. (В главе 25
    рассказывается как определить MTU с использованием SNMP.) Для каналов
    точка-точка нет необходимости, чтобы MTU был одним и тем же в обоих
    направлениях.

    Для определения использована следующая техника. Мы запустили ping с хоста solaris на хост bsdi,
    увеличивая размер пакета данных до тех пор, пока к пакетам не была применена
    фрагментация. Процесс показан на рисунке 11.10.

    Address resolution protocol - что это?

    Рисунок 11.10 Системы, которые были использованы для
    определения MTU SLIP канала между netb и sun.

    На хосте sun была запущена программа tcpdump,
    которая позволила посмотреть, как осуществляется фрагментация в SLIP канале.
    Фрагментация не появлялась и все было отлично до тех пор, пока размер данных в
    пакете ping не возрос от 500 байт до 600. Входящие эхо запросы были видны (как
    будто фрагментации не было), однако эхо отклики исчезли.

    Чтобы лучше разобраться в происходящем, программа tcpdump также была
    запущена на bsdi, после чего стало видно, что отправляется и что принимается. На
    рисунке 11.11 показан вывод.

    >

    1  0.0                      solaris > bsdi: icmp: echo request (DF)

    0.000000 (0.0000)       bsdi > solaris: icmp: echo reply (DF)
    3  0.000000
    (0.0000)       sun > bsdi: icmp: solaris unreachable –

    need to frag, mtu = 0 (DF)

    4  0.738400
    (0.7384)       solaris > bsdi: icmp: echo request (DF)
    5  0.748800
    (0.0104)       bsdi > solaris: icmp: echo reply (DF)
    6  0.748800
    (0.0000)       sun > bsdi: icmp: solaris unreachable –

    need to frag, mtu = 0 (DF)

    Рисунок 11.11 Вывод программы tcpdump от ping на bsdi от solaris с IP
    датаграммой размером в 600 байт.

    Во-первых, выражение (DF) в каждой строке означает, что в единицу
    установлен бит “не фрагментировать” в IP заголовке. Это означает, что
    Solaris 2.2 нормально устанавливает этот бит в единицу, что является частью
    механизма определения транспортного MTU.

    Строка 1 указывает, что эхо запрос проходит через маршрутизатор netb к
    sun без фрагментации и с установленным битом DF, поэтому можно сделать вывод,
    что критичный размер MTU для SLIP хоста netb еще не достигнут.

    Также, заметьте из строки номер 2, что DF флаг копируется в каждый эхо
    отклик. Как раз это и вызвало проблему. Эхо отклик того же размера, что и эхо
    запрос (чуть больше 600 байт), однако MTU исходящего SLIP интерфейса хоста sun
    равен 552. Эхо отклик должен быть фрагментирован, однако установлен флаг DF. Это
    заставляет sun генерировать ICMP ошибку о недоступности и отправлять ее к bsdi
    (где она уничтожается).

    Именно поэтому мы не видели эхо отклики от solaris. Отклики не
    проходили через sun. На рисунке 11.12 показан путь прохождения пакетов.

    Address resolution protocol - что это?

    Рисунок 11.12 Обмен пакетами для данного
    примера.

    И в завершение, отметим, что выражение mtu=0 в строках 3 и 6 на
    рисунке 11.11 указывает на то, что sun не возвращает MTU для исходящего
    интерфейса в ICMP сообщении о недоступности, как показано на рисунке 11.9. (В
    разделе “Дополнительные
    примеры”
    главы 25 мы решим эту проблему с использованием SNMP и
    убедимся в том, что MTU SLIP интерфейса netb равен 1500.)

    Определение транспортного MTU с использованием
    Traceroute

    Так как большинство систем не поддерживают функцию определения
    транспортного MTU, мы доработаем программу traceroute (глава 8)
    так, чтобы она могла определять транспортный MTU. Мы отправим пакет с
    установленным битом “не фрагментировать” (don’t fragment). Размер
    первого отправляемого пакета будет равен MTU исходящего интерфейса. Когда
    вернется ICMP ошибка “не могу фрагментировать” (can’t fragment), мы
    уменьшим размер пакета. Если маршрутизатор, отправивший ICMP ошибку,
    поддерживает новую версию, которая включает MTU исходящего интерфейса в ICMP
    сообщение, мы используем полученное значение; иначе мы попробуем следующий
    меньший MTU. Как утверждает RFC 1191 [Mogul and Deering
    1990], существует ограниченное количество значений MTU, наша программа имеет
    таблицу возможных значений и просто перейдет на следующее меньшее значение.

    Попробуем подобный алгоритм с хоста sun на хост slip, зная, что SLIP
    канал имеет MTU равный 296:

    >

    sun % traceroute.pmtu slip
    traceroute to slip (140.252.13.65), 30
    hops max
    outgoing MTU = 1500
    1 bsdi (140.252.13.35)  15 ms  6 ms  6
    ms
    2 bsdi (140.252.13.35)  6 ms
    fragmentation required and DF set, trying
    new MTU = 1492
    fragmentation required and DF set, trying new MTU =
    1006
    fragmentation required and DF set, trying new MTU = 576
    fragmentation
    required and DF set, trying new MTU = 552
    fragmentation required and DF set,
    trying new MTU = 544
    fragmentation required and DF set, trying new MTU =
    512
    fragmentation required and DF set, trying new MTU = 508
    fragmentation
    required and DF set, trying new MTU = 296
    2 slip (140.252.13.65)  377 ms  377
    ms  377 ms

    В этом примере маршрутизатор bsdi не вернул MTU исходящего интерфейса
    в ICMP сообщении, поэтому мы перейдем на следующее меньшее значение MTU. Первая
    строка вывода для TTL равного 2 сообщает имя хоста bsdi,
    однако это происходит из-за того, оно было возвращено маршрутизатором в ICMP
    ошибке. Последняя строка вывода для TTL равного 2 это как раз то, что мы
    ожидали.

    Не составляет труда модифицировать ICMP код на bsdi, чтобы получить
    MTU исходящего интерфейса. И если мы сделаем это и вернемся к нашей программе,
    то получим следующий вывод:

    >

    sun % traceroute.pmtu slip
    traceroute to slip (140.252.13.65), 30
    hops max
    outgoing MTU = 1500
    1 bsdi (140.252.13.35)  53 ms  6 ms  6
    ms
    2 bsdi (140.252.13.35)  6 ms
    fragmentation required and DF set, next
    hop MTU = 296
    2 slip (140.252.13.65)  377 ms  378 ms  377 ms

    Здесь нам нет смысла перебирать восемь различных значений MTU,
    маршрутизатор сообщил нужное значение.

    Мировой Internet

    Модифицированная версия traceroute была запущена несколько раз на
    различные хосты по всему миру. С ее помощью было достигнуто 15 стран (включая
    Антарктику), при этом были использованы различные трансатлантические и
    транстихоокеанские каналы. Однако, до того как сделать это, мы увеличили MTU
    SLIP канала с дозвоном между нашей подсетью и маршрутизатором netb (рисунок
    11.12) до 1500, как в Ethernet.

    Из 18 раз, когда была запущена программа, только в двух случаях
    траспортный MTU был меньше чем 1500. Один трансатлантический канал имел MTU
    равный 572 (странное значение, которое даже не приведено в списке в RFC 1191), и
    маршрутизатор не вернул ICMP ошибку в новом формате. Еще один канал между двумя
    маршрутизаторами в Японии не обрабатывал фреймы размером 1500 байт, также
    маршрутизатор не вернул ICMP ошибку в новом формате. После того как MTU было
    уменьшено до 1006, все заработало.

    Вывод, который мы можем сделать из этих экспериментов, заключается в
    том, что большинство (но не все) глобальных сетей в настоящее время могут
    обрабатывать пакеты больше чем 512 байт. Использование характеристики поиска
    транспортного MTU позволяет приложениям работать значительно продуктивнее,
    пользуясь большими MTU.

    Определение транспортного MTU при использовании
    UDP

    Давайте рассмотрим взаимодействие между приложением, использующим UDP,
    и механизмом определения транспортного MTU. Нам необходимо посмотреть, что
    произойдет в том случае, когда приложение отправляет датаграмму, которая слишком
    велика для некоторого промежуточного канала.

    Пример

    Так как единственная система, которая поддерживает механизм
    определения транспортного MTU, это Solaris 2.x, мы
    используем ее как хост источник, чтобы отправить датаграмму размером 650 байт на
    slip. Так как хост slip находится позади SLIP канала с MTU равным 296, любая UDP
    датаграмма больше чем 268 байт (296 – 20 – 8) с установленным битом “не
    фрагментировать” должна вызвать ICMP ошибку “не могу
    фрагментировать” с маршрутизатора bsdi. На рисунке 11.13 показана топология
    и MTU каналов.

    Address resolution protocol - что это?

    Рисунок 11.13 Системы, использованные для определения
    транспортного MTU с использованием UDP.

    Следующая команда генерирует десять UDP датаграмм размером 650 байт с
    интервалом в 5 секунд:

    >solaris % sock -u -i -n10 -w650 -p5
    slip discard

    На рисунке 11.14 показан вывод команды tcpdump.
    Когда этот пример был запущен, маршрутизатор bsdi был сконфигурирован таким
    образом, чтобы не возвращать MTU следующей пересылки как часть ICMP ошибки
    “не могу фрагментировать”.

    Первая посланная датаграмма с установленным битом DF (строка 1) генерирует ожидаемую ошибку от маршрутизатора
    bsdi (строка 2). Что интересно, следующая датаграмма, также посланная с
    установленным битом DF (строка 3), генерирует ту же самую ICMP ошибку (строка
    4). Мы ожидали, что эта датаграмма будет послана с выключенным битом
    DF.

    В строке 5 IP наконец понял, что датаграммы в этот пункт назначения не
    должны посылаться с установленным битом DF, после чего стал фрагментировать
    датаграммы на хосте источнике. Это поведение отличается от того, что было
    показано в ранних примерах, где IP отправлял датаграммы, которые он получал от
    UDP, и при этом маршрутизаторам с более маленькими MTU (bsdi в данном случае)
    позволялось осуществлять фрагментацию. 

    >

    1  0.0                  solaris.38196 > slip.discard: udp 650 (DF)

    0.004218 (0.0042)   bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu = 0
    (DF)

    3  4.980528 (4.9763)   solaris.38196 > slip.discard: udp 650
    (DF)
    4  4.984503 (0.0040)   bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu = 0
    (DF)

    5  9.870407 (4.8859)   solaris.38196 > slip.discard: udp 650
    (frag 47942:552@0 )
    6  9.960056 (0.0896)   solaris > slip: (frag
    47942:106@552)

    7  14.940338 (4.9803)  solaris.38196 > slip.discard:
    udp 650 (DF)
    8  14.944466 (0.0041)  bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu = 0
    (DF)

    9  19.890015 (4.9455)  solaris.38196 > slip.discard: udp 650
    (frag 47944:552@0 )
    10  19.950463 (0.0604)  solaris > slip: (frag
    47944:106@552)

    11  24.870401 (4.9199)  solaris.38196 > slip.discard:
    udp 650 (frag 47945:552@0 )
    12  24.960038 (0.0896)  solaris > slip: (frag
    47945:106@552)

    13  29.880182 (4.9201)  solaris.38196 > slip.discard:
    udp 650 (frag 47946:552@0 )
    14  29.940498 (0.0603)  solaris > slip: (frag
    47946:106@552)

    15  34.860607 (4.9201)  solaris.38196 > slip.discard:
    udp 650 (frag 47947:552@0 )
    16  34.950051 (0.0894)  solaris > slip: (frag
    47947:106@552)

    17  39.870216 (4.9202)  solaris.38196 > slip.discard:
    udp 650 (frag 47948:552@0 )
    18  39.930443 (0.0602)  solaris > slip: (frag
    47948:106@552)

    19  44.940485 (5.0100)  solaris.38196 > slip.discard:
    udp 650 (DF)
    20  44.944432 (0.0039)  bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu = 0
    (DF)

    Рисунок 11.14 Определение транспортного MTU с использованием
    UDP.

    Так как ICMP сообщение “не могу фрагментировать” не содержит
    MTU следующей пересылки, это означает, что IP решил что всех устраивает MTU
    равный 576. Первый фрагмент (строка 5) содержит 544 байта UDP данных, 8 байт UDP
    заголовка и 20 байт IP заголовка, полный размер IP датаграммы составляет 572
    байта. Второй фрагмент (строка 6) содержит оставшиеся 106 байт UDP данных и
    20-байтный IP заголовок.

    К сожалению, следующая датаграмма, строка 7, имеет установленный бит
    DF, поэтому она отбрасывается bsdi, после чего возвращается ICMP ошибка. Здесь
    произошло следующее: истек IP таймер, который сообщил IP о необходимости
    проверить, не увеличился ли транспортный MTU, путем повторной установки бита DF.
    Мы увидим, что это произойдет снова в строках 19 и 20. Сравнивая времена в
    строках 7 и 19, где DF бит устанавливается в единицу, мы видим, что проверка на
    увеличение транспортного MTU осуществляется каждые 30 секунд.

    Этот 30-секундный таймер слишком мал.
    RFC 1191 рекомендует установить значение таймера в 10 минут. Величину таймера
    можно изменить с помощью параметра ip_ire_pathmtu_interval
    (приложение E, раздел “Solaris 2.2”). В Solaris 2.2 не существует способа выключить определение
    транспортного MTU для одного UDP приложения или для всех UDP приложений. Оно
    может быть включено или выключено только для всей системы в целом с помощью
    изменения параметра ip_path_mtu_discovery. Как мы видим из
    данного примера, включение характеристики определения транспортного MTU, когда
    UDP приложения отправляют датаграммы, которые, возможно, будут фрагментированы,
    приведет к тому, что датаграмма может быть отброшена.

    Максимальный размер датаграммы, воспринимаемый IP уровнем на solaris
    (576 байт), неверен. На рисунке 11.13 мы видели, что реальный MTU составляет 296
    байт. Это означает, что фрагменты, генерируемые solaris, снова фрагментируются
    на bsdi. На рисунке 11.15 показан вывод tcpdump, полученный
    на хосте назначения (slip) для первой прибывшей датаграммы (строки 5 и 6 на
    рисунке 11.14).

    >

    1  0.0                    solaris.38196 > slip.discard: udp 650 (frag
    47942:272@0 )
    2  0.304513 (0.3045)     solaris > slip: (frag
    47942:272@272 )
    3  0.334651 (0.0301)     solaris > slip: (frag
    47942:8@544 )
    4  0.466642 (0.1320)     solaris > slip: (frag
    47942:106@552)

    Рисунок 11.15 Первая датаграмма, прибывшая на хост slip от
    solaris.

    В этом примере хост solaris не должен фрагментировать исходящие
    датаграммы, однако должен выключить бит DF и позволить маршрутизатору с меньшим
    MTU осуществить фрагментацию.

    Сейчас мы запустим тот же самый пример, однако изменим поведение
    маршрутизатора bsdi так, чтобы тот возвращал MTU следующей пересылки в ICMP
    сообщении “не могу фрагментировать”. На рисунке 11.16 показаны первые
    шесть строк вывода tcpdump.

    >

    1  0.0                    solaris.37974 > slip.discard: udp 650 (DF)

    0.004199 (0.0042)     bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu =
    296 (DF)

    3  4.950193 (4.9460)     solaris.37974 > slip.discard: udp
    650 (DF)
    4  4.954325 (0.0041)     bsdi > solaris: icmp:

    slip unreachable – need to frag, mtu =
    296 (DF)

    5  9.779855 (4.8255)     solaris.37974 > slip.discard: udp
    650 (frag 35278:272@0 )
    6  9.930018 (0.1502)     solaris > slip: (frag
    35278:272@272 )
    7  9.990170 (0.0602)     solaris > slip: (frag
    35278:114@544)

    Рисунок 11.16 Определение транспортного MTU с использованием
    UDP.

    И снова мы видим, что две первые датаграммы отправлены с установленным
    битом DF, на обе получены ICMP ошибки. Сейчас в ICMP ошибке указывается MTU
    следующей пересылки, который равен 296.

    В строках 5, 6 и 7 мы видим, что хост источник осуществляет
    фрагментацию, как на рисунке 11.14. Если известен MTU следующей пересылки,
    генерируются только три фрагмента, по сравнению с четырьмя фрагментами, которые
    генерируются маршрутизатором bsdi на рисунке 11.15.

    Взаимодействие между UDP и ARP

    Используя UDP, мы можем рассмотреть очень интересное взаимодействие
    между UDP и типичной реализацией ARP.

    Мы используем программу sock, чтобы сгенерировать
    одну UDP датаграмму с 8192 байтами данных. Мы ожидаем, что в этом случае будет
    сгенерировано шесть Ethernet фрагментов (см. упражнение 3 главы 11). Также, перед запуском
    программы, мы убедимся в том, что ARP кэш пуст, поэтому перед тем как будет
    отправлен первый фрагмент, должен произойти обмен ARP запросом и
    откликом.

    >

    bsdi % arp -a                     проверяем, что ARP кэш пуст
    bsdi
    % sock -u -i -n1 -w8192 svr4 discard

    Мы ожидаем, что первая датаграмма вызовет отправку ARP запроса.
    Следующие пять фрагментов, которые генерируются IP, ставят перед нами два
    вопроса, на которые мы можем ответить, только воспользовавшись tcpdump: будут ли готовы к отправке оставшиеся фрагменты, перед
    тем как будет получен ARP отклик, если так, что будет делать ARP с этими
    несколькими пакетами направляемыми на конкретный пункт назначения, пока
    ожидается ARP отклик? На рисунке 11.17 показан вывод программы tcpdump.

    >

    1  0.0                     arp who-has svr4 tell bsdi
    2  0.001234
    (0.0012)      arp who-has svr4 tell bsdi
    3  0.001941 (0.0007)      arp
    who-has svr4 tell bsdi
    4  0.002775 (0.0008)      arp who-has svr4 tell
    bsdi
    5  0.003495 (0.0007)      arp who-has svr4 tell bsdi
    6  0.004319
    (0.0008)      arp who-has svr4 tell bsdi
    7  0.008772 (0.0045)      arp reply
    svr4 is-at 0:0:c0:c2:9b:26
    8  0.009911 (0.0011)      arp reply svr4 is-at
    0:0:c0:c2:9b:26
    9  0.011127 (0.0012)      bsdi > svr4: (frag
    10863:800@7400)
    10  0.011255 (0.0001)      arp reply svr4 is-at
    0:0:c0:c2:9b:26
    11  0.012562 (0.0013)      arp reply svr4 is-at
    0:0:c0:c2:9b:26
    12  0.013458 (0.0009)      arp reply svr4 is-at
    0:0:c0:c2:9b:26
    13  0.014526 (0.0011)      arp reply svr4 is-at
    0:0:c0:c2:9b:26
    14  0.015583 (0.0011)      arp reply svr4 is-at
    0:0:c0:c2:9b:26

    Рисунок 11.17 Обмен пакетами при отправке по Ethernet UDP датаграммы
    размером 8192 байта.

    Этот вывод достаточно неожидан. Во-первых, перед тем как получен
    первый ARP отклик, генерируются шесть ARP запросов. Как можно догадаться, IP
    быстро генерирует шесть фрагментов, и для каждого отправляется ARP запрос.

    Затем, когда получен первый ARP отклик (строка 7), отправляется только
    последний фрагмент (строка 9)! Это означает, что первые пять фрагментов были
    отброшены. В действительности, это пример обычного функционирования ARP.
    Большинство реализаций держат только последний пакет, который должен быть
    отправлен на хост назначения, пока ожидается ARP отклик.

    Требования к хостам Host Requirements RFC требуют от реализаций, чтобы они
    предотвращали лавинообразную рассылку ARP запросов
    (повторная отправка ARP запросов для одного и того же IP адреса с большой
    частотой). Рекомендуемая максимальная частота составляет один раз в секунду.
    Здесь мы видим шесть ARP запросов в течение 4,3 миллисекунды. Требования к
    хостам Host Requirements RFC требуют, чтобы ARP сохранил по крайней мере один
    пакет, и это должен быть самый последний пакет. Это как раз то, что мы видели
    здесь.

    Следующая необъяснимая аномальность заключается в том, что svr4
    отправил назад семь ARP откликов, а не шесть.

    И последнее, про что хочется сказать, tcpdump работал еще 5 минут
    после того, как вернулся последний ARP отклик, ожидая увидеть, как svr4 пошлет
    назад ICMP ошибку “время истекло в течение повторной
    сборки” (time exceeded during reassembly). ICMP сообщение так и не
    появилось. (Мы показали формат этого сообщения на рисунке 8.2. Поле код,
    установленное в 1, указывает на то, что время истекло в течение повторной сборки датаграммы.)

    IP уровень должен запустить таймер, когда появляется первый фрагмент
    датаграммы. Здесь “первый” означает первый из прибывших фрагментов для
    данной датаграммы, а не просто первый фрагмент (со смещением
    фрагмента равным 0). Обычное значение тайм-аута находится в диапазоне от 30 до
    60 секунд. Если все фрагменты для этой датаграммы не прибыли за время до
    истечения таймера, все фрагменты отбрасываются. Если этого не сделать,
    фрагменты, которые уже никогда не прибудут (как мы видели в этом примере), могут
    вызвать переполнение приемного буфера.

    Существуют две причины, по которым мы не увидели ICMP сообщение.
    Во-первых, большинство реализаций Berkeley никогда не генерируют эту ошибку! Эти
    реализации устанавливают таймер и отбрасывают все фрагменты, когда таймер
    истечет, однако ICMP ошибка не генерируется. Во-вторых, первый фрагмент –
    фрагмент со смещением равным 0, содержащий UDP заголовок, не был принят. (Это
    был первый из пяти пакетов, отброшенных ARP.) Реализация не требует генерировать
    ICMP ошибку, если первый фрагмент не был принят. Причина заключается в том, что
    приемник ICMP ошибки не может сказать, который пользовательский процесс отправил
    датаграмму, которая была отброшена, потому что недоступен заголовок
    транспортного уровня. А высший уровень (либо TCP приложение, либо UDP
    приложение) отработает тайм-аут и повторит передачу.

    В этом разделе мы использовали IP фрагментацию, чтобы посмотреть, как
    осуществляется взаимодействие между UDP и ARP. Это взаимодействие можно увидеть,
    если отправитель быстро отправит несколько UDP датаграмм. Мы воспользовались
    фрагментацией, потому что пакеты быстро генерируются с помощью IP, что
    значительно быстрее, чем генерация нескольких пакетов пользовательским
    процессом.

    Максимальный размер UDP датаграммы

    Теоретически максимальный размер IP датаграммы может составлять 65535
    байт, что ограничивается 16-битным полем полной длины в IP заголовке (см. рисунок 3.1). При длине IP
    заголовка равной 20 байтам и длине UDP заголовка равной 8 байтам в UDP
    датаграмме для пользовательских данных остается максимум 65507 байт. В
    большинстве реализаций, однако, используются датаграммы значительно меньшего
    размера.

    Обычно играют роль два ограничения. Во-первых, программа приложение
    может быть ограничена программным интерфейсом. Сокеты API
    (глава 1, раздел “Интерфейсы прикладного
    программирования”
    ) предоставляют функцию, которая может быть вызвана
    приложением, чтобы установить размер буферов ввода и вывода. Для UDP сокета этот
    размер напрямую связан с максимальным размером UDP датаграммы, которая может
    быть прочитана и записана UDP. В настоящее время большинство систем
    предоставляют по умолчанию максимальный размер UDP датаграммы, которая может
    быть прочитана или записана, равный 8192 байтам. (Эта значение установлено в
    8192, потому что именно столько по умолчанию читается и записывается системой
    NFS.)

    Следующее ограничение определяется реализацией ядра TCP/IP. Могут
    существовать характеристики реализации (или ошибки), которые ограничивают размер
    UDP датаграммы значением меньшим, чем 65535 байт.

    Автор экспериментировал с различными
    размерами UDP датаграмм, используя программу sock. С
    использованием loopback интерфейса под SunOS 4.1.3, максимальный размер UDP датаграммы был 32767 байт.
    Использовать большее значение не удавалось. При передаче по Ethernet от BSD/386 к SunOS 4.1.3, максимальный размер IP датаграммы,
    которую мог принять Sun, составлял 32786 (при этом пользовательских данных было
    32758 байт). С использованием loopback интерфейса в Solaris
    2.2 максимальный размер IP датаграммы, которая могла быть отправлена и принята,
    составлял 65535 байт. При передаче от Solaris 2.2 к AIX
    3.2.2 удалось передать IP датаграмму максимального размера в 65535
    байт.

    В разделе “IP заголовок” главы 3
    мы упомянули, что хосту необходимо получать IP датаграммы размером по меньшей
    мере 576 байт. Большинство приложений UDP разработаны таким образом, чтобы
    ограничивать свои приложения в размере 512 байт данных или меньше, чтобы
    уложиться в это ограничение. В разделе “RIP: протокол обмена
    информацией о маршрутизации”
    главы 10, например, мы видели, что RIP
    всегда посылает в датаграмме меньше чем 512 байт. Это же самое ограничение мы
    найдем и в других UDP приложениях: DNS (глава 14), TFTP (глава 15), BOOTP (глава 16) и
    SNMP (глава
    25
    ).

    Усечение датаграмм

    Из того что IP может отправлять и принимать датаграммы определенного
    размера, не следует, что принимающее приложение готово прочитать датаграммы
    этого размера. Программный интерфейс UDP позволяет приложениям указывать
    максимальное количество байт, которые будут обработаны за один раз. Что
    произойдет, если принятая датаграмма по размеру больше, чем датаграмма, которую
    готово принять приложение?

    К сожалению, ответ зависит от программного интерфейса и
    реализации.

    Традиционные версии Berkeley сокет
    API обрезают датаграммы, отбрасывая любые непоместившиеся данные. Будет ли
    приложение поставлено в известность, зависит от версии. (4.3
    BSD Reno и более поздние версии могут уведомить приложение о том, что датаграмма
    была обрезана.) API сокеты под SVR4
    (включая Solaris 2.x) не обрезают датаграммы. Любые непоместившиеся данные
    последовательно считываются. Приложение не уведомляется о нескольких циклах
    считывания и ему будет передана одна UDP датаграмма. TLI API не отбрасывают
    данные. Вместо этого возвращается флаг, указывающий на то, что данных больше,
    чем можно считать за один раз, поэтому приложение начинает последовательно
    считывать оставшуюся датаграмму.

    Когда мы будем обсуждать TCP, то увидим, что этот протокол
    предоставляет последовательные потоки байт, направляемые в приложение, без
    каких-либо ограничений. TCP передает в приложение данные любого размера, которые
    требуются для приложения – причем данные на этом интерфейсе никогда не теряются.

    ICMP ошибка подавления источника

    Воспользовавшись UDP, можно сгенерировать ICMP ошибку “подавление источника” (source quench). Эта ошибка
    может быть сгенерирована системой (маршрутизатором или хостом), когда она
    принимает датаграммы быстрее, чем эти датаграммы могут быть обработаны. Обратите
    внимание на выражение “могут быть”. Система не требует послать
    подавление источника, даже если буферы переполнены и датаграммы отбрасываются.

    На рисунке 11.18 показан формат ICMP ошибки подавления источника. Мы
    имеем идеальную возможность сгенерировать подобную ошибку в нашей тестовой сети.
    Мы можем посылать датаграммы с bsdi на маршрутизатор sun по Ethernet, причем эти
    датаграммы должны быть смаршрутизированы через SLIP канал. Так как SLIP канал
    примерно в тысячу раз медленнее чем Ethernet, мы легко можем переполнить буфер.
    Следующая команда посылает 100 датаграмм размером 1024 байта с хоста bsdi через
    маршрутизатор sun на solaris. Мы отправляем датаграммы на стандартный discard
    сервис, где они будут игнорированы:

    >bsdi % sock -u -i -w1024 -n100
    solaris discard

    Address resolution protocol - что это?

    Рисунок 11.18 ICMP ошибка подавления
    источника.

    На рисунке 11.19 показан вывод команды tcpdump, соответствующий этой
    команде.

    >

      1  0.0              bsdi.1403 > solaris.discard: udp
    1024
    26 строк не показано
    27  0.10
    (0.00)     bsdi.1403 > solaris.discard: udp 1024
    28  0.11 (0.01)     sun
    > bsdi: icmp: source quench

    29  0.11 (0.00)     bsdi.1403 >
    solaris.discard: udp 1024
    30  0.11 (0.00)     sun > bsdi: icmp: source
    quench
    142 строки не показано
    173  0.71
    (0.06)     bsdi.1403 > solaris.discard: udp 1024
    174  0.71 (0.00)     sun
    > bsdi: icmp: source quench

    Рисунок 11.19 ICMP подавление источника от маршрутизатора
    sun.

    Из этого вывода мы удалили множество строк. Первые 26 датаграмм
    приняты без ошибок: мы показали вывод только для первой. Начиная с 27-й
    датаграммы, каждый раз, когда отправляется датаграмма, мы получаем ошибку
    подавление источника. Всего было 26 (74х2)=174 строк вывода.

    Из нашего расчета пропускной способности последовательной линии,
    приведенного в разделе “Вычисление загруженности
    последовательной линии”
    главы 2, видно, что на передачу датаграммы
    размером 1024 байта со скоростью 9600 бит/сек потребуется больше одной секунды.
    (В нашем примере для этого потребуется больше времени, так как датаграмма
    размером 20 8 1024 байт будет фрагментирована, потому что MTU SLIP канала от sun
    к netb составляет 552 байта.) Однако, из показателей времени, приведенных на
    рисунке 11.19, мы видим, что маршрутизатор sun получил все 100 датаграмм за
    время меньше чем одна секунда, перед тем как первая была отправлена в SLIP
    канал. При этом понятно, что мы использовали множество его буферов.

    Несмотря на то, что RFC 1009 [Braden and Postel 1987] требует, чтобы маршрутизатор
    генерировал ошибки подавления источника, когда переполняются его буферы, новые
    требования к маршрутизаторам Router Requirements RFC [Almquist 1993] меняют это положение и говорят, что
    маршрутизатор не должен генерировать ошибки подавления
    источника.

    Следующий момент, на который необходимо обратить внимание в примере,
    заключается в том, что программа sock никогда не получала
    уведомлений о том, что источник подавлен, или если и получала их, то
    игнорировала. Это говорит о том, что реализации BSD обычно игнорируют полученные
    сообщения о подавлении источника в случае использования протокола UDP. (В случае
    TCP, при получении уведомления передача данных по соединению, для которого
    сгенерировано подавление источника, замедляется. Мы это обсудим в разделе “ICMP ошибки” главы
    21.) Проблема заключается в том, что процесс, который сгенерировал данные,
    которые, в свою очередь, вызвали подавление источника, может уже завершиться,
    когда будет принято сообщение о подавлении источника. И действительно, если мы
    используем программу time в Unix, чтобы оценить, как долго
    работает программа sock, то узнаем, что она проработала всего лишь около 0,5
    секунды. Однако на рисунке 11.19 мы видели, что некоторые сообщения о подавлении
    источника были получены через 0,71 секунды после отправки первой датаграммы, то
    есть уже после того, как процесс прекратил работу. Что произойдет, если наша
    программа выдаст 100 датаграмм и завершится. Не все 100 датаграмм будут посланы
    – некоторые из них будут стоять в выходной очереди.

    Этот пример доказывает, что UDP – ненадежный
    протокол и показывает важность контроля за потоком данных (flow control).
    Несмотря на то, что программа sock успешно выдала в сеть 100 датаграмм, только
    26 достигли пункта назначения. Остальные 74 скорее всего были отброшены
    промежуточным маршрутизатором. Если приложение не поддерживает какую-либо форму
    уведомлений, отправитель не знает, принял ли получатель данные.

    Сервер UDP

    Существует несколько особенностей использования UDP, которые
    отражаются на разработке и реализации сервера. Разработка и реализация клиентов
    обычно легче, чем реализация серверов. Именно поэтому здесь мы обсудим
    разработку сервера, а не разработку клиента. Серверы обычно взаимодействуют с
    операционной системой, и большинство серверов требуют, чтобы существовал
    какой-либо способ, позволяющий обработать запросы от нескольких клиентов
    одновременно.

    Обычно когда клиент стартует, он сразу же устанавливает соединение с
    одним сервером. Сервера, с другой стороны, стартуют и затем
    “засыпают”, ожидая прибытия запроса от клиента. В случае UDP, сервер
    “просыпается”, когда прибывает датаграмма от клиента, эта датаграмма
    может содержать запрос в какой-либо форме.

    Мы не будем рассматривать аспекты программирования клиентов и серверов
    ([Stevens 1990] описывает все более подробно), однако рассмотрим характеристики
    протокола UDP, которые оказывают влияние на разработку и реализацию сервера,
    использующего UDP. (Мы обсудим подробности TCP сервера в разделе “Реализация TCP
    сервера”
    главы 18.) Некоторые характеристики мы обсудим в зависимости
    от реализаций UDP, которые будут использоваться, а также рассмотрим
    характеристики, которые являются общими для большинства
    реализаций.

    IP адрес клиента и номер порта

    От клиента прибывает UDP датаграмма. IP заголовок содержит IP адреса
    источника и назначения, а UDP заголовок содержит номера портов UDP источника и
    назначения. Когда приложение получает UDP датаграмму, операционная система
    должна сообщить ему, кто послал сообщение – IP адрес источника и номер
    порта.

    Эта характеристика позволяет одному UDP серверу обрабатывать несколько
    клиентов. Каждый отклик отправляется тому клиенту, который послал
    запрос.

    IP адрес назначения

    Некоторым приложениям необходимо знать, кому предназначена датаграмма,
    то есть IP адрес назначения. Например, требования к хостам Host Requirements RFC определяют, что TFTP
    сервер должен игнорировать принятые датаграммы, которые рассылаются на
    широковещательный адрес. (Мы опишем широковещательную адресацию в главе 12, а
    TFTP в главе
    15
    .)

    Это означает, что операционная система должна передать IP адрес
    назначения из принятой UDP датаграммы в приложение. К сожалению, не все
    реализации предоставляют эту возможность.

    Сокеты API
    предоставляют эту возможность с использованием опции IP_RECVDSTADDR. Из систем, которые описываются в тексте, только
    BSD/386, 4.4BSD и AIX 3.2.2 поддерживают эту опцию. SVR4, SunOS 4.x и Solaris 2.x не
    поддерживают.

    Входная очередь UDP

    В разделе “Модель клиент-сервер”
    главы 1 мы говорили, что большинство UDP серверов могут обслуживать все запросы
    к клиентам с использованием одного UDP порта (заранее известные порты серверов).

    Обычно размер входной очереди, связанный с каждым UDP портом, который
    используется приложением, ограничен. Это означает, что запросы, которые
    прибывают в одно и то же время от различных клиентов, автоматически ставятся в
    очередь UDP. Принятые UDP датаграммы передаются приложению (когда оно требует
    следующую датаграмму) в том порядке, в каком они были приняты.

    Однако существует вероятность, в случае если очередь переполнена, что
    модуль UDP в ядре отбросит входящие датаграммы. Мы можем пронаблюдать это с
    помощью следующего эксперимента. Стартуем нашу программу sock на хосте bsdi,
    запустив таким образом UDP сервер:

    >

    bsdi % sock -s -u -v -E -R256 -r256 -P30 6666
    from 140.252.13.33,
    to 140.252.13.63: 1111111111          от sun на широковещательный адрес
    from
    140.252.13.34, to 140.252.13.35: 4444444444444      от svr4 на персональный
    адрес

    Мы использовали следующие флаги : -s, запускает
    программу в роли сервера, -u для UDP, -v, печатает IP адрес клиента, и -E
    печатает IP адрес назначения (в данном случае система это позволяет). В
    дополнение, мы установили приемный буфер UDP для этого порта
    в 256 байт (-R), вместе с размером, который может быть
    прочитан каждым приложением (-r). Флаг -P30 сообщает о необходимости подождать 30 секунд после очистки
    UDP порта, перед считыванием первой датаграммы. Это дает нам время стартовать
    клиентов на двух других хостах, послать некоторые датаграммы и посмотреть, как
    работает очередь приема.

    После того как сервер стартован и прошла 30-секундная пауза, мы
    стартуем одного клиента на хосте sun и посылаем три датаграммы:

    >

    sun % sock -u -v 140.252.13.63 6666              на широковещательный
    адрес Ethernet
    connected on 140.252.13.33.1252 to
    140.252.13.63.6666
    1111111111                                       11
    байт данных (с символом новой строки)
    222222222
                                           10 байт данных (с символом новой
    строки)
    33333333333                                      12 байт
    данных (с символом новой строки)

    Адрес назначения это широковещательный адрес (140.252.13.63). Затем мы
    стартовали еще одного клиента на хосте svr4 и послали еще три
    датаграммы:

    >

    svr4 % sock -u -v bsdi 6666
    connected on 0.0.0.0.1042 to
    140.252.13.35.6666
    4444444444444                             14 байт
    данных (с символом новой строки)
    555555555555555
                              16 байт данных (с символом новой
    строки)
    66666666                                   9 байт данных (с
    символом новой строки)

    Первое, на что необходимо обратить внимание в интерактивном выводе,
    показанном ранее на bsdi, что только две датаграммы были приняты приложением:
    первая от sun, состоящая из всех единиц, и первая от svr4, состоящая из всех
    четверок. Остальные четыре датаграммы были отброшены.

    Вывод команды tcpdump на рисунке 11.20 показывает,
    что все шесть датаграмм были доставлены к хосту назначения. Датаграммы прибыли
    от двух клиентов в обратном порядке: первая от sun, затем от svr4 и так далее.
    Также мы можем заметить, что все шесть были доставлены примерно за 12 секунд, в
    течение 30-секундного периода пока сервер “спал”.

    >

    1   0.0                        sun.1252 > 140.252.13.63.6666: udp
    11
    2   2.499184 (2.4992)         svr4.1042 > bsdi.6666: udp 14
    3  
    4.959166 (2.4600)         sun.1252 > 140.252.13.63.6666: udp 10
    4  
    7.607149 (2.6480)         svr4.1042 > bsdi.6666: udp 16
    5  10.079059
    (2.4719)         sun.1252 > 140.252.13.63.6666: udp 12
    6  12.415943
    (2.3369)         svr4.1042 > bsdi.6666: udp 9

    Рисунок 11.20 Вывод tcpdump для UDP датаграмм, посланных двумя
    клиентами.

    Также необходимо отметить, что c опцией -E сервер может узнать IP
    адрес назначения каждой датаграммы. Сервер может выбирать, что сделать с первой
    принятой датаграммой, которая была отправлена на широковещательный адрес.

    В этом примере необходимо обратить внимание еще на некоторые
    особенности. Во-первых, приложение не сообщило, когда была переполнена входная
    очередь. Лишние датаграммы UDP просто отбросил. Также в выводе tcpdump мы видим,
    что ничего не было отправлено клиенту обратно, чтобы сообщить ему о том, что
    датаграммы были отброшены. Не было послано ничего похожего на ICMP сообщение подавления источника, абсолютно ничего. И в заключение, хочется
    отметить, что входная очередь UDP функционирует по принципу FIFO (первый вошел, первый вышел), тогда как, что мы видели в
    разделе “Взаимодействие между UDP и
    ARP”
    этой главы, входная очередь ARP – LIFO
    (последний зашел, первый вышел).

    Ограничение локального IP адреса

    Большинство UDP серверов используют символы
    подстановки для своих IP адресов, когда создают конечные точки UDP. Это
    означает, что входящая UDP датаграмма, направляющаяся на порт сервера, будет
    принята любым локальным интерфейсом. Например, мы можем стартовать UDP сервер на
    порте 7777:

    >sun % sock -u -s
    7777

    Затем мы воспользуемся командой netstat, чтобы
    посмотреть состояние этой конечной точки:

    >

    sun % netstat -a -n -f inet
    Active Internet connections
    (including servers)
    Proto Recv-Q Send-Q   Local Address          Foreign
    Address            (state)
    udp        0      0    *.7777                
    *.*

    В этом выводе мы удалили много строк и оставили только те, которые нам
    интересны. Флаг -a сообщает о всех конечных точках сети.
    Флаг -n печатает IP адреса в десятичном представлении,
    вместо того чтобы использовать DNS и конвертировать адреса в имена, а также
    печатает номера портов вместо имен сервисов. Опция -f inet
    сообщает только о точках TCP и UDP.

    Локальный адрес напечатан как *.7777, где звездочка означает, что в
    качестве локального IP адреса может быть подставлен любой адрес.

    Когда сервер создает свою конечную точку, он может указать один из
    локальных IP адресов хоста, включая один из его широковещательных адресов в
    качестве локального IP адреса конечной точки. При этом входящая UDP датаграмма
    будет передана на конечную точку только в том случае, если IP адрес назначения
    совпадет с указанным локальным адресом. С помощью программы sock можно указать
    IP адрес перед номером порта, и этот IP адрес становится локальным IP адресом
    для конечной точки. Например,

    >sun % sock -u -s 140.252.1.29
    7777

    из датаграмм, прибывающих на SLIP интерфейс, выбирает датаграммы с
    адресом 140.252.1.29. Вывод команды netstat будет выглядеть
    следующим образом:

    >

    Proto Recv-Q Send-Q   Local Address           Foreign Address          
    (state)
    udp        0      0    140.252.1.29.7777      *.*

    Если мы постараемся послать на этот сервер датаграмму с хоста bsdi,
    адрес которого 140.252.13.35, по Ethernet, вернется ICMP ошибка о недоступности порта. Сервер никогда не увидит эту датаграмму.
    На рисунке 11.21 это показано более подробно.

    >

    1  0.0                     bsdi.1723 > sun.7777: udp 13
    2  0.000822
    (0.0008)      sun > bsdi: icmp: sun udp port 7777
    unreachable

    Рисунок 11.21 Отказ обработки UDP датаграммы, вызванный несовпадением
    локального адреса сервера.

    Существует возможность запустить другие сервера для этого же порта,
    каждый с собственным локальным IP адресом. Однако, приложение должно разрешить
    системе повторно использовать тот же самый номер порта.

    Должна быть указана опция сокета в API SO_REUSEADDR. Это делается
    нашей программой sock с помощью опции -A.

    На хосте sun мы можем стартовать пять различных серверов на одном и
    том же UDP порте (8888):

    >

    sun % sock -u -s 140.252.1.29 8888               для канала
    SLIP
    sun % sock -u -s -A 140.252.13.33 8888           для
    Ethernet
    sun % sock -u -s -A 127.0.0.1 8888               для loopback
    интерфейса
    sun % sock -u -s -A 140.252.13.63 8888           для
    широковещательных запросов Ethernet
    sun % sock -u -s -A 8888
                            для всего остального (метасимволы в IP
    адресе)

    Ожидалось, что первый из серверов будет запущен с флагом -A, который
    сообщает системе о том, что можно повторно использовать тот же самый номер
    порта. Вывод команды netstat показывает следующие пять серверов:

    >

    Proto Recv-Q Send-Q   Local Address          Foreign Address            
    (state)
    udp        0      0    *.8888                 *.*
    udp       
    0      0    140.252.13.63.8888    *.*
    udp        0      0    127.0.0.1
    8888        *.*
    udp        0      0    140.252.13.33 8888   
    *.*
    udp        0      0    140.252.1.29 8888     *.*

    В этом сценарии только датаграммы, направляющиеся на адрес
    140.252.1.255, будут попадать на сервер с символами подстановки, используемыми в
    качестве локального IP адреса, потому что другие четыре сервера охватывают все
    возможные адреса.

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

    Ограничение внешних IP адресов

    Во всех выводах команды netstat, которую мы
    показывали ранее, удаленные IP адреса и удаленные номера портов показаны как
    *.*. Это означает, что конечная точка воспримет входящие UDP датаграммы с любого
    IP адреса и любого номера порта. В большинстве реализаций конечным точкам UDP
    позволяется ограничивать удаленные адреса.

    Другими словами, конечная точка может воспринимать только UDP
    датаграммы от указанного IP адреса и номера порта. Наша программа sock
    использует опцию -f, чтобы указать удаленный IP адрес и
    номер порта:

    >sun % sock -u
    -s -f 140.252.13.35.4444 5555

    При этом удаленный IP адрес устанавливается в 140.252.13.35 (наш хост
    bsdi), а удаленный номер порта в 4444. Заранее-известный порт сервера 5555. Если
    мы запустим netstat, то увидим, что локальный IP адрес также установлен, даже
    если мы не указывали его непосредственно:

    >

    Proto Recv-Q Send-Q   Local Address          Foreign Address            
    (state)
    udp        0      0    140.252.13.33.5555   
    140.252.13.35.4444

    Это побочный эффект указания удаленного IP адреса и удаленного номера
    порта в системах Berkeley: если локальный IP адрес не был выбран при установке
    удаленного адреса, локальный адрес выбирается автоматически. В качестве
    локального IP адреса устанавливается IP адрес интерфейса, который выбирается с
    помощью IP маршрутизации для достижения указанного удаленного IP адреса. И
    действительно, в этом примере IP адрес sun для Ethernet, который подключен к
    удаленному адресу, это 140.252.13.33.

    На рисунке 11.22 приведены три типа адресов и портов, которые UDP
    сервер может установить для самого себя.

    Локальный адрес

    Удаленный адрес

    Описание

    localIP.lport

    foreignIP.fport

    ограничено одним
    клиентом

    localIP.lport

    *.*

    ограничено датаграммами,
    прибывающими с одного локального интерфейса: localIP

    *.lport

    *.*

    принимает все датаграммы,
    посланные на lport

    Рисунок 11.22 Указание локального и удаленного IP адресов и номера
    порта для UDP сервера.

    Во всех случаях lport это заранее-известный порт сервера, а localIP
    должен быть IP адресом локального интерфейса. Порядок, в котором расположены три
    строки на рисунке 11.22, показывает тот порядок, в котором UDP модуль старается
    определить, на которую локальную конечную точку принята входящая датаграмма.
    Наиболее жесткая связь адреса с портом (первая строка) выбирается в первую
    очередь, а менее жесткая (последняя строка, где и IP адрес, и номер порта
    указаны в виде метасимволов) выбирается последней.

    Множественный прием на порт

    Несмотря на то, что это не описано в RFC, большинство реализаций
    позволяют только одному приложению в одно и то же время быть связанным с одним
    локальным адресом и номером UDP порта. Когда UDP датаграмма прибывает на хост
    назначения на свой IP адрес и номер порта, одна копия доставляется в
    единственную конечную точку. IP адрес конечной точки может быть представлен в
    виде символов подстановки, как было показано ранее.

    Например, в SunOS 4.1.3 мы стартовали один сервер
    на порт 9999 с локальным IP адресом в виде символов подстановки:

    >sun % sock -u -s
    9999

    Если затем попробовать стартовать еще один сервер с тем же локальным
    адресом в виде символов подстановки и с тем же портом, это не сработает, даже
    если мы укажем опцию -A:

    >

    sun % sock -u -s 9999         так получиться не должно
    can’t bind
    local address: Address already in use

    sun % sock -u -s -A 9999
         поэтому мы указали флаг -A
    can’t bind local address: Address already
    in use

    Для систем, которые поддерживают групповую адресацию (см. главу 12),
    все обстоит иначе. Несколько конечных точек могут использовать один и тот же
    локальный адрес и номер UDP порта, однако приложение должно сообщить API, что
    это допустимо (флаг -A использует опцию сокета SO_REUSEADDR).

    4.4BSD, которая
    поддерживает групповую адресацию, требует, чтобы приложение установило другую
    опцию сокета (SO_REUSEPORT), чтобы позволить нескольким
    конечным точкам делить один и тот же порт. Более того, каждая конечная точка
    должна указать эту опцию, включая первую конечную точку, которая использует этот
    порт.

    Когда UDP датаграмма прибывает на свой IP адрес назначения, который
    является широковещательным или групповым адресом, при этом с этим IP адресом и
    номером порта связано несколько конечных точек, копия входящей датаграммы
    направляется каждой конечной точке. (Локальный IP адрес конечной точки может
    быть указан в виде символов подстановки, при этом он совпадет с любым IP адресом
    назначения.) Однако, если у прибывшей IP датаграммы IP адрес назначения –
    персональный адрес, только одна копия датаграммы доставляется в одну конечную
    точку. Которая конечная точка получит датаграмму с персональным адресом, зависит
    от реализации.

    Краткие выводы

    UDP это простой протокол. Официальная спецификация RFC 768 [Postel 1980] состоит всего лишь из трех страниц. Сервисы,
    которые он предоставляет пользовательским процессам, находящиеся над и позади IP, это номера портов и необязательные контрольные суммы. Мы
    использовали UDP, чтобы просмотреть расчет контрольных сумм и посмотреть, как
    осуществляется фрагментация.

    Затем мы рассмотрели ICMP ошибку о недоступности, которая является
    частью новой характеристики определения транспортного MTU (см. главу 2, раздел
    “Транспортный MTU”).
    Мы рассмотрели определение транспортного MTU с использованием Traceroute и UDP.
    Также рассмотрен процесс взаимодействия UDP и ARP.

    Мы убедились, что ICMP ошибка подавления источника может быть послана
    системой, которая принимает IP датаграммы быстрее, чем может обработать.
    Существует возможность легко генерировать эти ICMP ошибки с использованием
    UDP.

    Упражнения

    1. В разделе “Фрагментация IP” этой главы мы
      вызвали фрагментацию в Ethernet, записав UDP датаграмму с
      размером пользовательских данных в 1473 байта. Какой наименьший размер
      пользовательских данных может вызвать фрагментацию в Ethernet, если используется
      инкапсуляция IEEE 802 (глава 2, раздел “Ethernet и IEEE 802
      инкапсуляция”
      )?
    2. Прочитайте RFC 791 [Postel 1981a] и скажите, почему все
      фрагменты кроме последнего должны иметь длину кратную 8 байтам.
    3. Представьте себе Ethernet и UDP датаграмму с 8192 байтами пользовательских
      данных. Сколько фрагментов будет передано и какова будет длина смещения для
      каждого фрагмента?
    4. Продолжая предыдущий пример, представьте себе, что эти датаграммы затем
      передаются в SLIP канал с MTU равным 552. Вам необходимо помнить, что количество
      данных в каждом фрагменте (все кроме IP заголовка) должно быть кратно 8 байтам.
      Сколько фрагментов передано и каковы смещение и длина каждого фрагмента?
    5. Приложение, использующее UDP, посылает датаграмму,
      которая фрагментирована на 4 части. Представьте себе, что фрагменты 1 и 2
      достигли своего пункта назначения, тогда как фрагменты 3 и 4 были потеряны.
      Приложение отрабатывает тайм-аут, а затем, через 10 секунд, повторяет передачу
      UDP датаграммы. Эта датаграмма фрагментируется точно так же, как и при первой
      передаче (то же смещение и та же длина). Теперь представьте, что фрагменты 1 и 2
      потеряны, однако фрагменты 3 и 4 достигли своего пункта назначения. Таймер повторной сборки на принимающем хосте установлен в 60
      секунд, поэтому когда фрагменты 3 и 4 прибыли на конечный пункт назначения,
      фрагменты 1 и 2 из первой передачи еще не были отброшены. Может ли получатель
      собрать IP датаграмму из этих четырех фрагментов?
    6. Как Вы можете узнать, что фрагменты на рисунке 11.15 действительно
      соответствуют строкам 5 и 6 на рисунке 11.14?
    7. После того как хост gemini работал 33 дня, программа
      netstat показала, что 129 IP датаграмм из 48 миллионов были отброшены из-за
      несовпадения контрольной суммы заголовка, а 20 сегментов из 30 миллионов
      сегментов TCP были отброшены из-за несовпадения контрольной суммы TCP. Однако,
      примерно из 18 миллионов UDP датаграмм ни одна не была отброшена по причине
      ошибки в контрольной сумме UDP. Приведите минимум две причины, почему это могло
      произойти. (Подсказка: см. рисунок 11.4.)
    8. В нашем описании фрагментации мы ни разу не упомянули, что происходит с IP опциями в IP заголовке – либо они копируются как часть IP
      заголовка в каждый фрагмент, либо остаются только в первом фрагменте? Мы описали
      следующие опции: запись маршрута (глава 7, раздел “Опция записи IP
      маршрута”
      ), временная марка (глава 7, раздел “IP опция временной
      марки”
      ), жесткая и свободная маршрутизация от
      источника (глава 8, раздел “Опция IP маршрутизации от
      источника”
      ). Как Вы считаете, как при фрагментации обрабатываются эти
      опции? Сопоставьте Ваш ответ с RFC 791.
    9. На рисунке 1.8 мы сказали, что
      входящие IP датаграммы демультиплексируются на основе номера
      порта назначения UDP. Правильно ли это?

    §

    Введение

    Из рисунка 1.4 видно, что основная задача канального уровня в
    семействе протоколов TCP/IP – посылать и принимать (1) IP датаграммы для IP
    модуля, (2) ARP запросы и отклики для ARP модуля, и (3) RARP запросы и отклики
    для RARP модуля. TCP/IP поддерживает различные канальные уровни, в зависимости
    от того какой тип сетевого аппаратного обеспечения используется: Ethernet, Token
    ring, FDDI (Fiber Distributed Data Interface), последовательные линии RS-232, и
    так далее.

    В этой главе мы подробно рассмотрим канальный уровень Ethernet, два
    специализированных канальных уровня для последовательных интерфейсов (SLIP и
    PPP) и драйвер loopback, который присутствует практически во
    всех реализациях. Ethernet и SLIP это канальные уровни, используемые для
    большинства примеров в данной книге. Также мы рассмотрим максимальный блок
    передачи (MTU – Maximum Transmission Unit), который является характеристикой
    канального уровня и к которой мы обращаемся много раз в этой главе и в
    следующих. Также мы покажем некоторые расчеты, с помощью которых можно выбрать
    MTU для последовательной линии.

    Ethernet и IEEE 802 инкапсуляция

    Термин Ethernet обычно означает стандарт, опубликованный в 1982 году
    компаниями Digital Equipment Corp., Intel Corp., и Xerox Corp. В настоящее время
    это основная технология применяемая в локальных сетях использующих TCP/IP. В
    Ethernet используется метод доступа, называемый CSMA/CD, что
    обозначает наличие несущей (Carrier Sense), множественный доступ (Multiple
    Access) с определением коллизий (Collision Detection). Обмен осуществляется со
    скоростью 10 Мбит/сек, с использованием 48-битных адресов.

    Несколько лет спустя Комитет 802 Института инженеров по электротехнике
    и радиоэлектронике (IEEE – Institute of Electrical and
    Electronics Engineers) опубликовал отличающийся набор стандартов. 802.3
    описывает полный набор сетей CSMA/CD, 802.4 описывает сети с передачей маркера и
    802.5 описывает сети Token ring. Общим для всех них является стандарт 802.2,
    который определяет управление логическим каналом (LLC –
    Logical link control) и который является общим для большинства сетей 802. К
    сожалению, комбинация 802.2 и 802.3 определяет форматы фрейма отличные от
    Ethernet ([Stallings 1987] описывает все
    детали стандартов IEEE 802).

    В мире TCP/IP инкапсуляция IP датаграмм определена в RFC 894[Hornig 1984] для сетей Ethernet
    и в RFC 1042[Postel and Reynolds 1988] для
    сетей IEEE 802. В Host Requirements RFC к каждому
    компьютеру, подключенному к Internet через кабель Ethernet
    10 Мбит/сек, предъявляются следующие требования:

    1. Компьютер должен иметь возможность посылать и получать пакеты,
      инкапсулированные с использованием RFC 894 (Ethernet).
    2. У компьютера должна быть возможность получать пакеты RFC 1042 (IEEE
      802), перемешанные с пакетами RFC 894.
    3. Компьютер должен иметь возможность посылать пакеты с использованием
      инкапсуляции RFC 1042. Если компьютер может посылать оба типа пакетов, то тип
      пакета должен быть конфигурируемым, а конфигурация по умолчанию должна быть
      настроена на пакеты RFC 894.

    Наиболее широко используется инкапсуляция RFC 894. На рисунке 2.1
    показаны два различных метода инкапсуляции. Цифры под каждым квадратиком на
    рисунке это размер в байтах.

    В обоих форматах фрейма используется 48-битовый (6-байтовый) формат
    представления адресов источника и назначения (802.3 позволяет использование
    16-битных адресов, однако обычно используются 48-битные). Это как раз то, что мы
    называем по тексту аппаратными адресами (hardware
    addresses). Протоколы ARP и RARP (см. главу 4 и главу 5) устанавливают соответствие
    между 32-битными IP адресами и 48-битными аппаратными адресами.

    Следующие 2 байта в этих форматах фрейма различаются. Поле длины
    (length) 802 содержит количество следующих за ним байтов, однако не содержит в
    конце контрольной суммы. Поле тип (type) в Ethernet определяет тип данных,
    которые следуют за ним. Во фрейме 802 то же поле типа (type) появляется позже в
    заголовке протокола доступа к подсети (SNAP – Sub-Network Access Protocol). К
    счастью, величины, находящиеся в поле длины (length) 802, никогда не совпадают с
    величинами, находящимися в поле типа (type) Ethernet, поэтому эти два формата
    фрейма легко различимы.

    Во фрейме Ethernet данные следуют сразу после поля
    тип (type), тогда как во фрейме 802 за ним следуют 3 байта LLC 802.2 и 5 байт
    SNAP 802.2. Поля DSAP (точка доступа к сервису назначения –
    Destination Service Access Point) и SSAP (точка доступа к
    сервису источника – Source Service Access Point) оба установлены в 0xAA. Поле
    ctrl установлено в 3. Следующие 3 байта, org code установлены в 0. Затем идет
    2-байтовое поле тип (type), такое же, как мы видели в формате фрейма Ethernet
    (дополнительные значения, которые могут появиться в поле типа, описаны в RFC
    1340[Reynolds and Postel 1992]).

    Поле контрольной суммы (CRC)
    определяет ошибки, возникшие при транспортировке фрейма (также оно иногда
    называется FCS или последовательность контроля фрейма –
    frame check sequence).

    Минимальный размер фреймов 802.3 и Ethernet требует, чтобы размер
    данных был хотя бы 38 байт для 802.3 или 46 байт для Ethernet. Чтобы
    удовлетворить этому требованию, иногда вставляются байты заполнения, для того
    чтобы фрейм был соответствующей длины.

    Мы еще столкнемся с минимальным размером, когда будем рассматривать
    движение пакетов по кабелям. Также мы еще не раз обратимся к инкапсуляции
    Ethernet, потому что это, пожалуй, самая широко распространенная форма
    инкапсуляции.

    Address resolution protocol - что это?

    Рисунок 2.1 Инкапсуляция IEEE 802.2/802.3 (RFC 1042) и
    инкапсуляция Ethernet (RFC 894).

    Инкапсуляция завершителей

    RFC 893[Leffler and Karels 1984]
    описывает другую форму инкапсуляции, которая используется в Ethernet и
    называется инкапсуляция завершителей (trailer
    encapsulation). С ранними версиями системы BSD на DEC VAX проводился
    эксперимент, который должен был увеличить производительность путем изменения
    порядка полей в IP датаграмме. Поля с переменной длиной, которые находились в
    начале данных фрейма Ethernet (IP заголовок и TCP заголовок), переносились в
    конец, сразу после контрольной суммы. Это позволяет данным, находящимся во
    фрейме, быть спланированными в аппаратную страницу с сохранением копии в памяти,
    когда данные копируются в ядро. Данные TCP, которые кратны 512 байтам, могут
    быть перемещены путем манипулирования страницами таблиц ядра. Два компьютера
    договариваются об использовании инкапсуляции завершителей, пользуясь расширением
    ARP. Для этих фреймов определены различные значения типа фрейма Ethernet.

    В настоящее время инкапсуляция завершителей не применяется, поэтому мы
    не будем приводить примеров. Читатели, которые интересуются этой темой, могут
    обратиться к RFC 893 или разделу 11.8[Leffler et al. 1989] за более
    подробной информацией.

    SLIP: IP по последовательной линии

    SLIP – это простая форма инкапсуляции IP датаграмм
    для последовательной линии, которая описана в RFC 1055[Romkey 1988]. SLIP стал широко
    использоваться для подключения домашних систем к Internet с
    того момента, когда практически на каждом компьютере появился последовательный
    порт RS-232, а также появились высокоскоростные модемы.

    Формирование фреймов с использованием SLIP подчиняется следующим
    правилам.

    1. IP датаграмма завершается специальным символом, который называется
      END (0xc0). Для того чтобы предотвратить шум в линии, перед тем как датаграмма
      будет интерпретирована как часть датаграммы, большинство реализаций передают
      символ END также и в начале датаграммы. (Если на линии есть шум, этот END
      прекращает передачу ошибочной датаграммы и позволяет текущей датаграмме быть
      переданной. Ошибочная датаграмма будет отброшена верхним уровнем, когда он
      определит, что ее содержимое повреждено.)
    2. Если байт, находящийся в IP датаграмме, эквивалентен символу END,
      вместо него передается 2-байтовая последовательность 0xdb, 0xdc. Специальный
      символ 0xdb называется SLIP ESC символ, однако его значение отличается от
      символа ASCII ESC (0xlb).
    3. Если байт IP датаграммы равен символу SLIP ESC, вместо него
      передается 2-байтовая последовательность 0xdb, 0xdd.

    На рисунке 2.2 показан пример создания подобных фреймов, при этом в
    исходной IP датаграмме появляются один END символ и один ESC символ. В этом
    примере количество байт, переданных по последовательной линии, равно длине IP
    датаграммы плюс 4. SLIP использует довольно простой метод организации фрейма.

    Существуют несколько правил, соблюдая которые можно работать со
    SLIP.

    1. Каждая конечная система должна знать противоположный IP адрес. 
      Метода, с помощью которого одна оконечная система может сообщить удаленной
      системе свой IP адрес, не существует.
    2. Не существует поля типа (напоминающего поля типа фрейма в Ethernet).
      Если последовательная линия используется для SLIP, она не может быть
      одновременно использована для какого-либо другого протокола.
    3. SLIP не добавляет контрольную сумму (как,
      например, поле контрольной суммы – CRC во фреймах Ethernet). Если из-за шума в
      телефонной линии датаграмма будет повреждена, это повреждение определяется
      верхним уровнем. (Однако новые модели модемов могут определять и исправлять
      испорченные фреймы). Это аналогично тому, как если бы верхние уровни
      предоставляли некоторую форму контрольной суммы. В главе 3 и главе 17 мы увидим, что
      контрольная сумма всегда присутствует в IP заголовке, в TCP заголовке и в TCP
      данных. А в главе 11 мы увидим, что контрольная
      сумма для заголовка UDP и для данных UDP необязательна.

    Address resolution protocol - что это?

    Рисунок 2.2 Инкапсуляция
    SLIP.

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

    История SLIP начинается с 1984 года,
    когда Рик Адамс (Rick Adams) разработал его в 4.2BSD. Популярность этого протокола постепенно росла,
    параллельно с ней росла скорость и надежность работы модемов. В конце концов
    появились свободно распространяемые разработки, а в настоящее время большинство
    поставщиков продают и поддерживают этот протокол.

    SLIP с компрессией (CSLIP)

    Так как линии SLIP как правило медленные (19200 бит/сек или меньше) и
    часто используют для диалогового трафика (как, например, Telnet и Rlogin, оба из
    которых используют TCP), возникает необходимость уменьшить TCP пакеты,
    проходящие по SLIP каналу. Для того чтобы перенести один байт данных, требуется
    20 байт IP заголовков и 20 байт TCP заголовков, то есть вместе 40 байт (в
    разделе “Интерактивный ввод”
    главы 19 показывается поток этих маленьких пакетов при вводе простых команд в
    течение сессии Rlogin).

    После того как было определено, что с меньшими пакетами достигается
    большая производительность, новые версии SLIP стали называться CSLIP, что означает сжатый SLIP, который описан в RFC 1044[Jacobson 1990a]. CSLIP обычно уменьшает 40-байтовый заголовок до 3-5 байт. CSLIP
    поддерживает до 16 TCP соединений на каждом конце канала и знает, что каждое
    поле в двух заголовках для данного соединения обычно не изменяется. Для тех
    полей, которые все же изменяются, изменения заключаются в небольшом увеличении.
    Подобные уменьшенные заголовки значительно улучшают время отклика при диалоговой
    работе.

    Большинство разработок SLIP в
    настоящее время поддерживают CSLIP. Оба SLIP канала в подсети, приведенной на рисунке 1.11, являются каналами
    CSLIP.

    PPP: протокол точка-точка (Point-to-Point)

    PPP, протокол точка-точка, устраняет все недостатки SLIP. PPP состоит
    из трех компонентов.

    1. Способ инкапсуляции IP датаграмм в
      последовательный канал. PPP поддерживает как асинхронный
      канал с 8 битами данных без контроля четности (последовательный интерфейс,
      который присутствует на большинстве компьютеров), так и бит-ориентированный синхронный канал.
    2. Протокол управления каналом (LCP – link control
      protocol) используется для установления конфигурации и тестирования соединения.
      С его помощью оконечные системы договариваются об использовании различных
      опций.
    3. Семейство протоколов управления сетью (NCP –
      network control protocol) указывает на различные протоколы сетевого уровня. В
      настоящее время существует RFC для IP, сетевого уровня OSI,
      DECnet и Apple Talk. Например, IP NCP позволяет каждой
      оконечной системе указать, будет ли он использовать сжатие заголовков, такое же
      как в CSLIP.

    RFC 1548 [Simpson 1993] описывает метод
    инкапсуляции, который будет использоваться в протоколе управления каналом. RFC
    1332[McGregor 1992] описывает
    протокол управления сетью для IP.

    Формат PPP фреймов был выбран таким образом, чтобы напоминать стандарт
    ISO HDLC (high-level data link control)
    . На рисунке 2.3 показан формат фреймов PPP.

    Address resolution protocol - что это?

    Рисунок 2.3 Формат фреймов
    PPP.

    Каждый фрейм начинается и заканчивается с байта флаг (flag), значение
    которого равно 0x7e. Затем следует байт адреса (address), значение которого
    всегда 0xff, и затем байт управления (control), значение которого 0x03.

    Затем следует поле протокола (protocol), функции которого напоминают
    функции поля типа (type) в Ethernet. Значение 0x0021 обозначает что в
    информационном (information) поле IP датаграмма, значение 0xc021 означает что в
    информационном поле данные управления каналом, а значение 0x8021 означает –
    данные управления сетью.

    Поле контрольной суммы (CRC) используется для
    определения ошибок во фрейме.

    Так как байт со значением 0x7e является символом flag, PPP необходимо
    экранировать этот байт когда он появляется в информационном поле. В
    последовательных каналах это делается аппаратным путем с использованием техники,
    называемой битовым заполнением (bit stuffing [Tanenbaum 1989]). В асинхронных
    каналах специальный байт 0x7d используется в качестве символа экранирования
    (escape символ). Если escape символ появляется во фрейме PPP, в следующем
    символе шестой бит инвертируется следующим образом:

    1. Байт 0x7e передается как 2-байтовая последовательность 0x7d, 0x5e.
      Это экранирование байта flag.
    2. Байт 0x7d передается как 2-байтовая последовательность 0x7d, 0x5d.
      Это экранирование escape байта.
    3. По умолчанию, байт со значением меньше чем 0x20 (ASCII управляющий
      символ) также экранируется. Например, байт 0x01 передается как 2-байтовая
      последовательность 0x7d, 0x21 (в данном случае инвертирование шестого бита
      устанавливает его в единицу, однако в двух предыдущих случаях инвертирование
      устанавливает бит в ноль.

      Это делается для того, чтобы данные байты не воспринимались как
      управляющие символы ASCII последовательным драйвером на каждом хосте, или
      модеме, которые иногда интерпретируют управляющие символы особенным образом.
      Существует возможность использовать протокол управления каналом, чтобы указать
      какие из этих 32 значений должны быть экранированы. По умолчанию экранируются
      все 32.

    Так как PPP, как и SLIP, часто используется на медленных
    последовательных каналах, уменьшение количества байт во фрейме значительно
    уменьшает задержку работы диалоговых приложений. С использованием протокола
    управления каналом большинство реализаций договариваются об исключении
    постоянных полей флага (flag) и адреса (address), для того чтобы уменьшить
    размер поля протокола (protocol) с 2 байтов до 1 байта. Однако, если мы сравним
    PPP фрейм и SLIP фрейм (рисунок 2.2), мы увидим что PPP добавляет 3
    дополнительных байта: 1 байт к полю протокола (protocol) и 2 байта к контрольной
    сумме (CRC). В дополнение, с использованием протокола управления IP каналом,
    большинство реализаций договариваются об использовании алгоритма Van Jacobson для сжатия заголовка (идентично сжатию CSLIP),
    чтобы уменьшить размер IP и TCP заголовков.

    В общем случае, PPP имеет следующие преимущества перед SLIP: (1)
    поддерживает несколько протоколов на одной последовательной линии, не только IP
    датаграммы, (2) высчитывает контрольную сумму для каждого фрейма, (3)
    динамически договаривается об IP адресах для каждой оконечной системы (с
    использованием протокола управления IP каналом), (4) осуществляет сжатие
    заголовков TCP и IP так же как в CSLIP, и (5) использует протокол управления
    каналом для установления договоренности о большинстве характеристик канала.
    Цена, которую мы платим за эти возможности, это и есть три дополнительных байта,
    которые появляются во фрейме, несколько фреймов, которые используются для
    установления договоренности при установлении канала, и несколько более сложная
    реализация.

    Несмотря на все добавочные
    преимущества PPP над SLIP, на сегодняшний день пользователей SLIP значительно
    больше, чем пользователей PPP. Когда PPP станет более доступен (когда его начнут
    поддерживать производители), он скорее всего заменит собой
    SLIP.

    Интерфейс Loopback

    Большинство реализаций поддерживают интерфейс loopback, который
    позволяет клиенту и серверу на одном и том же компьютере общаться друг с другом
    используя TCP/IP. Для интерфейса loopback зарезервирована сеть класса А с
    идентификатором 127. По договоренности большинство систем добавляют IP адрес
    127.0.0.1 для этого интерфейса и дают ему имя localhost. IP
    датаграмма, посылаемая в интерфейс loopback, не попадает в сеть.

    Мы можем решить, что транспортный уровень распознает, что удаленный
    адрес – это адрес loopback и каким-либо образом сокращает процесс обработки
    датаграммы. Однако этого не происходит. Осуществляется полная обработка данных
    на транспортном и сетевом уровнях, после чего IP датаграмма направляется по
    петле назад, когда выходит вниз из сетевого уровня. На рисунке 2.4 показана
    упрощенная диаграмма того, как loopback интерфейс обрабатывает IP датаграммы.

    Address resolution protocol - что это?

    Рисунок 2.4 Обработка IP датаграмм интерфейсом
    loopback.

    На этом рисунке необходимо обратить внимание на следующее:

    1. Все что отправляется на адрес loopback (обычно 127.0.0.1), попадает
      на вход IP.
    2. Датаграммы, отправляемые на широковещательный или групповой адреса,
      копируются в интерфейс loopback и отправляются в Ethernet.
      Это осуществляется исходя из определения широковещательной или групповой
      рассылки (глава 12), которая включает в себя
      посылающий хост.
    3. Все что отправляется на любой из собственных IP адресов хоста,
      посылается на интерфейс loopback.

    Может показаться неэффективным то что транспортный и IP уровни
    обрабатывают данные, которые посылаются по петле. Однако это упрощает
    разработку, потому что интерфейс loopback для сетевого уровня выглядит просто
    как еще один канальный уровень. Сетевой уровень направляет датаграммы в
    интерфейс loopback как в любой другой канальный уровень, а затем интерфейс
    loopback помещает датаграммы обратно во входную очередь IP.

    Другой интересный момент, который можно увидеть на рисунке 2.4,
    заключается в том, что IP датаграммы, посланные на один из собственных адресов
    хоста, обычно не попадают в соответствующую сеть. В комментариях к некоторым BSD
    драйверам Ethernet устройств указывается, что большинство интерфейсных плат
    Ethernet не способны читать свою собственную передачу. Так как хост должен
    обрабатывать IP датаграммы, которые он посылает самому себе, такая обработка
    пакетов, как показано на рисунке 2.4, это простейший путь добиться
    этого.

    4.4BSD имеет
    переменную useloopback (по умолчанию устанавливается ее в
    1). Если эта переменная установлена в 0, драйвера Ethernet посылают локальные
    пакеты в сеть, вместо того чтобы посылать их в драйвер loopback. Это может работать, а может и не работать, в
    зависимости от того, какая установлена интерфейсная плата Ethernet и какой
    драйвер.

    MTU

    Как мы видели на рисунке 2.1,
    существуют ограничения, накладываемые на размер фрейма для Ethernet инкапсуляции
    и инкапсуляции 802.3. Ограничение накладывается на количество байтов данных в
    1500 и 1492 соответственно. Эта характеристика канального уровня называется максимальный блок передачи (MTU – maximum transmission unit).
    Большинство типов сетей определяют верхний предел.

    Если IP хочет отослать датаграмму, которая больше чем MTU канального
    уровня, осуществляется фрагментация (fragmentation), при
    этом датаграмма разбивается на меньшие части (фрагменты). Каждый фрагмент должен
    быть меньше чем MTU. Мы обсудим IP фрагментацию в разделе “Фрагментация IP”
    главы 11.

    На рисунке 2.5 приведен список некоторых типичных значений MTU, взятых
    из RFC 1191[Mogul and Deering 1990]. Здесь
    приведены MTU для каналов точка-точка (таких как SLIP или PPP), однако они не являются физической характеристикой среды
    передачи. Это логическое ограничение, при соблюдении которого обеспечивается
    адекватное время отклика при диалоговом использовании. В разделе “Вычисление загруженности последовательной
    линии”
    главы 2 мы рассмотрим, откуда берется это ограничение.

    В разделе “Команда netstat”
    главы 3 мы воспользуемся командой netstat, чтобы определить
    MTU для определенного интерфейса.

    Network

    MTU (байты)

    Hyperchannel

    65535

    16 Мбит/сек Token ring (IBM)

    17914

    4 Мбит/сек Token ring (IEEE
    802.5)

    4464

    FDDI

    4352

    Ethernet

    1500

    IEEE
    802.3/802.2

    1492

    X.25

    576

    Точка-точка (с маленькой
    задержкой)

    296

    Рисунок 2.5 Типичные значения максимальных блоков передачи (MTU).

    Транспортный MTU

    Когда общаются два компьютера в одной и той же сети, важным является
    MTU для этой сети. Однако, когда общаются два компьютера в разных сетях, каждый
    промежуточный канал может иметь различные MTU. В данном случае важным является
    не MTU двух сетей, к которым подключены компьютеры, а наименьший MTU любого
    канала данных, находящегося между двумя компьютерами. Он называется транспортным
    MTU (path MTU).

    Транспортный MTU между любыми двумя хостами может быть не постоянным.
    MTU зависит от загруженности канала на настоящий момент. Также он зависит от
    маршрута. Маршрут может быть несимметричным (маршрут от A до B может быть совсем
    не тем, что маршрут от B к A), поэтому MTU может быть неодинаков для этих двух
    направлений.

    RFC 1191[Mogul and Deering 1990]
    описывает механизм определения транспортного MTU (path MTU discovery mechanism).
    Мы рассмотрим как функционирует этот механизм после того, как опишем
    фрагментацию ICMP и IP. В разделе “ICMP ошибки о
    недоступности”
    главы 11 мы подробно рассмотрим ошибку недоступности
    ICMP, которая используется в этом механизме, а в разделе “Определение транспортного MTU
    с использованием Traceroute”
    главы 11 мы покажем версию программы traceroute, которая использует механизм определения
    транспортного MTU до пункта назначения. В разделах “Определение транспортного MTU
    при использовании UDP”
    главы 11 и “Определение транспортного
    MTU”
    главы 24 показано, как функционируют UDP и TCP, когда реализация
    поддерживает определение MTU.

    Вычисление загруженности последовательной
    линии

    Если скорость в линии составляет 9600 бит/сек, при этом 1 байт
    составляет 8 бит плюс 1 старт-бит и 1 стоп-бит, скорость линии будет 960
    байт/сек. Передача пакета размером 1024 байта с этой скоростью займет 1066 мс.
    Если мы используем SLIP канал для диалогового приложения и одновременно с ним
    работает такое приложение как FTP, которое посылает или принимает пакеты по 1024
    байт, мы должны ждать, так как среднее время задержки нашего интерактивного
    пакета составит 533 мс.

    Это означает, что наш диалоговый пакет будет послан по каналу перед
    любым другим “большим” пакетом. Большинство SLIP
    приложений предоставляют разделение пакетов по типу сервиса, отправляя
    диалоговый трафик перед трафиком передачи данных. Диалоговый трафик это, как
    правило, Telnet, Rlogin и управляющая часть (пользовательские команды, но не
    данные) FTP.

    Естественно, что такое разделение по
    сервисам несовершенно. Оно не оказывает никакого воздействия на неинтерактивный
    трафик, который уже поставлен в очередь на передачу (например, последовательным
    драйвером). Новые модели модемов, которые имеют большие буферы и позволяют
    сбуферизировать неинтерактивный трафик в буфере модема, что также сказывается на
    задержке диалогового трафика.

    Ожидание в 533 мс неприемлемо для диалогового ответа. С точки зрения
    человеческого фактора мы знаем, что неприемлемой является задержка дольше чем
    100-200 мс [Jacobson 1990a]. Под задержкой подразумевается
    время между отправкой пакета и возвращением отклика (как правило, эхо символа).

    Уменьшение MTU в канале SLIP до 256 означает, что максимальное время,
    в течение которого канал может быть занят одним фреймом, составляет 266 мс, и
    половина от этого (наше среднее время ожидания) составляет 133 мс. Это лучше,
    однако до сих пор не идеально. Причина, по которой мы выбрали это значение (как
    сравниваются 64 и 128), заключается в том, чтобы обеспечить лучшее использование
    канала для передачи данных (как, например, при передаче большого файла). В
    случае CSLIP фрейма размером 261 байт с заголовком размером в 5 байт (256 байт
    данных), 98,1% линии используются для передачи данных и 1,9% на заголовки.
    Уменьшение MTU меньше чем 256 уменьшает максимальное значение пропускной способности линии, которую мы можем получить при
    передаче данных.

    Значение MTU равное 296 для канала точка-точка (рисунок 2.5),
    подразумевает 256 байт данных и 40 байт TCP и IP заголовков. Так как MTU это
    величина, о которой IP узнает от канального уровня, это значение должно включать
    в себя стандартные заголовки TCP и IP. Именно таким образом IP принимает решение
    о фрагментации. IP ничего не знает о сжатии заголовков, которое осуществляются
    CSLIP.

    Наш расчет средней задержки (половина того времени, которое требуется
    на передачу фрейма максимального размера) имеет отношение только к каналу SLIP
    (или каналу PPP), который используется для передачи интерактивного трафика и
    трафика данных. Когда идет обмен только интерактивным трафиком, время передачи
    одного байта данных в каждом направлении (в случае сжатого 5-байтового
    заголовка) составляет примерно 12,5 мс, при скорости 9600 бит/сек. Это хорошо
    укладывается в диапазон 100-200 мс, о котором мы упоминали ранее. Также
    заметьте, что сжатие заголовков с 40 до 5 байт уменьшает время задержки для
    одного байта с 85 до 12,5 мс.

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

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

    Краткие выводы

    В этой главе рассматривался самый нижний уровень из семейства
    протоколов Internet, канальный уровень. Мы рассмотрели различие между Ethernet и
    IEEE 802.2/802.3 инкапсуляциями, и инкапсуляцию, которая используется в SLIP и
    PPP. Так как оба SLIP и PPP часто используются на медленных каналах, они
    предоставляют методы, для сжатия общих полей (которые практически всегда
    неизменны). При этом улучшается время отклика.

    Интерфейс loopback существует в большинстве
    разработок. Доступ к этому интерфейсу может быть получен через специальный
    адрес, обычно 127.0.0.1, или путем посылки IP датаграмм на один из собственных
    IP адресов хоста. Данные, отправленные в loopback интерфейс, полностью
    обрабатываются транспортным уровнем и IP, когда они проходят по петле по стеку
    протоколов.

    Мы описали важную характеристику большинства канальных уровней, MTU и
    соответствующую концепцию транспортного MTU. Используя стандартный MTU для
    последовательных линий, мы вычислили временную задержку, которая существует в
    каналах SLIP и CSLIP.

    В этой главе рассматривается только несколько общих канальных
    технологий, используемых сегодня в TCP/IP. Одна из причин, по которой TCP/IP
    успешно используется, это возможность работать поверх практически любых
    канальных технологий.

    Упражнения

    Если Ваша система поддерживает команду netstat(1) (см. главу 3, раздел “Команда
    netstat”
    ), используйте ее, чтобы определить интерфейсы в Вашей системе
    и их MTU.

    §

    Введение

    IP это рабочая лошадь семейства протоколов TCP/IP.
    TCP, UDP, ICMP и IGMP передают свои данные как IP датаграммы (рисунок 1.4). Один
    факт часто удивляет новичков TCP/IP, особенно тех кто работал с X.25 или SNA,
    этот факт заключается в том, что IP ненадежный протокол, предоставляющий сервис
    доставки датаграмм без соединения.

    Под словом ненадежный мы подразумеваем то, что не
    существует гарантии того, что IP датаграмма успешно достигнет пункта назначения.
    Однако IP предоставляет определенный сервис обработки некоторых событий. Когда
    что-нибудь идет не так как хотелось бы, как например, временное переполнение
    буфера у маршрутизатора, IP применяет простой алгоритм обработки ошибок: он
    отбрасывает датаграмму и старается послать ICMP сообщение отправителю. Любая
    требуемая надежность должна быть обеспечена верхними уровнями (например TCP).

    Термин без соединения (connectionless) означает,
    что IP не содержит никакой информации о продвижении датаграмм. Каждая датаграмма
    обрабатывается независимо от других. Это также означает, что может быть
    доставлена испорченная датаграмма. Если источник отправляет две последовательные
    датаграммы (первая A, затем B) в один и то же пункт назначения, каждая из них
    маршрутизируется независимо и может пройти по разным маршрутам, датаграмма B
    может прибыть раньше чем A.

    В этой главе мы кратко рассмотрим поля IP заголовка, опишем IP
    маршрутизацию и коротко опишем разделение на подсети. Также мы рассмотрим две
    очень полезные команды: ifconfig и netstat. Более подробно некоторые поля IP заголовка будут
    описаны в следующих главах. Именно тогда мы точно расскажем, как используются те
    или иные поля. RFC 791 [Postel 1981a] является официальной
    спецификацией для IP.

    IP заголовок

    На рисунке 3.1 показан формат IP датаграммы. Стандартный размер IP
    заголовка составляет 20 байт, если не присутствуют опции.

    Address resolution protocol - что это?

    Рисунок 3.1 IP датаграмма, поля IP
    заголовка.

    Мы покажем как выглядят заголовки протоколов TCP/IP на рисунке 3.1.
    Старший значащий бит имеет номер 0 (слева), а младший значащий бит из 32-х бит
    имеет номер 31 и показан справа.

    4 байта из 32-битного значения передаются в следующем порядке: сначала
    биты 0 – 7, затем биты 8 – 15, затем 16 – 23 и, наконец, 24 – 31. Такой порядок
    движения байтов называется big endian (Примечание
    переводчика: big endian – метод хранения или передачи данных, при котором
    старший значащий бит или байт стоит первым) и обязателен для всех двоичных целых
    чисел в TCP заголовках при их передаче по сети. Это называется порядок сетевых байтов (network byte order). Машины, которые
    хранят двоичные целые в других форматах, как например в формате little endian (little endian – метод хранения или передачи
    данных, при котором младший значащий бит или байт стоит первым), должны
    конвертировать значения заголовков в соответствующий порядок сетевых байтов
    перед передачей данных.

    Текущая версия протокола – 4, поэтому IP иногда называется IPv4. В разделе “Будущее
    IP”
    рассматриваются некоторые предложения для новых версий IP.

    Длина заголовка (header length) это количество
    32-битных слов в заголовке, включая любые опции. Так как это 4-битное поле, оно
    ограничивает размер заголовка в 60 байт. В главе 8 мы увидим, что это ограничение
    сильно влияет на некоторые опции, такие как опция записи маршрута. Обычная
    величина в этом поле (когда отсутствуют опции) – 5.

    Поле типа сервиса (TOS – type-of-service) состоит
    из 3-битного поля приставки (которое в настоящее время игнорируется), 4 бит TOS
    и неиспользуемого бита, который должен быть равен 0. 4 бита TOS следующие:
    минимальная задержка, максимальная пропускная способность, максимальная
    надежность и минимальная стоимость. Только один из этих 4 бит может быть
    установлен в единицу одновременно. Если все 4 бита равны 0, это означает обычный
    сервис. RFC 1340 [Reynolds and Postel 1992] указывает, как
    эти биты должны быть установлены для всех стандартных приложений. RFC 1349 [Almquist 1992] содержит некоторые коррекции для этого RFC и
    более детальное описание характеристики TOS.

    На рисунке 3.2 показаны рекомендованные значения поля TOS для
    различных приложений. В последней колонке приведено шестнадцатиричное значение,
    которое мы увидим в выводе tcpdump позже в этой главе.

    Приложение

    Минимизация задержки

    Максимизация производительности

    Максимизация надежности

    Минимизация стоимости

    Шестнадцатиричное значение

    Telnet/Rlogin

    1

    0x10

    FTP     

    управление

    1

    0x10

    данные

    1

    0x08

    любые данные

    1

    0x08

    TFTP

    1

    0x10

    SMTP     

    фаза команд

    1

    0x10

    фаза данных

    1

    0x08

    DNS     

    UDP запрос

    1

    0x10

    TCP запрос

    0x00

    передача зоны

    1

    0x08

    ICMP     

    ошибка

    0x00

    запрос

    0x00

    любой IGP

    1

    0x04

    SNMP

    1

    0x04

    BOOTP

    0x00

    NNTP

    1

    0x02

    Рисунок 3.2 Рекомендованные значения поля типа сервиса (TOS).

    Диалоговые приложения, Telnet и Rlogin, требуют свести к минимуму
    задержку, так как они используются человеком интерактивно и осуществляют
    небольшую передачу данных. Передача файлов с использованием FTP, с другой
    стороны, требует максимальной пропускной способности. Максимальная надежность
    необходима для сетевого управления (SNMP) и для протоколов маршрутизации.
    Новости Usenet (NNTP) это единственное приложение, которое
    требует минимизации стоимости.

    Характеристика TOS, в настоящее время, большинством реализаций TCP/IP
    не поддерживается, однако она включена в новые системы, начиная с 4.3BSD Reno. Некоторые протоколы маршрутизации, такие как OSPF
    и IS-IS, имеют возможность принимать решение о маршрутизации
    на основе этого поля.

    В разделе “Вычисление загруженности
    последовательной линии”
    главы 2 мы упомянули, что драйверы SLIP обычно
    осуществляют построение очереди на основе типа сервиса, что позволяет
    диалоговому траффику обрабатываться перед передачей данных. Так как большинство
    реализаций не используют поле TOS, это построение очереди делается с помощью
    драйвера SLIP, который смотрит в поле протокола (для того
    чтобы определить, является ли данный сегмент TCP сегментом или нет) и затем
    проверяет номера портов TCP источника и назначения, чтобы определить,
    принадлежит ли этот номер диалоговому сервису.

    Поле полной длины (total length) содержит полную
    длину IP датаграммы в байтах. Благодаря этому полю и полю
    длины заголовка, мы знаем, с какого места начинаются данные в IP датаграмме и их
    длину. Так как это поле состоит из 16 бит, максимальный размер IP датаграммы
    составляет 65535 байт. (Обратитесь к рисунку 2.5 и обратите внимание, что
    Hyperchannel имеет MTU, равный 65535. В действительности это не MTU – здесь
    используется максимально возможный размер IP датаграммы). Это поле изменяется в
    момент фрагментации и повторной сборки датаграммы, что будет описано в разделе
    “Фрагментация IP”
    главы 11.

    Несмотря на то что существует возможность отправить датаграмму
    размером 65535 байт, большинство канальных уровней поделят подобную датаграмму
    на фрагменты. Более того, от хоста не требуется принимать датаграмму размером
    больше чем 576 байт. TCP делит пользовательские данные на части, поэтому это
    ограничение обычно не оказывает влияния на TCP. Что касается UDP, услугами
    которого пользуются многие приложения (RIP, TFTP, BOOTP, DNS, SNMP), то он
    ограничивает себя 512 байтами пользовательских данных, что даже меньше
    ограничения в 576 байт. Большинство приложений в настоящее время (особенно те,
    которые поддерживают NFS – Network File System) позволяют использовать IP
    датаграмму размером 8192 байта.

    Однако, поле полной длины требуется в IP заголовке
    для некоторых каналов (как например, Ethernet), который дополняет маленькие
    фреймы до минимальной длины. Несмотря на то что минимальный размер фрейма
    Ethernet составляет 46 байт (рисунок 2.1), IP датаграмма может быть еще меньше.
    Если поле полной длины не было представлено, IP уровень не будет знать, сколько
    46-байтных фреймов Ethernet получится из IP датаграммы.

    Поле идентификации (identification) уникально
    идентифицирует каждую датаграмму, отправленную хостом. Значение, хранящееся в
    поле, обычно увеличивается на единицу с посылкой каждой датаграммы. Мы обратимся
    к этому полю, когда будем рассматривать фрагментацию и обратную сборку в разделе “Фрагментация IP”
    главы 11. Там же мы рассмотрим поле флагов (flags) и поле смещения фрагментации
    (fragmentation offset).

    RFC 791 [Postel
    1981a] сообщает, что поле идентификации должно быть выбрано верхним уровнем,
    который отправляет IP датаграммы, а это означает, что две последовательно
    отправленные IP датаграммы, одна из которых сгенерирована TCP, а другая – UDP,
    должны иметь одно и то же поле идентификации. Подобный подход работает (алгоритм
    сборки может обработать такую ситуацию). Однако, большинство реализаций,
    произошедших от Berkeley, увеличивают соответствующую переменную в ядре IP
    уровня каждый раз, когда отправляется IP датаграмма, вне зависимости от того
    какой уровень отправляет данные через IP. Переменная ядра инициируется каждый
    раз в момент загрузки системы.

    Поле времени жизни (TTL – time-to-live) содержит
    максимальное количество пересылок (маршутизаторов), через которые может пройти
    датаграмма. Это поле ограничивает время жизни датаграммы. Значение
    устанавливается отправителем (как правило 32 или 64) и уменьшается на единицу
    каждым маршрутизатором, который обрабатывает датаграмму. Когда значение в поле
    достигает 0, датаграмма удаляется, а отправитель уведомляется об этом с помощью
    ICMP сообщения. Подобный алгоритм предотвращает зацикливание пакетов в петлях
    маршрутизации. Мы вернемся к этому полю в главе 8, когда будем рассматривать
    программу Traceroute.

    Мы говорили о поле протокола (protocol) в главе 1 и
    показали на рисунке 1.8 как оно используется
    в IP для демультиплексирования входящих датаграмм. Это поле
    указывает, какой протокол отправил данные через IP.

    Контрольная сумма заголовка (header checksum)
    рассчитывается только для IP заголовка. Она не включает в себя данные, которые
    следуют за заголовком. ICMP, IGMP, UDP и TCP имеют контрольные суммы в своих
    собственных заголовках, которые охватывают их заголовки и данные.

    Чтобы рассчитать контрольную сумму IP для
    исходящей датаграммы, поле контрольной суммы сначала устанавливается в 0. Затем
    рассчитывается 16-битная сумма с поразрядным дополнением (One’s complement –
    поразрядное дополнение к двоичной системе.) (заголовок целиком воспринимается
    как последовательность 16-битных слов). 16-битное поразрядное дополнение этой
    суммы сохраняется в поле контрольной суммы. Когда IP датаграмма принимается,
    вычисляется 16-битная сумма с поразрядным дополнением. Так как контрольная
    сумма, рассчитанная приемником, содержит в себе контрольную сумму, сохраненную
    отправителем, контрольная сумма приемника состоит из битов равных 1, если в
    заголовке ничего не было изменено при передаче. Если в результате не получились
    все единичные биты (ошибка контрольной суммы), IP отбрасывает принятую
    датаграмму. Сообщение об ошибке не генерируется. Теперь задача верхних уровней
    каким-либо образом определить, что датаграмма отсутствует, и обеспечить
    повторную передачу.

    ICMP, IGMP, UDP и TCP используют такой же алгоритм расчета контрольной
    суммы. Также TCP и UDP включают в себя различные поля из IP заголовка, в
    дополнение к своим собственным заголовкам и данным. RFC 1071 [Braden, Borman, and Partridge 1988] описывает технику и
    реализацию расчета контрольной суммы Internet. Так как маршрутизаторы обычно
    изменяют только поле TTL (уменьшают его значение на единицу), маршрутизатор
    может просто увеличить на единицу контрольную сумму, когда он перенаправляет
    полученную датаграмму, вместо того чтобы рассчитывать контрольную сумму заново
    для IP заголовка в целом. RFC 1141 [Mallory and Kullberg
    1990] описывает как этого можно добиться.

    Стандартные реализации BSD, однако,
    не используют метод обновления контрольной суммы на единицу при перенаправлении
    датаграммы.

    Каждая IP датаграмма содержит IP адрес источника (source IP address) и
    IP адрес назначения (destination IP address). Это 32-битные значения, которые мы
    описали в разделе “Адресация Internet”
    главы 1.

    И последнее поле – поле опций (options), это
    список дополнительной информации переменной длины. В настоящее время опции
    определены следующим образом:

    • безопасность и обработка ограничений (для военных
      приложений, описано в RFC 1108 [Kent 1991]),
    • запись маршрута (запись каждого маршрута и его IP адрес, глава 7,
      раздел “Опция записи IP
      маршрута”
      ),
    • временная марка (запись каждого маршрута, его IP адрес и время, глава
      7, раздел “IP опция временной
      марки”
      ),
    • свободная маршрутизация от источника (указывает список IP адресов,
      через которые должна пройти датаграмма, глава 8, раздел “Опция IP маршрутизации от
      источника”
      ), и
    • жесткая маршрутизация от источника (то же самое, что и в предыдущем
      пункте, однако IP датаграмма должна пройти только через указанные в списке
      адреса, глава 8, раздел “Опция IP маршрутизации от
      источника”
      ).

    Эти опции редко используются и не все хосты или маршрутизаторы
    поддерживают все опции.

    Поле опций всегда ограничено 32 битами. Байты заполнения, значение
    которых равно 0, добавляются по необходимости. Благодаря этому IP заголовок
    всегда кратен 32 битам (как это требуется для поля длины
    заголовка).

    IP маршрутизация

    IP маршрутизация это довольно простой процесс, особенно с точки зрения
    хоста. Если пункт назначения напрямую подключен к хосту (например канал
    точка-точка) или хост включен между несколькими сетями (Ethernet или Token
    ring), IP датаграмма направляется непосредственно в пункт назначения, иначе хост
    посылает датаграмму на маршрутизатор по умолчанию, тем самым предоставляя
    маршрутизатору решать как доставить датаграмму в пункт назначения. Эту простую
    схему реализуют практически все хосты.

    В этом разделе и в главе 9 мы рассмотрим наиболее общие
    случаи, когда IP уровень может быть сконфигурирован таким образом, чтобы
    выполнять функции маршрутизации, в дополнение к тому, что он работает в качестве
    сетевого интерфейса. Большинство многопользовательских систем в настоящее время,
    включая практически каждую UNIX систему, могут быть сконфигурированы таким
    образом, чтобы выступать в роли маршрутизатора. Существует возможность указать
    простой алгоритм маршрутизации, который будет использоваться как хостом, так и
    маршрутизатором. Основная и фундаментальная разница между хостом и
    маршрутизатором заключается в том, что хост никогда не перенаправляет датаграммы
    с одного своего интерфейса на другой, тогда как маршрутизатор перенаправляет. Мы
    рассмотрим более подробно опции конфигурирования в разделе “Перенаправлять или не
    перенаправлять”
    главы 9.

    В соответствии с общей схемой, IP может получать датаграммы от
    собственных уровней TCP, UDP, ICMP и IGMP (это датаграммы, формирующиеся здесь
    же), которые необходимо отправить, однако датаграммы могут быть приняты с
    какого-либо сетевого интерфейса (эти датаграммы должны быть перенаправлены). IP
    уровень имеет в памяти таблицу маршрутизации, которую он просматривает каждый
    раз при получении датаграммы, которую необходимо перенаправить. Когда датаграмма
    принята с сетевого интерфейса, IP, во-первых, проверяет, не принадлежит ли ему
    указанный IP адрес назначения или не является ли этот IP адрес
    широковещательным. Если это так, то датаграмма доставляется в модуль протокола,
    указанный в поле протокола в IP заголовке. Если датаграмма
    не предназначается для этого IP уровня (1), если IP уровень был сконфигурирован
    для того чтобы работать как маршрутизатор, пакет перенаправляется (в этом случае
    датаграмма обрабатывается как исходящая, что будет описано ниже), иначе (2)
    датаграмма молча уничтожается.

    Каждый пункт таблицы маршрутизации содержит следующую
    информацию:

    • IP адрес назначения. Это может быть как полный адрес хоста (host
      address) или адрес сети (network address), что указывается в поле флагов
      (описывается ниже). Адрес хоста имеет ненулевое значение идентификатора хоста
      (рисунок 1.5) и указывает на один конкретный хост, тогда как адрес сети имеет
      идентификатор хоста, установленный в 0, и указывает на все хосты, включенные в
      определенную сеть (Ethernet, Token ring).
    • IP адрес маршрутизатора следующей пересылки
      (next-hop router), или, иначе говоря, IP адрес непосредственно подключенной
      сети. Маршрутизатор следующей пересылки принадлежит одной из непосредственно
      подключенных сетей, в которую мы можем отправить датаграммы для их доставки.
      Маршрутизатор следующей пересылки это не конечный пункт назначения, однако он
      принимает датаграммы, которые мы посылаем, и перенаправляет их в направлении
      конечного пункта.
    • Флаги. Один флаг указывает, является ли IP адрес пункта назначения,
      адресом сети или адресом хоста. Другой флаг указывает на то, является ли
      маршрутизатор следующей пересылки действительно маршрутизатором или это
      непосредственно подключенный интерфейс (мы опишем эти флаги в разделе “Принципы
      маршрутизации”
      главы 9).
    • Указание на то, на какой сетевой интерфейс должны быть переданы
      датаграммы для передачи.

    IP маршрутизация осуществляется по принципу пересылка-за-пересылкой.
    Как мы можем увидеть из таблицы маршрутизации, IP не знает полный маршрут к
    пункту назначения (за исключением тех пунктов назначения, которые
    непосредственно подключены к посылающему хосту). Все что может предоставить IP
    маршрутизация – это IP адрес маршрутизатора следующей пересылки, на который
    посылается датаграмма. При этом делается предположение, что маршрутизатор следующей пересылки ближе к пункту назначения,
    чем посылающий хост. Также делается предположение, что маршрутизатор следующей
    пересылки напрямую подключен к посылающему хосту.

    IP маршрутизация осуществляет следующие действия:

    1. Осуществляется поиск в таблице маршрутизации, при этом ищется пункт,
      который совпадет с полным адресом пункта назначения (должен совпасть
      идентификатор сети и идентификатор хоста). Если пункт найден в таблице
      маршрутизации, пакет посылается на указанный маршрутизатор следующей пересылки
      или на непосредственно подключенный интерфейс (в зависимости от поля флагов).
      Как правило, так определяются каналы точка-точка, при этом другой конец такого
      канала, как правило, является полным IP адресом удаленного хоста.
    2. Осуществляется поиск в таблице маршрутизации пункта, который
      совпадет, как минимум, с идентификатором сети назначения. Если пункт найден,
      пакет посылается на указанный маршрутизатор следующей пересылки или на
      непосредственно подключенный интерфейс (в зависимости от поля флагов).
      Маршрутизация ко всем хостам, находящимся в сети назначения, осуществляется с
      использованием этого единственного пункта таблицы маршрутизации. Например, все
      хосты локальной сети Ethernet представляются в таблицах маршрутизации именно
      таким образом. Эта проверка совпадения идентификатора сети осуществляется с
      использованием возможной маски подсети, которую мы опишем в следующем
      разделе.
    3. В таблице маршрутизации ищется пункт, помеченный “по
      умолчанию” (default). Если пункт найден, пакет отсылается на указанный
      маршрутизатор по умолчанию.

    Если ни один из шагов не дал положительного результата, датаграмма
    считается недоставленной. Если недоставленная датаграмма была сгенерирована
    данным хостом, то обычно возвращается ошибка “хост недоступен” (host
    unreachable) или “сеть недоступна” (network unreachable). Этот код
    ошибки возвращается приложению, которое сгенерировало датаграмму.

    В начале всегда осуществляется сравнение на совпадение полного адреса
    хоста, после чего осуществляется сравнение идентификатора сети. Только в том
    случае, если результат обеих сравнений отрицательный, используется маршрут по
    умолчанию. Маршруты по умолчанию и сообщения ICMP о
    перенаправлении, отправляемые на маршрутизатор следующей пересылки (если для
    датаграммы выбрано неверное направление по умолчанию), являются довольно мощными
    характеристиками IP маршрутизации, к которым мы еще вернемся в главе 9.

    Еще одна фундаментальная характеристика IP маршрутизации заключается в
    возможности указать маршрут к сети, вместо того, чтобы указывать маршрут к
    каждому отдельно взятому хосту. Именно поэтому хосты включенные в Internet,
    например, имеют в своих таблицах маршрутизации тысячи пунктов, вместо того чтобы
    содержать в них не более чем миллион пунктов.

    Примеры

    Для начала представим себе простой пример: хост bsdi имеет IP
    датаграмму, которую необходимо послать на хост sun. Оба хоста находятся в одной
    и той же сети Ethernet (рисунок находится на внутренней стороне обложки). На
    рисунке 3.3 показан процесс доставки датаграммы.

    Когда IP принимает датаграмму от одного из верхних уровней, он
    просматривает свою таблицу маршрутизации и определяет, что IP адрес назначения
    (140.252.13.33) непосредственно подключен к сети (Ethernet 140.252.13.0). В
    таблице маршрутизации найден совпадающий адрес сети (в следующем разделе мы
    увидим, что благодаря разбиению на подсети сетевой адрес этого Ethernet в
    действительности 140.252.13.32, однако это не влияет на маршрутизацию).

    Датаграмма передается в драйвер устройства Ethernet и посылается на
    sun в виде Ethernet фрейма (рисунок 2.1). Адрес назначения в IP датаграмме это
    IP адрес Sun (140.252.13.33), а адрес назначения в заголовке канального уровня
    это 48-битовый Ethernet адрес интерфейса Ethernet машины sun. 48-битный Ethernet
    адрес получается с использованием ARP, как это делается – мы увидим в следующей
    главе.

    Рассмотрим еще один пример: bsdi имеет IP датаграмму, которую
    необходимо послать на хост ftp.uu.net, IP адрес которого 192.48.96.9. На рисунке
    3.4 показан путь датаграммы через первые три маршрутизатора. bsdi просматривает
    свою таблицу маршрутизации, однако не находит совпадающий хост или совпадающую
    сеть. Он использует пункт таблицы маршрутизации по умолчанию, в соответствии с
    которым необходимо послать датаграмму на sun, который является маршрутизатором следующей пересылки. Когда датаграмма
    передается от bsdi к sun, IP адрес для нее это конечный адрес назначения
    (192.48.96.9), однако адрес канального уровня – это 48-битный Ethernet адрес
    интерфейса Ethernet машины sun. Сравните эту датаграмму с одной из показанных на
    рисунке 3.3, где IP адрес назначения и адрес назначения канального уровня
    указывают на один и тот же хост (sun).

    Когда sun получает датаграмму, он понимает, что IP адрес назначения
    этой датаграммы не его собственный, и если sun сконфигурирован для того чтобы
    выполнять функции маршрутизатора, он перенаправляет датаграмму. Происходит
    просмотр его таблицы маршрутизации, в результате чего выбирается пункт по
    умолчанию. Из этого пункта следует, что sun должен перенаправить датаграмму на
    маршрутизатор следующей пересылки – netb, IP адрес которого 140.252.1.183.
    Датаграмма пересылается по SLIP каналу точка-точка с использованием минимальной
    инкапсуляции, показанной на рисунке 2.2. Мы не показываем заголовок канального
    уровня, как в случае с Ethernet, потому что его нет в случае SLIP канала.

    Когда netb получает датаграмму, он осуществляет те же самые шаги,
    которые только что осуществил sun: датаграмма не предназначается какому-либо из
    его IP адресов, а так как netb сконфигурирован так, чтобы выполнять функции
    маршрутизатора, он перенаправляет датаграмму. В данном случае также используется
    пункт таблицы маршрутизации по умолчанию, при этом датаграмма посылается на
    маршрутизатор следующей пересылки gateway (140.252.1.4). С использованием ARP в
    сети Ethernet 140.252.1, netb получает 48-битный Ethernet адрес соответствующий
    адресу 140.252.1.4. Именно этот Ethernet адрес становится адресом назначения в
    заголовке канального уровня.

    gateway осуществляет те же шаги, как и два предыдущих маршрутизатора,
    в его таблице маршрутизации пункт по умолчанию указывает на адрес 140.252.104.2
    как на адрес маршрутизатора следующей пересылки (мы
    убедимся, что этот маршрутизатор является маршрутизатором следующей пересылки
    для gateway с использованием Traceroute на рисунке 8.4).

    Из приведенного примера можно сделать несколько важных
    выводов.

    1. Все хосты и маршрутизаторы в данном примере используют маршрут по
      умолчанию.
    2. IP адрес назначения датаграммы никогда не меняется. (В разделе “Опция IP маршрутизации от
      источника”
      главы 8 мы увидим, что это не всегда верно, если
      используется маршрутизация от источника, что бывает довольно редко.) Все решения
      о маршрутизации основываются на этом адресе назначения.
    3. Для каждого канала могут быть использованы различные заголовки
      канального уровня, а адрес назначения канального уровня (если присутствует)
      всегда содержит адрес маршрутизатора следующей пересылки. В нашем примере
      датаграммы, инкапсулированные во фреймы канального уровня, содержали Ethernet
      адрес следующей пересылки, однако SLIP не содержал. Адреса Ethernet обычно
      получаются с использованием ARP.

    Address resolution protocol - что это?

    Рисунок 3.4 Путь датаграммы от bsdi к ftp.uu.net
    (192.48.96.9).

    В главе 9 мы снова вернемся к IP
    маршрутизации, после того как расскажем об ICMP. Также мы рассмотрим некоторые
    примеры таблиц маршрутизации и примеры того, как они используются при принятии
    решений о маршрутизации.

    Адресация подсетей

    В настоящее время существует требование, чтобы все хосты поддерживали
    адресацию подсетей (RFC 950 [Mogul and Postel 1985]). Теперь
    IP адрес не делится просто на идентификатор сети и идентификатор хоста:
    идентификатор хоста делится на идентификатор подсети и идентификатор хоста.

    В сетях класса A и в сетях класса B адреса отводится слишком много бит
    на идентификатор хоста: 224 – 2 и 216 – 2 соответственно.
    Как правило, такое количество хостов не подключается к одной сети. (На рисунке
    1.5 показан формат IP адресов сетей различных классов сетей.) В данном случае
    вычитается 2, потому что идентификатор хоста из всех нулевых битов или всех
    единичных битов не используется.

    После получения от InterNIC идентификатора сети
    определенного класса, системный администратор решает, делить ли сеть на подсети
    или нет, а если делить, то сколько бит будет отведено на идентификатор подсети и
    сколько на идентификатор хоста. Например, сети, описываемые в этом тексте, имеют
    адреса класса В (140.252), а из оставшихся 16 бит 8 отводятся под идентификатор
    подсети, а 8 на идентификатор хоста. Это показано на рисунке 3.5.

    Address resolution protocol - что это?

    Рисунок 3.5 Разделение на подсети адреса класса
    B.

    Подобное разделение позволяет создать 254 подсети по 254 хоста в
    каждой.

    Большинство администраторов использует 8 из 16-ти бит идентификатора
    хоста в сети класса В, для выделения подсетей. Это позволяет легко выделить
    идентификатор подсети из десятичного сетевого адреса, при этом для сетей
    класса А или класса В можно выделить различное количество битов для организации
    подсетей.

    В большинстве примеров разделение на подсети осуществляется с адресами
    класса В. Поделить на подсети можно и адреса класса С, однако в этом случае на
    идентификатор подсети выделяется очень мало битов. Разделение на подсети очень
    редко применяется по отношению адресов класса А, потому что адресов класса А
    очень мало (однако, большинство адресов класса А поделено на подсети).

    Разделение на подсети скрывает детали внутренней организации сети от
    внешних маршрутизаторов. В нашем примере, все IP адреса имеют идентификатор сети
    класса В – 140.252, однако в ней существует более чем 30 подсетей и более чем
    400 хостов, распределенных по этим подсетям. Один маршрутизатор обеспечивает
    подключение к Internet, как показано на рисунке 3.6.

    На этом рисунке мы пометили большинство маршрутизаторов как Rn, где n
    это номер подсети. Мы показали маршрутизаторы, которые соединяют эти подсети
    вместе с девятью системами, которые показаны на рисунке, находящимся на
    внутренней стороне обложки. Сети Ethernet показаны жирными линиями, а каналы
    точка-точка показаны пунктиром. Мы показали не все хосты, находящиеся в
    различных подсетях. Например, более 50 хостов находятся в подсети 140.252.3 и
    более 100 в подсети 140.252.1.

    Преимущество использования подсети заключается в том, что используется
    один адрес класса В с 30 подсетями, а не 30 адресов класса С. При этом
    разделение на подсети уменьшает размер таблиц маршрутизации Internet. Факт того
    что адрес сети класса В 140.252 поделен на подсети говорит о том, что они
    прозрачны для всех маршрутизаторов Internet, кроме тех, которые находятся в
    подсети 140.252.

    Address resolution protocol - что это?

    Рисунок 3.6 Настройки большинства подсетей noao.edu 140.252.

    Для того чтобы получить доступ к хосту, IP адрес которого начинается с
    140.252, внешний маршрутизатор должен всего лишь знать путь к IP адресу
    140.252.104.1. Это означает, что для доступа ко всем сетям 140.252 необходим
    только один пункт в таблице маршрутизации, вместо 30-ти пунктов в случае
    использования 30 адресов класса С. Таким образом, деление на подсети уменьшает
    размер таблиц маршрутизации (в разделе “CIDR: бесклассовая
    маршрутизация между доменами”
    главы 10 мы рассмотрим новую технологию,
    которая помогает уменьшить размер таблиц маршрутизации даже если используются
    адреса класса С).

    Для того чтобы показать что подсети непрозрачны для маршрутизаторов
    внутри подсети, обратимся к рисунку 3.6 и представим, что датаграмма прибывает в
    gateway из Internet с адресом назначения 140.252.57.1. Маршрутизатор gateway
    должен знать где находится подсеть 57 и что датаграммы для этой подсети надо
    посылать в kpno. В свою очередь, kpno должен посылать датаграммы в R55, который
    пошлет их в R57.

    Маска подсети

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

    В дополнение к IP адресу, хосту также необходимо
    знать, сколько бит будет использовано в качестве идентификатора подсети и
    сколько бит будет использовано в качестве идентификатора хоста. Это также
    определяется во время загрузки с использованием маски
    подсети. Маска это 32-битное значение, которое содержит биты, установленные в
    единицу для идентификатора сети и идентификатора подсети, и биты, установленные
    в 0 для идентификатора хоста. На рисунке 3.7 показано формирование маски подсети
    для двух различных разделений адреса класса В. В верхнем примере происходит
    разделение на хосте noao.edu, как показано на рисунке 3.5,
    где идентификатор подсети и идентификатор хоста занимают 8 бит. В нижнем примере
    показано разделение адреса класса В, при этом идентификатор подсети занимает 10
    бит, а идентификатор хоста – 6 бит.

    Address resolution protocol - что это?

    Рисунок 3.7 Пример масок подсетей для двух различных
    подсетей класса B.

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

    После того как хост получил свой IP адрес и маску подсети, он может
    определить, предназначена ли IP датаграмма для (1) хоста в его собственной
    подсети, (2) хосту в другой подсети его собственной сети, или (3) хосту в другой
    сети. Зная собственный IP адрес, можно определить, к какому классу он относится:
    А, В или С (по старшим битам), также можно определить, где проведена граница
    между идентификатором сети и идентификатором подсети. По маске подсети можно
    определить где проведена граница между идентификатором подсети и идентификатором
    хоста.

    Пример

    Представьте себе адрес хоста 140.252.1.1 (адрес класса В), и маску
    подсети – 255.255.255.0 (8 бит на идентификатор подсети и 8 бит на идентификатор
    хоста).

    • Если IP адрес назначения 140.252.4.5, мы знаем, что идентификатор
      сети класса В тот же самый (140.252), однако идентификатор подсети другой (1 и
      4). На рисунке 3.8 показано, как происходит сравнение двух IP адресов с
      использованием маски подсети.
    • Если IP адрес назначения 140.252.1.22, то идентификатор сети класса В
      тот же самый (140.252), и идентификатор подсети также тот же самый (1). Однако
      идентификатор хоста другой.
    • Если IP адрес назначения 192.43.235.6 (адрес класса С), идентификатор
      сети другой. С этим адресом не может быть произведено дальнейшее сравнение.

    Address resolution protocol - что это?

    Рисунок 3.8 Сравнение двух подсетей класса В,
    использующих маски подсети.

    В процессе IP маршрутизации, сравнения, подобные
    этому, делаются все время с использованием двух IP адресов и маски
    подсети.

    Специальные IP адреса

    В процессе описания подсетей может встретится семь специальных IP
    адресов (см. рисунок 3.9). На этом рисунке 0 означает поле, состоящее из всех
    бит, установленных в ноль, -1 означает поле из бит, установленных в единицы, а
    netid (идентификатор сети), subnetid (идентификатор подсети) и hostid
    (идентификатор хоста) обозначает соответствующие поля, которые установлены не в
    единицы и не в нули. Пустая колонка идентификатора подсети означает, что адреса
    не могут быть разбиты на подсети.

    Рисунок 3.9 Специальные IP адреса.

    Мы разделили эту таблицу на три секции. Первые две записи содержат
    специальные адреса источников, затем адрес интерфейса loopback, и последние четыре записи – это широковещательные
    адреса.

    Первые два пункта в таблице с идентификатором сети, равным 0, могут
    существовать только как адрес источника во время процедуры инициализации, когда
    хост определяет свой собственный IP адрес, например, с использованием протокола
    BOOTP (глава
    16
    ).

    В разделе “Широковещательные
    запросы”
    главы 12 мы рассмотрим четыре типа широковещательных адресов
    более подробно.

    Пример подсети

    В примере показаны подсети, использованные в тексте, а также, как
    используются две различные маски подсети.

    Address resolution protocol - что это?

    Рисунок 3.10 Настройки хостов и сетей в описываемой
    подсети.

    Если Вы сравните этот рисунок с одним из тех, что показаны на
    внутренней стороне обложки, то заметите, что мы опустили некоторые детали,
    которые описывают подсоединение маршрутизатора sun к верхнему Ethernet.
    (посредством SLIP соединения). Это не влияют на наше описание деления на подсети
    в данном разделе. Мы вернемся к подобному SLIP соединению в разделе “Уполномоченный агент
    ARP”
    главы 4, когда будем описывать уполномоченного агента ARP.

    Проблема заключается в том, что мы имеем две раздельные сети внутри
    подсети 13: Ethernet и канал точка-точка (выделенный канал SLIP). (Каналы
    точка-точка всегда привносят некоторые проблемы, так как каждый конец требует
    собственный IP адрес.) В будущем здесь может появиться больше хостов и сетей,
    однако недостаточно для того, чтобы выделять другой номер подсети. Мы принимаем
    решение расширить идентификатор подсети с 8 до 11 бит, и уменьшить идентификатор
    хоста с 8 до 5 бит. Это называется подсетями с переменной
    длиной, так как большинство сетей внутри сети 140.252 используют маску подсети
    длиной 8 бит, тогда как наша сеть использует маску подсети длиной 11 бит.

    RFC 1009 [Braden
    and Postel 1987] позволяет использовать в сетях с подсетями несколько масок
    подсетей. Новые требования к маршрутизаторам Router
    Requirements RFC [Almquist 1993] определяют все требования.
    Проблема, однако, заключается в том, что не все протоколы маршрутизации
    обмениваются масками подсети вместе с идентификатором сети назначения. Мы увидим
    в главе
    10
    , что RIP не поддерживает подсети переменной длины, однако RIP версии 2 и
    OSPF поддерживают. У нас не было подобных проблем, так как RIP не используется в
    подсетях, описываемых в тексте.

    На рисунке 3.11 показана структура IP адреса, используемая в подсети,
    описываемой в книге. Первые 8 бит в 11-битном идентификаторе подсети всегда
    равны 13 в данной подсети. Для оставшихся трех бит идентификатора подсети мы
    используем двоичное 001 для Ethernet и 010 для SLIP канала точка-точка.

    Address resolution protocol - что это?

    Рисунок 3.11 Использование подсетей переменной
    длины.

    Маска подсети с переменной длиной не создаст
    проблем для других хостов и маршрутизаторов в сети 140.252, так как все
    датаграммы, направляемые в подсеть 140.252.13, приходят на маршрутизатор sun (IP
    адрес 140.252.1.29. Рисунок 3.10) и если sun знает об 11-битном идентификаторе
    подсети для хостов в подсети 13, все будет нормально.

    Маска подсети для всех интерфейсов в подсети 140.252.13 установлена в
    255.255.255.224 или 0xffffffe0. Это означает, что крайние правые 5 битов
    отводятся на идентификатор хоста, а 27 бит слева оставлены на идентификатор сети
    и идентификатор подсети.

    На рисунке 3.12 показано распределение IP адресов и масок подсетей для
    интерфейсов, приведенных на рисунке 3.10.

    Рисунок 3.12 IP адреса описываемой подсети.

    Первая колонка помечена как “хост” (“Host”),
    однако и sun и bsdi также функционируют как маршрутизаторы, так как они имеют
    несколько интерфейсов и перенаправляют пакеты с одного интерфейса на другой.

    В последней строке таблицы показано, что
    широковещательный адрес сети Ethernet на рисунке 3.10 установлен 140.252.13.63:
    он формируется из идентификатора подсети Ethernet (140.252.13.32) и младших 5
    бит на рисунке 3.11, установленных в единицу (16 8 4 2 1=31). (В главе 12 мы
    увидим, что этот адрес называется широковещательным адресом
    подсети.)

    Команда ifconfig

    Сейчас, когда мы описали канальный уровень и IP уровень, мы можем
    показать команду, которая используется для конфигурирования сетевого интерфейса
    который используется TCP/IP. Команда ifconfig(8) обычно запускается в момент
    загрузки хоста при конфигурации каждого интерфейса.

    Для интерфейсов с дозвоном (dialup), которые могут включаться и
    выключаться (такие как SLIP каналы), ifconfig должна быть запущена каждый раз
    когда канал включается или выключается.

    В следующем примере показаны значения для подсети,
    описываемой в книге. Сравните эти значения с теми, что приведены на рисунке
    3.12.

    sun % /usr/etc/ifconfig -a опция -a в SunOS означает “все
    интерфейсы” le0: flags=63<UP,BROADCAST, NOTRAILERS, RUNNING> inet
    140.252.13.33 netmask ffffffe0 broadcast 140.252.13.63 sl0: flags=1051<UP,
    POINTOPOINT, RUNNING, LINK0> inet 140.252.1.29 –> 140.252.1.183 netmask
    ffffff00 lo0: flags=49<UP, LOOPBACK, RUNNING> inet 127.0.0.1 netmask
    ff000000

    Интерфейс loopback (глава 2, раздел “Интерфейс Loopback”)
    воспринимается как сетевой интерфейс. Он использует адрес класса A и не
    позволяет разбивать себя на подсети.

    Обратите внимание на то, что в Ethernet не используется инкапсуляция
    завершителей (глава 2, раздел “Инкапсуляция
    завершителей”
    ) и что Ethernet поддерживает широковещательные запросы,
    тогда как SLIP канал это канал точка-точка.

    Флаг LINK0 для SLIP интерфейса это опция
    конфигурирования, которая позволяет осуществлять сжатие slip (CSLIP, глава 2, раздел “SLIP с компрессией
    (CSLIP)”
    ). Еще одна возможная опция это LINK1,
    которая включает CSLIP, если принят сжатый пакет с удаленного конца, и LINK2, которая позволяет отбрасывать все исходящие пакеты ICMP.
    Мы рассмотрим адрес назначения этого SLIP канала в разделе “Уполномоченный агент
    ARP”
    главы 4.

    Комментарии, приведенные в инструкции
    по инсталляции, объясняют причину, по которой была введена последняя опция:
    “Она не должна быть установлена, однако некоторые кретины, посылающие pingи на Вас, могут свести
    производительность канала к нулю”.

    Еще один маршрутизатор – bsdi. Так как опция -a это характеристика SunOS, (BSD релизы не имеют подобной опции) мы должны исполнить
    ifconfig несколько раз, указывая имя интерфейса в качестве
    аргумента:

    bsdi % /sbin/ifconfig we0 we0: flags=863<UP, BROADCAST, NOTRAILERS,
    RUNNING, SIMPLEX> inet 140.252.13.35 netmask ffffff00 broadcast 140.252.13.63
    bsdi % /sbin/ifconfig sl0 sl0: flags=1011<UP, POINTOPOINT, LINK0>
    inet 140.252.13.66 –> 140.252.13.65 netmask ffffff00

    Здесь у интерфейса Ethernet мы видим новую опцию (we0): SIMPLEX. Эта опция из 4.4BSD, которая
    указывает на то, что интерфейс не может слушать свою собственную передачу. Она
    устанавливается в BSD/386 для всех интерфейсов Ethernet.
    Если опция установлена, интерфейс, посылающий фрейм с широковещательным адресом,
    посылает копию на локальный хост и посылает копию на loopback. (Мы покажем
    пример данной характеристики в разделе “ICMP запрос и отклик маски
    адреса”
    главы 6.)

    На хосте slip конфигурация интерфейса SLIP примерно такая же, как
    показано выше для bsdi, за исключением того, что IP адреса на двух концах
    переставлены местами:

    slip % /sbin/ifconfig sl0 sl0: flags=1011<UP, POINTOPOINT,
    LINK0> inet 140.252.13.65 –> 140.252.13.66 netmask
    ffffffe0

    Последний интерфейс – это Ethernet на хосте svr4. Он аналогичен
    интерфейсу Ethernet, показанному ранее, за исключением того, что версия команды
    ifconfig в SVR4 не печатает флаг RUNNING:

    svr4 % /usr/sbin/ifconfig emd0 emd0: flags=23<UP, BROADCAST,
    NOTRAILERS> inet 140.252.13.34 netmask ffffffe0 broadcast
    140.252.13.63

    Команда ifconfig обычно поддерживает и другие
    семейства протоколов (не TCP/IP), а также имеет несколько дополнительных опций.
    Обратитесь к руководству по вашей системе, для того чтобы изучить эту команду
    более подробно.

    Команда netstat

    Команда netstat(1) также предоставляет информацию
    об интерфейсах системы. Флаг -i выдает информацию об
    интерфейсах, а флаг -n выдает IP адреса вместо имен хостов.

    sun % netstat -in Name Mtu Net/Dest Address Ipkts Ierrs Opkts Oerrs
    Collis Queue le0 1500 140.252.13.32 140.252.13.33 67719 0 92133 0 1 0 sl0 552
    140.252.1.183 140.252.1.29 48035 0 54963 0 0 0 lo0 1536 127.0.0.1 127.0.0.1
    15548 0 15548 0 0 0

    Эта команда печатает MTU для каждого интерфейса, количество входящих
    пакетов, ошибки ввода, количество исходящих пакетов, ошибки вывода, коллизии
    (столкновения) и текущий размер исходящей очереди.

    Мы вернемся к команде netstat в главе 9, где будем использовать ее для
    рассмотрения таблиц маршрутизации, и в главе 13, когда будем использовать
    модифицированную версию команды, чтобы рассмотреть активные группы при групповой
    адресации.

    Будущее IP

    У IP существуют три проблемы. Все они явились результатом
    феноменального роста сети Internet за последние несколько лет. (Обратитесь к упражнению 2 главы 1.)

    1. Почти половина всех адресов класса В уже распределена. Если адреса
      класса В будут распространяться с такой же скоростью как сейчас, то их запас
      будет исчерпан где-то в 1995 году.
    2. 32-битные адреса в общем случае непригодны для долговременного роста
      Internet.
    3. Текущая структура маршрутизации не иерархическая, а плоская, при этом
      на каждую сеть требуется запись в таблицы маршрутизации. По мере роста
      количества сетей все более распространяются адреса класса С, а также узлы, в
      которых сосредоточено несколько сетей (вместо адреса класса В), при этом заметен
      рост таблиц маршрутизации.

    Бесклассовая маршрутизация между доменами (CIDR –
    Classless Interdomain Routing) призвана разрешить третью проблему, при этом к
    текущей версии IP будут добавлены некоторые расширения (IP версия 4). Мы обсудим
    это более подробно в разделе “CIDR: бесклассовая
    маршрутизация между доменами”
    главы 10.

    Что касается новой версии IP, которую часто называют IPng, было
    сделано четыре предложения для следующих поколений IP. В майском выпуске IEEE
    Network (vol.7, no.3) за 1993 год содержится обзор первых трех предложений
    вместе с CIDR. RFC 1454 [Dixon 1993] также сравнивает первые
    три предложения.

    1. Простой протокол Internet (SIP – Simple Internet
      Protocol) . Предлагается минимальный набор изменений к IP, после чего IP будет
      использовать 64-битные адреса и другой формат заголовка. (Первые 4 бита
      заголовка также содержат номер версии, которые устанавливается в 4.)
    2. PIP. Здесь также используются большие, переменной
      длины, иерархические адреса с другим форматом заголовка.
    3. TUBA, что означает TCP и UDP с увеличенными
      адресами (TCP and UDP with bigger Addresses), основан на OSI
      CLNP (сетевой протокол без соединения – Connectionless
      Network Protocol), протокол OSI похожий на IP. Он предлагает еще большие адреса:
      переменной длины, до 20 байт. Однако, СLNP это существующий протокол, тогда как
      SIP и PIP это всего лишь предложения, более того, CLNP уже документирован. RFC
      1347 [Callon 1992] описывает детали TUBA. Глава 7 [Perlman 1992] содержит сравнение IPv4 и CLNP. Множество
      маршрутизаторов уже поддерживают CLNP, однако большинство хостов не
      поддерживают.
    4. TP/IX, который описан в RFC 1475 [Ullmann 1993]. Как и в случае с SIP, он использует 64-битные IP
      адреса, также изменяя TCP и UDP заголовки: 32-битный номер порта для обоих
      протоколов, 64-битный номер последовательности, 64-битный номер подтверждения и
      32-битные окна для TCP.

    Первые три предложения используют в основном те же версии TCP и UDP в
    качестве транспортных уровней. Однако только одно из этих четырех предложений
    было выбрано в качестве основы для IPv4. Вполне возможно, что в тот момент,
    когда Вы читаете эти строки, решение принимается или уже принято, поэтому мы
    ничего не будем говорить об этом более. Однако, надо сказать, что пройдет еще
    много времени, прежде чем IPv4 станет действительно реальным
    протоколом.

    Краткие выводы

    Мы начали эту главу с описания IP заголовка, кратко описав все поля.
    Также было сделано введение в маршрутизацию IP, был рассмотрен простой роутинг:
    мы рассмотрели, как выбирается непосредственно подключенная сеть или
    маршрутизатор по умолчанию.

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

    IP маршрутизация осуществляется по принципу пересылка-за-пересылкой
    (hop-by-hop). IP адрес назначения никогда не меняется в процессе передачи
    датаграммы по пересылкам, однако инкапсуляция и адреса назначения канального
    уровня могут изменяться при каждой пересылке. Большинство хостов и многие
    маршрутизаторы используют маршрутизатор следующей пересылки
    по умолчанию для всего внешнего траффика.

    Адреса сетей класса А и класса В обычно разбиваются на подсети.
    Количество бит, используемых для идентификатора подсети, указывается с помощью
    маски подсети. Мы привели подробные примеры того как это делается с
    использованием подсети, которая описывается в этой книге, и немного рассказали о
    подсетях с переменной длиной. Мы использовали разбиение на подсети, для того
    чтобы уменьшить размер таблиц маршрутизации, так как ко множеству сетей можно
    получить доступ через одну точку. Информация об интерфейсах и сетях может быть
    получена с использованием команд ifconfig и netstat. Они включают в себя IP адреса интерфейсов, их маски
    подсетей, широковещательные адреса и MTU.

    Мы закончили главу, описав возможные изменения, которые могут
    произойти в протоколах Internet при появлении следующего поколения IP.

    Упражнения

    1. Должен ли быть адрес интерфейса
      loopback всегда 127.0.0.1?

    2. На рисунке 3.6 определите маршрутизаторы, которые имеют больше двух
      интерфейсов.
    3. В чем отличие маски подсети для адреса класса А с 16 битами для
      идентификатора подсети и адреса класса В с 8 битами для адреса подсети?
    4. Прочитайте RFC 1219 [Tsuchiya 1991], для того чтобы
      познакомиться с рекомендуемой технологией назначения идентификаторов подсетей и
      идентификаторов хостов.
    5. Можно ли использовать маску подсети 255.255.0.255 для адресов класса А?
    6. Почему MTU для интерфейса loopback,
      показанного в разделе “Команда
      netstat”
      , установлен в 1536?
    7. Семейство протоколов TCP/IP построено на основе уровня IP, который
      определяет технологию передачи датаграмм по сети. Существуют семейства
      протоколов, которые основаны на применении сетевых технологий, ориентированных
      на соединения. Прочитайте [Clark 1988], чтобы найти три
      преимущества сетей с передачей датаграмм.

    Протокол arp

     Протокол ARP (Address Resolution Protocol, RFC-826, std-38, Протокол распознавания адреса) предназначен для преобразования IP-адресов в MAC-адреса, часто называемые также физическими адресами.

    Важной особенностью интерфейса Ethernet является то, что каждая интерфейсная карта имеет свой уникальный адрес. Каждому производителю карт выделен свой пул адресов в рамках которого он может выпускать карты. Согласно протоколу Ethernet, каждый интерфейс имеет 6-ти байтовый адрес.

    Адрес записывается в виде шести групп шестнадцатеричных цифр по две в каждой (шестнадцатеричная записи байта). Первые три байта называются префиксом, и именно они закреплены за производителем. Каждый префикс определяет 224 различных комбинаций, что равно почти 17-ти млн. адресам.

    В сетях нет однозначного соответствия между физическим адресом сетевого интерфейса (MAC адресом сетевой карты) и его IP-адресом. Поиск по  IP-адресу соответствующего Ethernet-адреса производится протоколом ARP, функционирующим на уровне доступа к среде передачи. Протокол поддерживает в оперативной памяти динамическую arp-таблицу в целях кэширования полученной информации.

    Порядок функционирования протокола следующий.

    С межсетевого уровня поступает IP-дейтаграмма для передачи в физический канал (Ethernet), вместе с дейтаграммой передается, среди прочих параметров, IP-адрес узла назначения. Если в arp-таблице не содержится записи об Ethernet-адресе, соответствующем нужному IP-адресу, модуль arp ставит дейтаграмму в очередь и формирует широковещательный запрос.

    Полученные данные заносятся в таблицу, ждущая дейтаграмма извлекается из очереди и передается на инкапсуляцию в кадр Ethernet для последующей отправки по физическому каналу. Протокол ARP может поддерживать не только Ethernet, но и другие типы физических сред.

     Упрощенно, ARP-таблица состоит из двух столбцов:

    IP-адресEthernet-адрес
    223.1.2.108:00:39:00:2F:C3
    223.1.2.308:00:5A:21:A7:22
    223.1.2.408:00:10:99:AC:54

    В первом столбце содержится IP-адрес, а во втором Ethernet-адрес. Таблица соответствия необходима, так как адреса выбираются произвольно и нет какого-либо алгоритма для их вычисления. Если машина перемещается в другой сегмент сети, то ее ARP-таблица должна быть изменена.

    ARP-таблицы строятся согласно документу RFC-1213 и для каждого IP-адреса содержит четыре кода:

    ifindexФизический порт (интерфейс), соответствующий данному адресу;
    Физический адресMAC-адрес, например Ethernet-адрес;
    IP-адресIP-адрес, соответствующий физическому адресу;
    тип адресного соответствияэто поле может принимать 4 значения: 1 — вариант не стандартный и не подходит ни к одному из описанных ниже типов; 2 — данная запись уже не соответствует действительности; 3 — постоянная привязка; 4 — динамическая привязка

    В Linux таблицу ARP можно посмотреть, используя команду arp:

    :/>  Taskmgr.exe - Что это такое?

    Оставьте комментарий

    Adblock
    detector