нЦКЮБКЕМХЕ

TCP/IP КРУПНЫМ ПЛАНОМ

Глава 1 Введение

Глава 2 Канальный уровень

Глава 3 IP: протокол Internet

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

Глава 5 RARP: обратный протокол определения адреса

Глава 6 ICMP: протокол управления сообщениями Internet

Глава 7 Программа Ping

Глава 8 Программа Traceroute

Глава 9 IP маршрутизация

Глава 10 Динамические протоколы маршрутизации

Глава 11 UDP: User Datagram Protocol

Глава 12 Широковещательная и групповая адресация

Глава 13 IGMP: протокол управления группами Internet

Глава 14 DNS: система имен доменов

Глава 15 TFTP: простой протокол передачи данных

Глава 16 BOOTP: протокол загрузки

Глава 17 TCP: Transmission Control Protocol

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

Глава 19 Интерактивный поток данных TCP

Глава 20 Поток неинтерактивных данных TCP

Глава 21 Тайм-ауты и повторные передачи TCP

Глава 22 Устойчивый таймер TCP

Глава 23 TCP таймер "оставайся в живых"

Глава 24 Будущее TCP и его производительность

Глава 25 SNMP: протокол управления сетью

Глава 26 Telnet и Rlogin: удаленный терминал

Глава 27 FTP: протокол передачи файлов

Глава 28 SMTP: простой протокол передачи почты

Глава 29 NFS: сетевая файловая система

Глава 30 Другие приложения TCP/IP

Приложение А Программа tcpdump

Приложение В Компьютерные часы

Приложение С Программа sock

Приложение D Решения и ответы на упражнения

Приложение E Конфигурируемые опции

Приложение F Где можно взять исходные тексты

Приложение G Библиография

Предметный указатель


Введение

Глава 1 Введение

Введение

Семейство протоколов TCP/IP работает на любых моделях компьютеров, произведенных различными производителями компьютерной техники и работающих под управлением различных операционных систем. С помощью протоколов TCP/IP можно объединить практически любые компьютеры. И что самое удивительное, сегодняшние реализации протокола TCP/IP очень далеки от того, как он задумывался исходно. В конце 60-х годов начался исследовательский проект, финансируемый правительством США, по разработке сети пакетной коммутации, а в 90-х годах результаты этих исследований превратились в наиболее широко используемую форму сетевого взаимодействия между компьютерами. В настоящее время это действительно открытая система, а именно, семейство протоколов и большое количество бесплатных реализаций (либо достаточно дешевых). Они составляют основу того, что в настоящее время называется словом Internet.

В этой главе мы познакомимся с семейством протоколов TCP/IP, для того чтобы подойти к чтению следующих глав более или менее подготовленными. Чтобы познакомиться с ранней историей разработки TCP/IP, можно обратиться к [Lynch 1993].

Уровни

Сетевые протоколы обычно разрабатываются по уровням, причем каждый уровень отвечает за собственную фазу коммуникаций. Семейства протоколов, такие как TCP/IP, это комбинации различных протоколов на различных уровнях. TCP/IP состоит из четырех уровней, как показано на рисунке 1.1.

Прикладной | Telnet, FTP, e-mail и т.д.
Транспортный | TCP,UDP
Сетевой | IP, ICMP, IGMP
Канальный | драйвер устройства и интерфейсная плата

Рисунок 1.1 Четыре уровня протоколов TCP/IP.

Каждый уровень несет собственную функциональную нагрузку.

  1. Канальный уровень (link layer). Еще его называют уровнем сетевого интефейса. Обычно включает в себя драйвер устройства в операционной системе и соответствующую сетевую интерфейсную плату в компьютере. Вместе они обеспечивают аппаратную поддержку физического соединения с сетью (с кабелем или с другой используемой средой передачи).
  2. Сетевой уровень (network layer), иногда называемый уровнем межсетевого взаимодействия, отвечает за передачу пакетов по сети. Маршрутизация пакетов осуществляется именно на этом уровне. IP (Internet Protocol - протокол Internet), ICMP (Internet Control Message Protocol - протокол управления сообщениями Internet) и IGMP (Internet Group Management Protocol - протокол управления группами Internet) обеспечивают сетевой уровень в семействе протоколов TCP/IP.
  3. Транспортный уровень (transport layer) отвечает за передачу потока данных между двумя компьютерами и обеспечивает работу прикладного уровня, который находится выше. В семействе протоколов TCP/IP существует два транспортных протокола: TCP (Transmission Control Protocol) и UDP (User Datagram Protocol). TCP осуществляет надежную передачу данных между двумя компьютерами. Он обеспечивает деление данных, передающихся от одного приложения к другому, на пакеты подходящего для сетевого уровня размера, подтверждение принятых пакетов, установку тайм-аутов, в течение которых должно прийти подтверждение на пакет, и так далее. Так как надежность передачи данных гарантируется на транспортном уровне, на прикладном уровне эти детали игнорируются. UDP предоставляет более простой сервис для прикладного уровня. Он просто отсылает пакеты, которые называются датаграммами (datagram) от одного компьютера к другому. При этом нет никакой гарантии, что датаграмма дойдет до пункта назначения. За надежность передачи данных, при использовании датаграмм отвечает прикладной уровень. Для каждого транспортного протокола существуют различные приложения, которые их используют.
  4. Прикладной уровень (application layer) определяет детали каждого конкретного приложения. Существует несколько распространенных приложений TCP/IP, которые присутствуют практически в каждой реализации:
    • Telnet - удаленный терминал
    • FTP, File Transfer Protocol - протокол передачи файлов
    • SMTP, Simple Mail Transfer Protocol - простой протокол передачи электронной почты
    • SNMP, Simple Network Management Protocol - простой протокол управления сетью.
Если у нас есть два компьютера в локальной сети, (например, Ethernet) и на обоих запущен FTP, то данные протоколы будут работать так, как показано на рисунке 1.2.

Рисунок 1.2 Два хоста в локальной сети с работающим FTP.

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

Каждый уровень имеет один или несколько протоколов, который позволяет общаться с удаленным узлом на том же уровне. Один протокол, например, позволяет общаться двум TCP уровням, а другой протокол обеспечивает коммуникации между двумя IP уровнями.

С правой стороны на рисунке 1.2 мы видим, что прикладной уровень обеспечивается пользовательским процессом, тогда как три нижних уровня обычно встроены в ядро операционной системы. Несмотря на то, что возможны и другие способы реализации, во всех UNIX системах все построено именно по такому принципу.

Существует еще одно отличие между верхним уровнем и тремя нижними уровнями, приведенными на рисунке 1.2. Прикладной уровень обычно является приложением и взаимодействует с пользователем, а не занимается передачей данных по сети. Три нижних уровня ничего не знают о работающих над ними приложениях, однако отвечают за все детали коммуникаций.

На рисунке 1.2 мы показали четыре протокола, каждый на своем уровне. FTP это протокол прикладного уровня, TCP - протокол транспортного уровня, IP - протокол сетевого уровня, а протоколы Ethernet обеспечивают канальный уровень. Семейство протоколов TCP/IP объединяет в себе множество протоколов, однако наиболее часто используемые названия для данного семейства это TCP/IP, TCP и IP (иногда семейство называют Семейство Протоколов Internet).

Цели, решаемые сетевым и прикладным уровнями, различны - первый обеспечивает взаимодействие с различными средами передачи (Ethernet, Token ring, и т.д.), второй работает с конкретными пользовательскими приложениями (FTP, Telnet, и т.д.). На первый взгляд, разница между сетевым и транспортным уровнями достаточно туманна. На основании чего между ними проводится разграничение? Чтобы понять это, мы рассмотрим не одну отдельно взятую сеть, а несколько сетей.

Одной из причин феноменального роста сетевых технологий в течение 80-х годов явилось понимание того, что отдельно стоящий компьютер практически бесполезен. Несколько отдельных систем были объединены вместе в сеть. Однако, как выяснилось позже, а именно в 90-х годах, отдельно стоящая сеть также практически бесполезна. Поэтому люди начали объединять сети вместе. Именно результат такого объединения получил название internet (межсетевое взаимодействие). internet это несколько объединенных сетей, которые используют одно и то же семейство протоколов.

Наиболее простой путь осуществить межсетевое взаимодействие - это объединить две или более сетей с помощью маршрутизатора. Как правило, маршрутизатор представляет из себя аппаратное устройство. Огромное достоинство маршрутизаторов заключается в том, что они могут объединить сети, построенные на различных физических принципах: Ethernet, Token ring, point-to-point, FDDI (Fiber Distributed Data Interface), и так далее.

Эти устройства также иногда называются IP маршрутизаторами (IP router), однако мы будем использовать термин маршрутизатор (router).

Исторически эти устройства назывались шлюзами (gateway), и этот термин до сих пор широко используется в литературе о TCP/IP. Сегодня чаще всего термин шлюз используется для обозначения шлюза между приложениями: процесс, который объединяет два различных семейства протоколов (скажем, TCP/IP и IBM SNA) в одном конкретном приложении (чаще всего это электронная почта или передача файлов).

На рисунке 1.3 показано объединение двух сетей: Ethernet и Token ring с помощью маршрутизатора. Несмотря на то что мы показали связь только между двумя компьютерами, подсоединенными к маршрутизатору из разных сетей, любой компьютер в Ethernet может общаться с любым компьютером в Token ring.

На рисунке 1.3 мы также можем проследить разницу между конечной системой (end system), в данном случае это два компьютера на каждой стороне, и промежуточной системой (intermediate system), в данном случае это маршрутизатор в середине. Прикладной и транспортный уровни используют протоколы, ориентированные на соединение (end-to-end). На рисунке эти два уровня используются только конечными системами. Сетевой уровень, однако, использует протокол, не требующий соединения (пересылка-за-пересылкой - hop-by-hop), он используется в данном случае двумя конечными системами и каждой промежуточной системой.

Рисунок 1.3 Две сети, соединенные через маршрутизатор.

В семействе протоколов TCP/IP сетевой уровень - IP. Он предоставляет ненадежный сервис. Это означает, что в процессе своей работы протокол передает пакет от источника к пункту назначения, однако не предоставляет никаких гарантий того, что пакет дойдет по назначению. TCP, с другой стороны, предоставляет надежный транспортный уровень, который пользуется ненадежным сервисом IP. Чтобы обеспечить подобный сервис, TCP выставляет тайм-ауты и осуществляет повторные передачи, отсылает и принимает подтверждения и так далее. Транспортный уровень и сетевой уровень несут различную ответственность за передачу данных.

Маршрутизатор, по определению, имеет два или несколько интерфейсов сетевого уровня (если он объединяет две или более сетей). Любая система с несколькими интерфейсами называется многоинтерфейсной (multihomed). Компьютер, имеющий несколько интерфейсов, но не перенаправляющий пакеты с одного интерфейса на другой, не может называться маршрутизатором. Большинство реализаций TCP/IP позволяют компьютерам с несколькими интерфейсами функционировать в качестве маршрутизаторов. Однако компьютеры должны быть специально сконфигурированы, чтобы решать задачи маршрутизации. Таким образом, мы можем называть систему хостом, когда на нем работают такие приложения как FTP или Telnet, или маршрутизатором, когда он осуществляет передачу пакетов из одной сети в другую. В зависимости от того какие функции выполняются компьютером, мы будем использовать тот или иной термин.

Одна из основных задач объединения сетей заключается в том, чтобы скрыть все детали физического процесса передачи информации между приложениями, находящимися в разных сетях. Поэтому нет ничего удивительного в том, что в объединенных сетях, как, например, на рисунке 1.3, прикладные уровни не заботятся (и не должны заботиться) о том, что один компьютер находится в сети Ethernet, а другой в сети Token ring с маршрутизатором между ними. Даже если бы между сетями было 20 маршрутизаторов и различные типы физического соединения, приложения работали бы точно так же. Подобная концепция, при которой детали физического объединения сетей скрыты от приложений, определяет мощность и гибкость такой технологии объединения сетей.

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

Стоит отметить, что объединение TCP/IP сетей осуществляется в основном с помощью маршрутизаторов, а не с помощью мостов. Поэтому мы более подробно рассмотрим маршрутизаторы. В главе 12 [Perlman 1992] сравниваются маршрутизаторы и мосты.

Уровни TCP/IP

В действительности, семейство протоколов TCP/IP объединяет значительно больше протоколов. На рисунке 1.4 показаны некоторые дополнительные протоколы, которые мы рассмотрим в книге.

Рисунок 1.4 Различные протоколы на разных уровнях семейства протоколов TCP/IP.

TCP и UDP - два основных протокола транспортного уровня. Оба используют IP в качестве сетевого уровня.

TCP предоставляет надежный транспортный уровень, даже несмотря на то что он использует ненадежный сервис IP. В главах 17, 18, 19, 20, 21 и 22 мы подробно рассмотрим функционирование TCP. Затем мы рассмотрим некоторые приложения TCP: Telnet и Rlogin в главе 26, FTP в главе 27 и SMTP в главе 28. Приложения - это, как правило, пользовательские процессы.

UDP отправляет и принимает датаграммы (datagram). Датаграмма это блок информации (определенное количество байт информации, которое указывается отправителем), который отправляется от отправителя к приемнику. В отличие от TCP, UDP является ненадежным протоколом. Не существует гарантий, что датаграмма достигнет конечной точки назначения. В главе 11 мы рассмотрим UDP, а затем в главе 14 - систему имен доменов (Domain Name System), в главе 15 - простой протокол передачи файлов (Trivial File Transfer Protocol) и в главе 16 - протокол загрузки (Bootstrap Protocol). Это приложения, которые используют UDP. SNMP (Простой протокол управления сетью - Simple Network Management Protocol) также использует UDP, однако он работает с некоторыми другими протоколами, которые мы будем рассматривать вплоть до главы 25.

IP это основной протокол сетевого уровня. Он используется как TCP, так и UDP. Каждый блок информации TCP и UDP, который передается по объединенным сетям, проходит через IP уровень в каждой конечной системе и в каждом промежуточном маршрутизаторе. На рисунке 1.4 показаны приложения, которые имеют прямой доступ к IP. Такой доступ используется довольно редко, но существует возможность его осуществить (некоторые ранние протоколы маршрутизации были разработаны именно подобным образом). Также в процессе экспериментов при создании новых транспортных уровней используется возможность доступа к протоколу IP. В главе 3 рассматривается протокол IP, более подробно IP рассматривается в последующих главах. В главе 9 и главе 10 рассказывают о том как осуществляется IP маршрутизация.

ICMP является дополнением к протоколу IP. Он используется IP уровнем для обмена сообщениями об ошибках и другой жизненно важной информацией уровня IP. Глава 6 рассказывает об ICMP более подробно. Несмотря на то, что ICMP используется в основном IP уровнем, приложения также могут получить доступ к ICMP. Мы рассмотрим два наиболее популярных диагностических средства, Ping и Traceroute (глава 7 и глава 8), использующих ICMP.

Протокол управления группами Internet (IGMP - Internet Groupe Management Protocol), используется при групповой адресации: при этом UDP датаграммы рассылаются нескольким получателям. Мы опишем основные особенности широковещательной адресации (рассылка UDP датаграмм каждому компьютеру в указанной сети) и групповой адресации в главе 12, а затем опишем сам IGMP в главе 13.

Протокол определения адреса (ARP - Address Resolution Protocol) и обратный протокол определения адреса (RARP - Reverse Address Resolution Protocol) это специализированные протоколы, используемые только с определенным типом сетевых интерфейсов (такие как Ethernet и Token ring). Они применяются для преобразования формата адресов, используемого IP уровнем в формат адресов, используемый сетевым интерфейсом. Мы рассмотрим эти протоколы в главе 4 и главе 5 соответственно.

Адресация Internet

Каждый интерфейс в объединенной сети должен иметь уникальный IP адрес. Эти адреса представляют из себя тридцатидвухбитовые числа. Cуществует определенная структура адреса Internet. На рисунке 1.5 показано 5 классов адресов Internet.

Эти 32-битные адреса обычно записываются как 4 десятичных числа, по одному на каждый байт адреса. Такая форма записи называется "десятичной записью с точками" (dotted-decimal). Например, адрес сети класса B может быть записан как 140.252.13.33.

Определить класс адреса, или класс сети, можно по первому числу в адресе. На рисунке 1.6 показаны различные классы, причем первое число выделено.

Рисунок 1.5 Пять классов адресов Internet.

Класс | Диапазон
A | 0.0.0.0 - 127.255.255.255
B | 128.0.0.0 - 191.255.255.255
C | 192.0.0.0 - 223.255.255.255
D | 224.0.0.0 - 239.255.255.255
E | 240.0.0.0 - 247.255.255.255
Рисунок 1.6 Диапазоны IP адресов в разных классах сетей.

Здесь хотелось бы отметить, что хосты с несколькими интерфейсами имеют несколько IP адресов: по одному на каждый интерфейс.

Так как каждый интерфейс, подключенный к сети, должен иметь уникальный адрес, встает вопрос распределения IP адресов в глобальной сети Internet. Этим занимается сетевой информационный центр (Internet Network Information Center или InterNIC). InterNIC назначает только сетевые идентификаторы (ID). Назначением идентификаторов хостов в сети занимаются системные администраторы.

Регистрация сервисов Internet (IP адреса и имена доменов DNS) осуществляется в NIC, nic.ddn.mil. InterNIC была создана 1 апреля 1993 года. В настоящее время NIC регистрирует сервисы только для сети министерства обороны (DDN - Defence Data Network). Все другие пользователи Internet в настоящее время используют регистрационный сервис InterNIC в rs.internic.net.

Реально существует три части InterNIC: регистрационный сервис (rs.internic.net), сервис баз данных (ds.internic.net) и информационный сервис (is.internic.net). См. Упражнение 8 главы 1.

Существует три типа IP адресов: персональный адрес (unicast) - указывает на один хост, широковещательный адрес (broadcast) - указывает на все хосты в указанной сети, и групповой адрес (multicast) - указывает на группу хостов, принадлежащей к группе адресации. В главе 12 и главе 13 мы рассмотрим широковещательные и групповые запросы более подробно.

В разделе "Адресация подсетей" главы 3 мы продолжим описание IP адресации, включая подсети, после того как опишем IP маршрутизацию. На рисунке 3.9 показаны особые случаи IP адресов: идентификатор хоста и идентификатор сети, установленные во все нули или во все единицы.

Система имен доменов (DNS - Domain Name System)

Несмотря на то что каждый сетевой интерфейс компьютера имеет свой собственный IP адрес, пользователи привыкли работать с именами хостов. Существует распределенная мировая база данных TCP/IP, называемая системой имен доменов (DNS - Domain Name System), которая позволяет установить соответствие между IP адресами и именами хостов. В главе 14 мы рассмотрим DNS более подробно.

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

Большинство приложений, которые воспринимают имя хоста в качестве аргумента, также воспринимают и IP адреса. Когда мы используем Telnet, в главе 4, например, в одном случае мы указываем имя хоста, а в другом случае - IP адрес.

Инкапсуляция

Когда приложение посылает данные с использованием TCP, данные опускаются вниз по стеку протоколов, проходя через каждый уровень, до тех пор пока они не будут отправлены в виде потока битов по сети. Каждый уровень добавляет свою информацию к данным путем пристыковки заголовков (а иногда завершителей). На рисунке 1.7 показан этот процесс. Блок данных, который TCP посылает в IP, называется TCP сегментом. Блок данных, который IP посылает в сетевой интерфейс, называется IP датаграммой. Поток битов, который передается по Ethernet, называется фреймом (frame).

Числа, стоящие под заголовком и завершителем Ethernet фрейма на рисунке 1.7, показывают стандартный размер заголовков в байтах. В следующих разделах мы расскажем о заголовках более подробно.

Одной из физических характеристик фрейма Ethernet является та, что размер данных должен быть в диапазоне между 46 и 1500 байт. Минимальный размер мы обсудим в разделе "Примеры ARP" главы 4, а максимальный в разделе "MTU" главы 2.

Все стандарты Internet и большинство книг про TCP/IP используют термин октет (octet) вместо слова байт. Такая терминология сложилась исторически, однако мы будем использовать именно слово байт (byte).

Чтобы быть максимально точными, рассматривая рисунок 1.7, мы должны сказать, что блок данных, передаваемый между IP и сетевым интерфейсом, называется пакетом (packet). Этот пакет может быть как IP датаграммой, так и фрагментом IP датаграммы. Мы рассмотрим фрагментацию более подробно в разделе "Фрагментация IP" главы 11.

Что касается UDP данных, то картина там практически идентичная. Единственное различие заключается в том, что блок информации, который UDP передает в IP, называется UDP датаграммой, а размер UDP заголовка составляет 8 байт.

Рисунок 1.7 Инкапсуляция данных, осуществляемая по мере того, как они проходят по стеку протоколов.

Снова обратимся к рисунку 1.4, на котором показано как TCP, UDP, ICMP и IGMP посылают данные в IP. IP должен добавить какой-либо идентификатор к IP заголовку, который он генерирует, чтобы указать какому уровню принадлежат данные. IP делает это путем сохранения восьмибитного значения в своем заголовке, которое называется полем протокола. Это значение равно 1 для ICMP, 2 для IGMP, 6 для TCP и 17 для UDP.

Точно так же различные приложения могут использовать TCP или UDP в одно и то же время. Протоколы транспортного уровня сохраняют в заголовке идентификатор приложения, которое их использует. TCP и UDP оба используют шестнадцатибитный номер порта (port number), чтобы указать на приложения. TCP и UDP сохраняют номер порта источника и номер порта назначения в своих заголовках.

Сетевой интерфейс посылает и принимает фреймы, принадлежащие IP, ARP и RARP. Должна существовать форма идентификации в заголовке Ethernet, которая бы указывала, какой сетевой уровень сгенерировал данные. Для этого существует шестнадцатибитное поле типа фрейма в заголовке Ethernet.

Демультиплексирование (Demultiplexing)

Когда фрейм Ethernet принимается компьютером приемником, он начинает свой путь вверх по стеку протоколов, при этом все заголовки удаляются в соответствующих уровнях. Каждый протокол просматривает определенные идентификаторы в заголовке, чтобы определить, какой следующий верхний уровень должен получить данные. Этот процесс называется демультиплексированием (demultiplexing), он проиллюстрирован на рисунке 1.8.

Рисунок 1.8 Демультиплексирование полученного Ethernet фрейма.

Местоположение квадратиков, помеченных именами протоколов "ICMP" и "IGMP", всегда различно. На рисунке 1.4 мы показали их на том же уровне, что и IP, потому что в действительности они являются дополнением к протоколу IP. Однако здесь мы показали их выше чем IP, чтобы подчеркнуть то, что сообщения ICMP и IGMP инкапсулируются в IP датаграммы.

Тот же самый подход был использован с квадратиками, помеченными "ARP" и "RARP". Здесь мы показали их выше чем драйвер устройства Ethernet, потому что оба они имеют свой собственный тип фреймов Ethernet, как IP датаграммы. Однако на рисунке 2.4 мы покажем ARP как часть драйвера устройства Ethernet, что будет более логично.

Естественно, что рисунки, иллюстрирующие протоколы и их взаимодействия, всегда несовершенны.

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

Модель Клиент-Сервер

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

Можно подразделить серверы на два класса: последовательные (iterative) и конкурентные (concurrent). Последовательный сервер функционирует следующим образом.

t1_8_1.jpg (18114 bytes)

Рисунок 1.8.1 Алгоритм работы последоватеьного сервера

В процессе выполнения шага П2 могут возникнуть проблема. Она заключается в том, что в это время никакие другие клиенты не могут быть обслужены.

Конкурентный сервер, с другой стороны, работает следующим образом.

t1_8_3.jpg (15683 bytes)

Рисунок 1.8.2 Алгоритм работы конкурентного сервера

Запуск нового сервера на шаге К2 для обработки запроса клиента может выглядеть как создание нового процесса, задачи, в зависимости от того какая операционная система лежит в основе этого сервера. Новый сервер обрабатывает поступивший запрос клиента целиком. По завершении сервер уничтожается.

Преимущество конкурентного сервера заключается в том, что он просто запускает другие сервера для обработки запросов от клиентов. В подобном случае каждый клиент имеет собственный сервер. Предполагается, что операционная система поддерживает многозадачность и обслуживание нескольких клиентов одновременно.

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

В общем случае, серверы TCP - конкурентные, а серверы UDP - последовательные. Однако из этого правила могут быть исключения. Более подробно мы рассмотрим UDP серверы в разделе "Сервер UDP" главы 11, а TCP серверы - в разделе "Реализация TCP сервера" главы 18.

Номера портов

Как мы уже сказали, TCP и UDP идентифицируют приложения с использованием 16-битных номеров порта. Рассмотрим, как выбираются эти номера портов.

Обычно серверы знают свои заранее известные (well-known) номера портов. Например, каждая реализация TCP/IP, предоставляющая FTP сервер, знает, что сервисный порт TCP номер 21 зарезервирован для FTP сервиса. Каждый Telnet сервер имеет порт номер 23. Каждая реализация TFTP (Trivial File Transfer Protocol) использует UDP порт 69. Подобные сервисы, предоставляемые в любой реализации TCP/IP, имеют заранее известные номера портов в диапазоне от 1 до 1023. Заранее известные порты обслуживаются Internet Assigned Numbers Authority (IANA).

До 1992 года номера заранее известных портов находились в диапазоне от 1 до 255. Порты между 256 и 1023 обычно использовались UNIX системами для специальных сервисов. Эти сервисы присутствовали в UNIX системах, однако могли не присутствовать в других операционных системах. В настоящее время IANA обслуживает порты в диапазоне от 1 до 1023.

В качестве примера различия между Internet сервисом и специализированным UNIX сервисом можно показать различие между Telnet и Rlogin. Оба позволяют осуществить терминальный заход по сети на удаленный компьютер. Telnet это стандарт TCP/IP с номером заранее известного порта 23. Он поддерживается практически во всех операционных системах. Rlogin, с другой стороны, исходно был разработан в UNIX системах (однако многие не UNIX системы в настоящее время также предоставляют этот сервис). Однако заранее известный порт был выбран в начале 80-годов и установлен в значение 513.

Клиент обычно не заботится о том, какой порт используется с его стороны. Все что ему необходимо, это быть уверенным, что данный номер порта уникален на его компьютере. Номер порта клиента называется динамически назначаемым портом (ephemeral port), то есть портом с коротким временем жизни. Это объясняется тем, что клиент обычно существует ровно столько времени, сколько пользователь нуждается в клиентском сервисе, тогда как сервера функционируют все время, пока запущен компьютер.

Большинство реализаций TCP/IP располагают номера динамически назначаемых портов в диапазоне значений между 1024 и 5000. Номера портов свыше 5000 предназначены для других серверов (не зарезервированных в сети Internet) . Далее по тексту мы встретим множество примеров того, как располагаются или назначаются динамически назначаемые порты.

Solaris 2.2 является исключением. По умолчанию, динамически назначаемые порты TCP и UDP начинаются с 32768. В разделе "Solaris 2.2" приложения E подробно рассматриваются опции конфигурирования, которые могут быть модифицированы системным администратором, чтобы изменить установки по умолчанию.

В большинстве UNIX систем номера заранее известных портов находятся в файле /etc/services. Чтобы найти номер порта для сервера Telnet и Domain Name System, можно исполнить следующую команду:

sun % grep telnet /etc/services
telnet 23/tcp используется порт TCP 23
sun % grep domain /etc/services
domain 53/udp используются порты UDP 53
domain 53/tcp и TCP порт 53

Зарезервированные порты

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

Эти номера портов находятся в диапазоне от 1 до 1023 и используются некоторыми приложениями (например, Rlogin, глава 26, раздел "Протокол Rlogin") для разграничения прав доступа при общении клиент-сервер.

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

Кто контролирует семейство протоколов TCP/IP, добавляет новые стандарты и так далее? Существует 4 группы, которые несут ответственность за технологию Internet.

  1. Internet Society (ISOC) это сообщество профессионалов, которое отвечает за настройку и поддержку роста Internet как мировой исследовательской коммуникационной инфраструктуры.
  2. Internet Architecture Board (IAB) занимается технической координацией. Состоит примерно из 15 добровольцев по всему миру, которые отвечают за различные дисциплины и являются последней инстанцией при принятии и оценке качества стандартов Internet. IAB находится под управлением ISOC.
  3. Internet Engineering Task Force (IETF) - группа, занимающаяся стандартами. Поделена на 9 зон (приложения, маршрутизация и адресация, безопасность, и так далее). IETF разрабатывает спецификации, которые становятся стандартами Internet. В помощь IETF была сформирована группа Internet Engineering Steering Group (IESG).
  4. Internet Research Task Force (IRTF) занимается долговременными проектами.

IRTF и IETF находятся под управлением IAB. [Crocker 1993] предоставляет дополнительную информацию о процессе стандартизации в рамках Internet.

RFC

Все официальные стандарты сообщества internet публикуются в Request for Comment, или в RFC. В дополнение, существует множество RFC, которые не являются официальными стандартами, однако они публикуются с информационными целями. Диапазон размеров RFC колеблется от 1 до почти 200 страниц. Каждый из них имеет собственный номер.

Все RFC доступны бесплатно по электронной почте или с использованием FTP через Internet. Послать электронную почту можно следующим образом:

To: rfc-info@ISI.EDU
Subject: getting rfcs

help: ways_to_get_rfcs

после чего можно получить подробный список возможных путей получения RFC.

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

Существует несколько важных RFC.

  1. Assigned Numbers RFC содержит в себе все числа и константы, которые используются в протоколах Internet. В настоящее время самая последняя версия этого RFC имеет номер 1340 [Reynolds and Postel 1992]. Также здесь описаны все заранее известные порты глобальной сети Internet.

    2. Когда RFC обновляется (обычно это происходит ежегодно), список индексов для 1340 указывает, какой RFC заменил его.

  2. Официальные стандарты протоколов Internet (Internet Official Protocol Standards), в настоящее время RFC 1600 [Postel 1994]. Этот RFC содержит информацию о состоянии стандартизации различных протоколов Internet. Каждый протокол имеет одно из следующих состояний стандартизации: стандарт, необязательный стандарт, рекомендованный стандарт, экспериментальный, информационный, или исторический. В дополнение, каждый протокол имеет уровень необходимости: необходим, рекомендуется, на выбор, с ограниченным использованием, или не рекомендуется.

    3. Как и Assigned Numbers RFC, этот RFC регулярно переиздается. При чтении этого RFC убедитесь, что Вы используете последнюю копию.

  3. Требования к хостам Host Requirements RFC, 1122 и 1123 [Braden 1989a, 1989b]. RFC 1122 описывает канальный уровень, сетевой уровень и транспортный уровень, тогда как RFC 1123 описывает прикладной уровень. Эти два RFC корректируют и интерпретируют различные важные RFC, вышедшие раньше, и часто являются исходной точкой при изучении деталей того или иного протокола. Список характеристик и конкретных подробностей протоколов следующий: "должен", "следует", "может", "не следует" или "не должен".

    4. [Borman 1993b] приводит практический взгляд на эти два RFC, а RFC 1127 [Braden 1989c] содержит краткий информационный справочник дискуссий и выводов в рабочих группах, которые разрабатывали Host Requirements RFC.

  4. Требования к маршрутизаторам Router Requirements RFC. Официальная версия этого RFC - RFC 1009 [Braden and Postel 1987], однако новая версия близка к завершению [Almquist 1993]. Это RFC напоминает RFC требования к компьютерам, однако содержит уникальные требования к маршрутизаторам.
Стандартные простые сервисы

Существует несколько стандартных простых сервисов, которые присутствуют практически в каждой реализации. Мы используем некоторые из этих сервисов по тексту, обычно с клиентом Telnet. Эти сервисы описываются на рисунке 1.9. Можно заметить, что когда один и тот же сервис предоставляется с использованием и TCP и UDP, оба номера порта обычно выбираются одинаковыми.

Если повнимательнее посмотреть на номера портов этих и других стандартных сервисов TCP/IP (Telnet, FTP, SMTP, и так далее), то можно заметить, что большинство из них имеют нечетные номера. Так сложилось исторически, так как номера портов были заимствованы от номеров портов NCP. Протокол управления сетью (NCP - Network Control Protocol) предшествовал TCP в качестве транспортного уровня для ARPANET. NCP был симплексный, не полнодуплексный, каждое приложение требовало двух соединений, поэтому пара четных-нечетных чисел в качестве номеров портов, была зарезервирована для каждого приложения. Когда TCP и UDP стали стандартными транспортными уровнями, на каждое приложение потребовался один номер порта, поэтому в качестве номеров порта были использованы нечетные номера портов NCP.

Имя | порт TCP | порт UDP | RFC | Описание
echo | 7 | 7 | 862 | Сервер возвращает все посланное клиентом
discard | 9 | 9 | 863 | Сервер игнорирует все посланное клиентом
daytime | 13 | 13 | 867 | Сервер выдает время и дату в читаемом формате
chargen | 19 | 19 | 864 | TCP сервер посылает продолжающийся поток символов, до тех пор пока соединение не будет разорвано клиентом. UDP сервер посылает датаграммы с произвольным количеством каждый раз когда пользователь посылает датаграмму
time | 37 | 37 | 868 | Сервер выдает время как 32-х битовое бинарное число. Это число означает количество секунд с полуночи 1- го Января 1900, UTC.

Рисунок 1.9 Стандартные сервисы, предоставляемые большинством реализаций.

Internet

На рисунке 1.3 показано соединение двух сетей - Ethernet и Token ring. В разделах "Адресация Internet" и "Номера портов" главы 1 мы говорили о глобальной сети Internet и о необходимости централизованного распределения IP адресов (InterNIC), а также о заранее известных номерах портов (IANA) . Само слово internet имеет различный смысл, в зависимости от того, начинается ли оно с большой буквы или с маленькой.

Слово internet означает несколько сетей, соединенных вместе, использующих общее семейство протоколов. Слово Internet, начинающееся с большой буквы, обозначает определенное количество компьютеров (более миллиона), находящихся по всему миру, которые могут общаться друг с другом с использованием TCP/IP. Поэтому Internet это internet, а не наоборот.

Реализации

Де факто стандарт для реализаций TCP/IP появился в группе компьютерных исследований Калифорнийского университета в Berkeley. Исторически он распространялся с 4.x BSD system (Berkeley Software Distribution), и с BSD Networking Releases. Исходные тексты этой реализации явились отправной точкой для множества последующих.

На рисунке 1.10 показана хронология появления различных релизов BSD и указаны важнейшие характеристики TCP/IP. BSD Networking Releases, показанный слева, свободно распространяемый исходный код, который содержит все исходные сетевые коды: как самих протоколов, так и большинства приложений и утилит (таких как Telnet и FTP).

Надо сказать, что довольно сложно вычислить, когда и как появился релиз Net/3.

По тексту мы используем термин "реализации, произошедшие от Berkeley" или "Berkeley реализации", чтобы указать на следующие реализации: SunOS 4.x, SVR4, и AIX 3.2, которые были разработаны с использованием исходных текстов Berkeley.

Рисунок 1.10 Различные релизы BSD и их важнейшие функции.

Эти реализации имеют очень много общего, включая одни и те же ошибки!

Большинство исследований в Internet до сих пор осуществляется с использованием системы Berkeley - новые алгоритмы предотвращения переполнения (глава 21, раздел "Быстрая повторная передача и алгоритм быстрого восстановления"), групповые запросы (глава 12, раздел "Рассылка групповых сообщений"), работа с каналами с повышенной пропускной способностью (глава 24, раздел "Каналы с повышенной пропускной способностью (Long Fat Pipes)") и так далее.

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

Два популярных интерфейса прикладного программирования (API - application programming interface) для приложений, использующих протоколы TCP/IP, называются сокетами (sockets) и интерфейсом транспортного уровня (TLI - Transport Layer Interface) . Первый иногда называется "Berkeley sockets", что указывает на то где он был разработан. Последний, исходно разработанный в AT&T, иногда называется XTI (X/Open Transport Interface), это работа, произведенная X/Open, международной группой поставщиков компьютеров, которые создали свой собственный набор стандартов. XTI в действительности является расширением TLI.

При написании этой книги мы не задавались целью подробно рассматривать низкий уровень программирования, однако, где это возможно, будем ссылаться на отдельные характеристики TCP/IP, в частности на большинство популярных API. Все детали программирования, как для сокет так и для TLI, можно найти в [Stevens 1990].

Тестируемая сеть

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

Рисунок 1.11 Сеть, используемая для всех примеров в тексте. Все IP адреса начинаются с 140.252.

Большинство примеров запускаются на четырех системах, которые показаны в нижней части рисунка. Все IP адреса на этом рисунке принадлежат сети класса B 140.252. Все имена хостов принадлежат домену .tuc.noao.edu (noao это "National Optical Astronomy Observatories", а tuc это Tuscon). Например, нижняя правая система имеет полное имя хоста svr4.tuc.noao.edu с IP адресом 140.252.13.34. Над каждым квадратиком указана операционная система, под управлением которой работает данный компьютер. Подобный набор систем и сетей, компьютеров и маршрутизаторов позволяет подробно рассмотреть различные реализации TCP/IP.

Необходимо отметить, что в действительности, в домене noao.edu значительно больше сетей и хостов, нежели тех, что показаны на рисунке 1.11. Однако здесь показаны те системы, которые использовались в качестве примеров в книге.

В разделе "Адресация подсетей" главы 3 мы опишем способ деления этой сети на подсети, а в разделе "Уполномоченный агент ARP" главы 4 подробно рассмотрим dialup SLIP (IP по последовательной линии с дозвоном) соединения между sun и netb. В разделе "SLIP: IP по последовательной линии" главы 2 SLIP описан более подробно.

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

В этой главе мы совершили короткое путешествие в огромный мир семейства протоколов TCP/IP, представили большинство терминов и протоколов, которые будут обсуждены более подробно в следующих главах.

Существует четыре уровня семейства протоколов TCP/IP - канальный уровень (link layer), сетевой уровень (network layer), транспортный уровень (transport layer) и прикладной уровень (application layer). Мы показали, за что отвечает каждый из этих уровней. В TCP/IP существуют серьезные различия между сетевым и транспортным уровнями: сетевой уровень (IP) предоставляет сервис, не ориентированный на соединение (пересылка за пересылкой - hop-by-hop), тогда как транспортный уровень предоставляет сервис с соединением (точка в точку - end-to-end) (TCP и UDP).

"internet" это несколько сетей. Основное средство, используемое для объединения сетей (internet) это маршрутизатор, который соединяет сети на IP уровне. Заглавная "I" в слове "Internet" обозначает сеть, которая охватывает весь земной шар.

В сети каждый интерфейс имеет уникальный IP адрес, однако пользователи могут использовать имена хостов вместо IP адресов. Система имен доменов (DNS) позволяет установить динамическое соответствие между именами хостов и IP адресами. Номера портов используются для идентификации приложений, общающихся друг с другом. При этом мы сказали, что сервера используют заранее-известные порты (well-known), тогда как клиенты используют динамически назначаемые порты (ephemeral port).

Упражнения

  1. Вычислите максимально возможное количество компьютеров в сетях класса A, B и C.
  2. Воспользовавшись анонимным FTP (глава 27, раздел "Примеры FTP"), получите файл nfsnet/statistics/history.netcount с хоста ftp://nic.merit.edu. В этом файле содержится количество местных и иностранных сетей, объявленных в инфраструктуре NFSNET. Распределите полное количество сетей по годам (по оси ОХ - годы, по OY - количество сетей). Максимальное значение по оси OY должно совпадать со значением, рассчитанным в предыдущем упражнении. Если данные примерно совпадут, экстраполируйте значения, чтобы оценить, когда текущая схема адресации переполнится сетевыми идентификаторами (раздел "Будущее IP" главы 3 рассказывает о том, как можно решить подобную проблему).
  3. Достаньте копию Host Requirements RFC [Braden 1989a] и рассмотрите принцип непотопляемости (robustness principle) , который применяется к каждому уровню семейства протоколов TCP/IP. Какое применение можно найти для этого принципа?
  4. Получите копию последнего Assigned Numbers RFC. Каков зарезервированный порт протокола "quote of the day"? Какой RFC определяет этот протокол?
  5. Имеете ли Вы компьютер, который подключен к сети TCP/IP, какой его основной IP адрес? Подключен ли этот компьютер к мировой сети Internet? Имеет ли он несколько интерфейсов?
  6. Получите копию RFC 1000, чтобы понять, откуда появился термин RFC.
  7. Установите контакт с сообществом Internet, isoc@isoc.org или + 1 703 648 9888, чтобы получить информацию о вступлении.
  8. Получите файл about-internic/information-about-the-internic, используя анонимный FTP с хоста ftp://is.internic.net.


Канальный уровень

Глава 2 Канальный уровень

Введение

Из рисунка 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, потому что это, пожалуй, самая широко распространенная форма инкапсуляции.

Рисунок 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 необязательна.

Рисунок 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.

Рисунок 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 датаграммы.

Рисунок 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 успешно используется, это возможность работать поверх практически любых канальных технологий.

Упражнения

  1. Если Ваша система поддерживает команду netstat(1) (см. главу 3, раздел "Команда netstat"), используйте ее, чтобы определить интерфейсы в Вашей системе и их MTU.


IP: протокол Internet

Глава 3 IP: протокол Internet

Введение

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 байт, если не присутствуют опции.

Рисунок 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 | 0 | 0 | 0 | 0x10
FTP |  |  |  |  |
управление | 1 | 0 | 0 | 0 | 0x10
данные | 0 | 1 | 0 | 0 | 0x08
любые данные | 0 | 1 | 0 | 0 | 0x08
TFTP | 1 | 0 | 0 | 0 | 0x10
SMTP |  |  |  |  |
фаза команд | 1 | 0 | 0 | 0 | 0x10
фаза данных | 0 | 1 | 0 | 0 | 0x08
DNS |  |  |  |  |
UDP запрос | 1 | 0 | 0 | 0 | 0x10
TCP запрос | 0 | 0 | 0 | 0 | 0x00
передача зоны | 0 | 1 | 0 | 0 | 0x08
ICMP |  |  |  |  |
ошибка | 0 | 0 | 0 | 0 | 0x00
запрос | 0 | 0 | 0 | 0 | 0x00
любой IGP | 0 | 0 | 1 | 0 | 0x04
SNMP | 0 | 0 | 1 | 0 | 0x04
BOOTP | 0 | 0 | 0 | 0 | 0x00
NNTP | 0 | 0 | 0 | 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, как это делается - мы увидим в следующей главе.

Рисунок 3.3 Доставка IP датаграммы от bsdi к sun.

Рассмотрим еще один пример: 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.

Рисунок 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.

Рисунок 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.

Рисунок 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 бит.

Рисунок 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 (адрес класса С), идентификатор сети другой. С этим адресом не может быть произведено дальнейшее сравнение.

Рисунок 3.8 Сравнение двух подсетей класса В, использующих маски подсети.

В процессе IP маршрутизации, сравнения, подобные этому, делаются все время с использованием двух IP адресов и маски подсети.

Специальные IP адреса

В процессе описания подсетей может встретится семь специальных IP адресов (см. рисунок 3.9). На этом рисунке 0 означает поле, состоящее из всех бит, установленных в ноль, -1 означает поле из бит, установленных в единицы, а netid (идентификатор сети), subnetid (идентификатор подсети) и hostid (идентификатор хоста) обозначает соответствующие поля, которые установлены не в единицы и не в нули. Пустая колонка идентификатора подсети означает, что адреса не могут быть разбиты на подсети.

IP адрес |  |  | Может появляться как |  | Описание
ID сети | ID подсети | ID хоста | источник? | назначение?
0 |  | 0 | OK | никогда | этот хост в этой сети (см. ограничения ниже)
0 |  | hostid | OK | никогда | указывает хост в этой сети (см. ограничения ниже)
127 |  | любой | OK | OK | адрес loopback (см. раздел "Интерфейс Loopback" главы 2)
-1 |  | -1 | никогда | OK | ограниченный широковещательный запрос (никогда не перенаправляется)
netid |  | -1 | никогда | OK | широковещательный запрос, направляемый в сеть на netid
netid | subnetid | -1 | никогда | OK | широковещательный запрос, направляемый в подсеть на netid, subnetid
netid | -1 | -1 | никогда | OK | широковещательный запрос, направляемый во все подсети на netid

Рисунок 3.9 Специальные IP адреса.

Мы разделили эту таблицу на три секции. Первые две записи содержат специальные адреса источников, затем адрес интерфейса loopback, и последние четыре записи - это широковещательные адреса.

Первые два пункта в таблице с идентификатором сети, равным 0, могут существовать только как адрес источника во время процедуры инициализации, когда хост определяет свой собственный IP адрес, например, с использованием протокола BOOTP (глава 16).

В разделе "Широковещательные запросы" главы 12 мы рассмотрим четыре типа широковещательных адресов более подробно.

Пример подсети

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

Рисунок 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 канала точка-точка.

Рисунок 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.

Хост | IP адрес | Маска подсети | ID сети/ ID подсети | ID хоста | Комментарии
sun | 140.252.1.29 | 255.255.255.0 | 140.252.1. | 29 | в подсети 1
 | 140.252.13.33 | 255.255.255.244 | 140.252.13.32 | 1 | Ethernet, который рассматривается в качестве примера
svr4 | 140.252.13.34 | 255.255.255.244 | 140.252.13.32 | 2 |
bsdi | 140.252.13.35 | 255.255.255.244 | 140.252.13.32 | 3 | Ethernet
 | 140.252.13.66 | 255.255.255.244 | 140.252.13.64 | 2 | точка-точка
slip | 140.252.13.65 | 255.255.255.244 | 140.252.13.64 | 1 | точка-точка
 | 140.252.13.63 | 255.255.255.224 | 140.252.13.32 | 31 | широковещательный адрес в Ethernet

Рисунок 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: протокол определения адреса

Глава 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).

Рисунок 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 адреса, пожалуйста сообщите мне Ваш аппаратный адрес".

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

  7. Хост назначения на ARP уровне получает этот широковещательный запрос, определяет, что отправитель спрашивает именно его IP адрес, и отвечает на него ARP откликом (ARP reply). Этот отклик содержит IP адрес и соответствующий аппаратный адрес.
  8. ARP отклик принимается, и IP датаграмма, из-за которой начался обмен ARP запрос - ARP отклик, может быть послана.
  9. 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 адресов. Первые четыре поля, следующие за полем типа фрейма, указывают на типы и размеры заключительных четырех полей.)

Рисунок 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.

Рисунок 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 запрос при загрузке?


RARP: обратный протокол определения адреса

Глава 5 RARP: обратный протокол определения адреса

Введение

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

Каждая система в сети имеет уникальный аппаратный адрес, который назначается производителем сетевого интерфейса (сетевой платы). Принцип работы RARP заключается в том, что бездисковая система может считать свой уникальный аппаратный адрес с интерфейсной платы и послать RARP запрос (широковещательный фрейм в сеть), где потребует кого-нибудь откликнуться и сообщить IP адрес (с помощью RARP отклика).

Несмотря на то что концепция довольно проста, ее реализация как правило значительно сложнее чем ARP, который был описан в предыдущей главе. Официальная спецификация RARP находится в RFC 903 [Finlayson et al. 1984].

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

Формат пакета RARP практически идентичен пакету ARP (см. рисунок 4.3). Единственное отличие заключается в том, что поле тип фрейма (frame type) для запроса или отклика RARP установлено в 0x8035, а поле op имеет значение 3 для RARP запроса и значение 4 для RARP отклика.

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

Примеры RARP

В нашей сети мы можем заставить хост sun загружаться из сети, вместо того чтобы загружаться с локального диска. Если мы запустили RARP сервер и tcpdump на хосте bsdi, то получим вывод, показанный на рисунке 5.1. Мы используем флаг -e, чтобы команда tcpdump показывала аппаратные адреса:

1  0.0               8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                    rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
2  0.13 (0.13)      0:0:c0:6f:2d:40 8:0:20:3:f6:42 rarp 42:
                    rarp reply 8:0:20:3:f6:42 at sun
3  0.14 (0.01)      8:0:20:3:f6:42 0:0:c0:6f:2d:40 ip 65:
                    sun.26999>bsdi.tftp: 23 RRQ "8CFC0D21.SUN4C"

Рисунок 5.1 Запрос и отклик RARP.

Запрос RARP широковещательный (строка 1), а отклик RARP (строка 2) персональный. Вывод в строке 2, at sun, означает, что RARP отклик содержит IP адрес хоста sun (140.252.13.33).

В строке 3 мы видим, что как только sun получил свой IP адрес, он выдал TFTP запрос на чтение (RRQ) файла 8CFC0D21.SUN4C. (TFTP это простой протокол передачи файлов - Trivial File Transfer Protocol. Мы рассмотрим его более подробно в главе 15.) Восемь шестнадцатиричных цифр в имени файла это шестнадцатиричное представление IP адреса 140.252.13.33 хоста sun. Это IP адрес, который мы получили в отклике RARP. Оставшаяся часть имени файла, SUN4C, указывает на тип системы, которая загружается.

В строке 3 tcpdump сообщает, что это IP датаграмма длиной 65, а не UDP датаграмма (которая в действительности является ей), так как мы запустили tcpdump с флаго -e, чтобы получить в выводе аппаратные адреса. Еще один момент на, который необходимо обратить внимание на рисунке 5.1, заключается в том, что длина Ethernet фрейма в строке 2 меньше чем установленный минимум (который, как мы упоминали в разделе "Примеры ARP" главы 4, должен быть равен 60 байтам). Причина этого заключается в том, что мы запустили tcpdump на системе, которая посылает этот Ethernet фрейм (bsdi). Приложение, rarpd, пишет 42 байта в устройство пакетного фильтра BSD (BSD Packet Filter) (14 байт - Ethernet заголовок и 28 байт - отклик RARP), именно это и видит tcpdump. Однако драйвер устройства Ethernet дополняет этот короткий фрейм до минимального размера, необходимого для передачи (60 байт). Если бы мы запустили tcpdump на другом компьютере, длина составила бы 60 байт.

Также мы видим, что когда бездисковая система получает свой IP адрес в отклике RARP, она осуществляет TFTP запрос, чтобы прочитать загрузочный имидж. Сейчас мы не будем подробно рассматривать, как загружаются бездисковые системы. (В главе 16 описывается последовательность загрузки бездисковых X терминалов с использованием RARP, BOOTP и TFTP.)

На рисунке 5.2 показаны пакеты, которые появляются в том случае, если в сети нет RARP сервера. Адрес назначения каждого пакета - широковещательный адрес Ethernet. Адрес Ethernet, следующий за who-is, это аппаратный адрес хоста которому требуется информация, а адрес Ethernet, следующий за tell, это аппаратный адрес отправителя.

Обратите внимание на частоту повторных передач. Первая повторная передача происходит через 6,55 секунд, затем интервал увеличивается до 42,80 секунд, затем через 5,34 секунды, затем через 6,55 секунд и снова через 42,79 секунды. Если мы рассчитаем разницу между каждым тайм-аутом, мы заметим эффект удвоения: между 5,34 и 6,55 - 1,21 секунды, между 6,55 и 8,97 - 2,42 секунды, между 8,97 и 13,80 - 4,83 секунды и так далее.

1    0.0              8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
2    6.55 ( 6.55)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
3   15.52 ( 8.97)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
4   29.32 (13.80)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
5   52.78 (23.46)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
6   95.58 (42.80)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
7  100.92 ( 5.34)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
8  107.47 ( 6.55)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
9  116.44 ( 8.97)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
10  130.24 (13.80)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
11  153.70 (23.46)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42
12  196.49 (42.79)    8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff rarp 60:
                      rarp who-is 8:0:20:3:f6:42 tell 8:0:20:3:f6:42

Рисунок 5.2 RARP запрос при отсутствии в сети RARP сервера.

Когда тайм-аут достигает определенного предела (больше чем 42,80 секунды), он сбрасывается вновь в 5,34 секунды.

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

Реализация RARP сервера

Тогда как концепция RARP довольно проста, реализация RARP сервера сильно зависит от системы и довольно сложна. Напротив, реализация ARP сервера проста и является, как правило, частью реализации ядра TCP/IP. Так как ядро знает собственные IP адреса и аппаратные адреса, при получении ARP запроса для одного из IP адресов оно просто формирует отклик с соответствующим аппаратным адресом.

RARP серверы как пользовательские процессы

Основная задача RARP сервера заключается в том, чтобы предоставить соответствие между аппаратными адресами и IP адресами для множества хостов (все бездисковые системы в сети). Необходимая информация содержится в дисковом файле (обычно /etc/ethers в UNIX системах). Так как ядро обычно не читает дисковые файлы, функция RARP сервера реализуется с использованием пользовательского процесса, который не является частью ядра TCP/IP.

Далее, можно отметить, что RARP запросы передаются в качестве Ethernet фреймов со специфическим полем типа фрейма Ethernet (0x8035 на рисунке 2.1). Это означает, что RARP сервер должен обладать способностью отправлять и принимать Ethernet фреймы подобного типа. В приложении А мы опишем как для приема подобных фреймов используются BSD Packet Filter, Sun Network Interface Tap и SVR4 Data Link Provider Interface. Так как посылка и прием подобных фреймов зависит от системы, реализация RARP сервера также зависит от системы.

Несколько RARP серверов в сети

Еще одна особенность заключается в том, что RARP запросы посылаются в виде широковещательных запросов аппаратного уровня, как показано на рисунке 5.2. Это означает, что они не перенаправляются маршрутизаторами. Чтобы позволить бездисковым системам загружаться, даже если RARP сервер выключен, в сети обычно существуют несколько RARP серверов (на одном и том же кабеле).

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

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

RARP используется большинством бездисковых систем при загрузке, для получения свох IP адресов. Формат пакета RARP практически идентичен пакету ARP. Запрос RARP широковещательный, в нем содержится аппаратный адрес отправителя, при этом он спрашивает кого-либо послать ему его IP адрес. Отклик обычно персональный.

Проблемы с RARP заключаются в том, что он использует широковещательные запросы на канальном уровне, поэтому большинство маршрутизаторов не могут перенаправлять RARP запросы; а также в том, что передается минимум необходимой информации: только IP адрес системы. В главе 16 мы увидим, что BOOTP сообщает значительно больше информации необходимой при загрузке бездисковых систем: IP адрес, имя хоста, с которого происходит загрузка, и так далее.

Несмотря на то что концепция RARP довольно проста, реализация RARP сервера зависит от системы. Также надо отметить, что не все TCP/IP реализации предоставляют RARP сервер.

Упражнения

  1. Требуется ли для RARP отдельное поле типа фрейма (frame type)? Может ли быть использовано одно и то же значение 0x0806 для ARP и RARP?
  2. Если в сети находится несколько RARP серверов, как они могут сделать так, чтобы предотвратить коллизии своих ответов?


ICMP: протокол управления сообщениями Internet

Глава 6 ICMP: протокол управления сообщениями Internet

Введение

Обычно считается, что ICMP это часть IP уровня. С его помощью передаются сообщения об ошибках и сообщения о возникновении условий и ситуаций, которые требуют к себе особого внимания. ICMP сообщения обрабатываются IP уровнем или более высокими уровнями (TCP или UDP). При появлении некоторых ICMP сообщений генерируются сообщения об ошибках, которые передаются пользовательским процессам.

ICMP сообщения передаются внутри IP датаграмм, как показано на рисунке 6.1.

Рисунок 6.1 Инкапсуляция ICMP сообщений в IP датаграммы.

Официальная спецификация ICMP находится в RFC 792 [Postel 1981b].

На рисунке 6.2 показан формат ICMP сообщения. Первые 4 байта одинаковы для всех сообщений, однако остальные отличаются в зависимости от типа сообщения. Мы будем показывать точный формат каждого сообщения, по мере того как будем их описывать.

Существует 15 различных значений для поля типа (type), которые указывают на конкретныей тип ICMP сообщения. Для некоторых ICMP сообщений используются различные значения в поле кода (code), подобным образом осуществляется дальнейшее подразделение ICMP сообщений.

Поле контрольной суммы (checksum) охватывает ICMP сообщения целиком. Алгоритм, используемый при этом, такой же как тот, что был описан в разделе "IP заголовок" главы 3 при расчете контрольной суммы IP заголовка. Контрольная сумма ICMP присутствует всегда.

Рисунок 6.2 ICMP сообщение.

В этой главе мы рассмотрим ICMP сообщения в целом, некоторые из них подробно: запрос и отклик маски адреса, запрос и отклик временной марки и сообщение о недоступности порта. В главе 7 мы обсудим, с использованием программы Ping, эхо запрос и эхо отклик, а в главе 9 рассмотрим ICMP сообщения, связанные с IP маршрутизацией.

Типы сообщений ICMP

На рисунке 6.3 приведены возможные типы ICMP сообщений, как они определяются полями типа (type) и кода (code).

Последние две колонки на рисунке указывают, является ли ICMP сообщение запросом (query) или сообщением об ошибке (error). Подобное разделение необходимо, потому что сообщения об ошибках ICMP иногда обрабатываются специальным образом. Например, ICMP сообщение об ошибке никогда не генерируется в ответ на ICMP сообщение об ошибке. (Если не придерживаться этого правила, то ошибка будет генерироваться на ошибку до бесконечности.)

Когда посылается ICMP сообщение об ошибке, оно всегда содержит IP заголовок и первые 8 байт IP датаграммы, которая вызвала генерацию ICMP ошибки. Это позволяет принимающему ICMP модулю установить соответствие между полученным сообщением, одним из конкретных протоколов (TCP или UDP из поля протоколов в IP заголовке) и с одним из конкретных пользовательских процессов (с помощью номера порта TCP или UDP, который содержится в TCP или UDP заголовке в первых 8 байтах IP датаграммы). В разделе "ICMP ошибка недоступности порта (ICMP Port Unreachable Error)" главы 6 мы рассмотрим это более подробно.

Сообщение об ошибке ICMP никогда не генерируется в ответ на:

  1. ICMP сообщение об ошибке. (ICMP сообщение об ошибке, однако, может быть сгенерировано в ответ на ICMP запрос.)
  2. Датаграмму, направляющуюся на широковещательный IP адрес (рисунок 3.9) или групповой адрес IP (адрес класса D, рисунок 1.5).
  3. Датаграмму, которая посылается широковещательным запросом на канальном уровне.
  4. Фрагмент, который не является первым. (Мы опишем фрагментацию в разделе "Фрагментация IP" главы 11.)
  5. Датаграмму, адрес источника которой не указывает на конкретный хост. Это означает, что адрес источника не может быть нулевым, loopback адресом, широковещательным или групповым адресом.

тип | код | Описание | запрос | ошибка
0 | 0 | эхо-отклик (отклик-Ping, глава 7) | · |
3 |  | назначение недоступно: |  |
 | 0 | сеть недоступна - network unreachable (раздел "ICMP ошибки о недоступности хоста и сети" главы 9) |  | ·
 | 1 | хост недоступен - host unreachable (раздел "ICMP ошибки о недоступности хоста и сети" главы 9) |  | ·
 | 2 | протокол недоступен - protocol unreachable |  | ·
 | 3 | порт недоступен - port unreachable (раздел "ICMP ошибка недоступности порта (ICMP Port Unreachable Error)" главы 6) |  | ·
 | 4 | необходима фрагментация, однако установлен бит “не фрагментировать”- fragmentation needed but don’t-fragment bit set (раздел "ICMP ошибки о недоступности" главы 11) |  | ·
 | 5 | не работает маршрутизация от источника - source route failed (глава 8, раздел "Опция IP маршрутизации от источника") |  | ·
 | 6 | неизвестна сеть назначения - destination network unknown |  | ·
 | 7 | неизвестен хост назначения - destination host unknown |  | ·
 | 8 | хост источник изолирован - source host isolated |  | ·
 | 9 | сеть назначения закрыта администратором - destination network administrativrly prohibited |  | ·
 | 10 | хост назначения закрыт администратором - destination host administrativrly prohibited |  | ·
 | 11 | сеть недоступна для TOS - network unreachable for TOS (глава 9, раздел "ICMP ошибки о недоступности хоста и сети") |  | ·
 | 12 | хост недоступен для TOS - host unreachable for TOS (глава 9, раздел "ICMP ошибки о недоступности хоста и сети") |  | ·
 | 13 | связь административно закрыта путем фильтрации - communication administratively prohibited by filtering |  | ·
 | 14 | нарушено старшинство для хоста - host precedence violation |  | ·
 | 15 | старшинство разъединено - precedence cutoff in effect |  | ·
4 | 0 | подавление источника (элементарное управление потоком данных) - source quench (глава 11, раздел "ICMP ошибка подавления источника") |  | ·
5 |  | перенаправление - redirect (глава 11, раздел "ICMP ошибка подавления источника"): |  |
 | 0 | перенаправление в сеть - redirect for network |  | ·
 | 1 | перенаправление в хост - redirect for host |  | ·
 | 2 | перенаправление для типа сервиса и сети - redirect for type-of-service and network |  | ·
 | 3 | перенаправление для типа сервиса и хоста - redirect for type-of-service and host |  | ·
8 | 0 | эхо запрос - echo request (Ping запрос, глава 7) | · |
9 | 0 | объявление маршрутизатора - router advertisement (глава 9, раздел "ICMP сообщения поиска маршрутизатора") | · |
10 | 0 | запрос к маршрутизатору - router solicitation (глава 9, раздел "ICMP сообщения поиска маршрутизатора") | · |
11 |  | время истекло - time exceeded: |  |
 | 0 | время жизни стало равным 0 в процессе передачи - time-to-live equals 0 during transit (Traceroute, глава 8) |  | ·
 | 1 | время жизни стало равным 0 в процессе повторной сборки - time-to-live equals 0 during reassembly (глава 11, раздел "Фрагментация IP") |  | ·
12 |  | проблемы с параметрами - parameter problem: |  |
 | 0 | неверный IP заголовок - IP header bad |  | ·
 | 1 | отсутствует необходимая опция - required option missing |  | ·
13 | 0 | запрос временной марки - timestamp request (глава 6, раздел "ICMP запрос и отклик временной марки") | · |
14 | 0 | отклик с временной маркой - timestamp reply (глава 6, раздел "ICMP запрос и отклик временной марки") | · |
15 | 0 | информационный запрос - information request | · |
16 | 0 | информационный отклик - information reply | · |
17 | 0 | запрос маски адреса - address mask request (глава 6, раздел "ICMP запрос и отклик маски адреса") | · |
18 | 0 | отклик с маской адреса - address mask reply (глава 6, раздел "ICMP запрос и отклик маски адреса") | · |

Рисунок 6.3 Типы сообщений ICMP.

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

ICMP запрос и отклик маски адреса

ICMP запрос маски адреса используется бездисковыми системами, чтобы получить маску подсети (глава 3, раздел "Маска подсети") во время загрузки. Система посылает широковещательный ICMP запрос. (Это напоминает то, как бездисковые системы с использованием RARP получают свои IP адреса во время загрузки.) Альтернативный метод для бездисковых систем получить маски своих подсетей - протокол BOOTP, о котором рассказано в главе 16. На рисунке 6.4 показан формат ICMP запроса и отклика маски адреса.

Рисунок 6.4 ICMP запрос и отклик маски адреса.

Поля идентификатора и номера последовательности в ICMP сообщении могут быть установлены по выбору отправителя, эти же значения будут возвращены в отклике. Именно таким образом отправитель идентифицирует отклик на свой запрос.

Мы можем написать простую программу (которая называется icmpaddrmask), которая выдает ICMP запрос маски адреса и печатает все отклики. Обычно, запрос отправляется на широковещательный адрес, мы поступим точно так же. Адрес назначения (140.252.13.63) это широковещательный адрес для подсети 140.252.13.32 (см. рисунок 3.12).

sun % icmpaddrmask 140.252.13.63
received mask = ffffffe0, from 140.252.13.33          от самого себя
received mask = ffffffe0, from 140.252.13.35          от bsdi
received mask = ffff0000, from 140.252.13.34          от svr4

Первое, на что стоит обратить внимание в выводе программы, это то, что значение, возвращенное от svr4, неверно. Это произошло из-за того, что SVR4 возвращает общую маску подсети класса В, подразумевающую, что деление на подсети не осуществлено, даже несмотря на то что интерфейс svr4 сконфигурирован с правильной маской подсети:

svr4 % ifconfig emd0
emd0: flags=23<UP, BROADCAST, NOTRAILERS.
      inet 140.252.13.34 netmask ffffffe0 broadcast 140.252.13.63

Это одна из известных ошибок SVR4 возникающая при обработке ICMP запросов маски адреса.

Мы рассмотрим обмен пакетами для хоста bsdi, используя tcpdump. Вывод показан на рисунке 6.5. Была использована опция -e, которая позволяет посмотреть аппаратные адреса.

1 0.0               8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff ip 60:
                   sun > 140.252.13.63: icmp: address mask request
2 0.00 (0.00)      0:0:c0:6f:2d:40 ff:ff:ff:ff:ff:ff ip 46:
                   bsdi > sun: icmp: address mask is 0xffffffe0
3 0.01 (0.01)      0:0:c0:6f:2d:40 8:0:20:3:f6:42 ip 60:
                   svr4 > sun: icmp: address mask is 0xffff0000

Рисунок 6.5 ICMP запрос маски адреса, отправленный на широковещательный адрес.

Обратите внимание на то, что посылающий хост, sun, принимает ICMP отклик (строка вывода с комментарием "от самого себя", которая была показана ранее), даже если "в кабеле" ничего не было. Это основная характеристика широковещательных запросов: посылающий хост принимает копию широковещательного пакета через внутренний механизм loopback. Так как по определению термин "широковещательный запрос" означает все хосты в локальной сети, сюда же включается и посылающий хост. (Если обратиться к рисунку 2.4, можно увидить, что драйвер Ethernet, определив, что адрес назначения является широковещательным, посылает пакет в сеть и копирует его в интерфейс loopback.)

Затем bsdi отправляет широковещательным запросом отклик, тогда как svr4 посылает отклик только тому, кто отправил запрос. Обычно отклик должен быть персональным, если только IP адрес источника в запросе не установлен в 0.0.0.0, отправка откликов на широковещательный адрес это ошибка, допущенная в BSD/386.

Требования к хостам Host Requirements RFC гласят, что система не должна отправлять отклик о маске адреса, если она не является полномочным агентом для рассылки масок адресов. (Чтобы являтьься полномочным агентом и рассылать подобные отклики, система должна быть специально сконфигурирована. (См. приложение Е.) Однако, как мы видим из этого примера, большинство реализаций посылают отклик в ответ на полученный запрос. Иногда хосты даже посылают неверные отклики!

Обратимся к следующему примеру. Мы посылаем запрос о маске адреса на свой собственный IP адрес и на loopback адрес:

sun % icmpaddrmask sun
received mask = ff000000, from 140.252.13.33

sun % icmpaddrmask localhost
received mask = ff000000, from 127.0.0.1

В обоих случаях возвращенная маска адреса соответствует адресу loopback, адресу 127.0.0.1 сети класса А. Снова обратившись к рисунку 2.4 мы увидим, что IP датаграммы, посланные на собственный IP адрес хоста (140.252.13.33 в данном примере), в действительности посылаются на loopback интерфейс. ICMP отклик о маске адреса должен соответствовать маске подсети интерфейса, на которой был принят запрос (при этом хост с несколькими интерфейсами может иметь различные маски подсети для каждого интерфейса), а в нашем случае оба запроса получены с интерфейса loopback.

ICMP запрос и отклик временной марки

ICMP запрос временной марки позволяет системе запросить другую систему о текущем времени. Рекомендуемое значение, которое должно быть возвращено, это количество миллисекунд, которые прошли с полуночи в формате UTC, (Универсальное согласованное время - Coordinated Universal Time). (В старых руководствах UTC называется Среднее время по Гринвичу - Greenwich Mean Time.) Одна из основных особенностей ICMP сообщения заключается в том, что оно предоставляет время в с точностью до миллисекунд, тогда как другие методы используемые для получения времени от удаленных систем (например, команда rdate, существующая в некоторых UNIX системах) предоставляют время с точностью до секунд. Недостаток заключается в том, что сообщается только время, прошедшее с полуночи, - запрашивающая система должена знать текущую дату. На рисунке 6.6 показан формат ICMP запроса и формат ICMP отклика временной марки.

Рисунок 6.6 ICMP запрос и отклик временной марки.

Запрашивающий заполняет исходную временную марку и отправляет запрос. Отвечающая система заполняет временную марку приема, когда получает запрос, и временную марку передачи, когда отправляет отклик. Большинство реализаций устанавливают в два последних поля одно и то же значение. (Причина, по которой существуют три поля, заключается в том, что отправителю необходимо вычислить время, которое потребовалось на отправку запроса, и отдельно рассчитать время, которое потребуется на отправку отклика.)

Примеры

Воспользуемся простой программой (которая называется icmptime), которая посылает ICMP запрос временной марки и печатает полученный отклик. Запустим эту программу в нашей маленькой сети:

sun % icmptime bsdi
orig = 83573336, recv = 83573330, xmit = 83573330, rtt = 2 ms
difference = -6 ms

sun % icmptime bsdi
orig = 83577987, recv = 83577980, xmit = 83577980, rtt = 2 ms
difference = -7 ms

Программа выдала три временные марки из ICMP сообщения: исходную (orig), приема (recv) и передачи (xmit). Из этого и следующих примеров видно, что все хосты установили временные марки приема и передачи в одно и то же значение.

Также мы рассчитали время возврата (rtt) , которое рассчитывается как время, когда был принят отклик, минус время, когда был отправлен запрос. difference - это временная марка приема минус исходная временная марка. На рисунке 6.7 показана взаимосвязь между этими значениями.

Рисунок 6.7 Взаимосвязь между значениями, напечатанными программой icmptime.

Если принять за истину то, что одна половина RTT отводится под запрос, а другая половина под отклик, тогда часы отправителя должны быть настроены как difference минус половина RTT, чтобы иметь то же самое время, как у хоста к которому отправляется запрос. В предыдущем примере часы bsdi отставали на 7 и 8 миллисекунд от часов sun.

Так как временная марка является количеством миллисекунд после полуночи, UTC должны быть всегда меньше чем 86400000 (24х60х60х1000). Эти примеры были исполнены сразу после 16:00 во временной зоне имеющей отставание на 7 часов от UTC, таким образом, приемлемыми являются значения больше чем 82800000 (2300 часов).

Если мы запустим эту программу несколько раз на хост bsdi, то увидим, что последние цифры во временной марке передачи и приема всегда равны нулю. Это происходит из-за того, что программный релиз (Version 0.9.4) имеет часы с точностью 10 миллисекунд. (Мы опишем это в приложении В.)

Если мы запустим программу дважды на хост svr4, то увидим что уже три младшие цифры во временной марке SVR4 равны нулю:

sun % icmptime svr4
orig = 83588210, recv = 83588000, xmit = 83588000, rtt = 4 ms
difference = -210 ms

sun % icmptime svr4
orig = 83591547, recv = 83591000, xmit = 83591000, rtt = 4 ms
difference = -547 ms

По каким-то причинам SVR4 не предоставляет разрешение времени с точностью до миллисекунд при использовании временной марки ICMP. Подобная неточность делает расчет разницы во времени бесполезным, если разговор идет о миллисекундах.

Если мы обратимся к двум другим хостам в подсети 140.252.1, то увидим, что установка одних часов отличается от установки часов sun на 3,7 секунды, а других на 75 секунд:

sun % icmptime gemini
orig = 83601883, recv = 83598140, xmit = 83598140, rtt = 247 ms
difference = -3743 ms

sun % icmptime aix
orig = 83606768, recv = 83532183, xmit = 83532183, rtt = 253 ms
difference = -74585 ms

Другой интересный пример может быть получен при обращении к маршрутизатору gateway (маршрутизатор Cisco). Из примера видно, что если система возвращает нестандартное значение временной марки (отличающееся от количества миллисекунд после полуночи, UTC), старшие биты в 32-битной временной марке устанавливаются в единицу. Наша программа определяет это и печатает временные марки приема и передачи в треугольных скобках (после установки старших битов в ноль). Мы можем рассчитать разницу между исходной временной маркой и временной маркой приема.

sun % icmptime gateway
orig = 83620811, recv = <4871036>, xmit = <4871036>, rtt = 220 ms

sun % icmptime gateway
orig = 83641007, recv = <4891232>, xmit = <4891232>, rtt = 213 ms

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

И в качестве последнего примера сравним часы sun с часами системы, которые считаются абсолютно точными - сервер NTP. (Мы расскажем о протоколе сетевого времени NTP - Network Time Protocol, ниже.)

sun % icmptime clock.llnl.gov
orig = 83662791, recv = 83662919, xmit = 83662919, rtt = 359 ms
difference = 128 ms

sun % icmptime clock.llnl.gov
orig = 83670425, recv = 83670559, xmit = 83670559, rtt = 345 ms
difference = 134 ms

Если вычесть из разницы (difference) половину RTT, то можно будет сказать, что часы sun торопятся на величину в диапазоне 51,5 - 38,5 миллисекунды.

Другие возможные варианты

Существуют другие способы получить время и дату.

  1. Мы описали сервис дневного времени и сервис времени в разделе "Стандартные простые сервисы" главы 1. Первый возвращает текущую дату и время в формате, понятном для человека - в виде строк ACSII символов. Мы можем проверить работу этого сервиса с использованием команды telnet:

    sun % telnet bsdi daytime
    Trying 140.252.13.35 ...
    Connected to bsdi.
    Escape character is '^]'.               первые три строки - вывод Telnet клиента
    Wed Feb 3 16:38:33 1993                 это вывод сервиса дневного времени
    Connection closed by foreign host.     это опять вывод Telnet клиента

    Сервер времени возвращает 32-битное двоичное значение, содержащее количество секунд после полуночи 1 января 1900 года, UTC. (Команда rdate, которую мы упоминали ранее, использует сервис времени TCP.)

  2. Для получения более точного времени используется протокол сетевого времени (NTP - Network Time Protocol), который описан в RFC 1305 [Mills 1992]. Этот протокол использует определенную технику поддержания точности часов для группы систем в локальной или глобальной сети с точностью до миллисекунды. Для более подробного изучения функционирования этого протокола, необходимо обратиться к RFC.
  3. Open Software Foundation (OSF) Distributed Computing Environment (DCE) определяет сервис распределенного времени (DTS - Distributed Time Service), который также осуществляет синхронизацию часов между хостами. [Rosenberg, Kenney, and Fisher 1992] более подробно описывает этот сервис.
  4. В Unix системах Berkeley существует демон timed(8) , который используется для синхронизации часов систем, находящихся в локальной сети. В отличие от NTP и DTS, timed не работает в глобальных сетях.

ICMP ошибка недоступности порта (ICMP Port Unreachable Error)

В двух последних разделах описаны запросы ICMP - маски адреса, и запросы и отклики временной марки. Сейчас мы рассмотрим ICMP сообщения об ошибках, а именно: сообщение о недоступности порта, подкод сообщения ICMP о недоступности пункта назначения. Также рассмотрим дополнительную информацию, которая возвращается в ICMP сообщении об ошибке. Для этого воспользуемся UDP (глава 11).

Если UDP принимает датаграмму, порт назначения которой не соответствует порту, который обслуживается каким-либо процессом, UDP выдает ICMP сообщение о недоступности порта. Попробуем получить ошибку недоступности порта с использованием TFTP клиента. (TFTP подробно описан в главе 15.)

TFTP сервер использует заранее-известный UDP порт 69. Однако большинство программ TFTP клиента позволяют указать другой порт с помощью команды connect. В данном случае мы указали порт 8888:

bsdi % tftp
tftp> connect svr4 8888          указываем имя сервера и номер порта
tftp> get temp.foo               пробуем получить файл
Transfer timed out.               25 секунд спустя истекает время таймера
tftp> quit

Команда connect сохраняет имя хоста, с которым необходимо установить соединение, и номер порта на этом хосте для дальнейшего использования командой get. После того как введена команда get, UDP датаграмма посылается на порт 8888 хоста svr4. На рисунке 6.8 показан вывод команды tcpdump, иллюстрирующий обмен пакетами.

Перед тем как послать UDP датаграмму на svr4, отправляется ARP запрос, для того чтобы определить аппаратный адрес (строка 1). Возвращается ARP отклик (строка 2), после чего отправляются UDP датаграммы (строка 3). (Мы оставили ARP запрос-отклик в выводе команды tcpdump, чтобы напомнить Вам, что этот обмен необходим перед отправкой первой IP датаграммы с одного хоста на другой. В следующих примерах мы не будем приводить ARP обмен.)

1   0.0                    arp who-has svr4 tell bsdi
2   0.002050 (0.0020)     arp reply svr4 is-at 0:0:c0:c2:9b:26

3   0.002723 (0.0007)     bsdi.2924 > svr4.8888: udp 20
4   0.006399 (0.0037)     svr4 > bsdi: icmp: svr4 udp port 8888 unreachable

5   5.000776 (4.9944)     bsdi.2924 > svr4.8888: udp 20
6   5.004304 (0.0035)     svr4 > bsdi: icmp: svr4 udp port 8888 unreachable

7  10.000887 (4.9966)     bsdi.2924 > svr4.8888: udp 20
8  10.004416 (0.0035)     svr4 > bsdi: icmp: svr4 udp port 8888 unreachable

9  15.001014 (4.9966)     bsdi.2924 > svr4.8888: udp 20
10  15.004574 (0.0036)     svr4 > bsdi: icmp: svr4 udp port 8888 unreachable

11  20.001177 (4.9966)     bsdi.2924 > svr4.8888: udp 20
12  20.004759 (0.0036)     svr4 > bsdi: icmp: svr4 udp port 8888 unreachable

Рисунок 6.8 Генерация ICMP ошибки о недоступности порта в ответ на TFTP запрос.

ICMP ошибка о недоступности порта возвращается немедленно (строка 4). Однако TFTP клиент игнорирует ICMP сообщение и посылает следующую UDP датаграмму примерно через 5 секунд (строка 5). Это повторяется трижды, перед тем как клиент прекращает свои попытки.

Обратите внимание на то, что ICMP сообщения передаются между хостами без номера порта назначения, тогда как каждая 20-байтовая UDP датаграмма выходит из указанного порта (2924) в указанный порт (8888).

Число 20 в конце каждой UDP строки это длина данных в UDP датаграмме. В данном примере 20 - это сумма двухбайтного кода операции (opcode) TFTP, 9-байтного имени (temp.foo) и 9-байтной строки netascii. (На рисунке 15.1 подробно описано содержимое пакета TFTP.)

Если мы запустим тот же пример с опцией -e команды tcpdump, то увидим точную длину каждого ICMP сообщения о недоступности порта, которое возвращается отправителю. Длина составляет 70 байт. (см. рисунок 6.9).

Рисунок 6.9 ICMP сообщение, вернувшееся в нашем примере "порт UDP недоступен".

Одно из правил, которому подчиняется ICMP, заключается в том, что ICMP сообщение об ошибке (см. последнюю колонку на рисунке 6.3) должно включать IP заголовок (включая все опции) датаграммы, на которую сгенерирована ошибка. Также ICMP сообщение об ошибке содержит, по крайней мере, первые 8 байт из IP датаграммы, которые следуют за IP заголовком. В нашем примере первые 8 байт, следующие за IP заголовком, содержат UDP заголовок (рисунок 11.2).

Очень важный факт заключается в том, что UDP заголовок содержит номера портов источника и назначения. В данном случае это порт назначения (8888), из-за которого было сгенерировано ICMP сообщение о недоступности порта. Номер порта источника (2924) может быть использован системой, получившей ICMP сообщение об ошибке, чтобы установить соответствие между принятой ошибкой и конкретным пользовательским процессом (в данном случае TFTP клиент).

Одна из причин, по которой IP заголовок датаграммы, которая вызвала сообщение об ошибке, посылается обратно, заключается в том, что этот IP заголовок содержит поле протокола, которое позволяет ICMP модулю понять, как необходимо интерпретировать следующие 8 байт (в данном примере - UDP заголовок). Если обратиться к TCP заголовку (см. рисунок 17.2), то мы увидим, что номера портов источника и назначения содержатся в первых 8 байтах TCP заголовка. Общий формат ICMP сообщений о недоступности показан на рисунке 6.10.

Рисунок 6.10 Сообщение о недоступности ICMP.

На рисунке 6.3 мы видели, что существует 16 различных ICMP сообщений о недоступности с кодами от 0 до 15. ICMP сообщение о недоступности порта имеет код 3. На рисунке 6.10 показано, что второе 32-битное слово в ICMP сообщении должно быть установлено в 0. Механизм определения транспортного MTU (глава 2, раздел "Транспортный MTU") позволяет маршрутизатору поместить MTU исходящего интерфейса в младшие 16 бит этого 32-битного значения, когда код равен 4 ("необходима фрагментация, однако установлен бит не фрагментировать "). Мы покажем пример подобной ошибки в разделе "ICMP ошибки о недоступности (требуется фрагментация)" главы 11.

Правила, по которым действует ICMP, позволяют системе вернуть больше чем первые 8 байт раздела данных IP датаграммы, которая вызвала ICMP ошибку, однако большинство Berkeley реализаций возвращают ровно 8 байт. В Solaris 2.2, с использованием опции ip_icmp_return_data_bytes, может быть указано, сколько байт необходимо возвращать, однако по умолчанию возвращаются первые 64 байта данных (приложение E, раздел "Solaris 2.2").

Временная диаграмма работы команды tcpdump

Здесь мы приводим временную диаграмму, соответствующую выводу команды tcpdump, приведенному на рисунке 6.11.

Рисунок 6.11 Временная диаграмма запроса TFTP на неверный порт.

Время на рисунке увеличивается по направлению вниз, а метки, стоящие крайними слева на рисунке, соответствуют величинам времени в выводе команды tcpdump, приведенном на рисунке 6.8. Метки вверху - это имена хостов и номера портов каждой конечной системы. Однако, вертикальное представление времени на рисунке не точно соответствует реальному времени. При передаче данных UDP или TCP мы показываем пакет с помощью жирной линии.

Почему TFTP клиент осуществляет повторную передачу своего запроса после прихода сообщения ICMP? Особенность сетевого программирования, присутствующая в системах BSD, заключается в том, что пользовательский процесс, использующий UDP, не предупреждается о приеме ICMP сообщения на этот сокет, если только процесс не установил соединение (connect) к этому сокету. Стандартный TFTP клиент системы BSD не выдает connect, поэтому он никогда не получит уведомления о приходе ICMP ошибки.

Также необходимо обратить внимание на то, что при работе TFTP клиента используется алгоритм тайм-аутов и повторных передач. TFTP клиент просто осуществляет повторную передачу каждые 5 секунд, всего в течение 25 секунд. Позже мы увидим, что подобный алгоритм протокола TCP значительно лучше.

Устаревший алгоритм тайм-аутов и повторных передач, используемый TFTP клиентом, в настоящее время запрещен Host Requirements RFC. Тем не менее, все три системы в описываемой подсети и Solaris 2.2 до сих пор его используют. AIX 3.2.2 использует экспотенциальный рост тайм-аута, посылая пакеты с интервалом 0, 5, 15 и 35 секунд. В настоящее время рекомендуется именно такой способ расчета тайм-аутов. Более подробно мы обсудим тайм-ауты в главе 21.

И в заключение, обратите внимание на то, что ICMP сообщения вернулись примерно через 3,5 миллисекунды после отправки UDP датаграммы. В главе 7 мы увидим, что это примерно соответствует времени возврата для отклика Ping.

Обработка ICMP сообщений в 4.4BSD

Так как ICMP охватывает очень широкий диапазон различных условий, начиная от фатальных ошибок и заканчивая информационными сообщениями, каждое ICMP сообщение обрабатывается по-своему даже в рамках одной реализации. Рисунок 6.12 это повтор рисунка 6.3, который показывает обработку возможных ICMP сообщений системой 4.4BSD.

Если в последней колонке указано "ядро", ICMP сообщение обрабатывается ядром, если - "пользовательский процесс", это означает, что сообщение передается всем пользовательским процессам, которые зарегистрированы в ядре так, что могут читать принятые ICMP сообщения. Если подобных пользовательских процессов нет, сообщение молча удаляется. (Эти пользовательские процессы также получают копии всех других ICMP сообщений, даже если те обрабатываются ядром, однако только после того, как ядро обработало сообщение.) Некоторые сообщения полностью игнорируются. И в заключение, если в последней колонке находится строка в кавычках, то эта строка является сообщением об ошибке Unix, соответствующее создавшемуся условию. Некоторые из ошибок мы рассмотрим в следующих главах.

тип | код | Описание | Кем обрабатывается
0 | 0 | эхо отклик | пользовательский процесс
3 |  | назначение недоступно - destination unreachable: |
 | 0 | сеть недоступна - network unreachable | "No route to host"
 | 1 | хост недоступен - host unreachable | "No route to host"
 | 2 | протокол недоступен - protocol unreachable | "Connection refused"
 | 3 | порт недоступен - port unreachable | "Connection refused"
 | 4 | необходима фрагментация, но установлен бит DF - fragmentation needed but DF bit set | "Message too long"
 | 5 | сбой маршрутизация от источника - source route failed | "No route to host"
 | 6 | сеть назначения неизвестна - destination network unknown | "No route to host"
 | 7 | хост назначения неизвестен - destination host unknown | "No route to host"
 | 8 | хост назначения изолирован - source host isolated (obsolete) | "No route to host"
 | 9 | сеть назначения административно закрыта - destination network administratively prohibited | "No route to host"
 | 10 | хост назначения административно закрыт - destination host administratively prohibited | "No route to host"
 | 11 | сеть недоступна для TOS - network unreachable for TOS | "No route to host"
 | 12 | хост недоступен для TOS - host unreachable for TOS | "No route to host"
 | 13 | связь административно закрыта - communication administratively prohibited | (игнорируется)
 | 14 | нарушено старшинство для хоста - host precedence violation | (игнорируется)
 | 15 | старшинство разъединено - precedence cutoff in effect | (игнорируется)
4 | 0 | подавление источника - source quench | ядром TCP, игнорируется UDP
5 |  | перенаправление - redirect: |
 | 0 | перенаправление в сеть - redirect for network | ядро обновляет таблицу маршрутизации
 | 1 | перенаправление в хост - redirect for host | ядро обновляет таблицу маршрутизации
 | 2 | перенаправление для типа сервиса и сети - redirect for type-of-service and network | ядро обновляет таблицу маршрутизации
 | 3 | перенаправление для типа сервиса и хоста - redirect for type-of-service and host | ядро обновляет таблицу маршрутизации
8 | 0 | эхо запрос - echo request | ядро генерирует отклик
9 | 0 | объявление маршрутизатора - router advertisement | пользовательский процесс
10 | 0 | запрос к маршрутизатору - router solicitation | пользовательский процесс
11 |  | время истекло - time exceeded: |
 | 0 | время жизни стало равным 0 в процессе передачи - time-to-live equals 0 during transit | пользовательский процесс
 | 1 | время жизни стало равным 0 в процессе повторной сборки - time-to-live equals 0 during reassembly | пользовательский процесс
12 |  | проблемы с параметрами - parameter problem: |
 | 0 | неверный IP заголовок - IP header bad | "Protocol not available"
 | 1 | отсутствует необходимая опция - required option missing | "Protocol not available"
13 | 0 | запрос временной марки - timestamp request | ядро генерирует отклик
14 | 0 | отклик с временной маркой - timestamp reply | пользовательский процесс
15 | 0 | информационный запрос - information request | (игнорируется)
16 | 0 | информационный отклик - information reply | пользовательский процесс
17 | 0 | запрос маски адреса - address mask request | ядро генерирует отклик
18 | 0 | отклик с маской адреса - address mask reply | пользовательский процесс

Рисунок 6.12 Обработка ICMP сообщений в 4.4BSD.

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

В этой главе рассмотрен протокол управления сообщениями Internet (Internet Control Message Protocol), который является неотъемлемой частью каждой реализации. На рисунке 6.3 приведены все типы ICMP сообщений, большинство из которых мы обсудили или обсудим в тексте.

Мы рассмотрели ICMP запрос маски адреса и соответствующий отклик, а также запрос и отклик временной марки. Эти типы ICMP сообщений мы рассмотрели более или менее подробно. Они являются типичными сообщениями в форме запрос-отклик. Оба имеют идентификатор и номер последовательности в ICMP сообщении. Посылающее приложение сохраняет уникальное значение в поле идентификатора, чтобы провести различие между откликами для него самого (направляемые ему) и откликами для других процессов. Поле номера последовательности позволяет клиенту сопоставить запрос с полученным откликом.

Также мы рассмотрели ICMP ошибку недоступности порта, которая является наиболее общей ошибкой ICMP. Это позволило нам более подробно рассмотреть информацию, которая возвращается в ICMP ошибке: IP заголовок и следующие 8 байт из IP датаграммы, которая вызвала генерацию ошибки. Эта информация необходима ICMP модулю, принимающему ошибку, для того чтобы узнать больше о том, чем была вызвана ошибка. И TCP, и UDP сохраняют номера портов источника и назначения в первых 8 байтах своих заголовков именно по этой причине.

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

Упражнения

  1. В конце раздела "Типы сообщений ICMP" мы привели 5 специальных условий, при которых сообщение об ошибке ICMP не посылается. Что произойдет, если эти 5 условий не соблюдены, а мы послали широковещательную UDP датаграмму на несоответствующий порт в локальном кабеле?
  2. Прочитайте Host Requirements RFC [Braden 1989a], чтобы посмотреть, попадает ли генерация ICMP ошибки о недоступности порта под разделы "должен", "следует" или "может". В каком разделе и на какой странице это можно найти?
  3. Прочитайте RFC 1349 [Almquist 1992], чтобы посмотреть, как поле типа сервиса IP (см. рисунок 3.2) должно быть установлено для ICMP.
  4. Если Ваша система имеет команду netstat, используйте ее, чтобы посмотреть, какой тип ICMP сообщений принимается и посылается.


Программа Ping

Глава 7 Программа Ping

Введение

Программа Ping предназначена для проверки доступности удаленного хоста. Программа посылает ICMP эхо запрос на хост и ожидает возврата ICMP эхо отклика. (На рисунке 6.3 приведен список типов ICMP сообщений.)

Обычно, если Вы не можете послать Ping на хост, то не сможете получить доступ к этому хосту, используя Telnet или FTP. С другой стороны, если Вы не можете зайти на хост с помощью Telnet, Ping, как правило, начальная точка, с которой начинается идентификация проблемы. Помимо этого, с помощью Ping можно оценить время возврата пакета от хоста, что дает представление о том, "насколько далеко" находится хост.

В этой главе мы воспользуемся программой Ping в качестве диагностического средства, а также для дальнейшего рассмотрения ICMP. Кроме упомянутого выше, Ping имеет опции записи маршрута и временной марки. Раздел 11 [Stevens 1990] содержит исходные тексты программы Ping.

Раньше можно было считать верным утверждение, что если мы не можем послать Ping на хост, то не сможем работать с хостом и с использованием Telnet или FTP. В настоящее время это утверждение не является верным. Связано это с тем, что в сети Internet появились повышенные требования к секретности. Маршрутизаторы поддерживают списки доступа, появились шлюзы, использующие технологию firewall. В настоящее время доступность хоста основывается не только на доступности IP уровня, а также от того, какой используется протокол и какой при этом работает порт. Ping может показывать хост как недоступный, однако мы можем получить доступ через Telnet на порт 25 (почтовый сервер).

Программа Ping

Мы будем называть программу ping, которая посылает эхо запросы - клиент, а хост, на который посылаются эхо запросы - сервер. Большинство реализаций TCP/IP поддерживают Ping сервер непосредственно в ядре - сервер не является пользовательским процессом. (Два сервиса, работающие с ICMP запросами, которые мы описали в главе 6, маска адреса и запросы временной марки, также обрабатываются непосредственно в ядре.) На рисунке 7.1 показаны ICMP эхо запрос и эхо отклик.

Рисунок 7.1 Формат ICMP сообщения для эхо запроса и эхо отклика.

Так же, как в случае других ICMP запросов, в отклике сервера должены содержаться поля идентификатора (identifier) и номера последовательности (sequence number). Кроме того, любые дополнительные данные, посланные клиентам, должны быть отражены эхом.

Реализации ping, присутствующие в Unix, устанавливают в поле идентификатора ICMP сообщения идентификатор процесса, отправляющего запрос. Это позволяет программе ping идентифицировать вернувшийся ответ, если на одном и том же хосте в одно и то же время запущено несколько программ ping.

Номер последовательности начинается с 0 и увеличивается на единицу каждый раз когда посылается следующий эхо запрос. ping печатает номер последовательности каждого возвращенного пакета, позволяя нам увидеть, потерялся ли пакет, поменялась ли последовательность движения пакетов и был ли пакет продублирован. Так как IP является ненадежным сервисом доставки датаграмм, любое из трех вышеперечисленных условий может появиться при работе программы ping.

Исторически сложилось так, что программа ping посылает эхо запрос один раз в секунду, печатая каждый эхо отклик в момент его возвращения. Однако новые разработки требуют указания опции -s, чтобы программа работала подобным образом. По умолчанию новые реализации посылают только один эхо запрос и выдают сообщение "host is alive" (хост доступен), если эхо отклик получен, или "no answer" (не отвечает), если отклик не получен в течение 20 секунд.

Работа программы в локальных сетях

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

bsdi % ping svr4
PING svr4 (140.252.13.34): 56 data bytes
64 bytes from 140.252.13.34: icmp_seq=0 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=1 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=2 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=3 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=4 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=5 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=6 ttl=255 time=0 ms
64 bytes from 140.252.13.34: icmp_seq=7 ttl=255 time=0 ms
^?               чтобы остановить ping, вводим символ прерывания
--- svr4 ping statistics ---
8 packets transmitted, 8 packets received, 0% packet loss
round-trip min/avg/max = 0/0/0 ms

Когда принимается ICMP эхо отклик, печатается номер последовательности, затем параметр время жизни (TTL) и рассчитанное время возврата. (TTL это поле времени жизни в IP заголовке. В настоящее время программа ping в BSD печатает полученное TTL каждый раз, когда принимается эхо отклик - некоторые реализации этого не делают. Мы рассмотрим использование TTL в главе 8 с программой traceroute.)

Как видно из примера, приведенного выше, эхо отклики возвращаются в том же порядке, в котором были отправлены (0, 1, 2 и так далее).

ping может рассчитать время возврата, так как он сохраняет время, когда был отправлен эхо запрос, в разделе данных ICMP сообщения. Когда отклик возвращается, эти данные извлекаются и сравниваются с текущим временем. Обратите внимание на то, что посылающая система, bsdi, во всех случаях рассчитала время возврата равное 0 мс. Это объясняется тем, что программе доступен таймер с низким разрешением. Система BSD/386 может использовать только таймер с дискретом 10 мс. (Мы обсудим это более подробно в приложении В.) Позже, с использованием вывода команды tcpdump, мы увидим, что в системах с часами с более высоким разрешением (Sun) разница во времени между ICMP эхо запросом и эхо откликом составляет примерно 4 мс.

Первая строка вывода содержит IP адрес хоста назначения, даже если было указано имя (svr4). Это означает, что имя было преобразовано в IP адрес. Мы рассмотрим процедуру преобразования и DNS в главе 14. После запуска программы ping проходит несколько секунд, перед тем как появляется первая строка вывода с напечатанным IP адресом, это время необходимо DNS, чтобы определить IP адрес, соответствующий имени хоста.

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

1 0.0                   bsdi > svr4: icmp: echo request
2 0.003733 (0.0037)    svr4 > bsdi: icmp: echo reply

3 0.998045 (0.9943)    bsdi > svr4: icmp: echo request
4 1.001747 (0.0037)    svr4 > bsdi: icmp: echo reply

5 1.997818 (0.9961)    bsdi > svr4: icmp: echo request
6 2.001542 (0.0037)    svr4 > bsdi: icmp: echo reply

7 2.997610 (0.9961)    bsdi > svr4: icmp: echo request
8 3.001311 (0.0037)    svr4 > bsdi: icmp: echo reply

9 3.997390 (0.9961)    bsdi > svr4: icmp: echo request
10 4.001115 (0.0037)    svr4 > bsdi: icmp: echo reply

11 4.997201 (0.9961)    bsdi > svr4: icmp: echo request
12 5.000904 (0.0037)    svr4 > bsdi: icmp: echo reply

13 5.996977 (0.9961)    bsdi > svr4: icmp: echo request
14 6.000708 (0.0037)    svr4 > bsdi: icmp: echo reply

15 6.996764 (0.9961)    bsdi > svr4: icmp: echo request
16 7.000479 (0.0037)    svr4 > bsdi: icmp: echo reply

Рисунок 7.2 Вывод ping при работе в локальной сети.

Время между отправкой эхо запроса и приемом эхо отклика составляет 3,7 мс. Также мы видим, что эхо запросы посылаются с интервалом примерно в 1 секунду.

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

sun % arp -a              убедимся, что ARP кэш пуст

sun % ping svr4
PING svr4: 56 data bytes
64 bytes from svr4 (140.252.13.34): icmp_seq=0. time=7. ms
64 bytes from svr4 (140.252.13.34): icmp_seq=1. time=4. ms
64 bytes from svr4 (140.252.13.34): icmp_seq=2. time=4. ms
64 bytes from svr4 (140.252.13.34): icmp_seq=3. time=4. ms
^?                       вводим символ прерывания
---- svr4 PING Statistics ----
4 packet transmitted, 4 packets received, 0% packets loss
round-trip (ms) min/avg/max = 4/4/7

Дополнительные 3 миллисекунды в первом RTT скорее всего потрачены на отправку ARP запроса и получение отклика.

Этот пример был запущен на хосте sun, который имеет таймер с разрешением в одну микросекунду, но не смотря на это, программа ping печатает время возврата только с разрешением в одну миллисекунду. В предыдущем примере, запущенном под BSD/386 Version 0.9.4, время возврата равно 0 миллисекунд, так как таймер имеет разрешение в 10 миллисекунд. Следующий вывод получен с использованием BSD/386 Version 1.0, где есть таймер с разрешением в одну микросекунду. Существует версия программы ping, которая имеет более высокое временное разрешение.

bsdi % ping svr4
PING svr4 (140.252.13.34): 56 data bytes
64 bytes from 140.252.13.34: icmp_seq=0 ttl=255 time=9.304 ms
64 bytes from 140.252.13.34: icmp_seq=1 ttl=255 time=6.089 ms
64 bytes from 140.252.13.34: icmp_seq=2 ttl=255 time=6.079 ms
64 bytes from 140.252.13.34: icmp_seq=3 ttl=255 time=6.096 ms
^?                        вводим символ прерывания
--- svr4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 6.079/6.880/9.304 ms

Работа программы в глобальных сетях

При работе в глобальных сетях результат может значительно отличаться. Следующий пример был получен в рабочий день после полудня, время, когда Internet обычно довольно загружен:

gemini % ping vangogh.cs.berkeley.edu
PING vangogh.cs.berkeley.edu: 56 data bytes
64 bytes from (128.32.130.2): icmp_seq=0. time=660. ms
64 bytes from (128.32.130.2): icmp_seq=5. time=1780. ms
64 bytes from (128.32.130.2): icmp_seq=7. time=380. ms
64 bytes from (128.32.130.2): icmp_seq=8. time=420. ms
64 bytes from (128.32.130.2): icmp_seq=9. time=390. ms
64 bytes from (128.32.130.2): icmp_seq=14. time=110. ms
64 bytes from (128.32.130.2): icmp_seq=15. time=170. ms
64 bytes from (128.32.130.2): icmp_seq=16. time=100. ms
^?                        вводим символ прерывания
---- vangogh.CS.Berkeley.EDU PING Statistics ----
17 packets transmitted, 8 packets received, 52% packet loss
round-trip (ms) min/avg/max = 100/501/1780

Эхо запросы и эхо отклики с номерами последовательности 1, 2, 3, 4, 6, 10, 11, 12 и 13 были потеряны. Также обратите внимание на значительную разницу между величинами времен возврата. (Количество потерянных пакетов, а именно 52%, является ненормальным. Это неприемлимо для Internet даже в рабочие дни после полудня.)

При работе в глобальных сетях можно встретиться с дублированием пакетов (один и тот же номер последовательности появляется дважды или несколько раз), также может возникнуть перемешивание номеров последовательности (номер последовательности N+1 появляется перед номером последовательности N).

Выделенные SLIP каналы

Давайте рассмотрим время возврата при работе по SLIP каналам, так как они обычно работают в асинхронном режиме с низкими скоростями, например 9600 бит/сек или меньше. Обратимся к расчету пропускной способности последовательной линии, приведенному в разделе "Вычисление загруженности последовательной линии" главы 2. Предположим, скорость SLIP канала между хостами bsdi и slip составляет 1200 бит/сек.

Оценить время возврата можно следующим образом. Во-первых, обратимся к примеру вывода Ping, показанному ранее. По умолчанию, ICMP сообщение содержит 56 байт данных. А также, 20 байт IP заголовка и 8 байт ICMP заголовка, что в сумме дает размер IP датаграммы - 84 байта. (Мы можем проверить это, запустив tcpdump -e, с помощью этой опции можно посмотреть размеры Ethernet фреймов.) Из раздела "SLIP: IP по последовательной линии" главы 2 мы знаем, что в начало и в конец датаграммы добавляется по меньшей мере два дополнительных байта, а именно - байт END. Не исключено, что при создании фреймов SLIP будут добавлены дополнительные байты, однако это зависит от величины каждого байта в датаграмме. Предположим, что скорость составляет 1200 бит/сек, в байте 8 бит, один старт-бит и один стоп-бит, при этом скорость будет 120 байт/сек или 8,33 мс на один байт. Время возврата составит (86 x 8,33 x 2) или 1433 мс. (Здесь умножается на 2 потому, что мы рассчитываем время возврата - то есть время, за которое пакет ушел и вернулся.)

Следующий вывод должен подтвердить правильность наших вычислений:

svr4 % ping -s slip
PING slip: 56 data bytes
64 bytes from (192.42.62.1): icmp_seq=0. time=1480. ms
64 bytes from (192.42.62.1): icmp_seq=1. time=1480. ms
64 bytes from (192.42.62.1): icmp_seq=2. time=1480. ms
64 bytes from (192.42.62.1): icmp_seq=3. time=1480. ms
^?
---- slip PING Statistics ----
5 packets transmitted, 4 packets received, 20% packet loss
round-trip (ms) min/avg/max = 1480/1480/1480

(Опция -s необходима для SVR4, чтобы посылать один запрос каждую секунду.) Время возврата составляет почти 1,5 секунды, однако программа все еще посылает ICMP эхо запросы с интервалом в 1 секунду. Это означает, что будет выдано два эхо запроса (в момент времени 0 и в момент времени 1), перед тем как вернется первый отклик (момент времени 1,480). Именно поэтому программа считает, что один пакет потерян. В действительности он не был потерян, скорее всего он еще просто не вернулся.

Мы снова обратимся к медленному SLIP каналу в главе 8, когда будем рассматривать работу программы traceroute.

SLIP каналы с дозвоном (Dialup)

При использовании SLIP каналов с дозвоном существуют некоторые отличия, так как на каждом конце канала присутствуют модемы. Модемы, которые используются между системами netb и sun, предоставляют то, что называется модуляцией V.32 (9600 бит/сек), контроль ошибок V.42 (также иногда называемый LAP-M) и сжатие данных V.42bis. Это означает, что наши простые вычисления, которые были достаточно точны для выделенного канала, где известны все параметры, в таких условиях практически неверны.

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

В следующем примере мы послали ping на хост gemini с хоста sun.

sun % ping gemini
PING gemini: 56 data bytes
64 bytes from gemini (140.252.1.11): icmp_seq=0. time=373. ms
64 bytes from gemini (140.252.1.11): icmp_seq=1. time=360. ms
64 bytes from gemini (140.252.1.11): icmp_seq=2. time=340. ms
64 bytes from gemini (140.252.1.11): icmp_seq=3. time=320. ms
64 bytes from gemini (140.252.1.11): icmp_seq=4. time=330. ms
64 bytes from gemini (140.252.1.11): icmp_seq=5. time=310. ms
64 bytes from gemini (140.252.1.11): icmp_seq=6. time=290. ms
64 bytes from gemini (140.252.1.11): icmp_seq=7. time=300. ms
64 bytes from gemini (140.252.1.11): icmp_seq=8. time=280. ms
64 bytes from gemini (140.252.1.11): icmp_seq=9. time=290. ms
64 bytes from gemini (140.252.1.11): icmp_seq=10. time=300. ms
64 bytes from gemini (140.252.1.11): icmp_seq=11. time=280. ms
----gemini PING Statistics----
12 packets transmitted, 12 packets received, 0% packet loss
round-trip (ms) min/avg/max = 280/314/373

Обратите внимание на то, что в первой строке RTT не кратен 10 миллисекундам, однако в остальных строках значение RTT кратно 10 миллисекундам. Если запустить этот пример несколько раз, то можно заметить, что подобное поведение сохранится. (Это вызвано точностью часов хоста sun - они предоставляют разрешение в одну миллисекунду. Это было проверено тестами, которые приведены в приложении В.)

Также обратите внимание на то, что первый RTT больше чем следующие, а остальные уменьшаются в процессе работы команды и их диапазон находится между 280 и 300 мс. Если не останавливать программу примерно минуту или две, RTT останутся в этом диапазоне, никогда не уменьшаясь меньше чем 260 мс. Если рассчитать ожидаемый RTT для скорости 9600 бит/сек (упражнение 2 главы 7), величина составит 180 миллисекунд, таким образом мы ошиблись в расчете примерно в 1,5 раза от реального значения.

Если программа ping будет работать в течение 60 секунд, то среднее RTT при использовании V.42 и V.42bis составит 277 миллисекунд. (Это лучше, чем значение, полученное в предыдущем примере, так как программа работала долше, при этом значение RTT застабилизировались в определенном диапазоне.) Если выключить сжатие данных V.42bis, то среднее значение составит 330 миллисекунд. Если выключить контроль ошибок V.42 (который также выключается при выключении сжатия данных V.42bis), среднее значение составит 300 миллисекунд. Эти параметры модемов влияют на RTT, однако все же лучше использовать контроль ошибкок и сжатие данных.

Опция записи IP маршрута

Программа ping предоставляет возможность просмотреть IP опцию записи маршрута (RR). В большинстве версий программы ping присутствует опция -R, которая включает характеристику записи маршрута. При использовании этой опции ping устанавливает опцию IP записи маршрута (RR) в исходящих датаграммах (которые содержат ICMP эхо запрос). При этом каждый маршрутизатор, который обрабатывает датаграмму, добавляет свой IP адрес в список, находящийся в дополнительном поле. Когда датаграмма достигает конечного пункта назначения, список IP адресов копируется в исходящий ICMP эхо отклик, а все маршрутизаторы на обратном пути также добавляют свои IP адреса в список. Когда ping принимает эхо отклик, печатает список IP адресов.

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

Самая большая проблема, однако, заключается в ограниченном размере IP заголовка, в который должен поместиться список IP адресов. Из рисунка 3.1 видно, что поле длины заголовка (header length) в IP заголовке составляет 4 бита, что ограничивает размер IP заголовка в пределах пятнадцати 32-битных слов (60 байт). Так как фиксированный размер IP заголовка составляет 20 байт, а RR опция использует 3 байта для своей установки (что мы опишем ниже), то остается 37 байт (60-20-3) на список адресов, а это, в свою очередь, позволяет поместить туда до 9 IP адресов. На заре развития ARPANET 9 IP адресов - было очень много, однако, в настоящее время, подобный размер существенно ограничивает работу команды ping с опцией -R. (В главе 8 мы рассмотрим программу Traceroute, которая используется для отслеживания маршрута, по которому двигается датаграмма.) Несмотря на все ограничения, опция записи маршрута работает и предоставляет возможность пронаблюдать, как обрабатываются опции IP. На рисунке 7.3 показан общий формат опции записи маршрута в IP датаграмме.

Рисунок 7.3 Общий формат опции маршрута в IP заголовке.

Код (code) - однобайтовое поле, содержащее тип IP опции. Для опции RR установлено значение 7. Длина (len) - это полный размер в байтах опции RR, в данном случае 39. (Несмотря на то, что существует возможность указать опцию RR с размером меньше максимального, ping всегда предоставляет поле опции размером 39 байт, что позволяет записать до 9 IP адресов. Несмотря на то, что существует ограничение в размере опций в IP заголовке, оно, тем не менее, позволяет указать размер меньше максимального.)

Указатель (ptr) - это индекс в 39-байтной опции, который указывает на то, где хранится следующий IP адрес. Его минимальное значение 4, что указывает на первый IP адрес. Когда следующий IP адрес записывается в список, значение ptr меняется следующим образом: 8, 12, 16 и так до 36. После того как записан девятый адрес, ptr устанавливается в значение 40, указывая на то, что список полон. А теперь давайте зададим себе такой вопрос.

Когда маршрутизатор (который по определению имеет несколько интерфейсов) записывает свой IP адрес в список, какой IP адрес он записывает? Это должен быть адрес либо входящего интерфейса, либо исходящего. RFC 791 [Postel 1981a] указывает, что маршрутизатор записывает IP адрес исходящего интерфейса. Однако, мы увидим, что когда исходный хост (хост, запустивший ping) получает ICMP эхо отклик с включенной опцией RR, он вносит в список IP адрес своего входящего интерфейса.

Обычный пример

Давайте попробуем запустить программу ping с опцией RR. Мы запустили ping с хоста svr4 на хост slip. Промежуточный роутер (bsdi) обрабатывает датаграмму, следующий вывод будет получен от svr4:

svr4 % ping -R slip
PING slip (140.252.13.65): 56 data bytes
64 bytes from 140.252.13.65: icmp_seq=0 ttl=254 time=280 ms
RR: bsdi (140.252.13.66)
slip (140.252.13.65)
bsdi (140.252.13.35)
svr4 (140.252.13.34)
64 bytes from 140.252.13.65: icmp_seq=1 ttl=254 time=280 ms (same route)
64 bytes from 140.252.13.65: icmp_seq=2 ttl=254 time=280 ms (same route)
^?
--- slip ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 270/276/280 ms

На рисунке 7.4 показаны 4 пересылки, через которые проходит пакет (по две в каждом направлении), а также IP адреса добавляемые к списку RR при каждой пересылке.

Рисунок 7.4 Программа ping с опцией записи маршрутизации.

Маршрутизатор bsdi добавляет в список разные IP адреса в зависимости от направления движения датаграммы. Он всегда добавляет IP адрес исходящего интерфейса. Однако, когда ICMP эхо отклик достигает системы, которая инициировала запрос (svr4), она добавляет в список IP адрес входящего интерфейса.

Мы можем наблюдать за обменом пакетами с хоста sun, на котором запущена программа tcpdump с опцией -v (просмотр IP опций). На рисунке 7.5 показан вывод.

1  0.0                   svr4>slip: icmp: echo request (ttl 32, id 35835,
                        optlen=40 RR{39}=RR{#0.0.0.0/0.0.0.0/0.0.0.0/
                        0.0.0.0/0.0.0.0/0.0.0.0/0.0.0.0/0.0.0.0/0.0.0.0} EOL)

2  0.267746 (0.2677)    slip>svr4: icmp: echo reply (ttl 254, id 1976,
                        optlen=40 RR{39}=RR{140.252.13.66/140.252.13.65/
                        140.252.13.35/#0.0.0.0/0.0.0.0/0.0.0.0/0.0.0.0/ 0.0.0.0/0.0.0.0} EOL)

Рисунок 7.5 Вывод программы tcpdump c записью опций маршрутизации.

Вывод optlen=40 указывает на то, что пространство опций в IP заголовке, используемое в данном случае, равно 40 байтам. (Обратите внимание, длина IP заголовка должна быть кратна 4 байтам.) RR {39} означает, что включена опция записи маршрута, а длина ее поля составляет 39. Затем приводится список из 9 IP адресов, знак (#) показывает на который из IP адресов указывает поле ptr в заголовке опции RR. Так как мы наблюдали за пакетами с хоста sun (см. рисунок 7.4), то видели только ICMP эхо запросы с пустым списком и ICMP эхо отклики, в списке которых содержится 3 адреса. Мы удалили оставшиеся строки в выводе tcpdump, так как они практически идентичны тем, которые показаны на рисунке 7.5.

Комбинация EOL в конце записи маршрута указывает на IP опцию "конец списка" (end of list). Опция EOL имеет значение 0. В поле опций IP заголовка, состоящего из 40 байт присутствует 39 байт данных RR. Так как пространство опций устанавливается в 0, перед тем как датаграмма отсылается, последний байт 0 следующий за 39-ю байтами данных RR интерпретируется как EOL. Если в поле опций IP заголовка присутствует несколько опций и появляется необходимость использовать байты заполнения перед началом следующей опции, используется специальный символ "нет операции" (NOP - no operation), значение которого равно единице.

На рисунке 7.5 SVR4 устанавливает в поле TTL в эхо запросе значение 32, а BSD/386 устанавливает значение 255. (Это значение печатается как 254, потому что маршрутизатор bsdi уже успел уменьшить это значение на единицу.) Все новые системы устанавливают TTL ICMP сообщений по максимуму (255).

Необходимо отметить, что две системы, BSD/386 и SVR4, из трех TCP/IP реализаций, описываемых в качестве примера в этой книге, поддерживают опцию записи маршрута. Таким образом, они корректно обновляют RR список при перенаправлении датаграммы. Также они корректно отражают RR список из входящих ICMP эхо запросов в исходящий ICMP эхо отклик. SunOS 4.1.3, однако, обновляет RR список, когда перенаправляет датаграмму, но не отображает RR список. Solaris 2.x исправляет эту проблему.

Ненормальный вывод

Следующий пример был рассмотрен автором и является исходной точкой для дальнейшего описания ICMP сообщений о перенаправлении в главе 9. Мы посылаем ping на хост aix, находящийся в подсети 140.252.1, с хоста slip (доступ осуществляется через SLIP соединение с дозвоном на компьютере sun) с опцией записи маршрута. При этом получаем следующий вывод:

slip % ping -R aix
PING aix (140.252.1.92): 56 data bytes
64 bytes from 140.252.1.92: icmp_seq=0 ttl=251 time=650 ms
RR:       bsdi (140.252.13.35)
          sun (140.252.1.29)
          netb (140.252.1.183)
          aix (140.252.1.92)
          gateway (140.252.1.4)          почему используется этот маршрутизатор?
          netb (140.252.1.183)
          sun (140.252.13.33)
          bsdi (140.252.13.66)
          slip (140.252.13.65)
64 bytes from aix: icmp_seq=1 ttl=251 time=610 ms (same route)
64 bytes from aix: icmp_seq=2 ttl=251 time=600 ms (same route)
^?
--- aix ping statistics ---
4 packets transmitted, 3 packets recieved, 25% packet loss
round-trip min/avg/max = 600/620/650 ms

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

Странность этого вывода заключается в том, что исходящая датаграмма (ICMP эхо запрос) направляется непосредственно от netb к aix, а возвращается (ICMP эхо отклик) от aix через маршрутизатор gateway, перед тем как попасть в netb. То что мы видим здесь, является характеристикой IP маршрутизации, которую мы опишем ниже. На рисунке 7.6 показан путь датаграммы.

Рисунок 7.6 Работа программы ping с записью маршрута, показывающая характеристику IP маршрутизации.

Проблема заключается в том, что aix не знает как послать IP датаграмму, направляющуюся в подсеть 140.252.13 к netb. Однако, aix имеет в своей таблице маршрутизации пункт по умолчанию, который сообщает о необходимости посылать все датаграммы на маршрутизатор gateway, если не существует конкретного маршрута к пункту назначения. Маршрутизатор gateway знает значительно больше о существующих маршрутах, чем любой другой хост в подсети 140.252.1. (В этой сети Ethernet присутствует более чем 150 хостов, и вместо того чтобы каждому иметь запущенный демон маршрутизации, они используют пункт "по умолчанию" (default), который указывает на маршрутизатор gateway.)

Вопрос, на который пока нет ответа, заключается в том, почему gateway не послал сообщение ICMP о перенаправлении (глава 9, раздел "ICMP ошибки перенаправления") на aix, чтобы обновить его таблицу маршрутизации? По некоторым причинам (возможно потому, что датаграмма, генерирующая перенаправление, является ICMP эхо запросом) перенаправление не произошло. Однако, если мы используем Telnet и подключимся к серверу дневного времени на aix, ICMP перенаправление произойдет, и таблица маршрутизации aix будет обновлена. Если мы затем запустим ping со включенной опцией записи маршрута, маршрут покажет, что датаграммы идут от netb к aix и назад к netb без дополнительной пересылки через маршрутизатор gateway. Мы рассмотрим сообщения ICMP о перенаправлении более подробно в разделе "ICMP ошибки перенаправления" главы 9.

IP опция временной марки

Опция IP временной марки во многом напоминает опцию записи маршрута. На рисунке 7.7 показан формат IP опции временной марки (сравните с рисунком 7.3).

Рисунок 7.7 Общий формат опции временной марки в IP заголовке.

Для опции временной марки поле кода (code) устанавливается в 0x44. Два поля длина (len) и указатель (ptr) такие же как в опции записи маршрута: полная длина опции (обычно 36 или 40) и указатель на следующий доступный пункт (5, 9, 13 и так далее).

Размер двух следующих полей составляет 4 бита: OF - поле переполнения (overflow) и FL - поле флагов (flags). Функционирование опции временной марки определяется полем флагов (flags), как показано на рисунке 7.8.

флаги (flags) | Описание
0 | Запись только временных марок. Это как раз то, что мы показали на рисунке 7.7.
1 | Каждый маршрутизатор записывает свой IP адрес и временную марку. Места в списке опций хватает только на четыре такие пары.
3 | Отправитель устанавливает список опций, который должен состоять из 4-х пар IP адресов 0 (ноль) временных марок. Маршрутизатор записывает свою временную марку только в том случае, если следующий IP адрес из списка совпадает с IP адресом маршрутизатора.

Рисунок 7.8 Значение флагов в опции временной марки.

В случае если маршрутизатор не может добавить временную марку, из-за того что не хватает места, он увеличивает на единицу поле переполнения (overflow).

Обычное значение для временной марки это количество миллисекунд после полуночи, UTC, что напоминает запрос и отклик временной марки ICMP (глава 6, раздел "ICMP запрос и отклик временной марки"). Если маршрутизатор не поддерживает подобный стандарт, он может вставить то представление времени, которое он использует, однако затем он должен установить в единицу старшие биты временной марки, чтобы указать на нестандартный формат.

Заданные ограничения, которые мы рассматривали с опцией записи маршрута, с опцией временной марки становятся еще более жесткими. Если осуществляется запись и IP адресов и временных марок (флаги установлены в единицу), то можно сохранить только 4 подобные пары. Сохранение только временной марки практически бесполезно, потому что мы не имеем представления, какому маршрутизатору соответствует определенная временная марка (если только мы не имеет фиксированную топологию, которая никогда не изменяется). Если установить флаги в значение 3, то появится возможность выбрать, каким маршрутизаторам необходимо вставлять их временные марки. Более глобальная проблема заключается в том, что мы не можем контролировать то, насколько точно маршрутизаторы устанавливают временные марки. Поэтому довольно сложно оценить время пересылки между маршрутизаторами с использованием этой IP опции. Вскоре мы увидим, что программа traceroute (см. главу 8) предоставляет более совершенный способ оценки времени пересылки между маршрутизаторами.

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

Программа ping является основным тестирующим средством, которое позволяет определить наличие соединения между системами, использующими TCP/IP. Она использует ICMP эхо запрос и эхо отклик и не использует транспортные уровни (TCP или UDP). Ping сервер обычно является частью реализации ядра ICMP.

Мы рассмотрели стандартный вывод команды ping для локальных, глобальных сетей и каналов SLIP (с дозвоном и выделенных), и рассчитали пропускную способность последовательных линий выделенных SLIP каналов. ping также позволяет использовать IP опцию записи маршрута. Мы использовали эту IP опцию, чтобы посмотреть как используется маршрут по умолчанию. Мы вернемся к этой теме в главе 9. Также мы рассмотрели IP опцию временной марки, однако она имеет ограниченную практическую ценность.

Упражнения

  1. Нарисуйте временной график вывода программы ping для SLIP канала из раздела "Программа Ping".
  2. Рассчитайте RTT, если SLIP канал между bsdi и slip имеет скорость 9600 бит/сек. Считайте, что по умолчанию передается 56 байт данных.
  3. Текущая программа ping в BSD позволяет нам указать шаблон для раздела данных ICMP сообщения. (Первые 8 байт раздела данных не заполняются шаблоном, а в тот момент, когда пакет отправляется, они хранятся здесь.) Если мы укажем шаблон 0xc0, повторно рассчитайте ответ на предыдущее упражнение. (Подсказка: просмотрите раздел "SLIP: IP по последовательной линии" главы 2.)
  4. Влияет ли использование SLIP со сжатием (CSLIP, глава 2, раздел "SLIP с компрессией (CSLIP)") на временные интервалы, которые мы рассматривали в разделе "Программа Ping", при работе команды ping?
  5. Рассмотрите рисунок 2.4. Замечаете ли Вы разницу между pingом на loopback адрес и pingом на Ethernet адрес?


Программа Traceroute

Глава 8 Программа Traceroute

Введение

Программа Traceroute, написанная Van Jacobson, - отладочное средство, которое позволяет лучше понять устройство протоколов TCP/IP. Обычно две последовательные датаграммы отправленные от одного и того же источника к одному и тому же пункту назначения проходят по одному и тому же маршруту, однако гарантировать этого невозможно. Traceroute позволяет нам посмотреть маршрут, по которому двигаются IP датаграммы от одного хоста к другому. С помощью Traceroute можно воспользоваться IP опцией маршрутизации от источника.

В страницах помощи о программе Traceroute говорится: "Разработана Van Jacobson по предложению Steve Deering. Отлажена и настроена C. Philip Wood, Tim Seaver и Ken Adelman."

Функционирование программы Traceroute

В разделе "Опция записи IP маршрута" главы 7 мы описали IP опцию записи маршрута (RR). Возникает вопрос, зачем писать новое приложение, когда данная опция уже реализована? Существует три причины. Во-первых, исторически не все маршрутизаторы поддерживают опцию записи маршрута, из чего следует, что некоторые маршруты становятся неиспользуемыми. (Traceroute не требует каких-либо специальных характеристик на промежуточных маршрутизаторах.)

Во-вторых, запись маршрута обычно осуществляется в одном направлении. Отправитель включает опцию, а получатель должен вставить все значения из принятого IP заголовка и каким-либо образом вернуть их отправителю. В разделе "Опция записи IP маршрута" главы 7 мы видели, что большинство реализаций сервера Ping (функция ICMP эхо отклика, встроенная в ядро) отображают входящий RR список, однако при этом удваивается количество записанных IP адресов (путь туда и обратно), помимо этого существует еще несколько ограничений, которые будут рассмотрены в следующем параграфе. (Traceroute требует только того, чтобы на пункте назначения присутствовал работающий UDP модуль - никаких специальных серверных приложений не требуется.)

Третья и основная причина заключается в том, что размер, предоставляемый для опций в IP заголовке, недостаточен для того, чтобы обработать большинство маршрутов. В поле опций IP заголовка входит всего 9 IP адресов. Если во времена ARPANET этого хватало, на сегодняшний день этого слишком мало.

Traceroute использует ICMP и поле TTL в IP заголовке. Поле TTL (время жизни) это 8-битное поле, которое отправитель устанавливает в какое-либо значение. Рекомендуемое исходное значение указано в Assigned Numbers RFC и в настоящее время равно 64. Более старые системы устанавливают это значение в 15 или 32. Мы видели в некоторых примерах работы программы Ping (глава 7), что ICMP эхо отклики часто отправляются с TTL, установленным в максимальное значение - 255.

Каждый маршрутизатор, который обрабатывает датаграмму, уменьшает значение TTL на единицу или на количество секунд, в течение которых маршрутизатор обрабатывал датаграмму. Так как большинство маршрутизаторов задерживает датаграмму меньше чем секунду, поле TTL, как правило, уменьшается на единицу и довольно точно соответствует количеству пересылок.

RFC 1009 [Braden and Postel 1987] требует, чтобы маршрутизатор, задерживающий датаграмму на время большее чем 1 секунда, уменьшал TTL на количество секунд. Совсем немногие маршрутизаторы удовлетворяют этому требованию. Современные требования к маршрутизаторам, Router Requirements RFC [Almquist 1993], делают это требование необязательным, позволяя маршрутизаторам использовать поле TTL в качестве счетчика пересылок.

С помощью поля TTL предотвращается зацикливание датаграммы в петлях маршрутизации. Например, если маршрутизатор вышел из строя или соединение между двумя маршрутизаторами потеряно, может потребоваться некоторое время (от нескольких секунд до нескольких минут), для того чтобы определить, что маршрут потерян и что его необходимо обойти. В это время существует вероятность, что датаграмма будет уничтожена в петле маршрутизации. Чтобы предотвратить потерю датаграммы, поле TTL устанавливается в максимальную величину.

Когда маршрутизатор получает IP датаграмму с TTL равным либо 0, либо 1, он не должен отправлять эту датаграмму дальше. (Хост приемник должен доставить подобную датаграмму в приложение, так как датаграмма не может быть смаршрутизировна. Как правило, системы не должны получать датаграммы с TTL равным 0.) Если такую датаграмму получает маршрутизатор, он уничтожает ее и посылает хосту, который ее отправил ICMP сообщение "время истекло" (time exceeded). Принцип работы Traceroute заключается в том, что IP датаграмма, содержащая это ICMP сообщение, имеет в качестве адреса источника IP адрес маршрутизатора.

Теперь мы можем понять, как работает Traceroute. На хост назначения отправляется IP датаграмма с TTL, установленным в единицу. Первый маршрутизатор, который должен обработать датаграмму, уничтожает ее (так как TTL равно 1) и отправляет ICMP сообщение об истечении времени (time exceeded). Таким образом, определяется первый маршрутизатор в маршруте. Затем Traceroute отправляет датаграмму с TTL равным 2, что позволяет получить IP адрес второго маршрутизатора. Это продолжается до тех пор, пока датаграмма не достигнет хоста назначения. Однако, если датаграмма прибыла именно на хост назначения, он не уничтожит ее и не сгенерирует ICMP сообщение об истечении времени, так как датаграмма достигла своего конечного назначения. Как можно определить, что датаграмма достигла конечного пункта назначения?

В UDP датаграммах, которые посылает Traceroute, устанавливается несуществующий номер UDP порта (больше чем 30000), что делает невозможным обработку этой датаграммы каким-либо приложением. Поэтому когда прибывает подобная датаграмма, UDP модуль хоста назначения генерирует ICMP сообщение "порт недоступен" (port unreachable) (см. раздел "ICMP ошибка недоступности порта" главы 6). Все что необходимо в этом случае, Traceroute это определить тип принятого ICMP сообщения - либо об истечении времени, либо о недоступности порта - именно таким образом мы узнаем, доставлена ли датаграмма в пункт назначения.

Программа Traceroute должна уметь устанавливать поле TTL в исходящих датаграммах. Не все интерфейсы TCP/IP поддерживают это, и не все реализации предоставляют эту возможность, однако большинство современных систем предоставляют. А это означает, что Traceroute может быть запущена. Однако обычно требуется, чтобы эту программу запускал суперпользователь.

Работа в локальной сети

А сейчас запустим traceroute. Мы будем использовать сети, показанные на рисунке, приведенном на внутренней стороне обложки, и пройдем по маршруту от svr4 к slip через маршрутизатор bsdi. Выделенный SLIP канал между bsdi и slip имеет скорость 9000 бит/сек.

svr4 % traceroute slip
traceroute to slip (140.252.13.65), 30 hops max, 40 byte packets
1 bsdi (140.252.13.35)  20 ms  10 ms  10 ms
2 slip (140.252.13.65)  120 ms  120 ms  120 ms

Первая строка, без номера содержит имя и IP адрес пункта назначения и указывает на то, что величина TTL не может быть больше 30. Размер датаграммы установлен в 40 байт, из которых 20 байт отводится на IP заголовок, 8 байт на UDP заголовок и 12 байт на пользовательские данные. (В 12 байтах пользовательских данных содержится номер последовательности, который увеличивается на единицу при отправке каждой следующей датаграммы, копия исходящего TTL и время, когда датаграмма была отправлена.)

Следующие две строки вывода начинаются с TTL, после чего следует имя хоста или маршрутизатора и их IP адреса. Для каждого значения TTL отправляется 3 датаграммы. Для каждого возвращенного ICMP сообщения рассчитывается и печатается время возврата (round-trip). Если ответ не получен в течении пяти секунд на любую из трех датаграмм, печатается звездочка, после чего отправляется следующая датаграмма. В нашем примере первые три датаграммы имели TTL, установленный в единицу, а ICMP сообщения вернулись через 20, 10 и 10 миллисекунд. Следующие три датаграммы были отправлены с TTL равным 2, а ICMP сообщения вернулись с задержкой 120 миллисекунд. Так как TTL со значением 2 достигло конечного пункта назначения, программа прекратила свою работу.

Времена возврата (round-trip) рассчитывается программой traceroute на хосте отправителе. Они представляют из себя полные времена возврата от программы traceroute к маршрутизатору. Если необходимо расчитать время, затраченное на каждую пересылку, мы должны вычесть значение, полученное как TTL N, из значения, полученного как TTL N+1.

На рисунке 8.1 показан вывод tcpdump для данного исполнения программы traceroute. То что первый пробный пакет к bsdi имел RTT равное 20 миллисекундам, а следующие два имели RTT равное 10 миллисекундам объясняется тем, что был осуществлен ARP обмен.

Значение, которое выбирается как номер UDP порта назначения, начинается с величины 33435 и увеличивается на единицу каждый раз, когда отправляется следующая датаграмма. Номер порта может быть изменен с использованием опции командной строки. UDP датаграмма содержит 12 байт пользовательских данных, как упоминалось ранее, в том случае, если в выводе traceroute мы видим, что отправляются датаграммы размером в 40 байт.

Когда IP датаграмма имеет TTL равное единице, tcpdump печатает комментарий [ttl 1]. Подобное сообщение печатается, когда TTL равно 0 или 1, чтобы предупредить нас о том, что в датаграмме что-то не в порядке. В данном случае мы ожидаем увидеть TTL равное 1, однако некоторые другие приложения получат предупреждение о том, что датаграмма скорее всего не достигла своего конечного пункта назначения. Скорее всего мы никогда не увидим датаграммы с TTL равным 0, если только маршрутизатор, который отправил ее в кабель, не вышел из строя.

1  0.0                   arp who-has bsdi tell svr4
2  0.000586 (0.0006)    arp reply bsdi is-at 0:0:c0:6f:2d:40

3  0.003067 (0.0025)    svr4.42804>slip.33435: udp 12 [ttl 1]
4  0.004325 (0.0013)    bsdi>svr4: icmp: time exceeded in-transit

5  0.069810 (0.0655)    svr4.42804>slip.33436: udp 12 [ttl 1]
6  0.071149 (0.0013)    bsdi>svr4: icmp: time exceeded in-transit

7  0.085162 (0.0140)    svr4.42804>slip.33437: udp 12 [ttl 1]
8  0.086375 (0.0012)    bsdi>svr4: icmp: time exceeded in-transit

9  0.118608 (0.0322)    svr4.42804>slip.33438: udp 12
10  0.226464 (0.1079)    slip>svr4: icmp: slip udp port 33438 unreachable

11  0.287296 (0.0608)    svr4.42804>slip.33439: udp 12
12  0.395230 (0.1079)    slip>svr4: icmp: slip udp port 33439 unreachable

13  0.409504 (0.0143)    svr4.42804>slip.33440: udp 12
14  0.517430 (0.1079)    slip>svr4: icmp: slip udp port 33440 unreachable

Рисунок 8.1 Вывод tcpdump для примера traceroute от svr4 к slip.

ICMP cообщение "время истекло при передаче" (time exceeded in transit) это то, что мы ожидаем увидеть от маршрутизатора bsdi, в том случае если он уменьшит на единицу TTL и оно станет равным нулю. ICMP сообщение придет от маршрутизатора даже в том случае, если IP датаграмма, которая была уничтожена, направлялась на slip.

Существуют два различных ICMP сообщения об истечении времени (рисунок 6.3), в каждом из них содержится различное поле code. На рисунке 8.2 показан формат этих ICMP сообщений.

Рисунок 8.2 ICMP сообщение об истечении времени ("time exceeded").

В сообщении, которое генерируется, когда TTL достигает нуля, поле code равно нулю.

Существует возможность, что хост пошлет ICMP сообщение "время истекло в течении повторной сборки" (time exceeded during reassembly) в том случае, если время истекло в течении повторной сборки фрагментированной датаграммы. (Мы подробно рассмотрим фрагментацию и повторную сборку в разделе "Фрагментация IP" главы 11.) В этом случае поле code устанавливается в единицу.

Строки с 9-ой по 14-ую на рисунке 8.1 соответствуют трем датаграммам, которые посылаются с TTL равным 2. Они достигают конечного пункта назначения, при этом генерируется ICMP сообщение о недоступности порта.

Попробуем рассчитать время возврата, которое соответствует SLIP каналу, как это было сделано в разделе "Программа Ping" главы 7, когда при работе программы Ping была установлена скорость канала - 1200 бит/сек. Исходящая UDP датаграмма содержит 12 байт данных, 8 байт UDP заголовка, 20 байт IP заголовка и 2 байта (минимум) для создания SLIP фреймов (см. раздел "SLIP: IP по последовательной линии" главы 2), что в целом составляет 42 байта. В отличие от Ping, размер возвращающихся датаграмм изменяется. На рисунке 6.9, мы видели, что возвращаемое ICMP сообщение содержит IP заголовок датаграммы, которая вызвала ошибку и первые 8 байт данных, которые следуют за IP заголовком (содержащие UDP заголовок, в данном случае traceroute). При этом мы получаем 20+8+20+8+2 или 58 байт. При скорости передачи в 960 байт/сек, ожидаемое RTT составляет (42+58/960) или 104 миллисекунды. Это сопоставимо со значением, рассчитанным для svr4 и равным 110 миллисекундам.

Номер порта источника на рисунке 8.1 достаточно большой (42804). traceroute устанавливает номер порта источника IP датаграмм, которые она посылает в логическое ИЛИ (logical OR), со своим идентификатором процесса (32768). В этом случае, если traceroute запущена несколько раз на одном и том же хосте, каждый процесс просматривает номер порта источника в UDP заголовке, который возвращается в ICMP сообщении, и обрабатывает только те сообщения, которые были отправлены в качестве отклика на его запрос.

Необходимо отметить некоторые особенности traceroute. Во-первых, не существует гарантии, что маршрут, который используется сегодня, будет использоваться и завтра, даже если две последовательные датаграммы были отправлены к одному и тому же пункту назначения. Если маршрут изменится в процессе работы программы, Вы увидите это, потому что traceroute напечатает новые IP адреса для определенных TTL.

Во-вторых, не существует гарантии того, что путь, по которому вернется ICMP сообщение, совпадет с путем, по которому traceroute отправила UDP датаграмму. Это означает, что время возврата, которое печатает программа, может не совпадать со временем, потребовавшимся на передачу исходящей датаграммы и возвращенного сообщения. (Возможен вариант, что UDP датаграмма дойдет от источника до маршрутизатора за 1 секунду, однако ICMP сообщение проделает обратный путь за 3 секунды, при этом время возврата будет напечатано как 4 секунды.)

В-третьих, IP адрес, который возвращается в сообщение ICMP, это IP адрес интерфейса, на который маршрутизатор принял UDP датаграмму. Тогда как при использовании опции записи маршрута (см. раздел "Опция записи IP маршрута" главы 7) записывается IP адрес исходящего интерфейса. Так как каждый маршрутизатор по умолчанию имеет 2 или более интерфейсов, запуск traceroute от хоста А к хосту В может отличаться от того, который запущен с хоста В на хост А. Действительно, если мы запустим traceroute от хоста slip к svr4, вывод будет следующим:

slip % traceroute svr4
traceroute to svr4 (140.252.13.34), 30 hops max, 40 byte packets
1 bsdi (140.252.13.66)  110 ms  110 ms  110 ms
2 slip (140.252.13.34)  110 ms  120 ms  110 ms

В этом случае IP адрес, напечатанный для хоста bsdi, это адрес SLIP интерфейса (140.252.13.66), тогда как до этого адрес был 140.252.13.35, что соответствовало интерфейсу Ethernet. Так как traceroute пытается определить имя, связанное с IP адресом, имена будут различны. (В нашем примере оба интерфейса bsdi имеют одно и то же имя.)

Обратимся к рисунку 8.3. На нем показаны две локальные сети, соединенные через маршрутизаторы. Два маршрутизатора соединены по каналу точка-точка. Если мы запустим traceroute с хоста в левой локальной сети на хост в правой локальной сети, IP адреса для маршрутизатора будут if1 и if3. При использовании другого пути, будут получены адреса if4 и if2. Два интерфейса if2 и if3 имеют один и тот же идентификатор сети, тогда как два других интерфейса имеют разные идентификаторы сетей.

Рисунок 8.3 Идентификация интерфейсов программой traceroute.

И в заключение отметим, что при работе с глобальными сетями вывод traceroute значительно легче читать, если вместо IP адресов печатаются читаемые имена доменов. Однако, так как в ICMP сообщении, принимаемом при работе traceroute, содержится IP адрес, он и будет выдан, если преобразовать IP адрес в имя домена не удалось. В этом случае администратор должен позаботиться о том, чтобы принятый IP адрес мог быть корректно трансформирован в имя домена. Мы опишем, как IP адреса конвертируются в имена с использованием DNS, в разделе "Запросы указателя" главы 14.

Вывод при работе в глобальных сетях

Вывод, показанный ранее для нашей маленькой сети, достаточно реально показывает, как функционируют протоколы. Однако, будет очень интересно посмотреть, как работает traceroute в больших сетях, например, в сети Internet.

На рисунке 8.4 показано как отправляется запрос от sun к Сетевому информационному центру (NIC - Network Information Center).

sun % traceroute nic.ddn.mil
traceroute to nic.ddn.mil (192.112.36.5), 30 hops max, 40 byte packets

1 netb.tuc.noao.edu (140.252.1.183)  218 ms  227 ms  233 ms
2 gateway.tuc.noao.edu (140.252.1.4)  233 ms  229 ms  204 ms

3 butch.telcom.arizona.edu (140.252.104.2)  204 ms  228 ms  234 ms
4 Gabby.Telcom.Arizona.EDU (128.196.128.1)  234 ms  228 ms  204 ms
5 NSIgate.Telcom.Arizona.EDU (192.80.43.3)  233 ms  228 ms  234 ms

6 JPL1.NSN.NASA.GOV (128.161.88.2)  234 ms  590 ms  262 ms
7 JPL3.NSN.NASA.GOV (192.100.15.3)  238 ms  223 ms  234 ms
8 GSFC3.NSN.NASA.GOV (128.161.3.33)  293 ms  318 ms  324 ms
9 GSFC8.NSN.NASA.GOV (192.100.13.8)  294 ms  318 ms  294 ms
10 SURA2.NSN.NASA.GOV (128.161.166.2)  323 ms  319 ms  294 ms
11 nsn-FIX-pe.sura.net (192.80.214.253)  294 ms  318 ms  294 ms
12 GSI.NSN.NASA.GOV (128.161.252.2)  293 ms  318 ms  324 ms

13 NIC.DDN.MIL (192.112.36.5)  324 ms  321 ms  324 ms

Рисунок 8.4 traceroute от sun к nic.ddn.mil.

Чтобы включить этот пример в текст, мы запустили его для не-DDN узлов (не военные узлы) от nic.ddn.mil к rs.internic.net, новый "InterNIC".

Когда датаграмма выходит из сети tuc.noao.edu, она попадает в сеть telcom.arizona.edu. Затем она попадает в сеть Национального агентства по аэронавтике США (NASA Science Internet), nsn.nasa.gov. Маршрутизаторы с TTL равным 6 и 7 находятся в лаборатории Jet Propulsion (JPL). Сеть sura.net (в выводе TTL равно 11) это сеть Исследовательской ассоциации университетов (Southeastern Universities Research Association Network). GSI (TTL равно 12) это Government Systems, Inc., оператор для NIC.

Второе RTT для TTL равного 6 (590) почти в два раза больше, чем два другие RTT (234 и 262). Это показывает динамику IP маршрутизации. Подобное может произойти где-нибудь по пути от источника к маршрутизатору если какой-нибудь промежуточный маршрутизатор задержал датаграмму. Однако мы не можем сказать, была ли задержена исходящая датаграмма или возвращающееся ICMP сообщение.

RTT для первой попытки с TTL равным 3 (204) меньше, чем RTT для первой попытки с TTL равной 2 (233). Так как каждое полученное RTT является полным временем прохода от посылающего хоста к маршрутизатору, это вполне объснимо.

В примере на рисунке 8.5 показана работа программы Traceroute от хоста sun на хост нашего издателя.

sun % traceroute aw.com
traceroute to aw.com (192.207.117.2), 30 hops max, 40 byte packets
1 netb.tuc.noao.edu (140.252.1.183)  227 ms  227 ms  234 ms
2 gateway.tuc.noao.edu (140.252.1.4)  233 ms  229 ms  234 ms

3 butch.telcom.arizona.edu (140.252.104.2)  233 ms  229 ms  234 ms
4 Gabby.Telcom.Arizona.EDU (128.196.128.1)  264 ms  228 ms  234 ms
5 Westgate.Telcom.Arizona.EDU (192.80.43.2) 234 ms 228 ms 234 ms

6 uu-ua.AZ.westnet.net (192.31.39.233)  263 ms  258 ms  264 ms
7 enss142.UT.westnet.net (192.31.39.21)  263 ms  258 ms  264 ms

8 t3-2.Denver-cnss97.t3.ans.net (140.222.97.3)  293 ms  288 ms  275 ms
9 t3-3.Denver-cnss96.t3.ans.net (140.222.96.4)  283 ms  263 ms  261 ms
10 t3-1.St-Louis-cnss80.t3.ans.net (140.222.80.2)  282 ms  288 ms  294 ms
11 t3-1.Chicago-cnss24.t3.ans.net (140.222.24.2)  293 ms  288 ms  294 ms
12 t3-2.Cleveland-cnss40.t3.ans.net (140.222.40.3)  294 ms  288 ms  294 ms
13 t3-1.New-York-cnss32.t3.ans.net (140.222.32.2)  323 ms  318 ms  324 ms
14 t3-1.Washington-DC-cnss56.t3.ans.net (140.222.56.2)  323 ms  318 ms  324 ms
15 t3-0.Washington-DC-cnss58.t3.ans.net (140.222.58.1)  324 ms  318 ms  324 ms
16 t3-0.enss136.t3.ans.net (140.222.136.1)  323 ms  318 ms  324 ms

17 Washington.DC.ALTER.NET (192.41.177.248)  323 ms  377 ms  324 ms
18 Boston.MA.ALTER.NET (137.39.12.2)  324 ms  347 ms  324 ms
19 AW-gw.ALTER.NET (137.39.62.2)  353 ms  378 ms  354 ms

20 aw.com (192.207.117.2)  354 ms  349 ms  354 ms

Рисунок 8.5 traceroute от хоста sun.tuc.noao.edu к хосту aw.com.

После того как датаграмма вышла из сети telcom.arizona.edu она попадает в региональную сеть western.net (TTL 6 и 7). Затем датаграмма попадает на магистраль (backbone) NSFNET, t3.ans.net, которая используется Advanced Network & Services. (T3 это общепринятая аббревиатура для каналов 45 Мбит/сек, которые используются в качестве магистралей.) И последняя сеть это alter.net, точка подсоединения к Internet для aw.com.

Опция IP маршрутизации от источника

Обычно IP маршрутизация осуществляется динамически, т.е. каждый маршрутизатор принимает решение о том, на какой маршрутизатор следующей пересылки необходимо отправить датаграмму. Приложения не могут управлять этим процессом и обычно этим и не занимаются. Поэтому приходится использовать средства, такие как Traceroute, чтобы проследить, как в действительности происходит маршрутизация.

Идея, заложенная в маршрутизации от источника, заключается в том, что отправитель сам указывает маршрут по которому пройдет датаграмма. Существует две формы:

  • Жесткая (strict) маршрутизация от источника. Отправитель указывает точный путь, по которому должна пройти IP датаграмма. Если маршрутизатор обнаруживает, что следующая пересылка, указанная в маршрутизации от источника, не является непосредственно подключенной сетью, возвращается ICMP ошибка "маршрутизация от источника невозможна" (source route failed).
  • Свободная (loose) маршрутизация от источника. Отправитель указывает список IP адресов, через который должна пройти IP датаграмма, однако датаграмма может также пройти через другие маршрутизаторы между любыми двумя адресами, указанными в списке.

Traceroute позволяет использовать маршрутизацию от источника.

Некоторые свободно распространяемые исходные тексты программы Traceroute содержат дополнения, которые позволяют установить свободную маршрутизацию от источника. Однако стандартные версии обычно не включают эту опцию. Комментарии к дополнениям гласят: "исходные тексты программы Traceroute, написанные Van Jacobson (весна 1988 года), поддерживали эту характеристику, однако она была удалена по требованию людей, которые вывели из строя свои шлюзы". Для того чтобы показать пример, приведенный в этом разделе, автор установил эти дополнения и модифицировал их таким образом, чтобы можно было использовать оба типа маршрутизации от источника.

На рисунке 8.6 показан формат опции маршрутизации от источника.

Рисунок 8.6 Общий формат опции маршрутизации от источника в IP заголовке.

Формат практически идентичен формату опции записи маршрута, которую мы рассмотрели на рисунке 7.3. Однако, в случае маршрутизации от источника мы должны заполнить список IP адресов, перед тем как будет отправлена IP датаграмма, тогда как в опции записи маршрута мы выделяли пространство и устанавливали в 0 список IP адресов, позволяя маршрутизаторам заполнить их в процессе передачи датаграммы. В случае с маршрутизацией от источника мы выделяем область для заполнения и инициализируем определенное количество требуемых IP адресов, обычно их количество меньше чем 9. В случае опции записи маршрута мы старались выделить как можно больше места, для того чтобы использовать более чем 9 адресов.

Поле code устанавливается в 0x83 для свободной маршрутизации от источника, и в 0x89 для жесткой маршрутизации от источника. Поля len и ptr идентичны тем, что мы описали в разделе "Опция записи IP маршрута" главы 7.

Опции маршрутизации от источника обычно называются "маршрутизацией от источника с записью" (source and record route) (LSRR - свободная и SSRR - жесткая), так как список IP адресов обновляется в процессе того, как датаграмма проходит по маршруту. Происходит следующее:

  • Отправляющий хост берет из приложения маршрут от источника, удаляет первый пункт (он становится адресом назначения для датаграммы), перемещает все оставшиеся пункты влево на один пункт (где лево - показано на рисунке 8.6) и помещает исходный адрес назначения на место последнего пункта в списке. Указатель все еще указывает на первый пункт списка (значение указателя равно 4).
  • Каждый маршрутизатор, который обрабатывает датаграмму, проверяет, является ли этот адрес адресом назначения. Если нет, датаграмма обрабатывается как обычная. В этом случае должна быть использована свободная маршрутизация от источника, иначе мы не получим датаграмму.
  • Если маршрутизатор является пунктом назначения и указатель не больше чем длина, в этом случае (1) следующий адрес в списке (куда указывает ptr) становится адресом назначения датаграммы, (2) IP адрес, соответствующий исходящему интерфейсу, замещает собой только что использованный адрес источника, и (3) указатель увеличивается на 4.

Все это лучше показать на примере. На рисунке 8.7 мы предположили, что посылающее приложение на хосте S отправляет датаграмму к D, указав маршрут от источника как R1, R2 и R3.

Рисунок 8.7 Пример IP маршрутизации от источника.

На этом рисунке символ (#) означает поле указателя, которое может принимать значение 4, 8, 12 и 16. Поле длины всегда будет 15 (3 IP адреса + 3 байта). Обратите внимание на то, как меняется IP адрес назначения в IP датаграмме при каждой пересылке.

Когда приложение получает данные, которые маршрутизировались от источника, оно должно выделить значение принятого маршрута и использовать обратный маршрут для отправки отклика.

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

Примеры traceroute с использованием свободной маршрутизации от источника

Опция -g программы traceroute позволяет нам указать промежуточные маршрутизаторы, которые должны быть использованы при свободной маршрутизации от источника. Эта опция может быть указана до 8 раз. (Причина того, что указывается именно 8, а не 9 раз, заключается в том, что программный интерфейс, который будет использоваться, потребует, чтобы последний пункт являлся конечным пунктом назначения.)

Повторно обратимся к рисунку 8.4, из которого видно, что маршрутизация к NIC, nic.ddn.mil, была осуществлена через сеть NASA. На рисунке 8.8 мы заставим датаграммы пройти через NSFNET, вместо того чтобы использовать маршрутизатор enss142.UT.westnet.net (192.31.39.21) в качестве промежуточного маршрутизатора:

sun % traceroute -g 192.31.39.21 nic.ddn.mil
traceroute to nic.ddn.mil (192.112.36.5), 30 hops max, 40 byte packets
1 netb.tuc.noao.edu (140.252.1.183)  256 ms  256 ms  235 ms

2 butch.telcom.arizona.edu (140.252.104.2)  234 ms  228 ms  234 ms
3 Gabby.Telcom.Arizona.EDU (128.196.128.1)  234 ms  257 ms  233 ms

4 enss142.UT.westnet.net (192.31.39.21)  294 ms  288 ms  295 ms

5 t3-2.Denver-cnss97.t3.ans.net (140.222.97.3)  294 ms  286 ms  293 ms
6 t3-3.Denver-cnss96.t3.ans.net (140.222.96.4)  293 ms  288 ms  294 ms
7 t3-1.St-Louis-cnss80.t3.ans.net (140.222.80.2)  294 ms  318 ms  294 ms
8 * t3-1.Chicago-cnss24.t3.ans.net (140.222.24.2)  318 ms  295 ms
9 t3-2.Cleveland-cnss40.t3.ans.net (140.222.40.3)  319 ms  318 ms  324 ms
10 t3-1.New-York-cnss32.t3.ans.net (140.222.32.2)  324 ms  318 ms  324 ms
11 t3-1.Washington-DC-cnss56.t3.ans.net (140.222.56.2)  353 ms  348 ms  325 ms
12 t3-0.Washington-DC-cnss58.t3.ans.net (140.222.58.1)  348 ms  347 ms  325 ms
13 t3-0.enss145.t3.ans.net (140.222.145.1)  353 ms  348 ms  325 ms

14 nsn-FIX-pe.sura.net (192.80.214.253)  353 ms  348 ms  325 ms
15 GSI.NSN.NASA.GOV (128.161.252.2)  353 ms  348 ms  354 ms
16 NIC.DDN.MIL (192.112.36.5)  354 ms  347 ms  354 ms

Рисунок 8.8 traceroute на nic.ddn.mil со свободной маршрутизацией от источника через NSFNET.

Создается впечатление, что было сделано 16 пересылок со средним RTT около 350 миллисекунд, тогда как обычный маршрут, показанный на рисунке 8.4, состоял только из 13 пересылок, и среднее RTT равнялось примерно 322 миллисекундам. Из этого можно сделать вывод, что маршрут по умолчанию предпочтительнее. (Существуют и другие ограничения, в соответствии с которыми принимаются решения о прокладке маршрута; в том числе организационные или политические.)

Мы сказали "создается впечатление, что было сделано 16 пересылок", так как имели возможность сравнить этот вывод с нашим предыдущим примером через NFSNET (см. рисунок 8.5), который показал 3 отсутствующих маршрута в примере, который использует свободную маршрутизацию от источника. (Это, возможно, было вызвано ошибками в работе маршрутизаторов при генерации ICMP сообщения об истечении времени при получении датаграмм, маршрутизируемых от источника.) Маршрутизатор gateway.tuc.noao.edu отсутствует между netb и butch, а маршрутизаторы Westgate.Telcom.Arizona.edu и uu-ua.AZ.westnet.net отсутствуют между Gabby и enss142.UT.westnet.net. Вполне возможно, что мы не видим эти маршрутизаторы так как они не могут корректно обработать входящие датаграммы со свободной маршрутизации от источника. В действительности, при использовании NSFNET осуществляется 19 пересылок между источником и NIC. В упражнении 5 главы 8 будет продолжено рассмотрение отсутствующих маршрутизаторов.

Из этого примера видна еще одна проблема. В командной строке мы должны указать IP адрес маршрутизатора enss142.UT.westnet.net вместо его имени. Это происходит потому, что процедура определяющая соответствие между именем и адресом (возвращается имя по заданному IP адресу, раздел "Запросы указателя", главы 14), или наоборот, когда задается имя, а возвращается IP адрес, не работает. Функции определения адреса по имени и имени по адресу используют два различных файла в системе DNS (Domain Name System), и не все администраторы синхронизируют эти два файла друг с другом. Нет ничего необычного в том, что в одном направлении DNS работает, но не работает в другом.

Вместо первого RTT для TTL равного 8 мы видим в выводе звездочку (*). Это указывает на то, что был отработан тайм-аут и на первую посылку в течении пяти секунд не был получен отклик.

И последнее, на что необходимо обратить внимание, сравнивая этот рисунок с рисунком 8.4, заключается в том, что маршрутизатор nsn-FIX-pe.sura.net подключен как к NSFNET, так и к сети NASA.

Примеры traceroute при использовании жесткой маршрутизации от источника

Опция -G в нашей версии traceroute идентична опции -g, описанной ранее, однако она определяет жесткую маршрутизацию от источника вместо свободной. Посмотрим что произойдет, если указан неверный жесткий маршрут от источника. Обратимся к рисунку 8.5, на котором показана нормальная последовательность маршрутизаторов для датаграмм, двигающихся из нашей подсети к NSFNET через netb, gateway, butch и gabby. (Мы отбросили суффиксы доменов, .tuc.noao.edu и .telcom.arizona.edu, во всех строках вывода, показанных ниже, чтобы сделать их более читаемыми.) Мы указали жесткий маршрут от источника, который не использует butch, а пытается пройти непосредственно от gateway к gabby. Это не должно сработать, что показано на рисунке 8.9.

sun % traceroute -G netb -G gateway -G gabby westgate
traceroute to westgate (192.80.43.2), 30 hops max, 40 byte packets
1 netb (140.252.1.183)  272 ms  257 ms  261 ms
2 gateway (140.252.1.4)  263 ms  259 ms  234 ms
3 gateway (140.252.1.4)  263 ms !S *  235 ms !S

Рисунок 8.9 traceroute с жесткой маршрутизацией от источника, который не работает.

Здесь необходимо обратить внимание на выражение !S, следующее за RTT для TTL равного 3. Это означает, что программа traceroute получила ICMP сообщение "маршрутизация от источника не сработала" (source route failed): type равняется 3 и code равняется 5 на рисунке 6.3. Звездочка во втором RTT для TTL равного 3 указывает на то, что на эту посылку не был получен ответ. Это как раз то что мы ожидали, так как для gateway не существует возможности послать датаграмму непосредственно к gabby.

Причина того, что датаграммы с TTL 2 и 3 пришли именно от gateway, заключается в том, что TTL с номером 2 отправлялась из gateway, когда он получил входящую датаграмму с TTL равным 1. Он определяет, что время жизни (TTL) истекло перед тем, как был обнаружен жесткий маршрут от источника (кстати, неправильный), поэтому и было отправлено ICMP сообщение об истечении времени. Строка с TTL равным 3 получена gateway с входящим TTL равным 2, он просмотрел жесткий маршрут от источника, определил, что он неверен, после чего послал ICMP сообщение о том, что маршрутизация от источника не может быть осуществлена.

На рисунке 8.10 показан вывод tcpdump, соответствующий этому примеру. Этот вывод получен на SLIP канале между sun и netb. Мы указали опцию -v для tcpdump, чтобы получить информацию о маршрутизации от источника. При этом появляется часть вывода, который нам не нужен, как, например, идентификатор датаграммы. Этот вывод был удален. Сокращение SSRR означает "жесткая маршрутизация от источника с записью" (strict source and record route).

1 0.0                     sun.33593 > netb.33435: udp 12 [ttl 1]
                          (optlen=16 SSRR{#gateway gabby westgate} EOL)
2 0.270278 (0.2703)      netb > sun: icmp: time exceeded in-transit
3 0.284784 (0.0145)      sun.33593 > netb.33436: udp 12 [ttl 1]
                          (optlen=16 SSRR{#gateway gabby westgate} EOL)
4 0.540338 (0.2556)      netb > sun: icmp: time exceeded in-transit
5 0.550062 (0.0097)      sun.33593 > netb.33437: udp 12 [ttl 1]
                          (optlen=16 SSRR{#gateway gabby westgate} EOL)
6 0.810310 (0.2602)      netb > sun: icmp: time exceeded in-transit
7 0.818030 (0.0077)      sun.33593 > netb.33438: udp 12 (ttl 2,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
8 1.080337 (0.2623)      gateway > sun: icmp: time exceeded in-transit
9 1.092564 (0.0122)      sun.33593 > netb.33439: udp 12 (ttl 2,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
10 1.350322 (0.2578)      gateway > sun: icmp: time exceeded in-transit
11 1.357382 (0.0071)      sun.33593 > netb.33440: udp 12 (ttl 2,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
12 1.590586 (0.2332)      gateway > sun: icmp: time exceeded in-transit
13 1.598926 (0.0083)      sun.33593 > netb.33441: udp 12 (ttl 3,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
14 1.860341 (0.2614)      gateway > sun:
                          icmp: gateway unreachable - source route failed
15 1.875230 (0.0149)      sun.33593 > netb.33442: udp 12 (ttl 3,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
16 6.876579 (5.0013)      sun.33593 > netb.33443: udp 12 (ttl 3,
                          optlen=16 SSRR{#gateway gabby westgate} EOL)
17 7.110518 (0.2339)      gateway > sun:
                          icmp: gateway unreachable - source route failed

Рисунок 8.10 Вывод tcpdump для traceroute с неработающей жесткой маршрутизацией от источника.

Обратите внимание на то, что каждая UDP датаграмма, посланная sun, имеет в качестве назначения netb, а не хост назначения (westgate). Мы описали это с помощью примера, показанного на рисунке 8.7. Два других маршрутизатора указаны с опцией -G (gateway и gabby), а конечный пункт назначения (westgate) появляется в списке опций SSRR для первой пересылки.

Также из этого вывода можно заметить, что тайм-аут, используемый traceroute (время между строками 15 и 16), составляет 5 секунд.

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

Раньше мы уже упоминали о том, что не существует гарантии того, что маршрут от А до В тот же самый, как и маршрут от В до А. Найти различие между этими двумя маршрутами можно только зайдя терминалами на обе системы и запустив traceroute на каждой из них. Однако, используя свободную маршрутизацию от источника, мы можем определить маршрут в обоих направлениях.

Добиться этого можно, если указать свободную маршрутизацию от источника с назначением по свободному маршруту и указать посылающий хост в качестве конечного пункта назначения. Например, с хоста sun мы можем найти маршрут к и от хоста bruno.cs.colorado.edu (см. рисунок 8.11).

sun % traceroute -g bruno.cs.colorado.edu sun
traceroute to sun (140.252.13.33), 30 hops max, 40 byte packets
1 netb.tuc.noao.edu (140.252.1.183)  230 ms  227 ms  233 ms
2 gateway.tuc.noao.edu (140.252.1.4)  233 ms  229 ms  234 ms

3 butch.telcom.arizona.edu (140.252.104.2)  234 ms  229 ms  234 ms
4 Gabby.Telcom.Arizona.EDU (128.196.128.1)  233 ms  231 ms  234 ms
5 NSIgate.Telcom.Arizona.EDU (192.80.43.3)  294 ms  258 ms  234 ms

6 JPL1.NSN.NASA.GOV (128.161.88.2)  264 ms  258 ms  264 ms
7 JPL2.NSN.NASA.GOV (192.100.15.2)  264 ms  258 ms  264 ms
8 NCAR.NSN.NASA.GOV (128.161.97.2)  324 ms *  295 ms

9 cu-gw.ucar.edu (192.43.244.4)  294 ms  318 ms  294 ms

10 engr-gw.Colorado.EDU (128.138.1.3)  294 ms  288 ms  294 ms
11 bruno.cs.colorado.edu (128.138.243.151)  293 ms  317 ms  294 ms
12 engr-gw-ot.cs.colorado.edu (128.138.204.1)  323 ms  317 ms  384 ms
13 cu-gw.Colorado.EDU (128.138.1.1)  294 ms  318 ms  294 ms

14 enss.ucar.edu (192.43.244.10)  323 ms  318 ms  294 ms

15 t3-1.Denver-cnss97.t3.ans.net (140.222.97.2)  294 ms  288 ms  384 ms
16 t3-0.enss142.t3.ans.net (140.222.142.1)  293 ms  288 ms  294 ms

17 Gabby.Telcom.Arizona.EDU (192.80.43.1)  294 ms  288 ms  294 ms
18 Butch.Telcom.Arizona.EDU (128.196.128.88)  293 ms  317 ms  294 ms

19 gateway.tuc.noao.edu (140.252.104.1)  294 ms  289 ms  294 ms
20 netb.tuc.noao.edu (140.252.1.183)  324 ms  321 ms  294 ms
21 sun.tuc.noao.edu (140.252.13.33)  534 ms  529 ms  564 ms

Рисунок 8.11 Пример traceroute, показывающий несимметричные маршруты.

Исходящий маршрут (TTL 1-11) отличается от обратного маршрута (TTL 11-21), это является хорошей иллюстрацией того, что маршрутизация в Internet может быть несимметричной.

Этот вывод также иллюстрирует проблему, которую мы обнаружили при рассмотрении рисунка 8.3. Сравните вывод для TTL 2 и 19: оба относятся к маршрутизатору gateway.tuc.noao.edu, однако IP адреса различны. Так как traceroute идентифицирует входящий интерфейс, это означает, что мы прошли через маршрутизатор с разных сторон, в тот момент, когда использовали исходящий маршрут (TTL 2) и когда использовали обратный маршрут (TTL 19). Точно так же можно сравнить TTL 3 и 18, TTL 4 и 17.

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

Traceroute это незаменимое средство при работе с сетями TCP/IP. Оно функционирует довольно просто: отправляет UDP датаграммы, начинающиеся с TTL 1, увеличивает TTL на единицу, для того чтобы определить пересылку через каждый встретившийся маршрутизатор. Каждый маршрутизатор, который отбрасывает UDP датаграмму, возвращает сообщение ICMP об истечении времени (ICMP time exceeded), а пункт конечного назначения генерирует ICMP сообщение о недоступности порта (ICMP port unreachable).

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

Упражнения

  1. Что произойдет, если IP модуль уменьшит входящий TTL на единицу, а затем проверит на равенство нулю?
  2. Как traceroute рассчитывает RTT? Сравните это с расчетом RTT, который осуществляет ping.
  3. (Это и следующее упражнения основано на реальных проблемах, которые возникли при разработке traceroute и взяты из комментариев к исходным текстам traceroute.) Представьте, что между источником и пунктом назначения находится 3 маршрутизатора (R1, R2 и R3), средний маршрутизатор (R2) уменьшил на единицу TTL, однако некорректно перенаправил IP датаграмму, когда входящий TTL был равен единице. Опишите, что произойдет. Что Вы увидите, если запустите traceroute?
  4. Представьте еще раз, что между источником и конечным пунктом назначения находится 3 маршрутизатора. При этом хост, который является пунктом назначения, работает с ошибкой. Он использует входящий TTL в качестве исходящего TTL для ICMP сообщения. Опишите, что произойдет, и как Вы это можете увидеть.
  5. Мы могли бы запустить tcpdump на SLIP канале между sun и netb, когда запускали пример с рисунка 8.8. Если мы укажем опцию -v, то можем увидеть значение TTL для возвращающихся ICMP сообщений. При этом, мы увидим, что входящий TTL от netb равен 255, от butch равен 253, от Gabby равен 252 и от enss142.UT.westnet.net равен 249. Дает ли это какую-либо дополнительную информацию о том, где существуют отсутствующие маршрутизаторы?
  6. SunOS и SVR4 предоставляют версию программы ping с опцией -l, что позволяет использовать свободную маршрутизацию от источника. В страницах помощи говорится, что это идентично использованию опции -R (которая указывает на то, что необходима запись маршрута). Если Вы имеете доступ к обеим этим системам, попробуйте использовать эти две опции вместе. Что произойдет? Если Вы можете просмотреть датаграммы с использованием tcpdump, опишите что будет происходить.
  7. Сравните пути прохождения ping и traceroute с разных рабочих станций на один и тот же хост.
  8. Сравните время возврата для ping и traceroute.
  9. Мы указали traceroute о необходимости использовать исходный номер порта назначения UDP, равный 33435, и увеличивать его на единицу для каждого следующего отправляемого пакета. В разделе "Номера портов" главы 1 говорится, что обычно номера динамически назначаемых портов находятся в диапазоне от 1024 до 5000, при этом порт назначения Traceroute никогда не будет занят на хосте, который является пунктом назначения. Является ли это справедливым для Solaris 2.2? (Подсказка: прочитайте раздел "Solaris 2.2" приложения E.)
  10. Прочитайте RFC 1393 [Malkin 1993b], и найдите альтернативные способы определения маршрута к пункту назначения. В чем их преимущества и недостатки?


9 IP маршрутизация

Глава 9 IP маршрутизация

Введение

Маршрутизация - это одна из наиболее важных функций IP. На рисунке 9.1 показана упрощенная модель того, что реализуется на IP уровене. Датаграммы, которые должны быть смаршрутизированы, могут генерироваться как локальным хостом, так и каким-либо удаленным хостом. В последнем случае наш хост должен быть сконфигурирован как маршрутизатор, иначе датаграммы, получаемые через сетевые интерфейсы и не предназначенные нашему хосту, будут молча удалены.

На рисунке 9.1 также показан демон маршрутизации, который обычно является пользовательским процессом. Наиболее часто в Unix системах используются демоны routed и gated. Термин демон (daemon) означает, что процесс работает "в фоновом режиме" и его функционирование не оказывает влияние на систему в целом. Демоны обычно стартуют, когда система загружается, и живут все время, пока система работает. Протоколы маршрутизации, используемые на конкретном хосте, определяют, как будет происходить обмен информацией о маршрутах с удаленными маршрутизаторами. (Заинтересованные читатели могут обратиться к [Perlman 1992] для получения более подробной информации.) Мы вкратце рассмотрим динамическую маршрутизацию и протокол обмена информацией о маршрутизации (RIP - Routing Information Protocol) в главе 10. В этой главе мы рассмотрим, как IP уровень принимает решения о маршрутизации.

IP уровень обращается к таблице маршрутизации, которая показана на рисунке 9.1 (на загруженных хостах подобное обращение может происходить несколько сот раз в секунду), однако демон маршрутизации обновляет таблицу значительно реже (примерно один раз каждые 30 секунд). Таблица маршрутизации также может быть обновлена, когда принимается ICMP сообщение о перенаправлении (ICMP redirect), мы увидим это в разделе "ICMP ошибки перенаправления", когда будем рассматривать команду route. Эта команда обычно исполняется при загрузке системы и устанавливает некоторые исходные маршруты. Также в этой главе мы будем использовать команду netstat, для просмотра таблиц маршрутизации.

Рисунок 9.1 Действия, выполняемые IP уровнем.

Принципы маршрутизации

Прежде чем начать обсуждение IP маршрутизации, необходимо понять, что именно ядро обновляет в таблице маршрутизации. Информация, содержащаяся в таблице маршрутизации, необходима IP уровню для принятия решения о маршрутизации. В разделе "IP маршрутизация" главы 3 мы показали, каким образом IP просматривает свою таблицу маршрутизации.

  1. Поиск совпадающего адреса хоста.
  2. Поиск совпадающего адреса сети.
  3. Поиск пункта по умолчанию. (Пункт по умолчанию обычно указывается в таблице маршрутизации как сеть с идентификатором сети равным нулю.)

Совпавший адрес хоста используется всегда перед совпавшим адресом сети.

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

Простая таблица маршрутизации

Давайте рассмотрим типичные таблицы маршрутизации. На хосте svr4 мы запустили команду netstat с опцией -r, чтобы просмотреть таблицу маршрутизации, и с опцией -n, которая печатает IP адреса в цифровом формате, а не в виде имен. (Мы сделали это, потому что некоторые пункты таблицы маршрутизации это сети, а не хосты. Без опции -n команда netstat просматривает файл /etc/networks, и берет оттуда имена сетей. При этом может получиться некоторая путаница, потому что в выводе команды помимо имен хостов появятся имена сетей.)

svr4 % netstat -rn
Routing tables
Destination        Gateway            Flags       Refcnt  Use     Interface
140.252.13.65      140.252.13.35     UGH         0       0        emd0
127.0.0.1          127.0.0.1          UH          1       0        lo0
default            140.252.13.33      UG         0        0       emd0
140.252.13.32      140.252.13.34     U           4        25043   emd0

В первой строке говорится, что для пункта назначения 140.252.13.65 (хост slip) шлюз (маршрутизатор), на который будут посылаться пакеты, это 140.252.13.35 (bsdi). Хост slip подсоединен к bsdi через SLIP канал, а bsdi находится в той же сети Ethernet, что и данный хост.

Для конкретного маршрута может быть показано 5 различных флагов.

U

Маршрут активен.

G

Маршрут подключен к шлюзу (маршрутизатору). Если этот флаг не установлен, считается, что пункт назначения подключен непосредственно.

H

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

D

Маршрут был создан посредством перенаправления ("ICMP ошибки перенаправления").

M

Маршрут был модифицирован посредством перенаправления ("ICMP ошибки перенаправления").

Флаг G очень важен, потому что именно этот флаг определяет различие между непрямым маршрутом (indirect route) и прямым маршрутом (direct route). (Флаг G не устанавливается для прямого маршрута.) Отличие заключается в том, что у пакета, направляющегося по прямому маршруту, IP адрес и адрес канального уровня указывают на конечный пункт назначения (рисунок 3.3). Когда пакет отправляется по непрямому маршруту, IP адрес указывает на конечный пункт назначения, а адрес канального уровня указывает на маршрутизатор следующей пересылки. Мы видели подобный пример этого на рисунке 3.4. В этой таблице маршрутизации мы видим непрямой маршрут (флаг G установлен), при этом IP адрес пакета, который будет передаваться по этому маршруту, будет совпадать с IP адресом конечного пункта назначения (140.252.13.65), а адрес канального уровня будет указывать на соответствующий маршрутизатор, IP адрес которого 140.252.13.35.

Очень важно понимать разницу между флагами G и H. Флаг G определяет различие между прямым и непрямым маршрутом, как описано выше. Флаг H указывает на то, что адрес пункта назначения (первая колонка вывода команды netstat) это полный адрес хоста. Отсутствие флага H означает, что адрес назначения это адрес сети (идентификатор хоста должен быть установлен в 0). При просмотре таблицы маршрутизации используются следующие правила: если маршрут указывает на хост он должен полностью совпадать с IP адресом пункта назначения, если маршрут указывает на сеть - сопасть должны идентификаторы сети и любого идентификатора подсети в адресе пункта назначения. Большинство версий команды netstat сначала печатают все пункты, указывающие на хосты, после чего печатаются пункты, указывающие на сети.

Колонка счетчика обращений (reference count) сообщает о количестве использований каждого маршрута. Протоколы, ориентированные на соединения, такие как TCP, занимают маршрут все время, пока соединение установлено. Если установить Telnet соединение между хостами svr4 и slip, то счетчик обращений будет установлен в 1. Если установить еще одно Telnet соединение, счетчик будет показывать 2, и так далее.

Следующая колонка (use) показывает количество пакетов, прошедших по этому маршруту. Если запустить программу ping, которая отправит 5 пакетов, счетчик установится в значение 5 (Естественно, при условии, что никто больше не использует этот маршрут). Последняя колонка интерфейс (interface) сообщает нам имя локального интерфейса.

Вторая строка в выводе относится к loopback интерфейсу (глава 2, раздел "Интерфейс Loopback"), который всегда имеет имя lo0. Флаг G не установлен, так как маршрут не ведет к маршрутизатору. Флаг H указывает, что адрес назначения (127.0.0.1) это адрес хоста, а не адрес сети. Когда флаг G не установлен, это указывает на прямой маршрут, в колонке gateway указывается IP адрес исходящего интерфейса.

Третья строка вывода описывает маршрут по умолчанию. Каждый хост должен иметь один или несколько маршрутов по умолчанию. Этот пункт указывает на то, что необходимо посылать пакеты на маршрутизатор 140.252.13.33 (sun), если не был найден конкретный маршрут. Это означает, что хост svr4 может получить доступ к другим системам в Internet через маршрутизатор sun (и его SLIP канал) воспользовавшись этим единственным пунктом таблицы маршрутизации. Возможность установить маршрут по умолчанию это одна из наиболее мощных концепций IP маршрутизации. Флаги для этого маршрута (UG) говорят о том, что маршрут указывает на маршрутизатор.

Здесь мы специально назвали sun маршрутизатором, а не хостом, потому что когда он работает как маршрутизатор по умолчанию, используются его функции IP перенаправления, при этом он не выступает в роли хоста.

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

И последняя строка указывает на подключенный Ethernet. Флаг H не установлен, это указывает на то, что адрес назначения (140.252.13.32) это адрес сети, при этом идентификатор хоста установлен в 0. И действительно, 5 младших битов установлены в 0 (рисунок 3.11). Это прямой маршрут (флаг G не установлен), поэтому в колонке gateway указывается IP адрес исходящего интерфейса.

Существует еще один немаловажный аспект, оказывающий влияние на маршрутизацию, однако он не отражен в выводе команды netstat. Это маска подсети, связанная с адресом назначения (140.252.13.32). Когда адрес пункта назначения сравнивается с IP адресом 140.252.13.33, адрес перед сравнением, логически суммируется с маской подсети, связанной с этим адресом назначения (0xffffffe0 из раздела "Пример подсети" главы 3). Так как ядро знает каждый интерфейс, связанный с каждым пунктом таблицы маршрутизации, и так как каждый интерфейс имеет маску подсети, каждый пункт таблицы маршрутизации имеет собственную маску подсети.

Сложность таблицы маршрутизации хоста зависит от топологии сетей, к которым хост имеет доступ.

  1. Самый простой (однако наименее интересный) случай - это когда хост вообще не подключен к сетям. Протоколы TCP/IP могут использоваться на этом хосте, однако только для общения с самим собой! Таблица маршрутизации в данном случае содержит единственный пункт соответствующий loopbackv интерфейсу.
  2. Хост подключен к одной локальной сети и имеет возможность получить доступ к хостам только в этой локальной сети. Таблица маршрутизации имеет 2 пункта: один для loopback интерфейса и один для локальной сети (например, Ethernet).
  3. Возможен вариант, когда другие сети (например, Internet) доступны через один маршрутизатор. В этом случае обычно используется пункт по умолчанию, указывающий на этот маршрутизатор.
  4. И последнее, когда добавляются маршруты к хостам или сетям. Примером этого может служить маршрут к хосту slip через маршрутизатор bsdi.

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

  1. Предположим, что адрес назначения принадлежит хосту sun, 140.252.13.33. Во-первых, осуществляется поиск совпадающего адреса хоста. Два пункта хостов в таблице (slip и localhost) не совпадают, поэтому поиск в таблице маршрутизации осуществляется снова на предмет совпадающего адреса сети. Совпадение найдено в пункте 140.252.13.32 (идентификатор сети и идентификатор подсети совпали), поэтому используется интерфейс emd0. Это прямой маршрут, поэтому адрес канального уровня будет являться адресом назначения.
  2. Предположим, что адрес назначения это адрес хоста slip, 140.252.13.65. Первый поиск в таблице маршрутизации на предмет совпадающего адреса хоста заканчивается успешно. Это непрямой маршрут, поэтому IP адрес назначения остается 140.252.13.65, а адресом канального уровня будет адрес канального уровня маршрутизатора 140.252.13.35, используется интерфейс emd0.
  3. Теперь мы посылаем датаграмму по Internet на хост aw.com (192.207.117.2). Первый поиск в таблице маршрутизации на предмет совпадающего адреса хоста заканчивается неудачей, поэтому осуществляется поиск на предмет совпадающего адреса сети. Он также завершается неудачно. И последний шаг это поиск пункта по умолчанию, который заканчивается успешно. Маршрут является непрямым через маршрутизатор 140.252.13.33 с использованием интерфейса emd0.
  4. В нашем последнем примере мы послали датаграмму на свой собственный хост. Существует 4 способа сделать это, используя имя хоста, IP адрес хоста, loopback имя или loopback IP адрес:

    ftp svr4
    ftp 140.252.13.34

    ftp localhost
    ftp 127.0.0.1

    В двух первых случаях второй поиск в таблице маршрутизации приведет к совпадению с сетью 140.252.13.32, и пакет посылается через Ethernet драйвер. Как мы показали на рисунке 2.4, пакет, предназначенный на собственный IP адрес хоста, посылается в loopback драйвер, который ставит пакет во входную очередь IP.

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

    Во всех четырех случаях пакет отправляется в loopback драйвер, однако принимаются два разных решения о маршрутизации.

Инициализация таблицы маршрутизации

Мы никогда не упоминали о том, как создаются пункты в таблице маршрутизации. Когда инициализируется интерфейс (обычно, когда адрес интерфейса устанавливается командой ifconfig), автоматически создается прямой маршрут для этого интерфейса. Для каналов точка-точка и loopback интерфейса устанавливается маршрут к хосту (то есть устанавливается флаг H). Для широковещательных интерфейсов, таких как Ethernet, маршрутом является сама эта сеть.

Маршруты к хостам или сетям, которые не подключены непосредственно, должны быть помещены в таблицу маршрутизации каким-либо иным образом. Самый распространенный способ создания маршрутов - с помощью команды route, что обычно делается из загрузочных файлов при старте системы. На хосте svr4 были исполнены следующие команды, которые добавляют пункты, показанные ранее:

route add default sun 1
route add slip bsdi 1

Третий аргумент (default и slip) это пункты назначения, четвертый аргумент это маршрутизатор (gateway) и последний аргумент это показатель маршрутизации. Команда route использует показатель маршрутизации следующим образом: она устанавливает флаг G, если показатель больше чем 0, и не устанавливает флаг G, если показатель равен 0.

К сожалению, совсем немногие системы содержат команды route в своих стартовых файлах. В 4.4BSD и BSD/386 это /etc/netstat, в SVR4 - /etc/inet/rc.inet, в Solaris 2.x - /etc/rc2.d/S69inet, в SunOS 4.1.x - /etc/rc.local и в AIX 3.2.2 - /etc/rc.net.

В некоторых системах маршрутизатор по умолчанию содержится в файле, например, /etc/defaultrouter. Этот пункт по умолчанию добавляется в таблицу маршрутизации при каждой перезагрузке.

Существует еще один способ заполнить таблицу маршрутизации с помощью запуска демона маршрутизации (см. главу 10) или с помощью нового протокола определения маршрутов (раздел "ICMP сообщения поиска маршрутизатора").

Более сложные таблицы маршрутизации

Хост sun является маршрутизатором по умолчанию для всех хостов в нашей подсети, так как он имеет SLIP канал с дозвоном, который подключен к Internet (см. рисунок на внутренней стороне обложки).

sun % netstat -rn
Routing tables
Destination      Gateway            Flags     Refcnt  Use      Interface
140.252.13.65    140.252.13.35     UGH       0       171       le0
127.0.0.1        127.0.0.1          UH       1        766      lo0
140.252.1.183    140.252.1.29      UH        0       0         sl0
default          140.252.1.183      UG       1        2955     sl0
140.252.13.32    140.252.13.33     U         8       99551     le0

Первые два пункта таблицы маршрутизации sun идентичны первым двум пунктам для хоста svr4: маршрут, указывающий на хост slip, через маршрутизатор bsdi и loopback маршрут.

Третья строка отличается. Это прямой маршрут (флаг G не установлен) на хост (флаг H установлен), соответствующий каналу точка-точка, интерфейс SLIP. Если мы посмотрим на вывод команды ifconfig,

sun % ifconfig sl0
sl0: flags=1051<UP, POINTOPOINT, RUNNING>
        inet 140.252.1.29 --> 140.252.1.183 netmask ffffff00

то увидим, что адрес назначения в таблице маршрутизации на другом конце канала точка-точка (маршрутизатор netb), а адрес маршрутизатора в действительности это локальный IP адрес исходящего интерфейса (140.252.1.29). (Раньше мы говорили, что для прямых маршрутов адрес маршрутизатора печатается командой netstat как локальный IP адрес используемого интерфейса.)

Пункт по умолчанию это непрямой маршрут (флаг G), указывающий на сеть (нет флага H). Адрес маршрутизатора - 140.252.1.183, (адрес на другом конце SLIP канала), а не локальный IP адрес SLIP интерфейса (140.252.1.29) (так как это непрямой маршрут).

Необходимо обратить внимание на то, что третья и четвертая строки в выводе команды netstat (интерфейс sl0) создаются программным обеспечением SLIP при установлении канала SLIP, они удаляются, когда канал SLIP отключается.

Нет маршрута к пункту назначения

Во всех наших примерах сделано предположение, что поиск в таблице маршрутизации заканчивается успешно и будет обнаружено соответствие, хотя бы с маршрутором по умолчанию. Что произойдет, если маршрут по умолчанию отсутствует, и не будет найдено совпадение с указанным пунктом назначения?

Ответ зависит от того, какого рода IP датаграмма обрабатывается, то есть была ли она сгенерирована непосредственно этим хостом или она должна быть перенаправлена (в том случае, если хост выступает в роли маршрутизатора). Если датаграмма была сгенерирована этим хостом, приложению, которое отправило датаграмму, возвращается ошибка "хост недоступен" (host unreachable) или "сеть недоступна" (network unreachable). Если датаграмма должна быть перенаправлена, посылающему хосту возвращается ICMP собщение о недоступности хоста. Мы рассмотрим эту ошибку в следующем разделе.

ICMP ошибки о недоступности хоста и сети

ICMP ошибка о недоступности хоста (host unreachable) отправляется маршрутизатором, когда он получает IP датаграмму, которую невозможно перенаправить. (На рисунке 6.10 мы показали формат ICMP сообщений о недоступности.) Мы сможем пронаблюдать это в нашей сети, если выключим SLIP канал с дозвоном на маршрутизаторе sun и попробуем отправить пакет через SLIP канал с любого другого хоста, указав sun как маршрутизатор по умолчанию.

Ранние реализации TCP/IP в BSD генерировали и ошибки о недоступности хоста, и ошибки о недоступности сети, в зависимости от того, принадлежал ли пункт назначения локальной подсети или нет. 4.4BSD генерирует только ошибку о недоступности хоста.

Обратимся снова к выводу команды netstat запущенной на маршрутизаторе sun, вывод показан в предыдущем разделе. Мы видим, что пункт таблицы маршрутизации, который соответствует SLIP каналу, добавляется, когда SLIP канал включается, и удаляется, когда SLIP канал выключается. Это означает, что когда SLIP канал отключен, маршрута по умолчанию на sun не существует. Однако мы не будем пытаться изменить все таблицы маршрутизации у хостов в нашей маленькой сети, оставив им возможность самим удалить свои маршруты по умолчанию. Вместо этого мы посчитаем ICMP сообщения о недоступности хоста, сгенерированные sun для любых пакетов, которые он не может перенаправить.

Мы можем увидеть это, запустив ping с svr4 на хост, находящийся на другом конце SLIP канала (в настоящее время этот хост выключен):

svr4 % ping gemini
ICMP Host Unreachable from gateway sun (140.252.13.33)
ICMP Host Unreachable from gateway sun (140.252.13.33)
^?                                   символ прерывания

На рисунке 9.2 мы показали вывод команды tcpdump для этого примера. (Команда запущена на хосте bsdi).

1 0.0               svr4 > gemini: icmp: echo request
2 0.00 (0.00)      sun > svr4: icmp: host gemini unreachable

3 0.99 (0.99)      svr4 > gemini: icmp: echo request
4 0.99 (0.00)      sun > svr4: icmp: host gemini unreachable

Рисунок 9.2 Сообщение ICMP о недоступности хоста в ответ на ping.

Когда маршрутизатор sun обнаруживает, что на хост gemini нет маршрута, он отвечает на эхо запрос сообщением о недоступности хоста.

Если мы включим SLIP канал, подключающий нас к Internet, и попробуем послать ping на несуществующий в Internet IP адрес, то рано или поздно получим сообщение об ошибке. Интересно посмотреть, как далеко уйдет пакет по Internet, перед тем как будет получена ошибка:

sun % ping 192.82.148.1               этот IP адрес не подключен к Internet
PING 192.82.148.1: 56 data bytes
ICMP Host Unreachable from gateway enss142.UT.westnet.net (192.31.39.21)
  for icmp from sun (140.252.1.29) to 192.82.148.1

Обратившись к рисунку 8.5, мы увидим, что пакет прошел через шесть маршрутизаторов, перед тем как было определено, что IP адрес не существует. Только когда он дошел до пределов NSFNET магистрали, была выявлена ошибка. Это произошло из-за того, что шесть маршрутизаторов, которые перенаправляли пакет, отправляли его на пункт назначения по умолчанию. И только когда пакет достиг NSFNET магистрали, маршрутизатор, имеющий полное представление о каждой сети, подключенной к Internet, смог определить ошибку. Это иллюстрирует тот факт, что большинство маршрутизаторов функционируют, не представляя себе полной топологии сетей.

[Ford, Rekhter, and Braun 1993] определяет домены маршрутизации верхнего уровня (top-level routing domain) как маршрутизаторы, поддерживающие и обрабатывающие информацию о большинстве узлов Internet и не использующие маршрутов по умолчанию. В Internet существует пять доменов маршрутизации верхнего уровня: NFSNET магистраль, Commercial Internet Exchange (CIX), NASA Science Internet (NSI), SprintLink и European IP Backbone (EBONE).

Перенаправлять или не перенаправлять

Мы уже несколько раз упоминали о том, что хост не сможет перенаправить IP датаграммы, если он специально не сконфигурирован, чтобы выступать в роли маршрутизатора. Как осуществляется подобная конфигурация?

Большинство Berkeley реализаций, имеют переменную ядра, названную ipforwarding (или похоже). (См. приложение Е.) Некоторые системы (BSD/386 и SVR4, например) перенаправляют датаграммы, если эта переменная установлена в ненулевое значение. В SunOS 4.1.x определено три значения для этой переменной: -1 обозначает, что перенаправление никогда не будет осуществляться и что никогда нельзя будет сменить значение этой переменной, 0 обозначает, что перенаправление не осуществляется, однако значение переменной устанавливается в 1, когда два или более интерфейсов активизированы, и 1 обозначает, что перенаправление осуществляется всегда. У Solaris 2.x также существует три значения, а именно 0 (перенаправление не осуществляется), 1 (перенаправление осуществляется всегда) и 2 (перенаправление осуществляется только тогда, когда активизированы два или более интерфейсов).

В более ранних реализациях 4.2BSD датаграммы перенаправляются по умолчанию. При этом, если система сконфигурирована неверно, возникает очень много проблем. Именно поэтому данная опция ядра должна быть всегда по умолчанию установлена в значение "без перенаправления" (never forward), пока системный администратор специально не включит перенаправление.

ICMP ошибки перенаправления

ICMP ошибка перенаправления отправляется маршрутизатором на хост, пославший IP датаграмму, когда датаграмма должна быть послана на другой маршрутизатор. Подобная концепция довольно проста. Мы привели три составные части этой концепции на рисунке 9.3. Увидеть ICMP перенаправление можно только когда хост имеет выбор, на какой маршрутизатор послать пакет. (Обратитесь к примерам, которые мы приводили на рисунке 7.6.)

  1. Предположим, что хост посылает IP датаграмму на R1. Подобное решение принято потому, что R1 - это маршрутизатор по умолчанию для этого хоста.
  2. R1 принимает датаграмму, просматривает свою таблицу маршрутизации, и определяет, что маршрутизатором следующей пересылки является R2, и именно туда необходимо отправить датаграмму. Когда R1 отправляет датаграмму на R2, он определяет, что отправляет ее на тот же самый интерфейс, с которого датаграмма была получена (локальная сеть, к которой подключен хост и два маршрутизатора). В этом случае маршрутизатор отправляет ошибку перенаправления на хост, пославший датаграмму.
  3. R1 посылает ICMP перенаправление на хост, сообщая тем самым, что следующие датаграммы необходимо посылать на R2 вместо R1.

Рисунок 9.3 Пример ICMP перенаправления.

Перенаправления используются для того, чтобы позволить хосту с минимальным знанием о маршрутах поддерживать и обновлять свою таблицу маршрутизации. Как правило, формирование таблицы маршрутизации хоста начинается с создания маршрута по умолчанию (R1 или R2 из нашего примера на рисунке 9.3), при этом с использованием перенаправления хост может обновить свою таблицу маршрутизации. ICMP перенаправление позволяет TCP/IP хостам полностью полагаться на интеллектуальность маршрутизаторов в вопросе выбора маршрутов. Маршрутизаторы R1 и R2 в нашем примере должны точно представлять топологию подключенных сетей, тогда как хосты, подключенные к локальной сети, могут начинать свою маршрутизацию с маршрута по умолчанию, узнавая затем более подробно о новых маршрутах из принятых перенаправлений.

Пример

Посмотрим ICMP перенаправления в действии на примере нашей сети (на внутренней стороне обложки). Мы показали всего три хоста (aix, solaris и gemini) и два маршрутизатора (gateway и netb) в верхней части сети, в действительности там более 150 хостов и 10 маршрутизаторов. Большинство хостов считают gateway маршрутизатором по умолчанию, так как он предоставляет доступ к Internet.

Как можно получить доступ из подсети 140.252.1 к нижней подсети (4 нижние хоста на рисунке)? Во-первых, вспомним, что если на конце SLIP канала находится единственный хост, используется уполномоченный агент ARP (глава 4, раздел "Уполномоченный агент ARP"). Это означает, что для хостов в верхней сети 140.252.1 не требуется специальных средств, для того чтобы получить доступ к хосту sun (140.252.1.29). Доступ обеспечит программное обеспечение уполномоченного агента ARP на netb.

Однако, если на удаленном конце SLIP канала присутствует сеть, то необходима маршрутизация. Одно из возможных решений заключается в том, чтобы каждый хост и маршрутизатор знали о том, что маршрутизатор netb является шлюзом в сеть 140.252.13. Этого можно добиться путем внесения статического маршрута в каждую таблицу маршрутизации на каждом хосте или запустив на каждом хосте маршрутизирующий демон. Однако существует более простой способ (метод, который обычно используется) - использовать ICMP перенаправление.

Запустим программу ping с хоста solaris в верхней сети на хост bsdi (140.252.13.35) в нижней сети. Так как идентификаторы подсети различны, уполномоченный агент ARP не может быть использован. Предположим, что статический маршрут не установлен, поэтому при посылке первого пакета будет использован маршрут по умолчанию на маршрутизатор gateway. Ниже мы приводим таблицу маршрутизации, перед тем как запустили ping:

solaris % netstat -rn
Routing Table:
Destination       Gateway       Flags  Ref   Use      Interface
-------------- --------------- ------- --- ------- --------------
127.0.0.1      127.0.0.1         UH       0      848  lo0
140.252.1.0    140.252.1.32     U         3   15042  le0
224.0.0.0      140.252.1.32     U         3       0   le0
default        140.252.1.4       UG       0     5747

(Пункт 224.0.0.0 используется для групповой адресации IP. Мы опишем это в главе 12.) Если указать опцию -v в командной строке ping, то будут видны все ICMP сообщения принятые хостом. Нам необходимо указать эту опцию, чтобы увидеть сообщения о перенаправлении, которые будут посланы.

solaris % ping -sv bsdi
PING bsdi: 56 data bytes
ICMP Host redirect from gateway gateway (140.252.1.4)
to netb (140.252.1.183) for bsdi (140.252.13.35)
64 bytes from bsdi (140.252.13.35): icmp_seq=0. time=383. ms
64 bytes from bsdi (140.252.13.35): icmp_seq=1. time=364. ms
64 bytes from bsdi (140.252.13.35): icmp_seq=2. time=353. ms
^?                                       символ прерывания
----bsdi PING Statistics----
4 packets transmitted, 3 packets received, 25% packet loss
round-trip (ms) min/avg/max = 353/366/383

Перед тем как мы получим первый ответ на ping, хост примет ICMP перенаправление от маршрутизатора по умолчанию gateway. Если мы затем посмотрим таблицу маршрутизации, то увидим, что появился новый маршрут к хосту bsdi. (Этот пункт выделен жирным шрифтом.)

solaris % netstat -rn
Routing Table:
  Destination       Gateway        Flags  Ref   Use      Interface
--------------- --------------- -------- --- ------- -------------
127.0.0.1       127.0.0.1          UH       0      848  lo0
140.252.13.35   140.252.1.183     UGHD      0       2
140.252.1.0     140.252.1.32      U         3   15045  le0
224.0.0.0       140.252.1.32      U         3       0   le0
default         140.252.1.4        UG       0     5749

Pltcmm мы впервые видиv флаг D, который означает, что маршрут был установлен с использованием ICMP перенаправления. Флаг G обозначает, что это непрямой маршрут к шлюзу (netb), а флаг H обозначает, что это маршрут к хосту (как мы и ожидали), а не маршрут к сети.

Так как это маршрут к хосту, добавленный путем перенаправления, он обслуживает только хост bsdi. Если затем мы попробуем получить доступ к хосту svr4, будет сгенерировано еще одно перенаправление, и будет создан еще один маршрут к хосту. Точно так же, попытка получить доступ к хосту slip создаст еще один маршрут. Каждое перенаправление на конкретный хост приводит к созданию нового маршрута к хосту. Все три хоста в подсети (bsdi, svr4 и slip) будут обслуживаться одним маршрутом к сети, указывающим на маршрутизатор sun. Однако, ICMP перенаправления создают маршруты к хостам, а не маршруты к сетям, так как маршрутизатор, генерирующий перенаправление в данном примере (gateway), не имеет представления о структуре подсетей в сети 140.252.13.

Более подробно

На рисунке 9.4 показан формат сообщения ICMP о перенаправлении.

Рисунок 9.4 ICMP сообщение о перенаправлении.

Существуют четыре различных типа сообщений о перенаправлении, с различными значениями кода (code), как показано на рисунке 9.5.

код | Описание
0 | перенаправление для сети
1 | перенаправление для хоста
2 | перенаправление для типа сервиса (TOS) и сети
3 | перенаправление для типа сервиса (TOS) и хоста

Рисунок 9.5 Различные значения code для ICMP перенаправления.

Существуют три IP адреса, которые должен знать получатель ICMP перенаправления: (1) IP адрес, который вызвал перенаправление (находится в IP заголовке, возвращенном как данные в ICMP сообщении о перенаправлении), (2) IP адрес маршрутизатора, который послал перенаправление (является IP адресом источника IP датаграммы, содержащей перенаправление) и (3) IP адрес маршрутизатора, который должен быть использован (находится в байтах с 4-го по 7-ой в ICMP сообщении).

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

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

  1. Исходящий и входящий интерфейс один и тот же.
  2. Маршрут, который будет использоваться для исходящей датаграммы, не должен быть создан или модифицирован с использованием ICMP перенаправления. Также он не должен быть маршрутом по умолчанию для маршрутизатора.
  3. Датаграмма не должна быть смаршрутизирована от источника.
  4. Ядро должно быть сконфигурировано таким образом, чтобы посылать перенаправления.

Переменная ядра с именем ip_sendredirects (или похожим). (См. приложение Е.) Большинство современных систем (4.4BSD, SunOS 4.1.x, Solaris 2.x и AIX 3.2.2, например) включают эту переменную по умолчанию. Другие системы, например, SVR4, имеют эту переменную по умолчанию выключенной.

В дополнение, хост 4.4BSD, который принимает ICMP перенаправления, осуществляет некоторые проверки перед модификацией своей таблицы маршрутизации. Это предотвращает странное поведение маршрутизатора или хоста, вызванное некорректной модификацией таблицы маршрутизации системы.

  1. Новый маршрутизатор должен быть в непосредственно подключенной сети.
  2. Перенаправление должно быть от текущего маршрутизатора на указанный пункт назначения.
  3. Перенаправление не может заставить хост использовать самого себя в качестве маршрутизатора.
  4. Модифицируемый маршрут должен быть непрямым маршрутом.

И в заключение стоит повториться, что маршрутизаторы должны посылать только перенаправления к хостам (codes 1 или 3 на рисунке 9.5), а не перенаправления к сетям. Из-за разделения на подсети не всегда можно однозначно указать, когда должно быть послано перенаправление к сети вместо перенаправления к хосту. Некоторые хосты воспринимают принятое перенаправление к сети как перенаправление к хосту, в том случае если маршрутизатор послал неверный тип (type).

ICMP сообщения поиска маршрутизатора (ICMP Router Discovery Messages)

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

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

RFC 1256 [Deering 1991] содержит формат этих ICMP сообщений. На рисунке 9.6 показан формат ICMP сообщения запроса маршрутизатора. На рисунке 9.7 показан формат ICMP сообщения объявления маршрутизатора, которое рассылается маршрутизаторами.

В одном сообщении маршрутизатор может объявить несколько адресов. Поле количества адресов. Размер записи адреса - количество 32-битных слов для каждого адреса маршрутизатора, оно всегда установлено в 2. Время жизни - количество секунд, в течение которого данное объявление адресов считается действительным.

Рисунок 9.6 Формат ICMP сообщения запроса маршрутизатора.

Рисунок 9.7 Формат ICMP сообщения объявления маршрутизатора.

Затем следует одна или несколько пар IP адресов. IP адрес должен быть одним из адресов посылающего маршрутизатора. Уровень предпочтительности - это 32-битное целое число со знаком, указывающее на предпочтительность этого адреса в качестве адреса маршрутизатора по умолчанию, по сравнению с другими адресами маршрутизаторов в той же подсети. Большее значение указывает на большую предпочтительность адреса. Уровень предпочтительности 0x80000000 указывает на то, что соответствующий адрес, несмотря на то что он объявлен, не должен быть использован получателем в качестве адреса маршрутизатора по умолчанию. Обычное значение предпочтительности это 0.

Функционирование маршрутизатора

Когда маршрутизатор стартует, он начинает периодически рассылать объявления на все интерфейсы, которые поддерживают групповой и широковещательный тип адресации. В действительности эти объявления не периодические, они рассылаются случайным образом. Это сделано для того, чтобы объявления не перемешивались и не синхронизировались с другими маршрутизаторами в той же подсети. Обычный интервал между объявлениями составляет от 450 до 600 секунд. Время жизни по умолчанию для каждого объявления составляет 30 минут.

Поле времени жизни также используется, когда интерфейс маршрутизатора выключается. В этом случае маршрутизатор может передать последнее объявление с временем жизни, установленным в 0.

Помимо периодических объявлений, маршрутизатор отвечает на запросы от хостов. Он отвечает на запросы объявлением маршрутизатора.

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

Функционирование хоста

При старте хост обычно посылает три запроса о поиске маршрутизатора с интервалом в 3 секунды. После того как принято объявление от маршрутизатора, запросы прекращаются.

Хост также слушает объявления от маршрутизатора. Эти объявления могут привести к смене маршрутизатора по умолчанию для данного хоста. Если объявление не получено для текущего маршрута по умолчанию, он может быть удален по тайм-ауту.

Пока текущий маршрутизатор по умолчанию функционирует, он отправляет объявления каждые 10 минут с временем жизни в 30 минут. Это означает, что маршрут по умолчанию в таблице маршрутизации хоста не будет удален по тайм-ауту, даже если одно или два объявления будут потеряны.

Реализация

Сообщения о поиске маршрутизатора обычно генерируются и обрабатываются пользовательскими процессами (демонами). Поэтому добавляется еще один программный способ обновления таблицы маршрутизации к тем, что показаны на рисунке 9.1, несмотря на то, что он может добавить или удалить только пункт по умолчанию. Демон должен быть сконфигурирован так, чтобы выступать либо в роли маршрутизатора, либо в роли хоста.

Эти два ICMP сообщения достаточно новы и поддерживаются не всеми системами. В нашей сети их поддерживает только Solaris 2.x (демон in.rdisc). Несмотря на то, что RFC рекомендует использовать групповую адресацию IP, где это возможно, поиск маршрутизаторов может осуществляться с использованием широковещательных сообщений.

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

IP маршрутизация это краеугольный камень, на котором держится функционирование систем, использующих TCP/IP, будь то хост или маршрутизатор. Записи в таблице маршрутизации довольно просты: до 5 флаговых битов, IP адрес назначения (хост, сеть или по умолчанию), IP адрес маршрутизатора следующей пересылки (для непрямого маршрута) или IP адрес локального интерфейса (для прямого маршрута) и указатель на используемый локальный интерфейс. Записи, соответствующие хостам, имеют более высокий приоритет, чем записи, соответствующие сетям, а оба типа записей имеют более высокий приоритет по сравнению с маршрутом по умолчанию.

Просмотр таблицы маршрутизации осуществляется для каждой IP датаграммы, которую система генерирует или пропускает через себя. Таблица маршрутизации может быть обновлена с помощью демона маршрутизации или ICMP перенаправления. По умолчанию система никогда не пропустит через себя датаграмму, если система не сконфигурирована как маршрутизатор. Статические маршруты могут быть добавлены с помощью команды route, а ICMP сообщения поиска маршрутизатора могут быть использованы для инициализации и динамического обновления маршрутов по умолчанию. Хост может запуститься с очень простой таблицей маршрутизации, которая динамически обновляется с помощью ICMP перенаправлений, приходящих с маршрутизатора по умолчанию.

Эта глава была посвящена тому, как отдельная система использует свою таблицу маршрутизации. В следующей главе мы рассмотрим, как маршрутизаторы обмениваются друг с другом маршрутной информацией.

Упражнения

  1. Как Вы думаете, почему существуют два типа ICMP перенаправлений - на сеть и на хост?
  2. Обратитесь к таблице маршрутизации хоста svr4, показанной в начале раздела "Принципы маршрутизации". Является ли необходимым указание маршрута на хост slip (140.252.13.65)? Что изменится, если этот пункт будет удален из таблицы маршрутизации?
  3. Представьте, что существует кабель между 4.2BSD и 4.3BSD хостами. Также представьте, что идентификатор сети - 140.1. Хост 4.2BSD распознает в качестве широковещательных адресов только адреса с идентификаторами хостов, установленными во все нули (140.1.0.0), тогда как хост 4.3BSD обычно отправляет широковещательные сообщения с идентификаторами хостов, состоящих из всех единичных битов (140.1.255.255). Также, хост 4.2BSD по умолчанию старается перенаправить входящие датаграммы, даже если он имеет только один интерфейс. Опишите, что произойдет, когда хост 4.2BSD получает IP датаграммы с адресом назначения 140.1.255.255.
  4. Продолжим предыдущее упражнение. Представьте себе, что кто-либо исправил эту проблему, добавив запись в ARP кэш на одной из систем в подсети 140.1. (воспользовавшись командой arp), сказав при этом, что IP адрес 140.1.255.255 соответствует Ethernet адресу из всех единичных битов (широковещательный запрос Ethernet). Опишите, что произойдет в этом случае.
  5. Просмотрите таблицу маршрутизации Вашей системы и опишите каждую запись.


Глава 10 Динамические протоколы

Глава 10 Динамические протоколы маршрутизации

Введение

Все о чем мы говорили в предыдущей главе, имело отношение к статической маршрутизации. Записи в таблице маршрутизации создавались при конфигурации интерфейса (для интерфейсов подключенных напрямую), добавлялись командой route (обычно из системных загрузочных файлов) или создавались с помощью ICMP перенаправления (при использовании неверных маршрутов).

Все это просто замечательно, если сеть маленькая. А именно, если существует только одна точка подключенияния к другим сетям и не существует запасных маршрутов (запасные маршруты могут быть использованы в случае выхода из строя основных). Если хотя бы одно из трех приведенных выше условий неверно, используется динамическая маршрутизация.

В этой главе рассматриваются протоколы динамической маршрутизации, которыми пользуются маршрутизаторы для общения друг с другом. Мы подробно рассмотрим RIP, протокол обмена информацией о маршрутизации (Routing Information Protocol), широко используемый протокол, который присутствует практически во всех версиях TCP/IP. Затем мы рассмотрим еще два протокола маршрутизации, OSPF и BGP. В конце главы мы познакомимся с новой техникой маршрутизации, которая называтеся безклассовой маршрутизацией между доменами, и которая начинает применяться в Internet для экономии адресов класса B.

Динамическая маршрутизация

Динамическая маршрутизация используется для общения маршрутизаторов друг с другом. Маршрутизаторы передают друг другу информацию о том, какие сети в настоящее время подключены к каждому из них. Маршрутизаторы общаются, используя протоколы маршрутизации. Пользовательский процесс, посредством которого маршрутизаторы могут общаться с соседними маршрутизаторами, называется демоном маршрутизации (routing daemon). Как видно из рисунка 9.1, демон маршрутизации обновляет таблицу маршрутизации в ядре в соответствии с информацией, которую он получает от соседних маршрутизаторов.

Динамическая маршрутизация не меняет способы, с помощью которых ядро осуществляет маршрутизацию на IP уровне, как описано в разделе "Принципы маршрутизации" главы 9. Мы назвали это механизмом маршрутизации (routing mechanism). Ядро точно так же просматривает свою таблицу маршрутизации, отыскивая маршруты к хостам, маршруты к сетям и маршруты по умолчанию. Меняется только способ помещения информации в таблицу маршрутизации - вместо запуска команды route или использования загрузочных файлов маршруты добавляются и удаляются динамически демоном маршрутизации, который работает постоянно.

Как было отмечено ранее, демон маршрутизации отвечает за политику маршрутизации (routing policy) , выбирая, какие маршруты необходимо поместить в таблицу маршрутизации. Если демон обнаружил несколько маршрутов к пункту назначения, он выбирает (каким-либо образом), какой маршрут лучше, и именно этот маршрут (единственный) добавляет в таблицу маршрутизации. Если демон определил, что канал изчез (возможно по причине выхода из строя маршрутизатора или телефонной линии), он может удалить соответствующие маршруты или добавить альтернативные маршруты, чтобы обойти возникшую неисправность.

В Internet, на сегодняшний день, используется множество различных протоколов маршрутизации. Internet организован как сообщество автономных систем (AS - autonomous systems), каждая из которых обычно администрируется независимо от остальных. Например, сеть, построенная в университетском городке, обычно считается автономной системой. Магистраль (backbone) NSFNET с точки зрения Internet это автономная система, потому что все маршрутизаторы на магистрали находятся под единым административным контролем.

Для каждой автономной системы выбирается собственный протокол маршрутизации, с помощью которого осуществляется взаимодействие между маршрутизаторами в этой автономной системе. Такой протокол называется протоколом внутренних маршрутизаторов (IGP - interior gateway protocol) или протоколом внутридоменной маршрутизации (intradomain routing protocol). Наиболее популярный IGP - это протокол обмена информацией о маршрутизации (RIP - Routing Information Protocol). Более новый IGP это протокол Open Shortest Path First (OSPF). Он был разработан как замена для RIP. Устаревший IGP, который в настоящее время не используется, HELLO - это IGP, который первоначально использовался на магистрали NSFNET вплоть до 1986 года.

Новые требования к маршрутизаторам Router Requirements RFC [Almquist 1993] определяют, что маршрутизатор, который реализует любые динамические протоколы маршрутизации, должен поддерживать OSPF и RIP, а также может поддерживать другие IGP.

Существуют протоколы маршрутизации, которые называются протоколами внешних маршрутизаторов (EGP - exterior gateway protocols) или протоколами междоменной маршрутизации (interdomain routing protocols). Они предназначены для общения между маршрутизаторами, находящихимися в разных автономных системах. Исторически (и к большому сожалению) предшественником всех EGP был протокол с тем же самым именем: EGP. Более новый EGP - протокол пограничных маршрутизаторов (BGP - Border Gateway Protocol) в настоящее время используется между магистралью NSFNET и некоторыми региональными сетями, которые подключены к магистрали. Планируется, что BGP заменит собой EGP.

Демоны маршрутизации в Unix

В Unix системах обычно запускается демон маршрутизации, называемый routed. Он присутствует практически в каждой версии TCP/IP. Этот демон понимает только протокол RIP. (Мы опишем routed в следующем разделе.) routed предназначен для сетей малого или среднего размеров.

Альтернативная программа - gated. Этот демон поддерживает как IGP, так и EGP. [Fedor 1988] описывает раннюю реализацию gated. На рисунке 10.1 приведены протоколы маршрутизации, поддерживаемые демоном routed и двуми версиями демона gated. Большинство систем, которые используют демоны маршрутизации, запускают routed, однако если возникает необходимость поддерживать разные протоколы маршрутизации, используется gated.

Демон | Протоколы внутренних маршрутизаторов |  |  | Протоколы внешних маршрутизаторов
 | HELLO | RIP | OSPF | EGP | BGP
routed |  | V1 |  |  |
gated, Version 2 | · | V1 |  | · | V1
gated, Version 3 | · | V1,V2 | V2 | · | V2,V3

Рисунок 10.1 Протоколы маршрутизации, поддерживаемые routed и gated.

Мы опишем RIP Version 1 в следующем разделе, а отличия RIP Version 2, OSPF и BGP соответственно в разделах "RIP Version 2", "OSPF: открыть первым наикратчайший маршрут" и "BGP: протокол граничных маршрутизаторов" этой главы.

RIP: протокол обмена информацией о маршрутизации

В этом разделе приводится обзор RIP, так как это наиболее широко используемый протокол маршрутизации. Официальная спецификация протокола RIP находится в RFC 1058 [Hedrick 1988a], однако этот RFC был написан через несколько лет после того, как протокол получил широкое распространение.

Формат сообщения

RIP сообщения передаются в UDP датаграммах, как показано на рисунке 10.2. (Мы рассмотрим UDP в главе 11.)

Рисунок 10.2 Инкапсуляция RIP сообщения в UDP датаграмму.

На рисунке 10.3 показан формат RIP сообщения, вместе с IP адресами.

Если поле команда равно 1 - это запрос, если 2 - отклик. Существуют еще две значения поля команды (3 и 4), а также два недокументированных значения: опрос (5) и пункт опроса (6). В запросе находится требование к другой системе послать всю или часть ее таблицы маршрутизации. В отклике содержится вся или часть таблицы маршрутизации отправителя.

Поле версия обычно установлено в 1, однако для RIP Version 2 (раздел "RIP Version 2") это значение устанавливается в 2.

Следующие 20 байт сдержат: семейство адресов (которое всегда равно 2 для IP адресов), IP адрес и соответствующий показатель. В следующих разделах мы увидим, что в роли показателя RIP выступает счетчик пересылок.

В RIP сообщении может быть объявлено до 25 маршрутизаторов. Ограничение в 25 определяется полным размером RIP сообщения, 20х25+4=504, меньше чем 512 байт. Из-за ограничения в 25 маршрутизаторов, на один запрос, как правило, требуется послать несколько откликов, чтобы передать всю таблицу маршрутизации.

Рисунок 10.3 Формат RIP сообщения.

Обычное функционирование

Давайте посмотрим, как обычно работает routed с использованием RIP. Номер зарезервированного порта для RIP - UDP порт 520.

  • Инициализация. Когда демон стартует, он определяет все активизированные интерфейсы и посылает пакет с запросом на каждый интерфейс, с требованием к удаленным маршрутизаторам послать полные таблицы маршрутизации. В случае канала точка-точка этот запрос отправляется на другой конец канала. Запрос рассылается широковещательными сообщениями, если сеть их поддерживает. Порт назначения - UDP порт 520 (демон маршрутизации на другом маршрутизаторе). Характеристики подобного запроса следующие: поле команды установлено в 1, поле семейство адресов установлено в 0 и показатель установлен в 16. Подобный формат соответствует специальному запросу, в ответ на который требуется послать полную таблицу маршрутизации.
  • Запрос принят. В случае специального запроса, который мы только что описали, запрашивающему отправляется полная таблица маршрутизации. Иначе обрабатывается каждый пункт в запросе: если присутствует маршрут на указанный адрес, показатель устанавливается в определенное значение, иначе показатель устанавливается в 16. (Показатель, установленный в 16, это специальное значение, которое означает "бесконечно" (infinity) и сообщает, что маршрута к этому пункту назначения не существует.) Возвращается ответ.
  • Ответ принят. Если ответ признан корректным, таблица маршрутизации может быть обновлена. Могут быть добавлены новые записи, существующие записи могут быть модифицированы или удалены.
  • Регулярное обновление маршрутизации. Каждые 30 секунд вся или часть таблицы маршрутизации отправляется каждому соседнему маршрутизатору. Таблица маршрутизации распространяется широковещательными сообщениями (в случае Ethernet) или отправляется на другой конец канала точка-точка.
  • Незапланированное обновление. Происходит в том случае, если изменяется показатель маршрута. В этом случае нет необходимости посылать таблицу маршрутизации целиком, передается только та запись, которая была изменена.

С каждым маршрутом связан тайм-аут. Если система, использующая RIP, определила, что маршрут не был обновлен в течение трех минут, показатель маршрута устанавливается в состояние "бесконечно" (16) и помечается для удаления. Это означает, что было пропущено шесть 30-секундных обновлений от маршрутизатора, который объявил маршрут. Однако, удаление маршрута из локальной таблицы маршрутизации откладывается еще на 60 секунд, чтобы убедиться что маршрут действительно исчез.

Показатели (metrics)

В качестве показателя в RIP используются счетчик пересылок. Для всех непосредственно подключенных интерфейсов счетчик пересылок равен 1. Рассмотрим маршрутизаторы и сети, показанные на рисунке 10.4. Четыре пунктирные линии показывают широковещательные сообщения RIP.

Рисунок 10.4 Пример маршрутизаторов и сетей.

Маршрутизатор R1 объявляет маршрут к N2 со счетчиком пересылок равным 1, послав широковещательное сообщение на N1. (Бессмысленно объявлять маршрут к N1 в широковещательном сообщении, посланном на N1.) Он также объявляет маршрут к N1 со счетчиком пересылок равным 1, послав широковещательное сообщение на N2. Точно так же, R2 объявляет маршрут к N2 с показателем 1 и маршрут к N3 с показателем 1.

Если смежный с нами маршрутизатор объявил маршрут к удаленной сети со счетчиком пересылок равным 1, то для нас показатель к этой сети будет равен 2, так пакет необходимо послать сначала на наш маршрутизатор, чтобы получить доступ к сети. В примере, приведенном выше, показатель к N1 для R2 равен 2, так же как и показатель к N3 для R1.

Так как каждый маршрутизатор посылает свои таблицы маршрутизации соседям, определяется каждая сеть в каждой автономной системе (AS). Если внутри AS существует несколько путей от маршрутизатора к сети, маршрутизатор выбирает путь с наименьшим количеством пересылок и игнорирует другие пути.

Величина счетчика пересылок ограничена значением 15, что означает, что RIP может быть использован только внутри AS, где максимальное количество пересылок между хостами составляет 15. Специальное значение показателя, равное 16, указывает на то, что на данный IP адрес не существует маршрута.

Проблемы

Как бы просто это ни звучало, все равно существуют проблемы. Во-первых, RIP не имеет представления о делении на подсети. Если обычный 16-битный идентификатор хоста в адресе класса В ненулевой, RIP не может определить, принадлежит ли ненулевая часть идентификатору подсети или IP адрес - это целиком адрес хоста. Некоторые реализации используют маску подсети того интерфейса, через который пришла RIP информация, однако такой способ не всегда корректен.

Во-вторых, для RIP требуется очень много времени, чтобы восстановить функционирование сети, после того как вышел из строя маршрутизатор или канал. Время обычно составляет несколько минут. В это время могут возникнуть петли маршрутизации. В современных реализациях RIP существует множество рекомендаций, которые позволяют избавляться от петель маршрутизации и увеличить скорость сходимости сетей. В RFC 1058 [Hedrick 1988a] подробно описано, как должен быть реализован RIP.

Использование количества пересылок в качестве показателя маршрутизации не позволяет использовать другие переменные, которые также должны приниматься во внимание при выборе маршрута. И в заключение, максимальное значение - 15 пересылок ограничивает размер сетей, в которых может быть использован RIP.

Пример

Мы воспользуемся программой ripquery, чтобы получить таблицы маршрутизации некоторых маршрутизаторов. ripquery отправляет один из недокументированных запросов (названный "опрос" (poll), при этом поле команда установливается в 5, рисунок 10.3) на маршрутизатор, требуя от него послать полную таблицу маршрутизации. Если ответ не получен в течении 5 секунд, посылается стандартный RIP запрос (поле команда установлено в 1). (Ранее мы говорили, что запрос с полем семейства протоколов установленным в 0 и с показателем установленным в 16 запрашивает полную таблицу маршрутизации маршрутизатора.)

На рисунке 10.5 показаны два маршрутизатора, у которых мы потребуем их таблицы маршрутизации на хосте sun. Мы запустим ripquery с sun, и получим информацию о маршрутизации с маршрутизатора следующей пересылки, netb:

sun % ripquery -n netb
504 bytes from netb (140.252.1.183):   первое сообщение содержит 504 байта
                                       тут удалено несколько строк
       140.252.1.0, metric 1            верхний Ethernet на рисунке 10.5
       140.252.13.0, metric 1           нижний Ethernet на рисунке 10.5
244 bytes from netb (140.252.1.183):   второе сообщение с оставшимися 244 байтами
                                       несколько строк удалено

Как мы и ожидали, netb объявляет нашу подсеть с показателем 1. Верхний Ethernet, к которому также подключен netb (140.252.1.0), имеет показатель равный 1. (Флаг -n говорит о необходимости выводить IP адреса в цифровом представлении, вместо того чтобы печатать имена хостов.) В этом примере netb сконфигурирован таким образом, чтобы считать все хосты, находящиеся в подсети 140.252.13, подключенными к нему напрямую - таким образом, netb ничего не знает о тех хостах, которые действительно находятся в подсети 140.252.13. Так как существует только одна точка подключения к подсети 140.252.13, объявление различных показателей для каждого хоста не имеет практического смысла.

Рисунок 10.5 Два маршрутизатора netb и gateway, на которые мы послали запрос об их таблицах маршрутизации.

На рисунке 10.6 показан обмен пакетами, который получен с помощью команды tcpdump. Мы указали SLIP интерфейс с опцией -i sl0.

sun % tcpdump -s600 -i sl0
1  0.0                     sun.2879 > netb.route: rip-poll 24
2  5.014702 (5.0147)      sun.2879 > netb.route: rip-req 24
3  5.560427 (0.5457)      netb.route > sun.2879: rip-resp 25:
4  5.710251 (0.1498)      netb.route > sun.2879: rip-resp 12:

Рисунок 10.6 Вывод команды tcpdump при запуске программы ripquery.

Первый отправляемый запрос - это команда опроса RIP (poll) (строка 1). В этом месте отрабатывается тайм-аут в 5 секунд, после чего отправляется обычный RIP запрос (строка 2). Число 24 в конце строк 1 и 2 это размер пакетов запроса в байтах: 4 байта RIP заголовок (с командой и версией), после чего следует один 20-байтовый адрес и показатель.

В строке 3 показан первый отклик, причем число 25 в конце строки указывает, что в сообщении находится 25 пар адресов и показателей, что, как мы рассчитали ранее, составляют 504 байта. Именно эту информацию выдает ripquery. Мы указали опцию -s600 для tcpdump, сообщая о необходимости прочитать из сети 600 байт. Это позволяет принять UDP датаграмму целиком (вместо того чтобы принять только ее первую часть) и затем напечатать содержимое RIP ответа.

В строке 4 показано второе ответное сообщение от маршрутизатора, со следующими 12-ю парами адресов и показателей. Мы можем рассчитать размер этого сообщения, который будет составлять 12х20+4=244, что как раз и печатала ripquery ранее.

Если мы обратимся к маршрутизатору, который находится позади netb, к gateway, то скорее всего получим, что показатель до нашей подсети 140.252.13.0 будет равен 2. Давайте проверим это предположение:

sun % ripquery -n gateway
504 bytes from gateway (140.252.1.4):
                                     несколько строк удалено
       140.252.1.0, metric 1          верхний Ethernet на рисунке 10.5
       140.252.13.0, metric 2         нижний Ethernet на рисунке 10.5

Здесь показатель для верхнего Ethernet на рисунке 10.5 (140.252.1.0) остался равным 1, так как этот Ethernet непосредственно подключен и к gateway, и к netb, однако наша подсеть 140.252.13.0, как мы и ожидали, имеет показатель равный 2.

Еще один пример

Сейчас мы рассмотрим все обновления RIP, которые происходят в сети Ethernet без получения запросов, и посмотрим, что RIP регулярно отправляет своим соседям. На рисунке 10.7 показаны сети в noao.edu. Для простоты маршрутизаторы названы как Rn, где n - номер подсети. Каналы точка-точка показаны с помощью пунктирных линий с указанием IP адресов для каждого конца канала.

Рисунок 10.7 Сети noao.edu 140.252.

Мы запустили на Solaris 2.x программу snoop, напоминающую программу tcpdump, которую мы запускали на компьютере solaris. Эту программу можно запустить без привилегии суперпользователя, однако только для того, чтобы перехватывать широковещательные пакеты, групповые пакеты или пакеты, посылаемые на хост. На рисунке 10.8 показаны пакеты, пойманные за 60 секунд. Мы заменили большинство официальных имен хостов на наше представление в форме Rn.

solaris % snoop -P -tr udp port 520
0.00000 R6.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
4.49708 R4.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
6.30506 R2.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
11.68317 R7.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
16.19790 R8.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
16.87131 R3.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
17.02187 gateway.tuc.noao.edu -> 140.252.1.255 RIP R (15 destinations)
20.68009 R10.tuc.noao.edu -> BROADCAST    RIP R (4 destinations)

29.87848 R6.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
34.50209 R4.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
36.32385 R2.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
41.34565 R7.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
46.19257 R8.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
46.52199 R3.tuc.noao.edu -> 140.252.1.255 RIP R (1 destinations)
47.01870 gateway.tuc.noao.edu -> 140.252.1.255 RIP R (15 destinations)
50.66453 R10.tuc.noao.edu -> BROADCAST    RIP R (4 destinations)

Рисунок 10.8 RIP широковещательные сообщения, пойманные на solaris за 60 секунд.

Благодаря флагу -P пакеты ловятся не перемешиваясь, благодаря -tr печатаются соответствующие временные марки, а благодаря port 520 захватываются только UDP датаграммы у которых в качестве порта источника или порта назначения указан порт 520.

Первые 6 пакетов приходят от R6, R4, R2, R7, R8 и R3, в каждом объявляется только одна сеть. Если мы заглянем внутрь пакетов, то увидим, что R6 объявляет маршрут к сети 140.252.6.0 со счетчиком пересылок равным 1, R4 объявляет маршрут к 140.252.4.0 со счетчиком пересылок равным 1, и так далее.

Маршрутизатор gateway, однако, объявил 15 маршрутизаторов. Мы можем запустить snoop с флагом -v и посмотреть содержимое RIP сообщения. (Этот флаг выдает полное содержимое входящего пакета: Ethernet заголовок, IP заголовок, UDP заголовок и RIP сообщение. Мы удалили всю информацию за исключением информации RIP.) На рисунке 10.9 показан вывод.

Сравните объявленные счетчики пересылок для сети 140.252.1 с топологией, приведенной на рисунке 10.7.

Очень странный факт заключается в том, что в выводе на рисунке 10.8 R10 объявляет четыре сети, тогда как на рисунке 10.7 показано только три. Если мы заглянем в RIP пакет с помощью snoop, то мы увидим следующие объявленные маршруты:

RIP: Address         Metric
RIP: 140.251.0.0     16 (not reachable)
RIP: 140.252.9.0     1
RIP: 140.252.10.0    1
RIP: 140.252.11.0    1

Маршрут к сети класса В 140.251 фиктивный и не должен объявляться. Он не принадлежит к noao.edu.

solaris % snoop -P -v -tr udp port 520 host gateway
                                   много строк удалено
RIP: Opcode = 2 (route response)
RIP: Version = 1

RIP: Address          Metric

RIP: 140.252.101.0    1
RIP: 140.252.104.0    1

RIP: 140.252.51.0     2
RIP: 140.252.81.0     2
RIP: 140.252.105.0    2
RIP: 140.252.106.0    2

RIP: 140.252.52.0     3
RIP: 140.252.53.0     3
RIP: 140.252.54.0     3
RIP: 140.252.55.0     3
RIP: 140.252.58.0     3
RIP: 140.252.60.0     3
RIP: 140.252.82.0     3
RIP: 192.68.189.0     3

RIP: 140.252.57.0     4

Рисунок 10.9 RIP ответ от gateway.

Выражение "BROADCAST" в выводе snoop на рисунке 10.8 для RIP пакета, посланного R10, означает, что IP адрес назначения это ограниченный широковещательный адрес 255.255.255.255 (глава 12, раздел "Широковещательные запросы"), а не широковещательный адрес, указывающий на подсеть (140.252.1.255), который используют другие маршрутизаторы.

RIP Version 2

RFC 1388 [Malkin 1993a] определяет новые расширения RIP, которые в целом обычно называются RIP-2. Эти расширения не изменяют протокол, однако добавляют дополнительную информацию в поля, помеченные как "должны быть равны нулю" (must be zero) на рисунке 10.3. RIP и RIP-2 могут взаимодействовать в том случае, если RIP игнорирует поля, которые должны быть установлены в ноль.

На рисунке 10.10 показан формат сообщения RIP-2. Для RIP-2 поле версии устанавливается в 2.

Домен маршрутизации - это идентификатор маршрутизирующего демона, которому принадлежит этот пакет. В реализациях Unix это должен быть идентификатор процесса демона. Это поле позволяет администратору запустить RIP на одном и том же маршрутизаторе несколько раз, причем каждый будет функционировать с одним доменом маршрутизации.

Поле метки маршрута предназначено для того, чтобы поддерживать протоколы внешних маршрутизаторов. Здесь хранится номер автономной системы для EGP и BGP.

Маска подсети для каждого пункта соответствует своему IP адресу. IP адрес следующей пересылки - это IP адрес пункта назначения, куда должны посылаться пакеты. Значение 0 в этом поле означает, что пакеты должны отправляться в систему, которая послала RIP сообщение.

Рисунок 10.10 Формат сообщения RIP2.

В RIP-2 предоставляется простая схема аутентификации. Первые 20 байт в RIP сообщении содержащие семейство адресов, которое установлено в 0xffff, а поле route tag, установлено в значение 2. Оставшиеся 16 байт содержат пароль в открытом виде.

И в заключение, RIP-2 поддерживает групповые запросы в дополнение к широковещательным (глава 12). При этом уменьшается загрузка хостов, которые не принимают RIP-2 сообщения.

OSPF: "открыть первым наикратчайший маршрут" (Open Shortest Path First)

OSPF это альтернативный RIP протокол внутренних маршрутизаторов. В OSPF сняты все ограничения, присущие для RIP. OSPF Version 2 описывается в RFC 1247 [Moy 1991].

OSPF - протокол состояния канала (link-state) , тогда как RIP - протокол вектора расстояний (distance-vector) . Термин вектор расстояний означает, что сообщения, посылаемые RIP, содержат вектор расстояний (счетчик пересылок). Каждый маршрутизатор обновляет свою таблицу маршрутизации на основании векторов расстояний, который он получает от своих соседей.

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

С практической точки зрения основное отличие заключается в том, что протокол состояния канала работает значительно быстрее, чем протокол вектора расстояний. Нужно отметить, что в случае протокола состояния канала значительно быстрее осуществляется сходимость сети. Под понятием сходимости (converge) мы подразумеваем стабилизацию сети после каких-либо изменений, как, например, поломки маршрутизатора или выхода из строя канала. В разделе 9.3 [Perlman 1992] сравниваются между собой два типа протоколов маршрутизации.

OSPF также отличается от RIP (как и многие другие протоколы маршрутизации) тем, что OSPF использует непосредственно IP. Это означает, что он не использует UDP или TCP. OSPF имеет собственную величину, которая устанавливается в поле протокола (protocol) в IP заголовке (рисунок 3.1).

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

  1. OSPF может рассчитать отдельный набор маршрутизаторов для каждого типа сервиса IP (type-of-service) (рисунок 3.2). Это означает, что для любого пункта назначения может быть несколько пунктов в таблице маршрутизации, по одному для каждого типа сервиса IP.
  2. Каждому интерфейсу назначается цена. Она может быть назначена на основании пропускной способности, времени возврата, надежности или по какому-либо другому параметру. Отдельная цена может быть назначена для каждого типа сервиса IP.
  3. Если существует несколько маршрутов к одному пункту назначения с одинаковой ценой, OSPF распределяет траффик (поток данных) поровну между этими маршрутами. Это называется балансом загруженности.
  4. OSPF поддерживает подсети: маска подсети соответствует каждому объявленному маршруту. Это позволяет разбить IP адрес любого класса на несколько подсетей различного размера. (Мы показали это в примере в разделе "Пример подсети" главы 3 и назвали подсетями переменной длины.) Маршруты к хостам объявляются с маской подсети, из всех единичных бит. Маршрут по умолчанию объявляется как IP адрес 0.0.0.0 с маской из всех нулевых битов.
  5. Каналы точка-точка между маршрутизаторами не имеют IP адресов на каждом конце. Это называется сетями без адреса (unnumbered). Такой подход позволяет сэкономить IP адреса - очень ценный ресурс в настоящее время!
  6. Используется простая схема аутентификации. Может быть указан пароль в виде открытого текста, так же как это делается в схеме RIP-2 (раздел "RIP Version 2").
  7. OSPF использует групповую адресацию (глава 12) вместо широковещательной, что уменьшает загруженность систем, которые не распознают OSPF.

Так как большинство поставщиков маршрутизаторов поддерживают OSPF, он начинает постепенно замещать собой RIP в большинстве сетей.

BGP: протокол граничных маршрутизаторов (Border Gateway Protocol)

BGP это протокол внешних маршрутизаторов, предназначенный для связи между маршрутизаторами в различных автономных системах. BGP заменяет собой старый EGP, который использовался в ARPANET. BGP Version 3 определен в RFC 1267 [Lougheed and Rekhter 1991].

RFC 1268 [Rekhter and Gross 1991] описывает использование BGP в Internet. Практически все, что приведенного ниже, взято именно из этих двух RFC. В дополнение, необходимо отметить, что в 1993 году BGP Version 4 разрабатывался таким образом (RFC 1467 [Topolcic 1993]), чтобы поддерживать CIDR, который мы опишем в разделе "CIDR: бесклассовая маршрутизация между доменами" этой главы.

Системы, поддерживающие BGP, обмениваются информацией о доступности сети с другими BGP системами. Эта информация включает в себя полный путь по автономным системам, по которым должен пройти траффик (поток данных), чтобы достичь этих сетей. Эта информация адекватна построению графа соединений AS (автономных систем). При этом возникает возможность легко обходить петли маршрутизации, а также упрощается процесс принятия решений о маршрутизации.

Во-первых, необходимо сказать, что IP датаграмма в AS может принадлежать как к локальному траффику, так и к транзитному траффику. Локальный - это траффик у которого источник и пункт назначения находятся в одной AS. При этом IP адреса источника и назначения указывает на хосты, принадлежащие одной автономной системе. Весь остальной траффик называется транзитным. Основное преимущество использования BGP в Internet заключается в уменьшении транзитного траффика.

Автономная система может принадлежать к следующим категориям:

  1. Ограниченная (stub) AS автономная система имеет единственное подключение к одной внешней автономной системе. В такой автономной системе присутствует только локальный траффик.
  2. Многоинтерфейсная (multihomed) AS имеет подсоединение к нескольким удаленным автономным системам, однако по ней запрещено прохождение транзитного траффика.
  3. Транзитная (transit) AS имеет подключение к нескольким автономным системам и в соответствии с ограничениями может пропускать через себя как локальный, так и транзитный траффик.

Общая топология Internet состоит из транзитных, многоинтерфейсных и ограниченных автономных систем. Ограниченные и многоинтерфейсные автономные системы не нуждаются в использовании BGP - они могут использовать EGP, чтобы обмениваться информацией о доступности с транзитными автономными системами.

BGP позволяет использовать маршрутизацию, основанную на политических решениях (policy-based routing). Все правила определяются администратором автономной системы и указываются в конфигурационных файлах BGP. "Политические решения" не являются частью протокола, однако позволяют делать выбор между маршрутами, когда существует несколько альтернативных, и позволяют управлять перераспределением информации. Решения принимаются в соответствии с вопросами безопасности или экономической целесообразности.

BGP отличается от RIP или OSPF тем, что BGP использует TCP в качестве транспортного протокола. Две системы, использующие BGP, устанавливают TCP соединения между собой и затем обмениваются полными таблицами маршрутизации BGP. Обновления представляются в виде изменений таблицы маршрутизации (таблица не передается целиком).

BGP это протокол вектора расстояний, однако, в отличие от RIP (который объявляет пересылки к пункту назначения), BGP перечисляет маршруты к каждому пункту назначения (последовательность номеров автономных систем к пункту назначения). При этом исчезают некоторые проблемы, связанные с использованием протоколов вектора расстояний. Каждая автономная система идентифицируется 16-битным номером.

BGP определяет выход из строя канала или хоста на другом конце TCP соединения путем регулярной отправки сообщения "оставайся в живых" (keepalive) своим соседям. Рекомендуемое время между этими сообщениями составляет 30 секунд. Сообщение "оставайся в живых", которое используется на уровне приложений, независимо от TCP опций "оставайся в живых" (глава 23).

CIDR: бесклассовая маршрутизация между доменами (Classless Interdomain Routing)

В главе 3 было сказано, что в настоящее время ощущается нехватка адресов класса В, поэтому узлам с несколькими сетями приходится присваивать несколько идентификаторов сетей класса С вместо одного идентификатора сети класса В. С одной стороны, появление адресов класса С решает одну проблему (переполнение количества адресов класса В). С другой стороны, появляется еще одна проблема: каждая сеть класса С требует записи в таблице маршрутизации. Бесклассовая маршрутизация между доменами (CIDR - Classless Interdomain Routing) это способ, который позволяет свести к минимуму рост таблиц маршрутизации в Internet. Этот способ также называется supernetting и описывается в RFC 1518 [Rekhter and Li 1993] и в RFC 1519 [Fuller et al. 1993], обзор можно найти в [Ford, Rekhter and Braun 1993]. CIDR одобрен Internet Architecture Board [Huitema 1993]. RFC 1467 [Topolcic 1993] кратко описывает состояние и развитие CIDR в Internet.

Основная концепция, заложенная в CIDR, заключается в том, что несколько IP адресов можно расположить определенным образом, что позволит уменьшить количество записей в таблице маршрутизации. Например, если один узел состоит из 16 адресов класса С, все 16 могут быть расположены таким образом, что они будут суммироваться, поэтому ко всем 16-ти можно будет обратиться через одну запись в таблице маршрутизации. Также, если 8 различных узлов подключены к одному и тому же Internetовскому "поставщику сервиса" через одну и ту же точку подключения к Internet, и если все 8 узлов имеют 8 различных IP адресов, они могут быть суммированы, после чего потребуется только одна запись в таблице маршрутизации, которая будет использоваться в Internet для всех 8 узлов.

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

  1. Несколько IP адресов, которые будут сложены вместе для маршрутизации, должны иметь в своих адресах одинаковые биты старшего порядка.
  2. Таблицы маршрутизации и алгоритмы маршрутизации должны быть расширены таким образом, чтобы решения о маршрутизации принимались с использованием 32-битных IP адресов и 32-битных масок.
  3. Протоколы маршрутизации, которые будут использоваться, должны быть расширены таким образом, чтобы позволять передачу 32-битных масок и 32-битных адресов. OSPF (раздел "OSPF: открыть первым наикратчайший маршрут") и RIP-2 (раздел "RIP Version 2") способны передавать 32-битные маски, так же как и BGP Version 4.

В качестве примера скажем, что RFC 1466 [Gerich 1993] рекомендует, чтобы новые адреса класса С в Европе находились в диапазоне 194.0.0.0 - 195.255.255.255. В шестнадцатиричном представлении эти адреса лежат в диапазоне 0xc2000000 - 0xc3ffffff. Это позволяет использовать 65536 различных идентификаторов сети класса С, однако все они имеют одинаковые 7 бит старшего порядка. В других (неевропейских) странах может быть использована единственная запись в таблице маршрутизации с IP адресом 0xc2000000 и 32-битной маской 0xfe000000 (254.0.0.0), чтобы организовать маршрут ко всем этим идентификаторам сетей класса С (в количестве 65536) через одну точку. Последовательность битов в адресе класса С (это биты, следующие за 194 или 195) может быть расположена в иерархическом порядке, например, по странам или по поставщикам сервиса, чтобы тем самым позволить дополнительное сложение внутри европейских маршрутизаторов с использованием дополнительных битов, стоящих после 7 бит старшего порядка в 32-битной маске.

CIDR также использует технику, которая определяет, что "лучшее совпадение это всегда наиболее длинное совпадение (longest match)": то есть совпадение максимального количества битов в 32-битной маске. Продолжая пример из предыдущего параграфа, нужно отметить следующее. Предположим, что один поставщик сервиса в Европе нуждается в использовании другого маршрутизатора для входа в Internet, чем вся остальная Европа. Если этот поставщик сервиса находится в диапазоне адресов 194.0.16.0 - 194.0.31.255 (16 идентификаторов сетей класса С), записи в таблице маршрутизации для этих сетей должны иметь IP адрес 194.0.16.0 и маску 255.255.240.0 (0xfffff000). Датаграмма, которая маршрутизируется на адрес 194.0.22.1, будет совпадать с этим пунктом таблицы маршрутизации и с одной из сетей класса С в остальной Европе. Однако, так как маска 255.255.240 "длиннее" чем маска 254.0.0.0, будет использован пункт в таблице маршрутизации, который имеет самую длинную маску.

Термин "бесклассовый" используется потому, что решения о маршрутизации принимаются на основе масок, накладываемых на полный 32-битный IP адрес. При этом не существует различия между адресами класса А, В или С.

Первоначально предполагается использовать CIDR в адресах новых сетей класса С. Благодаря внесению подобного изменения, значительно уменьшится рост таблиц маршрутизации в Internet, при этом не потребуется вносить никаких изменений в существующие маршрутизаторы. Хотя надо отметить, что подобное решение является кратковременной мерой. Более значительное улучшение можно получить, если CIDR будет использоваться для всех IP адресов, и существующие IP адреса будут переназначены (при этом все существующие хосты получат новые адреса!) в соответствии с ограничениями по странам, континентам и поставщикам сервисов [Ford, Rekhter and Braun 1993]. Выигрыш будет заключаться в следующем: современная таблица маршрутизации содержит до 10000 записей, тогда как после применения CIDR количество записей уменьшится до 200.

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

Существуют два основных типа протоколов маршрутизации: протоколы внутренних маршрутизаторов (IGP - interior gateway protocol), для маршрутизаторов, находящихся внутри автономной системы (autonomous system), и протоколы внешних маршрутизаторов (EGP - exterior gateway protocol), для маршрутизаторов, которые общаются с маршрутизаторами в других автономных системах.

Наиболее популярный IGP это Routing Information Protocol (RIP), однако постепенно новый IGP - OSPF становится более популярным, и все чаще и чаще начинает заменять собой RIP. Новый и популярный EGP это Border Gateway Protocol (BGP). В этой главе мы рассмотрели RIP и типы сообщений, которые он использует. RIP Version 2 это современное расширение, которое поддерживает разбиение на подсети и другие важные улучшения. Также мы описали OSPF, BGP и бесклассовую маршрутизацию между доменами (CIDR), новую технологию, которая призвана уменьшить размер таблиц маршрутизаций в Internet.

Существует еще два OSI протокола маршрутизации, на которые Вам следует обратить внимание. Протокол междоменной маршрутизации (IDRP - Interdomain Routing Protocol) появился как версия BGP, модифицированная для использования с OSI адресами вместо IP. Протокол промежуточная система - промежуточная система (IS-IS - Intermediate System to Intermediate System) это OSI стандарт IGP. Он используется как протокол маршрутизации в сетях без соединения (CLNP - Connectionless Network Protocol), OSI протокол, похожий на IP. IS-IS и OSPF очень похожи.

Динамическая маршрутизация до сих пор остается плодотворной почвой для исследований в области межсетевого взаимодействия. Поэтому выбрать, какой протокол маршрутизации необходимо использовать и какой демон маршрутизации необходимо запустить, довольно сложно. [Perlman 1992] предоставляет множество подробностей.

Упражнения

  1. Обратитесь к рисунку 10.9. По какому маршруту можно пройти от маршрутизатора kpno к маршрутизатору gateway?
  2. Представьте, что маршрутизатор имеет 30 маршрутов, которые необходимо объявить с использованием RIP, при этом требуется одна датаграмма на 25 маршрутов и другая на оставшиеся 5. Что произойдет, если один раз в час первая датаграмма с 25-ю маршрутами будет потеряна?
  3. В пакете OSPF имеется поле контрольной суммы, а в пакете RIP - нет. Почему?
  4. Какой эффект дает балансировка загруженности, которая используется в OSPF. Как это влияет на транспортный уровень?
  5. Прочитайте RFC 1058, откуда Вы узнаете дополнительные подробности того как реализуется RIP. На рисунке 10.8 каждый маршрутизатор объявляет только те маршруты, которые он предоставляет, и не объявляет другие маршруты, про которые он знает из широковещательных сообщений от других маршрутизаторов в сети 140.252.1. Как называется подобная технология?
  6. В разделе "Адресация подсетей" главы 3 мы сказали, что в подсети 140.252.1 существует более чем 100 хостов в дополнение к 8 маршрутизаторам, которые мы показали на рисунке 10.7. Что делают эти 100 хостов с восемью широковещательными сообщениями, которые прибывают каждые 30 секунд (рисунок 10.8)?


UDP: User Datagram Protocol

Глава 11 UDP: User Datagram Protocol

Введение

UDP простой, ориентированный на передачу датаграмм, протокол транспортного уровня: за один раз процесс выдает одну UDP датаграмму, в результате чего передается одна IP датаграмма. Это отличается от поток-ориентированных протоколов, таких как TCP, где количество данных, которое выдается приложением, практически не имеет отношения к количеству отправленных IP датаграмм.

На рисунке 11.1 показана инкапсуляция UDP датаграммы в IP датаграмму.

Рисунок 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 заголовке.

Рисунок 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 датаграмма.

Рисунок 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.720114 (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 0

Рисунок 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
2  21.008303 (21.0083)        bsdi.1114 > svr4.discard: udp 1472

3  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 датаграммы.

Рисунок 11.8 Пример UDP фрагментации.

ICMP ошибки о недоступности (требуется фрагментация)

Необходимо обсудить еще одну ICMP ошибку о недоступности. Она генерируется, когда маршрутизатор принимает датаграмму, которую необходимо фрагментировать, однако в IP заголовке установлен флаг "не фрагментировать" (DF). Эта ошибка может быть использована программой, которой необходимо определить минимальный MTU в маршруте до пункта назначения - что называется механизмом определения транспортного MTU (path MTU discovery) (глава 2, раздел "Транспортный MTU").

На рисунке 11.9 показан формат ICMP ошибки о недоступности для данного случая. Он отличается от формата, приведенного на рисунке 6.10, потому что биты 16-31 во втором 32-битном слове могут содержать MTU следующей пересылки, вместо того чтобы быть установленными в 0.

Рисунок 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.

Рисунок 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)
2  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 показан путь прохождения пакетов.

Рисунок 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 каналов.

Рисунок 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)
2  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)
2  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

Рисунок 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. Правильно ли это?


Широковещательная и групповая адресация

Глава 12 Широковещательная и групповая адресация

Введение

В главе 1 мы упомянули, что существуют три типа IP адресов: персональный (unicast), широковещательный (broadcast) и групповой (multicast) . В этой главе мы обсудим широковещательные и групповые адреса более подробно.

Широковещательные и групповые запросы применимы только к UDP, подобные типы запросов позволяют приложению послать одно сообщение нескольким получателям. TCP - протокол, ориентированный на соединение, с его помощью устанавливается соединение между двумя хостами (по указанному IP адресу) с использованием одного процесса на каждом хосте (который идентифицируется по номеру порта).

Представьте себе несколько хостов в Ethernet сети. Каждый Ethernet фрейм содержит Ethernet адрес источника и назначения (48 бит). И обычно каждый фрейм предназначается одному получателю. Адрес назначения, указывающий на один интерфейс, - называется персональным (unicast). Остальные хосты, присутствующие на кабеле, не участвуют в общении между двумя хостами (если не учитывать то, что все хосты находятся все-таки на одном кабеле).

Однако иногда возникает необходимость послать фрейм всем хостам, находящимся на кабеле, - это называется широковещательной рассылкой (broadcast). Мы видели это, когда рассматривали ARP и RARP. Групповая адресация логически находится между персональной и широковещательной: фрейм должен быть доставлен определенному количеству хостов, которые принадлежат к группе.

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

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

Рисунок 12.1 Фильтрация, которая осуществляется стеком протоколов, когда принимается фрейм.

В настоящее время большинство интерфейсов могут быть сконфигурированы таким образом, чтобы принимать фреймы, IP адрес которых является групповым адресом или групповым адресом какой-либо подгруппы. В групповом адресе Ethernet младший бит старшего байта установлен в единицу. В шестнадцатиричном представлении этот бит выглядит следующим образом: 01:00:00:00:00:00. (Мы можем считать, что широковещательный адрес Ethernet ff:ff:ff:ff:ff:ff это особый случай группового адреса Ethernet.)

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

Затем драйвер устройства передает фрейм следующему уровню, например, IP, если фрейм является IP датаграммой. IP также осуществляет фильтрацию, основанную на проверке IP адресов источника и назначения, и, в свою очередь, передает датаграмму следующему уровню (например TCP или UDP, если все в порядке).

Каждый раз когда UDP получает датаграмму от IP, он осуществляет фильтрацию, основанную на номере порта назначения, а иногда и на номере порта источника. Если указанный порт не обслуживается в текущий момент каким-либо процессом, датаграмма отбрасывается, и генерируется ICMP сообщение о недоступности порта. (TCP осуществляет подобную фильтрацию, основанную на своих номерах портов.) Если в UDP датаграмме обнаружена ошибка контрольной суммы, UDP молча ее отбрасывает.

Проблема широковещательных запросов заключается в том, что хосты, которые совсем не заинтересованы в получении этих запросов, должны их обрабатывать. Представьте себе приложение, которое должно обрабатывать широковещательные запросы UDP. Если на кабеле находится 50 хостов, но только из них 20 участвуют в работе этого приложения, в каждый момент времени один из 20 посылает широковещательный запрос UDP, при этом остальные 30 хостов должны обработать этот запрос, который проходит весь путь по стеку протоколов до UDP уровня, прежде чем датаграмма будет отброшена. UDP датаграмма отбрасывается этими 30-ю хостами, потому что порта назначения не обслуживается каким-либо процессом.

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

Широковещательные запросы

На рисунке 3.9 показаны четыре различные формы широковещательных адресов IP. Сейчас мы опишем их более подробно.

Ограниченный широковещательный запрос

Ограниченный широковещательный адрес (limited broadcast address) - это адрес 255.255.255.255. Он может быть использован в качестве адреса назначения для IP датаграмм в процессе конфигурации хоста, когда хост может не знать собственной маски подсети и даже своего IP адреса.

Датаграмма, направляющаяся на ограниченный широковещательный адрес, никогда (ни при каких условиях) не будет перенаправлена маршрутизатором. Она может существовать только на локальном кабеле.

Но тут возникает вопрос, на который практически невозможно дать ответ: если хост имеет несколько интерфейсов и процесс посылает датаграмму с ограниченным широковещательным адресом, должна ли датаграмма быть отправлена на каждый подсоединенный интерфейс, который поддерживает широковещательную адресацию? Если нет, то приложение, которое хочет разослать широковещательный запрос на все интерфейсы, должно само определить все интерфейсы хоста, которые поддерживают широковещательную адресацию, и послать копию на каждый интерфейс.

Большинство BSD систем воспринимают 255.255.255.255 как адрес широковещательного адреса первого интерфейса, который был сконфигурирован, и не позволяют послать датаграмму на все подключенные интерфейсы, поддерживающие широковещательную адресацию. И действительно, два приложения, которые посылают UDP датаграммы на каждый интерфейс, это routed (глава 10, раздел "Демоны маршрутизации в Unix") и rwhod (сервер BSD клиента rwho). Оба этих приложения проходят через похожую процедуру запуска, чтобы определить все интерфейсы хоста и те, которые поддерживают широковещательную адресацию. Широковещательный адрес сети, соответствующий этому интерфейсу затем используется как адрес назначения для датаграмм, которые посылаются в этот интерфейс.

Требования к хостам Host Requirements RFC не определяют, должен ли хост, имеющий несколько интерфейсов, посылать ограниченный broadcast на все свои интерфейсы.

Широковещательный запрос в сеть

В широковещательном адресе сети (net-directed broadcast) идентификатор хоста устанавливается во все единичные биты. Широковещательный адрес для сети класса А, имеет вид netid.255.255.255, где netid это идентификатор сети класса А.

Маршрутизаторы должны перенаправлять широковещательные запросы, направляемые в сеть, однако должна присутствовать опция, позволяющая отключить это перенаправление.

Широковещательный запрос в подсеть

Широковещательный адрес подсети (subnet-directed broadcast address) имеет идентификатор хоста, установленный в единицы, однако определенный идентификатор подсети. Для того, чтобы классифицировать адрес как широковещательный адрес подсети, необходимо знать маску подсети. Например, если маршрутизатор получил датаграмму, с адресом назначения 128.1.2.255, можно считать, что это широковещательный запрос, направленный в подсеть, если сеть класса В 128.1 имеет маску подсети 255.255.255.0, однако это не широковещательный запрос, если маска подсети 255.255.254.0 (0xfffffe00).

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

Широковещательный адрес всех подсетей (all-subnets-directed broadcast address), также требует знания маски подсети в сети назначения. Только в этом случае можно определить различие между широковещательным адресом всех подсетей и широковещательным адресом сети. И идентификатор подсети, и идентификатор хоста, в данном случае, устанавливаются в единицы. Например, если маска подсети назначения 255.255.255.0, то IP адрес 128.1.255.255 это широковещательный запрос, направленный во все подсети. Однако, если сеть не разбита на подсети, это широковещательный запрос, направляемый в сеть.

В настоящее время такой тип широковещательных запросов считается устаревшим [Almquist 1993]. В настоящее время используются групповые запросы, а не широковещательные запросы, направленные во все подсети.

[Almquist 1993] отмечает, что в RFC 922 требуется, чтобы широковещательные запросы, направленные во все подсети, посылались во все подсети, однако не все маршрутизаторы это поддерживают. И это хорошо, так как неверно сконфигурированый хост, без собственной маски подсети, будет посылать эти "локальные" широковещательные запросы во все подсети. Например, если хост с IP адресом 128.1.2.3 не имеет установленной маски подсети, его широковещательный адрес по умолчанию устанавливается в 128.1.255.255. Однако, если маска подсети должна быть определена как 255.255.255.0, то широковещательные запросы от неправильно сконфигурированного хоста появятся во всех подсетях.

Первая широкораспространенная реализация TCP/IP, в системе 4.2BSD (1983 год), использовала для широковещательных адресов идентификатор хоста, установленного во все нули. Один из самых ранних примеров обращения к широковещательным IP адресам это IEN 212 [Gurwitz and Hinden 1982], и именно здесь было определено использовать широковещательные IP адреса с идентификаторами хостов, установленными во все единицы. (IEN это Internet Experiment Notes, непосредственный предшественник RFC.) В RFC 894 [Hornig 1984] говорится, что 4.2BSD использует нестандартный широковещательный адрес, однако в RFC 906 [Finlayson 1984] замечается, что тогда не существовало стандарта Internet для широковещательных адресов. При редакции RFC была сделана сноска на RFC 906, где говорилось об отсутствии стандарта на широковещательные адреса, однако очень рекомендовалось использовать широковещательные адреса с идентификаторами хоста, установленными во все единицы. К тому же, Berkeley начал использовать все единичные биты для широковещательного адреса, начиная с 4.3BSD (1986 год), однако некоторые операционные системы (и что интересно, даже SunOS 4.x) продолжали использовать нестандартные широковещательные адреса до начала 90-х.

Примеры широковещательных запросов

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

Во-первых, приложение должно поддерживать широковещательные запросы. Если мы запустим

sun % ping 255.255.255.255
/usr/etc/ping: unknown host 255.255.255.255

то есть постараемся послать запрос на локальный кабель, это не сработает. Однако проблема здесь заключена в самом приложении (ping). Большинство приложений, которые воспринимают как цифровые IP адреса (в десятичном выражении), так и имена хостов, вызывают функцию inet_addr (3), чтобы конвертировать строку чисел в 32-битный двоичный IP адрес, и если это не удалось, воспринимают строку символов как имя хоста. Эта библиотечная функция возвращает код ошибки -1 если в строке обнаружен символ, отличный от цифры или десятичной точки, в случае ограниченного широковещательного адреса (255.255.255.255) также выдается код -1. Большинство программ, которые воспринимают символьную строку как имя хоста, обрабатывают его с использованием DNS (глава 14) и ограничиваются выводом ошибки, такой как "неизвестный хост" (unknown host).

Даже если мы исправим эту ошибку в программе ping, результаты будут все равно не такими как хотелось бы. Из шести протестированных систем, только одна генерировала широковещательные пакеты в локальный кабель. Большинство ищут IP адрес 255.255.255.255 в таблице маршрутизации, в конце концов находят маршрут по умолчанию и посылают персональный пакет на маршрутизатор по умолчанию. Естественно, такой пакет уничтожается.

В подобном случае необходимо использовать широковещательные запросы, направленные в подсеть. И действительно, в разделе "ICMP запрос и отклик маски адреса" главы 6 мы посылали датаграммы на IP адрес 140.252.13.63 для нижнего Ethernet в нашей тестовой сети и получали отклики от всех хостов Ethernet. Широковещательный адрес, направленный в подсеть, соответствует каждому интерфейсу, именно этот адрес используется командой ifconfig (глава 3, раздел "Команда ifconfig"). Если мы пошлем ping на этот адрес, результат будет таким, как мы хотели:

sun % arp -a                        ARP кэш пуст

sun % ping 140.252.13.63
PING 140.252.13.63: 56 data bytes
64 bytes from sun (140.252.13.33): icmp_seq=0. time=4. ms
64 bytes from bsdi (140.252.13.35): icmp_seq=0. time=172. ms
64 bytes from svr4 (140.252.13.34): icmp_seq=0. time=192. ms

64 bytes from sun (140.252.13.33): icmp_seq=1. time=1. ms
64 bytes from bsdi (140.252.13.35): icmp_seq=1. time=52. ms
64 bytes from svr4 (140.252.13.34): icmp_seq=1. time=90. ms

^?                                  введен символ прерывани
----140.252.13.63 PING Statistics----
2 packets transmitted, 6 packets received, -200% packet loss
round-trip (ms) min/avg/max = 1/85/192

sun % arp -a                        снова проверяем ARP кэш
svr4 (140.252.13.34) at 0:0:c0:c2:9b:26
bsdi (140.252.13.35) at 0:0:c0:6f:2d:40

IP определяет, что адрес назначения (140.252.13.63) - это широковещательный адрес, направленный в подсеть, и посылает датаграммы на широковещательный адрес канального уровня.

Мы упоминали в разделе "ICMP запрос и отклик маски адреса" главы 6, что этот тип широковещательных запросов означает обращение ко всем хостам локальной сети, включая отправителя. Из примера видно, что мы получили отклик от отправляющего хоста sun и от всех остальных хостов, находящихся на кабеле.

В этом примере показан ARP кэш перед и после того, как был запущен ping на широковещательный адрес. Это сделано для того, чтобы показать взаимосвязь между рассылкой широковещательных запросов и состоянием ARP. ARP кэш пуст перед запуском ping, и заполнен по окончании. (В кэше находится по одной записи на каждый хост, находящийся на кабеле и ответивший на эхо запрос.) Как заполнился кэш, раз мы сказали, что Ethernet фрейм посылается на широковещательный адрес канального уровня 0xffffffff? Отправка этих фреймов хостом sun не требует ARP.

Если мы посмотрим работу ping с использованием tcpdump, то увидим, что получатели широковещательных фреймов генерируют ARP запрос на sun, перед тем как отправить отклик. Причем, надо отметить, что каждый отклик персональный. Мы говорили в разделе "Примеры ARP" главы 4, что получатель ARP запроса (sun в данном примере) обычно добавляет IP адрес и аппаратный адрес запрашивающего в свой ARP кэш, и отправляет ARP отклик. Это делается из предположения, что если запрашивающий послал нам пакет, мы, возможно, в будущем захотим послать что-нибудь и ему.

Использование ping - это особый случай, потому что подобный тип программного интерфейса, (который в большинстве Unix реализаций называется "символьный сокет" (raw socket)), всегда позволяет послать датаграмму на широковещательный адрес. Что если мы воспользуемся приложением, которое не поддерживает широковещательные запросы, как, например, TFTP? (Мы опишем TFTP более подробно в главе 15.)

bsdi % tftp                          запускаем клиента
tftp> connect 140.252.13.63          указываем IP адрес сервера
tftp> get temp.foo                   и пытаемся получить файл с сервера
tftp: sendto: Permission denied
tftp> quit                           прекращение работы клиента

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

Для сокет API, приложение должно установить опцию сокет SO_BROADCAST перед отправкой UDP датаграммы на широковещательный адрес. Однако, не все системы применяют это ограничение. Некоторые реализации позволяют любому процессу отправлять UDP датаграммы широковещательным образом, причем не требуют от процесса, чтобы он объявлял об этом. Другие имеют более строгие ограничения и требуют, чтобы процесс имел привилегии суперпользователя, чтобы использовать широковещательную рассылку.

Следующий вопрос заключается в том, перенаправляются ли широковещательные пакеты. Некоторые ядра и маршрутизаторы имеют опцию, которая позволяет включить или выключить эту характеристику. (См. приложение Е.)

Если мы включим характеристику перенаправления широковещательных пакетов на маршрутизаторе bsdi и запустим ping с хоста slip, то увидим, что bsdi перенаправляет широковещательные запросы, направленные в подсеть. Перенаправление направленных широковещательных запросов означает, что маршрутизатор воспринимает входящие датаграммы как персональные, определяет, что адрес назначения это направленный широковещательный адрес одного из его интерфейсов, и затем перенаправляет датаграммы в соответствующую сеть, используя широковещательный адрес канального уровня.

slip % ping 140.252.13.63
PING 140.252.13.63 (140.252.13.63): 56 data bytes
64 bytes from 140.252.13.35: icmp_seq=0 ttl=255 time=190 ms
64 bytes from 140.252.13.33: icmp_seq=0 ttl=254 time=280 ms (DUP!)
64 bytes from 140.252.13.34: icmp_seq=0 ttl=254 time=360 ms (DUP!)

64 bytes from 140.252.13.35: icmp_seq=1 ttl=255 time=190 ms
64 bytes from 140.252.13.33: icmp_seq=1 ttl=254 time=270 ms (DUP!)
64 bytes from 140.252.13.34: icmp_seq=1 ttl=254 time=360 ms (DUP!)

^?                                        введен символ прерывания
--- 140.252.13.63 ping statistics ---
3 packets transmitted, 2 packets received, +4 duplicates, 33% packet loss
round-trip min/avg/max = 180/273/360 ms

Мы видим, что это работает. Также мы видим, что программа ping в BSD проверяет отслеживает повторные номера последовательности и помечает их как DUP!. Это обычно означает, что пакет был каким-либо образом продублирован, однако здесь именно это и должно было произойти, так как запросы были отправлены на широковещательный адрес.

Этот тест можно запустить с более удаленного хоста (от той сети, к которой направляется широковещательный запрос). Мы запустили ping с хоста vangogh.cs.berkeley.edu (который находится на расстоянии 14 пересылок от нашей сети), и это также будет работать, если маршрутизатор sun сконфигурирован таким образом, чтобы перенаправлять направленные широковещательные запросы. В данном случае IP датаграммы (переносящие ICMP эхо запросы) перенаправляются каждым маршрутизатором, встречающимся им на пути, как обычные датаграммы. Никто из них не знает, что в действительности это направленные широковещательные запросы. И последний маршрутизатор, netb, думает, что пакеты предназначены хосту с идентификатором равным 63, и перенаправляет их на sun. В этом месте sun определяет, что IP адрес назначения в действительности это широковещательный адрес подключенного интерфейса, и передает датаграммы в виде широковещательных запросов канального уровня для этой сети.

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

Рассылка групповых сообщений

Рассылка групповых сообщений IP предоставляет приложениям два сервиса.

  1. Доставка к нескольким пунктам назначения. Существует множество приложений, которые доставляют информацию нескольким адресатам, например, диалоговые конференции, распространение почты или новостей. Если групповая адресация не используется, эти типы сервисов вынуждены использовать TCP (при этом осуществляется доставка отдельной копии на каждый пункт назначения). Даже при существовании групповой формы сообщений, некоторые приложения все-таки используют TCP из-за его надежности.
  2. Запрос от клиента к серверу. Например, бездисковая рабочая станция старается определить положение сервера загрузки. В настоящее время это осуществляется с использованием широковещательных запросов (как мы увидим в случае с BOOTP в главе 16), однако решение с использованием групповых запросов может уменьшить загруженность хостов, которые не предоставляют этот сервис.

В этом разделе мы рассмотрим групповые адреса, а в следующей главе рассмотрим протоколы, которые используют групповую адресацию хостов и маршрутизаторов (IGMP).

Адреса групп

На рисунке 12.2 показан формат IP адреса класса D.

Рисунок 12.2 Формат IP адреса класса D.

В отличие от трех других классов IP адресов (A,B и C), форматы которых приведены на рисунке 1.5, 28 бит, отведенные под групповой идентификатор, не подвергаются дальнейшему делению.

Групповой адрес (multicast group address) состоит из четырех старших бит, установленных в 1110, и идентификатора группы. В десятичном виде групповые адреса находятся в диапазоне от 224.0.0.0 до 239.255.255.255.

Некоторое количество хостов, просматривающих определенный групповой IP адрес, называется группой хостов (host group). Группа хостов может объединять хосты в разных сетях. Членство в группе динамическое - хост может вступать в группу и выходить из группы по собственному желанию. Не существует ограничений на количество хостов в группе, и хост не должен принадлежать к группе, чтобы послать сообщение в эту группу.

Некоторые адреса групп назначаются как заранее известные адреса от IANA (Internet Assigned Numbers Authority). В этом случае группа считается постоянной группой хостов (permanent host group). Заранее известные групповые адреса приведены в последних RFC назначенных номеров (Assigned Numbers RFC). Обратите внимание на то, что постоянным в данном случае является групповой адрес, а не членство в группе.

Например, 224.0.0.1 означает "все системы в этой подсети", а 224.0.0.2 означает "все маршрутизаторы в этой подсети". Групповой адрес 224.0.1.1 предназначен для сетевого протокола времени (NTP - Network Time Protocol), 224.0.0.9 для RIP-2 (глава 10, раздел "RIP Version 2") и 224.0.1.2 для SGI (Silicon Graphics) dogfight приложений.

Преобразование групповых адресов в адреса Ethernet

IANA владеет блоком Ethernet адресов, которые в шестнадцатиричном представлении выглядят как 00:00:5e. Это старшие 24 бита Ethernet адреса, означающие, что блок включает адреса в диапазоне от 00:00:5e:00:00:00 до 00:00:5e:ff:ff:ff. IANA отвела половину этого блока для групповых адресов. Установлено правило, что первый байт Ethernet адреса равный 01 указывает на групповой адрес. Это означает, что Ethernet адреса, соответствующие групповым адресам IP, должны находиться в диапазоне от 01:00:5e:00:00:00 до 01:00:5e:7f:ff:ff.

Приведенные здесь выражения используют стандартную последовательность битов для Internet, для сетей CSMA/CD или Token bus, а именно такую, как биты располагаются в памяти. Это как раз то, с чем сталкивается большинство программистов и системных администраторов. IEEE документация использует порядок бит, который используется при передаче. Assigned Numbers RFC предоставляет дополнительные подробности о различиях между этими представлениями.

Подобное расположение позволяет 23 битам в Ethernet адресе соответствовать идентификатору группы IP. В процессе преобразования адресов 23 младших бита идентификатора группы помещаются в 23 бита Ethernet адреса. (См. рисунок 12.3.)

Старшие 5 бит в идентификаторе группы игнорируются, так как они не уникальны. Каждому Ethernet адресу соответствует 32 различных идентификатора группы. Например, групповой адрес 224.128.64.32 (в шестнадцатиричном представлении e0.80.40.20) и 224.0.64.32 (в шестнадцатиричном представлении e0.00.40.20) оба будут трансформированы в Ethernet адрес 01:00:5e:00:40:20.

Так как подобное сопоставление не уникально, предполагается, что драйвер устройства или IP модуль на рисунке 12.1 должен осуществить фильтрацию, так как сетевая плата может получить групповой фрейм, который хосту не предназначен. Если сетевая плата не осуществляет адекватную фильтрацию групповых фреймов, драйвер устройства, вполне возможно, должен будет получать все групповые фреймы и сам осуществлять фильтрацию.

Рисунок 12.3 Соответствие между IP адресами класса D и групповыми адресами Ethernet.

Существует два варианта реализации групповой адресации в сетевых платах, использующиеся в локальных сетях. Одни осуществляют групповую фильтрацию, основанную на значении аппаратного группового адреса, что означает, что некоторые нежелательные фреймы могут пройти. В другом случае имеется небольшое фиксированное количество групповых адресов, принимаемых платой, при этом, если хосту необходимо принять больше групповых адресов, чем поддерживается, интерфейс должен быть помещен в режим "разных групп" (multicast promiscuous). Однако, оба типа интерфейсов все еще требуют, чтобы драйвер устройства осуществлял проверку на предмет того, необходимо ли дальше обрабатывать принятый фрейм. Даже если интерфейс осуществляет идеальную групповую фильтрацию (основанную на 48-битном аппаратном адресе) фильтрация все еще необходима, так как сопоставление IP адресов класса D и 48-битных аппаратных адресов осуществляется не один к одному. Однако, если абстрагироваться от несовершенства преобразования адресов и аппаратной фильтрации, групповая адресация все же лучше, чем широковещательная.

Осуществить групповой запрос в единственную физическую сеть довольно просто. Отправляющий процесс указывает IP адрес назначения, который является групповым адресом, драйвер устройства конвертирует это в соответствующий Ethernet адрес и отправляет. Принимающие процессы должены указать своим IP модулям, что они хочет получать датаграммы, предназначенные определенному групповому адресу, и драйвера устройств должен каким-либо образом получать эти групповые фреймы. Все это называется "вступлением в группу". (Причина, по которой мы использовали выражение "принимающие процессы" во множественном числе, объясняется тем, что обычно существует несколько получателей, которым предназначено групповое сообщение, либо на одном, либо на разных хостах.) Когда групповая датаграмма получена хостом, копия доставляется всем процессам, которые принадлежат к группе. Это отличается от UDP, где единственный процесс получает входящую персональную UDP датаграмму. Несколько процессов на одном хосте могут принадлежать к одной группе.

Однако сложности растут как снежный ком, когда группа распространяется на несколько сетей, и групповые пакеты должны проходить через маршрутизаторы. Маршрутизаторам необходимо знать, принадлежат ли какие-либо хосты в данной физической сети к определенной группе. Для определения этого, существует протокол, называемый протоколом группового управления Internet (IGMP - Internet Group Management Protocol). Этому протоколу полностью посвящена следующая глава.

Широковещательные запросы в сетях FDDI и Token Ring

Сети FDDI используют ту же схему преобразования между IP адресами класса D и 48-битными FDDI адресами [Katz 1990]. Сети Token ring обычно используют отличную систему сопоставления из-за ограничений, которые накладываются на большинство управляющих устройств Token ring [Pusateri 1993].

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

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

Существуют четыре типа широковещательных адресов: ограниченный, широковещательный адрес сети, широковещательный адрес подсети и широковещательный адрес всех подсетей. Наиболее распространенный - широковещательный адрес подсети. Ограниченный широковещательный адрес обычно используется системой только в случае загрузки из сети.

Проблемы возникают, когда делается попытка послать широковещательный запрос через маршрутизаторы, чаще всего из-за того, что маршрутизатор не знает маску подсети в сети назначения. Результат зависит от того, какого типа широковещательный адрес, от параметров конфигурации и так далее.

IP адреса класса D называются групповыми адресами. Они преобразовываются в адреса Ethernet путем помещения их 23 младших битов в фиксированный Ethernet адрес. Подобное сопоставление не является уникальным, поэтому требуется дополнительная фильтрация одним из протоколов.

Упражнения

  1. Увеличивается ли сетевой траффик (загрузка сети) в случае широковещательных запросов?
  2. Представьте 50 хостов в Ethernet: 20 запускают TCP/IP, а 30 используют какое-либо другое семейство протоколов. Как широковещательные запросы от одного семейства протоколов обрабатываются хостами, использующими другое семейство протоколов?
  3. Вы зашли терминалом на Unix систему, с которой раньше никогда не работали, и хотите найти широковещательный адрес, направленный в подсеть, для всех подключенных интерфейсов, которые поддерживают широковещательные сообщения. Как это можно сделать?
  4. Если Вы запустили ping на широковещательный адрес с большим размером пакетов, как в примере:

    sun % ping 140.252.13.63 1472
    PING 140.252.13.63: 1472 data bytes
    1480 bytes from sun (140.252.13.33): icmp_seq=0. time=6. ms
    1480 bytes from svr4 (140.252.13.34): icmp_seq=0. time=84. ms
    1480 bytes from bsdi (140.252.13.35): icmp_seq=0. time=128. ms

    это работает, однако увеличение размера пакета на 1 байт приведет к следующей ошибке:

    sun % ping 140.252.13.63 1473
    PING 140.252.13.63: 1473 data bytes
    sendto: Message too long

    Что произошло?

  5. Повторите упражнение 6 главы 10, представив себе 8 RIP сообщений, направляемых с помощью групповой адресации вместо широковещательной (представьте, что используется RIP Version 2). Что изменится?


IGMP: протокол управления группами Internet (Internet Group Management Protocol)

Глава 13 IGMP: протокол управления группами Internet (Internet Group Management Protocol)

Введение

В разделе "Рассылка групповых сообщений" главы 12 приводится общее описание групповой адресации IP и описывается, как IP адреса класса D преобразуются в Ethernet адреса. Мы кратко упомянули, как осуществляется групповая рассылка в сети, состоящей из одного физического кабеля, однако отложили на потом рассмотрение вопроса, что происходит при групповой адресации в нескольких сетях и каким образом групповые датаграммы проходят через маршрутизаторы.

В этой главе мы рассмотрим протокол управления группами Internet (IGMP - Internet Group Management Protocol), который используется хостами и маршрутизаторами, для того чтобы поддерживать групповую рассылку сообщений. Он позволяет всем системам физической сети знать, какие хосты в настоящее время объединены в группы и к каким группам они принадлежат. Эта информация необходима для групповых маршрутизаторов, именно так они узнают, какие групповые датаграммы необходимо перенаправлять и на какие интерфейсы. IGMP определен в RFC 1112 [Deering 1989].

Как и ICMP, IGMP является частью IP уровня. Так же как ICMP, IGMP сообщения передаются в IP датаграммах. В отличие от других протоколов, которые мы уже рассмотрели, IGMP имеет сообщение фиксированного размера, без необязательных данных. На рисунке 13.1 показана инкапсуляция IGMP сообщения в IP датаграмму.

Рисунок 13.1 Инкапсуляция IGMP сообщения в IP датаграмму.

На то, что в IP датаграмме находится IGMP сообщение, указывает величина в поле протокола равная 2.

IGMP сообщение

На рисунке 13.2 показан формат 8-байтового IGMP сообщения.

Рисунок 13.2 Формат полей IGMP сообщения.

Поле версии IGMP (IGMP version) установлено в 1. Поле тип IGMP (IGMP type) устанавливается в 1 для запроса, посылаемого групповым маршрутизатором, и в 2 для ответа, отправляемого хостом. Контрольная сумма (checksum) рассчитывается так же, как контрольная сумма ICMP.

Групповой адрес (group address) это IP адрес класса D.. В запросе поле группового адреса устанавливается в 0. В отчете оно содержит групповой адрес. Мы расскажем более подробно об этом в следующих разделах, когда будем говорить о том, как функционирует IGMP.

Протокол IGMP

Вступление в группу

Фундаментальной концепцией для работы группы является то, что процесс вступает в группу с определенным интерфейсом хоста. (Мы используем термин процесс, который обозначает программу, которая запущена операционной системой.) Членство в группе динамическое - со временем процесс может как вступать, так и выходить из группы.

Естественно, процесс должен иметь возможность вступить в группу с указанным интерфейсом. Процесс также может покинуть группу, в которую он до этого вступил. Для вступления в группу и выхода из группы требуется, чтобы какой-либо API на хосте поддерживал групповую рассылку. Мы используем выражение "интерфейс", потому что членство в группе связано с интерфейсом. Процесс может вступить в одну и ту же группу с разных интерфейсов.

Релиз IP, поддерживающий групповую рассылку в Berkeley Unix Стэндфордского университета, детализирует эти изменения сокетов API. Эти изменения также присутствуют в Solaris 2.x и описаны в страницах помощи ip(7).

Поэтому идентификатор хоста в группе это адрес группы и интерфейс. Хост должен помнить таблицу всех групп, к которым принадлежит хотя бы один процесс, и счетчик обращений, то есть количество процессов, принадлежащих к группе.

IGMP запросы и отчеты

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

  1. Хост отправляет первый IGMP отчет, когда первый процесс вступает в группу. Если несколько процессов на данном хосте вступили в одну и ту же группу, отправляется только один отчет, в тот момент, когда процесс первый раз вступил в группу. Отчет посылается на тот же интерфейс, с которым процесс вступил в группу.
  2. Хост не посылает отчет, когда процесс выходит из группы, даже когда последний процесс вышел из группы. Хост знает, что в этой группе больше нет членов, поэтому когда он получает следующий запрос (следующий шаг), он не отправляет отчет.
  3. Групповой маршрутизатор отправляет IGMP запрос с регулярными интервалами, чтобы выяснить, принадлежат ли процессы каких-либо хостов к каким-либо группам. Маршрутизатор послает один запрос на каждый интерфейс. Групповой адрес в запросе установлен в 0, так как маршрутизатор ожидает приход одного отклика от хоста для каждой группы, к которой от хоста принадлежит один или несколько членов.
  4. Хост отвечает на IGMP запрос посылкой одного IGMP отчета для каждой группы, которая содержит хотя бы один процесс.

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

На рисунке 13.3 показаны два типа IGMP сообщений, отчеты, отправленные хостом, и запросы, отправленные маршрутизатором. Маршрутизатор опрашивает каждый хост, чтобы тот идентифицировал каждую группу для данного интерфейса.

Рисунок 13.3 IGMP отчеты и запросы.

Мы поговорим о поле TTL позже в этом разделе.

Детали реализации

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

Когда хост получает запрос от маршрутизатора, он не отвечает сразу же, а откладывает ответы на более позднее время. (Мы используем множественное число "ответы", потому что хост должен послать один отчет для каждой группы, которая содержит одного или несколько членов.) Так как несколько хостов могут отправить отчет для одной и той же группы, каждый отправляет свой отчет с задержкой, выбранной случайным образом. Также обратите внимание на то, что все хосты подключенные к одной физической сети получают все отчеты от других хостов, находящихся в той же группе, потому что адрес назначения отчета (см. рисунок 13.3) - это групповой адрес. А это, в свою очередь, означает, что если хост отложил момент отправки отчета, однако получил копию того же самого отчета от другого хоста, ответ может быть отменен. Это объясняется тем, что групповому маршрутизатору нет необходимости знать, сколько хостов принадлежит к группе - ему достаточно знать, что по крайней мере один хост принадлежит к группе.

На одном физическом кабеле без групповых маршрутизаторов, траффик, принадлежащий IGMP, - это отчеты, которые отправляются хостами, поддерживающими групповую адресацию IP, когда они вступают в новую группу.

Поле времени жизни

На рисунке 13.3 мы видели, что поле TTL в отчете и запросе установлено в 1. Это напоминает обычное TTL поле в IP заголовке. Групповая датаграмма с TTL исходно равным 0 не "уйдет" дальше своего хоста. По умолчанию групповые датаграммы рассылаются с TTL равным 1. Это позволяет датаграммам распространяться только в своей подсети. Значение TTL больше единицы может быть установлено групповым маршрутизатором.

Обратитесь к разделу "Типы сообщений ICMP" главы 6, где говорится, что ICMP ошибка никогда не генерируется в ответ на датаграмму, направляемую на групповой адрес. Групповые маршрутизаторы не генерируют ICMP ошибку "время истекло" (time exceeded), когда значение TTL становится равным 0.

Обычно, пользовательский процесс не заботится о значении исходящего TTL. Одно исключение, пожалуй, программа Traceroute (см. главу 8), принцип работы которой основан как раз на изменении значения поля TTL. Однако, приложения, которые работают с групповой адресацией, должны иметь возможность установить исходящее поле TTL. Это означает, что программный интерфейс должен предоставлять эту возможность пользовательским процессам.

Путем увеличения TTL приложение может осуществить расширенный поиск (expanding ring search) конкретного сервера. В этом случае первая групповая датаграмма посылается с TTL равным 1, если ответ не получен, посылается датаграмма с TTL равным 2, затем 3 и так далее. В этом случае приложение определяет положение ближайшего сервера в количествах пересылок.

Специальный диапазон адресов 224.0.0.0 - 224.0.0.255 отводится для приложений, которые не будут рассылать групповые запросы дальше чем на одну пересылку. Групповые маршрутизаторы не должны перенаправлять датаграммы с такими адресами назначения, вне зависимости от TTL.

Группа всех хостов (All-Hosts)

На рисунке 13.3 мы видели, что IGMP запрос от маршрутизатора отправляется на IP адрес назначения 224.0.0.1. Этот адрес называется адресом группы всех хостов (all-hosts). Он имеет отношение ко всем хостам и маршрутизаторам подключенным к физической сети и поддерживающим групповую адресацию. Каждый хост автоматически вступает в эту группу со всеми интерфейсами, которые поддерживают групповую адресацию, при инициализации интерфейса. О членстве в этой группе никогда не сообщается (рассылкой отчетов).

Пример

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

Во-первых, мы используем модифицированную версию команды netstat, которая сообщает о членстве в группах для каждого интерфейса. (Стандартный вывод netstat -ni для этого хоста приводится в разделе "Команда netstat" главы 3.) Ниже мы выделили строки, имеющие отношение к группам, жирным шрифтом:

sun % netstat -nia
Name  Mtu   Network      Address                Ipkts Ierrs    Opkts Oerrs Coll
le0   1500  140.252.13.  140.252.13.33          4370      0      4924     0    0
                         224.0.0.1
                         08:00:20:03:f6:42
                         01:00:5e:00:00:01
sl0   552   140.252.1    140.252.1.29           13587     0     15615     0    0
                         224.0.0.1
lo0   1536  127           127.0.0.1              1351       0     1351     0     0
                         224.0.0.1

Так как указана опция -n, IP адреса печатает в цифровом формате (а не в виде имен), -i печатает статистику по интерфейсам, и -a предоставляет отчет о всех сконфигурированных интерфейсах.

Вторая строка в выводе для le0 (Ethernet) сообщает о том, что этот интерфейс принадлежит к группе 224.0.0.1 (группа "все хосты"), а через одну строку показан соответствующий Ethernet адрес: 01:00:5e:00:00:01. Это как раз то, что мы и ожидали для Ethernet адреса. Подобное соответствие описано в разделе "Рассылка групповых сообщений" главы 12. Мы видим, что два других интерфейса, которые поддерживают рассылку групповых сообщений, канал SLIP - sl0 и loopback интерфейс lo0, также принадлежат к группе всех хостов.

Для маршрутизации групповых датаграмм используется обычная таблица маршрутизации. Пункты, помеченные жирным шрифтом, показывают все датаграммы для 224.0.0.0, отправлены в Ethernet:

sun % netstat -rn
Routing tables
Destination     Gateway            Flags   Refcnt Use      Interface
140.252.13.65   140.252.13.35     UGH      0      32       le0
127.0.0.1       127.0.0.1          UH      1       381     lo0
140.252.1.183   140.252.1.29      UH       0      6        sl0
default         140.252.1.183      UG      0       328     sl0
224.0.0.0       140.252.13.33     U        0      66       le0
140.252.13.32   140.252.13.33     U        8      5581     le0

Если Вы сравните эту таблицу маршрутизации с той, которую мы показали в разделе "Принципы маршрутизации" главы 9 для маршрутизатора sun, то увидите, что единственным отличием является запись для группы.

А сейчас запустим тестовую программу, которая позволит вступить в группу для определенного интерфейса. (Вывод этой тестовой программы не приводится.) Мы вступили в группу 224.1.2.3 с интерфейсом Ethernet (140.252.13.33). Исполнение netstat показывает, что ядро вступило в группу Ethernet адресом, как мы и ожидали. Отличия от предыдущего вывода команды netstat приведены жирным шрифтом:

sun % netstat -nia
Name  Mtu   Network      Address             Ipkts Ierrs     Opkts Oerrs  Coll
le0   1500  140.252.13.  140.252.13.33       4374      0      4929     0     0
                         224.1.2.3
                         224.0.0.1
                         08:00:20:03:f6:42
                         01:00:5e:01:02:03
                         01:00:5e:00:00:01
sl0   552   140.252.1    140.252.1.29        13862     0     15943     0     0
                         224.0.0.1
lo0   1536  127           127.0.0.1            1360      0     1360     0      0
                         224.0.0.1

Здесь снова показан вывод для двух других интерфейсов, sl0 и lo0, из чего видно, что в группу вступил только один интерфейс.

На рисунке 13.4 показан вывод команды tcpdump, соответствующий процессу вступления в группу.

1  0.0                  8:0:20:3:f6:42 1:0:5e:1:2:3 ip 60:
                       sun > 224.1.2.3: igmp report 224.1.2.3 [ttl 1]

2  6.94 (6.94)         8:0:20:3:f6:42 1:0:5e:1:2:3 ip 60:
                       sun > 224.1.2.3: igmp report 224.1.2.3 [ttl 1]

Рисунок 13.4 Вывод команды tcpdump при вступлении хоста в группу.

Строка 1 соответствует моменту, когда хост вступает в группу. Следующая строка - это задержанный отчет, который, как мы говорили, отправляется с некоторой случайной задержкой (до 10 секунд).

В этих двух строках показаны аппаратные адреса, чтобы убедиться в том, что адрес назначения Ethernet это корректный групповой адрес. Также стоит убедиться в том, что IP адрес источника это один из соответствующих адресов sun, а IP адрес назначения это групповой адрес. Адрес, сообщенный в отчете, - это тот же групповой адрес.

И в заключение, необходимо отметить, что значение TTL установлено в 1, как было указано ранее. Команда tcpdump печатает TTL в квадратных скобках, когда его значение равно 0 или 1. Это потому, что TTL обычно больше, чем эти значения. В случае групповой адресации мы ожидаем увидеть множество IP датаграмм с TTL равным 1.

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

Пример работы группового маршрутизатора

Давайте продолжим предыдущий пример, однако сейчас запустим демон групповой маршрутизации на хосте sun. В данном случае нас интересуют не детали реализации протоколов групповой маршрутизации, а то, как происходит обмен IGMP запросами и отчетами. Даже несмотря на то, что демон групповой маршрутизации запущен только на одном хосте, и только этот (единственный) хост поддерживает групповую адресацию (sun), групповые запросы и отчеты перемещаются по Ethernet.

Перед стартом демона маршрутизации мы вступили в другую группу: 224.9.9.9. На рисунке 13.5 показан вывод.

1 0.0 sun > 224.0.0.4: igmp report 224.0.0.4

2    0.00 (  0.00)            sun > 224.0.0.1: igmp query
3    5.10 (  5.10)            sun > 224.9.9.9: igmp report 224.9.9.9

4    5.22 (  0.12)            sun > 224.0.0.1: igmp query
5    7.90 (  2.68)            sun > 224.1.2.3: igmp report 224.1.2.3
6    8.50 (  0.60)            sun > 224.0.0.4: igmp report 224.0.0.4
7   11.70 (  3.20)            sun > 224.9.9.9: igmp report 224.9.9.9

8  125.51 (113.81)           sun > 224.0.0.1: igmp query
9  125.70 (  0.19)            sun > 224.9.9.9: igmp report 224.9.9.9
10  128.50 (  2.80)            sun > 224.1.2.3: igmp report 224.1.2.3
11  129.10 (  0.60)            sun > 224.0.0.4: igmp report 224.0.0.4

12  247.82 (118.72)           sun > 224.0.0.1: igmp query
13  248.09 (  0.27)            sun > 224.1.2.3: igmp report 224.1.2.3
14  248.69 (  0.60)            sun > 224.0.0.4: igmp report 224.0.0.4
15  255.29 (  6.60)            sun > 224.9.9.9: igmp report 224.9.9.9

Рисунок 13.5 Вывод команды tcpdump за время работы демона маршрутизации.

Мы не включили Ethernet адреса в этот вывод, потому что уже убедились в том, что они именно такие, какие должны быть. Также мы удалили весь вывод, посвященный TTL равным 1, потому что они также соответствуют ожидаемым.

В строке 1 показан момент старта демона маршрутизации. Он отправляет отчет о том, что вступил в группу 224.0.0.4. Групповой адрес 224.0.0.4 - это заранее известный адрес и используется протоколом групповой маршрутизации вектора расстояний (DVMRP - Distance Vector Multicast Routing Protocol). Это протокол используется в настоящее время для групповой маршрутизации. (DVMRP описан в RFC 1075 [Waitzman, Partridge, and Deering 1988].)

При старте демон посылает запрос (строка 2). IP адрес назначения в запросе 224.0.0.1 (все хосты), как показано на рисунке 13.3.

Первый отчет (строка 3) приходит примерно через 5 секунд, для группы 224.9.9.9. Это единственный отчет, принятый перед тем, как был отправлен следующий запрос (строка 4). Два запроса (строки 2 и 4) отправляются с небольшим промежутком в момент старта демона, так как он пытается построить групповую таблицу маршрутизации.

В строках 5, 6 и 7 мы видим по одному отчету для каждой группы, к которой принадлежит хост sun. Обратите внимание, что принят отчет от группы 224.0.0.4, в дополнение к двум группам, в которые мы вступили, потому что в то время пока работает демон, он принадлежит к этой группе.

Следующий запрос в строке 8 появляется через 2 минуты после предыдущего запроса. В ответ на него также приходит три отчета (строки 9, 10 и 11). В этот раз отчеты приходят в другом порядке, так как время между приходом запроса и отправкой отчета выбирается случайным образом.

И последний запрос, отправляется примерно через 2 минуты после предыдущего запроса, и снова получены ожидаемые отклики.

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

Групповая рассылка это способ разослать сообщения нескольким получателям. Для многих приложений это вариант предпочтительней, нежели рассылка широковещательных запросов, так как с помощью групповой рассылки можно уменьшить загруженность хостов, которые не заинтересованы в принятии сообщений. Простой протокол, определяющий принадлежность хостов к группе (IGMP), - это краеугольный камень, на котором строится групповая адресация.

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

Однако, для глобальных сетей проблема групповых запросов до сих пор до конца не решена. [Deering and Cheriton 1990] предлагает расширение существующих протоколов маршрутизации для поддержки групповых запросов. В разделе 9.13 [Perlman 1992] обсуждаются некоторые проблемы, связанные с групповыми запросами в глобальных сетях.

[Casner and Deering 1992] описывает доставку звуковой информации для IETF конференций по Internet с использованием групповых запросов и виртуальной сети, которая называется MBONE (групповая магистраль).

Упражнения

  1. Мы сказали, что хосты рассылают IGMP отчеты со случайными задержками. Как можно убедиться, что в локальной сети два хоста не генерируют отчет с одинаковой случайной задержкой?
  2. В [Casner and Deering 1992] сказано, что UDP не имеет двух характеристик, необходимых для посылки звуковой информации по MBONE: определение изменения порядка движения пакетов и определение дублированных пакетов. Как Вы можете добавить эти возможности в UDP?


DNS: система имен доменов (Domain Name System)

Глава 14 DNS: система имен доменов (Domain Name System)

Введение

Система имен доменов (DNS - Domain Name System) это распределенная база данных, которая используется приложениями TCP/IP, для установления соответствия между именами хостов и IP адресами. DNS также используется для маршрутизации электронной почты. Мы используем термин распределенная, потому что на одном узле Internet не хранится вся необходимая информация. Каждый узел (университет, университетский городок, компания или отдел внутри компании) поддерживает собственую информационную базу данных и запускает программу сервер, которая может отправить запрос по Internet к другим системам. DNS предоставляет протокол, который позволяет клиентам и серверам общаться друг с другом.

С точки зрения приложения, доступ к DNS осуществляется посредством разборщика (resolver) (разборщик (resolver) - подпрограммы, которые используются для создания, отправки и интерпретации пакетов, используемых серверами имен Internet). В Unix системах, к разборщику можно получить доступ через две библиотечные функции, gethostbyname(3) и gethostbyaddr(3), которые линкуются с приложением, когда оно строится. Первая воспринимает в качестве аргумента имя хоста и возвращает IP адрес, а вторая воспринимает в качестве аргумента IP адрес и возвращает имя хоста. Разборщик устанавливает контакты с одним или несколькими серверами DNS (name servers), чтобы установить это соответствие.

На рисунке 4.2 показано, что разборщик - это часть приложения. Он не является частью ядра операционной системы как протоколы TCP/IP. Приложение должно конвертировать имя хоста в IP адрес, перед тем как оно попросит TCP открыть соединение или послать датаграмму с использованием UDP. Протоколы TCP/IP внутри ядра ничего не знают о DNS.

В этой главе мы рассмотрим, как разборщики общаются с DNS серверами с использованием протоколов TCP/IP (в основном UDP). Однако мы не будем рассматривать установку и администрирование DNS серверов или все опции, существующие у разборщиков и серверов. Это может составить еще одну книгу. (В публикации [Albitz and Liu 1992] приведены подробности функционирования стандартных Unix разборщиков и серверов DNS.)

RFC 1034 [Mockapetris 1987a] описывает концепции, лежащие в основе DNS, а RFC 1035 [Mockapetris 1987b] содержит подробности разработки и спецификации DNS. Наиболее широкоиспользуемая реализация DNS, как разборщика, так и сервера - BIND (Berkeley Internet Name Domain). Процесс сервера называется named. Анализ траффика, генерируемого DNS в глобальных сетях, приводится в [Danzig, Obraczka, and Kumar 1992].

Основы DNS

Пространство имен DNS имеет иерархическую структуру, которая внешне напоминает файловую систему Unix. На рисунке 14.1 показана иерархическая организация DNS.

Рисунок 14.1 Иерархическая организация DNS.

Каждый узел (кружочки на рисунке 14.1) имеет метку длиной до 63 символов. Корень дерева это специальный узел без метки. Метки могут содержать заглавные буквы или маленькие. Имя домена (domain name) для любого узла в дереве - это последовательность меток, которая начинается с узла выступающего в роли корня, при этом метки разделяются точками. (Здесь видно отличие от файловой системы Unix, где полный путь всегда начинается с вершины (корня) и опускается вниз по дереву.) Каждый узел дерева должен иметь уникальное имя домена, однако одинаковые метки могут быть использованы в различных точках дерева.

Имя домена, которое заканчивается точкой, называется абсолютным именем домена (absolute domain name) или полным именем домена (FQDN - fully qualified domain name). Например, sun.tuc.noao.edu.. Если имя домена не заканчивается на точку, подразумевается, что имя должно быть завершено. Как будет закончено имя, зависит от используемого программного обеспечения DNS. Если незаконченное имя состоит из двух или более меток, его можно воспринимать как законченное или полное; иначе справа от имени должен быть добавлен локальный суффикс. Например, имя sun может быть завершено локальным суффиксом .tuc.noao.edu..

Домены верхнего уровня поделены на три зоны:

  1. arpa это специальный домен, используемый для сопоставления адрес - имя (раздел "Запросы указателя" этой главы).
  2. Семь 3-символьных доменов называются общими (generic) доменами. В некоторых публикациях они называются организационными (organizational) доменами.
  3. Все 2-символьные домены, основанные на кодах стран, можно найти в ISO 3166. Они называются доменами стран (country), или географическими (geographical) доменами.

На рисунке 14.2 приведен список обычной классификации семи основных доменов.

Домен | Описание
com | коммерческие организации
edu | учебные организации
gov | правительственные организации США
int | международные организации
mil | военные организации США
net | сети
org | другие организации

Рисунок 14.2 3-символьные общие домены.

Иногда считается, что 3-символьные общие домены используются только организациями Соединенных Штатов, а 2-символьные домены стран всеми остальными, однако это не так. Существуют неамериканские организации в основных доменах, и множество организаций в Соединенных Штатах находятся в домене страны .us. (RFC 1480 [Cooper and Postel 1993] описывает домен .us более подробно.) Единственные общие домены, которые закреплены за Соединенными Штатами, это .gov и .mil.

Многие 2-символьные домены стран второго уровня, очень похожи на основные домены: .ac.uk, например, принадлежит академическим институтам, а .co.uk коммерческим организациям Великобритании.

Одна важная характеристика DNS, не показанная на рисунке 14.1, это передача ответственности внутри DNS. Не существует организации, которая бы управляла и обслуживала все дерево в целом и каждую метку в отдельности. Вместо этого, одна организация (NIC) обслуживает только часть дерева (домены верхнего уровня), а ответственность за определенные зоны передает другим организациям.

Зона (zone) это отдельно администрируемая часть дерева DNS. Например, домен второго уровня noao.edu это отдельная зона. Многие домены второго уровня поделены на меньшие зоны. Например, университет может поделить свою зону на подзоны по факультетам, а компания может поделить себя на зоны по принципу деления на филиалы или отделы.

Если Вы знакомы с файловой системой Unix, то обратите внимание, что деление дерева DNS на зоны очень напоминает деление на логические файловые системы физических дисковых разделов. Однако мы не можем сказать, основываясь на рисунке 14.1, под чьим руководством находятся зоны, также как мы не можем по подобному рисунку сказать, какие директории в файловой системе находятся в определенном дисковом разделе.

С того момента, как выбрана организация или персона, которая несет ответственность за управление зоной, эта организация или персона должна организовать несколько серверов DNS (name servers) для этой зоны. Как только в зоне появляется новая система, администратор этой зоны помещает имя и IP адрес нового хоста в базу данных сервера DNS. В небольших университетах, например, один человек может делать это каждый раз при появлении новой системы, однако в больших университетах ответственность должна быть распределена (например, по департаментам), так как один человек не может осуществлять эту работу в целом.

Сервер DNS, скажем, обслуживает одну зону или несколько зон. Человек, который несет ответственность за зону, администрирует основной сервер DNS (primary name server) для этой зоны и один или несколько вторичных серверов DNS (secondary name servers). Первичный и вторичный сервера должны быть независимы и избыточны таким образом, чтобы система DNS не вышла из строя при отказе одного из серверов.

Основное отличие между первичными и вторичными серверами заключается в том, что первичные загружают всю необходимую информацию из дисковых файлов, тогда как вторичные получают информацию от первичного. Процесс передачи информации от первичного сервера вторичному называется передачей зоны (zone transfer). Когда в зоне появляется новый хост, администратор добавляет соответствующую информацию (минимум, имя и IP адрес) в дисковый файл на первичном сервере. После чего первичный сервер DNS уведомляется о необходимости повторно считать свои конфигурационные файлы. Вторичные сервера регулярно опрашивают первичные (обычно каждые 3 часа), и если первичные содержат новую информацию, вторичный получает ее с использованием передачи зоны.

Что произойдет, если сервер DNS не содержит необходимой информации? Он должен установить контакт с другим сервером DNS. (В этом заключается распределенная природа DNS.) Однако не каждый сервер DNS знает, как обратиться к другому серверу. Вместо этого каждый сервер DNS должен знать, как установить контакт с корневыми серверами DNS (root name servers). В апреле 1993 года существовало восемь корневых серверов, все первичные сервера должны знать IP адреса каждого корневого сервера. (Эти IP адреса находятся в конфигурационных файлах первичного сервера. Первичные сервера должны знать именно IP адреса корневых серверов, а не их DNS имена.) Корневой сервер, в свою очередь, знает имена и положения (IP адрес) каждого официального сервера DNS для всех доменов второго уровня. При этом возникает последовательный процесс: запрашивающий сервер должен установить контакт с корневым сервером. Корневой сервер сообщает запрашивающему серверу о необходимости обратиться к другому серверу и так далее. Мы рассмотрим эту процедуру и соответствующие примеры позже в этой главе.

Вы можете получить текущий список корневых серверов, воспользовавшись анонимным (anonymous) FTP. Получите файл netinfo/root-servers.txt с ftp.rs.internic.net или nic.ddn.mil.

Фундаментальная характеристика DNS - это кэширование (caching). Когда DNS сервер получает информацию о соответствии (скажем, IP адресов именам хостов), он кэширует эту информацию таким образом, что в случае следующего запроса может быть использована информация из кэша, дополнительный запрос на другие сервера не делается. В разделе "Кэширование" этой главы мы рассмотрим кэширование более подробно.

Формат сообщения DNS

Для DNS запроса и для DNS отклика используется одинаковый формат. На рисунке 14.3 показан общий формат DNS сообщения.

Рисунок 14.3 Общий формат DNS запроса и ответа.

Сообщение содержит фиксированный 12-байтный заголовок, за которым следуют четыре поля переменной длины.

Значение в поле идентификации (identification) устанавливается клиентом и возвращается сервером. Это поле позволяет клиенту определить, на какой запрос пришел отклик.

16-битовое поле флагов (flags) поделено на несколько частей, как показано на рисунке 14.4.

Рисунок 14.4 Поле флагов (flags) в заголовке DNS.

Описание каждого поля мы начнем с крайне левых битов.

  • QR (тип сообщения), 1-битовое поле: 0 обозначает - запрос, 1 обозначает - отклик.
  • opcode (код операции), 4-битовое поле. Обычное значение 0 (стандартный запрос). Другие значения - это 1 (инверсный запрос) и 2 (запрос статуса сервера).
  • AA - 1-битовый флаг, который означает "авторитетный ответ" (authoritative answer). Сервер DNS имеет полномочия для этого домена в разделе вопросов.
  • TC - 1-битовое поле, которое означает "обрезано" (truncated). В случае UDP это означает, что полный размер отклика превысил 512 байт, однако были возвращены только первые 512 байт отклика.
  • RD - 1-битовое поле, которое означает "требуется рекурсия" (recursion desired). Бит может быть установлен в запросе и затем возвращен в отклике. Этот флаг требует от DNS сервера обработать этот запрос самому (т.е. сервер должен сам определить требуемый IP адрес, а не возвращать адрес другого DNS сервера), что называется рекурсивным запросом (recursive query). Если этот бит не установлен и запрашиваемый сервер DNS не имеет авторитетного ответа, запрашиваемый сервер возвратит список других серверов DNS, к которым необходимо обратиться, чтобы получить ответ. Это называется повторяющимся запросом (iterative query) . Мы рассмотрим примеры обоих типов запросов в следующих примерах.
  • RA - 1-битовое поле, которое означает "рекурсия возможна" (recursion available). Этот бит устанавливается в 1 в отклике, если сервер поддерживает рекурсию. Мы увидим в наших примерах, что большинство серверов DNS поддерживают рекурсию, за исключением нескольких корневых серверов (коневые сервера не в состоянии обрабатывать рекурсивные запросы из-за своей загруженности).
  • Это 3-битовое поле должно быть равно 0.
  • rcode это 4-битовое поле кода возврата. Обычные значения: 0 (нет ошибок) и 3 (ошибка имени). Ошибка имени возвращается только от полномочного сервера DNS и означает, что имя домена, указанного в запросе, не существует.

Следующие четыре 16-битных поля указывают на количество пунктов в четырех полях переменной длины, которые завершают запись. В запросе количество вопросов (number of questions) обычно равно 1, а остальные три счетчика равны 0. В отклике количество ответов (number of answers) по меньшей мере равно 1, а оставшиеся два счетчика могут быть как нулевыми, так и ненулевыми.

Раздел вопросов в DNS запросе

Формат каждого вопроса в разделе вопросов (question) показан на рисунке 14.5. Обычно присутствует только один вопрос.

Имя запроса (query name) это искомое имя. Оно выглядит как последовательность из одной или нескольких меток. Каждая метка начинается с 1-байтового счетчика, который содержит количество следующих за ним байт. Имя заканчивается байтом равным 0, который является меткой с нулевой длиной. И является, в свою очередь, меткой корня. Каждый счетчик байтов должен быть в диапазоне от 0 до 63, так как длина метки ограничена 63 байтами.

Рисунок 14.5 Формат раздела вопроса (question) в запросе DNS.

(Дальше в этом разделе мы увидим, что байт счетчик с двумя старшими битами, установленными в 1, значения от 192 до 255, используется в схеме со сжатием.) В отличие от многих других форматов сообщений, которые мы рассмотрели, этому полю разрешено заканчиваться на ограничителе не равном 32 битам. Заполнение не используется.

На рисунке 14.6 показано, как хранится имя домена gemini.tuc.noao.edu.

Рисунок 14.6 Представление имени домена gemini.tuc.noao.edu.

У каждого вопроса есть тип запроса (query type), а каждый отклик (называемый записью ресурса, о чем мы поговорим ниже) имеет тип (type). Существует около 20 различных значений, некоторые из которых в настоящее время уже устарели. На рисунке 14.7 показаны некоторые из этих значений. Тип запроса это надмножество (множество, подмножеством которого является данное множество) типов: два из показанных значений, могут быть использованы только в вопросах.

Имя | Цифровое значение | Описание | тип (type)? | тип запроса (query type)?
A | 1 | IP адрес | · | ·
NS | 2 | сервер DNS | · | ·
CNAME | 5 | каноническое имя | · | ·
PTR | 12 | запись указателя | · | ·
HINFO | 13 | информация о хосте | · | ·
MX | 15 | запись об обмене почтой | · | ·
AXFR | 252 | запрос на передачу зоны |  | ·
* или ANY | 255 | запрос всех записей |  | ·

Рисунок 14.7 Значения type и query type для DNS вопросов и ответов.

Наиболее распространенный тип запроса - тип A, который обозначает, что необходим IP адрес для запрашиваемого имени (query name). PTR запрос требует имена, соответствующие IP адресу. Это запрос указателя, который мы опишем в разделе "Запросы указателя" этой главы. Другие типы запросов мы опишем в разделе "Записи ресурсов" этой главы.

Класс запроса (query class) обычно равен 1, что указывает на адреса Internet. (В некоторых случаях поддерживаются не-IP значения.)

Часть записи ресурса в отклике DNS

Последние три поля в DNS сообщении это ответы (answers), полномочия (authority) и дополнительная информация (additional information), общий формат называется записью ресурса (RR - resource record). На рисунке 14.8 показан формат записи ресурса.

Рисунок 14.8 Формат записи ресурса DNS.

Имя домена (domain name) это имя, которому соответствуют следующие данные ресурса. Формат имени домена тот же, что мы описали ранее для поля имени запроса (query name) (рисунок 14.6).

Тип (type) указывает на один из типов кодов RR. Это то же самое, что и значения типа запроса (query type), которые мы описали раньше. Для данных Internet класс (class) обычно установлен в 1.

Поле время жизни (time-to-live) это количество секунд, в течение которых RR может быть кэширована клиентом. Обычно TTL RR равно 2 дням.

Длина записи ресурса (resource data length) указывает на количество данных ресурса (resource data). Формат этих данных зависит от типа (type). Для типа равного 1 (запись A) данные ресурса - это 4-байтный IP адрес. Сейчас мы описали основной формат DNS запросов и откликов.

Теперь посмотрим с использованием tcpdump, как они упаковываются в пакеты и как происходит обмен.

Простой пример

Давайте посмотрим, как происходит общение между разборщиком и сервером DNS. Мы запустили клиента Telnet с хоста sun на хост gemini, подключившись к серверу времени:

sun % telnet gemini daytime
Trying 140.252.1.11 ...                первые три строки вывода от Telnet клиента
Connected to gemini.tuc.noao.edu.
Escape character is '^]'.
Wed Mar 24 10:44:17 1993               вывод от сервера дневного времени
Connection closed by foreign host.    вывод от Telnet клиента

В этом примере мы указали разборщику на хосте sun (где запущен клиент Telnet) использовать сервер DNS на хосте noao.edu (140.252.1.54). На рисунке 14.9 показано взаимное расположение этих трех систем.

Рисунок 14.9 Системы, используемые в примере работы DNS.

Как мы уже упомянули ранее, разборщик является частью клиента. Он устанавливает контакт с сервером DNS, чтобы получить IP адрес, перед тем как будет установлено TCP соединение между Telnet и сервером времени.

На этом рисунке мы опустили подробности, описывающие, как происходит общение между sun и Ethernet сетью 140.252.1, которое в действительности осуществляется по SLIP каналу, потому что это не относится к нашим рассуждениям. Мы запустим tcpdump на SLIP канале, чтобы посмотреть, как происходит обмен пакетами между разборщиком и сервером DNS.

Файл /etc/resolv.conf на хосте sun сообщает разборщику о необходимости сделать следующее:

sun % cat /etc/resolv.conf
nameserver 140.252.1.54
domain tuc.noao.edu

Первая строка сообщает IP адрес DNS сервера - хоста noao.edu. Может быть указано до трех строк nameserver, таким образом, будет обеспечен запасной сервер на случай, если один из них выключен или недоступен. Строка domain содержит домен по умолчанию. Если искомое имя не является полным именем домена (не заканчивается точкой), к имени добавляется имя домена по умолчанию .tuc.noao.edu. Именно поэтому мы можем ввести telnet gemini вместо telnet gemini.tuc.noao.edu.

На рисунке 14.10 показан обмен пакетами между разборщиком и сервером DNS.

1  0.0                         140.252.1.29.1447 > 140.252.1.54.53: 1+ A?
                              gemini.tuc.noao.edu. (37)

2  0.290820 (0.2908)           140.252.1.54.53 > 140.252.1.29.1447: 1* 2/0/0 A
                              140.252.1.11 (69)

Рисунок 14.10 Вывод команды tcpdump для запроса на сервер DNS на хосте gemini.tuc.noao.edu.

Мы проинструктировали tcpdump не печатать имена доменов для IP адресов источника и назначения каждой IP датаграммы. Вместо этого он печатает 140.252.1.29 для клиента (разборщик) и 140.252.1.54 для сервера DNS. Порт 1447, используемый клиентом, это порт, назначаемый динамически, а 53 это заранее известный порт DNS сервера. Если tcpdump постарается напечатать имена вместо IP адресов, ему придется обратиться к тому же DNS серверу (осуществляя запрос указателя), что может привести к нежелательному выводу.

Начиная со строки 1, поле после двоеточия (1+) означает, что поле идентификации равно 1, а знак плюс обозначает, что установлен флаг RD (требуется рекурсия). Мы видим, что по умолчанию разборщик требует рекурсию.

Следующее поле, A?, означает, что тип запроса - A (мы хотим получить IP адрес), а маркировка вопроса обозначает, что это запрос (не ответ). Затем печатается имя запроса: gemini.tuc.noao.edu.. Разборщик добавляет последнюю точку к имени запроса, указывая на то, что это абсолютное имя домена.

Длина пользовательских данных в UDP датаграмме составляет 37 байт: 12 байт - заголовок фиксированного размера (рисунок 14.3), 21 байт - имя запроса (рисунок 14.6) и 4 байта - тип запроса и класс запроса. То что UDP датаграмма имеет нечетную длину напоминает нам, что в DNS сообщениях не используются биты заполнения.

Строка 2 в выводе команды tcpdump это ответ от DNS сервера, где 1* в поле идентификации со звездочкой обозначает, что установлен флаг AA (авторитетный ответ). (Мы ожидали от сервера именно этого, так как первичный сервер для домена noao.edu имеет полное представление об именах внутри этого домена.)

Вывод 2/0/0 показывает количество записей ресурсов в трех последних полях с переменной длиной отклика: 2 ответ RR, 0 полномочия RR и 0 дополнительные RR. Команда tcpdump печатает только первый ответ, который в данном случае имеет тип A (IP адрес) со значением 140.252.1.11.

Почему мы получили два ответа на наш запрос? Потому что хост gemini имеет несколько интерфейсов. Поэтому возвращено два IP адреса. Другое полезное средство, использующее DNS, - это программа host. Она позволяет нам отправить запрос на DNS сервер и посмотреть что вернется. Если мы запустим эту программу, то увидим два IP адреса для хоста gemini:

sun % host gemini
gemini.tuc.noao.edu A 140.252.1.11
gemini.tuc.noao.edu A 140.252.3.54

Первый ответ на рисунке 14.10 и первая строка вывода команды host - IP адрес, принадлежащий той же подсети (140.252.1), что и запрашивающий хост. В этом нет ничего странного. Если сервер DNS и хост, отправляющий запрос, находятся в той же самой сети (или подсети), BIND сортирует результаты таким образом, чтобы адреса, принадлежащие общей сети, появлялись в первую очередь.

Мы также можем получить доступ к хосту gemini с использованием другого адреса, однако это будет не так эффективно. С использованием traceroute в этом примере можно увидеть, что обычный путь от подсети 140.252.1 к 140.252.3 не проходит через хост gemini, а проходит через другой маршрутизатор, который подключен к обеим сетям. В данном случае, если мы получим доступ к gemini через другой IP адрес (140.252.3.54), все пакеты потребуют еще одной дополнительной пересылки. Мы вернемся к этому примеру и рассмотрим более подробно причины, по которым используется альтернативный маршрут, в разделе "Дополнительные примеры" главы 25, где мы сможем использовать SNMP, чтобы посмотреть таблицу маршрутизации маршрутизатора.

Существуют и другие программы, предоставляющие простой интерактивный доступ к DNS. Программа nslookup поставляется с большинством реализаций DNS. Глава 10 [Albitz and Liu 1992] подробно описывает эту программу. Программа dig (Domain Internet Groper) это еще одна общедоступная программа, с помощью которой можно отправить запросы на DNS сервера. Программа doc (Domain Obscenity Control) - shellовский скрипт, который использует dig и диагностирует поведение доменов, отправляя запросы на соответствующие DNS сервера и осуществляя простой анализ откликов. В приложении F подробно рассказано, как можно получить эти программы.

И последняя деталь, на которую необходимо обратить внимание в этом примере, это размер UDP данных в отклике: 69 байт. Чтобы объяснить эту величину, надо знать две вещи.

  1. Вопрос возвращается в отклике.
  2. При отправке отклика с именами доменов может быть использовано множество повторов. Поэтому используется схема сжатия. И действительно, в нашем примере имя домена gemini.tuc.noao.edu появляется трижды. Схема сжатия довольно проста. Везде, где в имени домена появляется метка, используется единственный байт-счетчик (который находится в диапазоне от 0 до 63), у которого два старших бита установлены в 1. Это 16-битный указатель, а не 8-битный байт-счетчик. Следующие 14 байт в указателе определяют смещение следующей метки в DNS сообщении. (Смещение первого байта в поле идентификации равно 0.) Мы специально сказали, что этот указатель может появиться там, где появляется метка, а не только там, где появляется полное имя домена, однако возможно, что указатель будет иметь как форму полного имени домена, так и всего лишь окончательной части имени. (Это потому, что окончательные метки в именах заданных доменов часто бывают идентичны.)

На рисунке 14.11 показан формат DNS отклика, что соответствует строке 2 на рисунке 14.10. Здесь показаны IP и UDP заголовки, чтобы напомнить о том, что DNS сообщения обычно инкапсулируются в UDP датаграммы. Мы специально показали байты счетчики в метках имен доменов в вопросе. Два возвращенных ответа одинаковы, за исключением различных IP адресов. В этом примере каждый указатель в ответе имеет значение 12, что является смещением от начала DNS заголовка полного имени домена.

И последнее, на что необходимо обратить внимание, это вторая строка из вывода команды Telnet, которая повторена здесь:

sun % telnet gemini daytime              мы напечатали только gemini
Trying 140.252.1.11 ...
Connected to gemini.tuc.noao.edu.        однако в выводе клиента Telnet появился FQDN

Рисунок 14.11 Формат DNS отклика, соответствующий строке 2 на рисунке 14.10.

Мы указали только имя хоста (gemini), а не полное имя домена (FQDN) , однако клиент Telnet вывел именно полное имя домена. В данном случае клиент Telnet ищет имя, которое мы ввели, вызвав разборщик (gethostbyname), который возвращает IP адрес и FQDN. Затем Telnet выводит IP адрес, с которым он старается установить TCP соединение, и когда соединение установлено, печатает FQDN.

Пауза между вводом команды Telnet и печатью IP адреса, вызвана тем, что разборщик устанавливает контакт с DNS сервером, чтобы преобразовать имя в IP адрес. Пауза между выводом Trying и Connected to, вызвана установлением TCP соединения между клиентом и сервером, а не DNS.

Запросы указателя

Для понимания работы DNS важно знать, как обрабатываются запросы указателя - задан IP адрес, возвращается имя (или имена), соответствующее этому адресу.

Во-первых, вернемся к рисунку 14.1 и рассмотрим домен верхнего уровня arpa, а также домен in-addr, находящийся ниже. Когда организация вступает в Internet и получает часть простраства имен DNS, как, например, noao.edu, она также получает право на часть пространства имен in-addr.arpa, соответствующее ее IP адресам в Internet. В данном случае noao.edu - это сеть класса B с идентификатором 140.252. Уровень дерева DNS ниже in-addr.arpa должен быть первым байтом IP адреса (140 в данном примере), следующий уровень это следующий байт IP адреса (252), и так далее. Однако помните, что имена пишутся, снизу-вверх по дереву DNS. Это означает, что DNS имя хоста sun с IP адресом 140.252.13.33 будет 33.13.252.140.in-addr.arpa.

Мы должны написать 4 байта IP адреса задом наперед, потому что полномочия делегируются на основе идентификаторов сетей: первый байт адрес класса A, первый и второй байты адреса класса B, а первый, второй и третий байты это адреса класса C. Первый байт IP адреса должен быть непосредственно под меткой in-addr, однако полные имена доменов (FQDN) пишутся снизу вверх по дереву. Если бы FQDN писались сверху вниз, DNS имя для IP адреса было бы arpa.in-addr.140.252.13.33, однако в этом случае FQDN для хоста должно быть edu.noao.tuc.sun.

Без отдельных ветвей дерева DNS осуществить преобразования адрес - имя, (обратное преобразование) можно было бы только начиная от корня дерева и просматривая каждый домен верхнего уровня. При сегодняшнем размере Internet это могло бы занять дни или даже недели. Использование же in-addr.arpa приемлемый вариант, несмотря на переставленные местами байты в IP адресе и специальные домены, иногда вносящие определенную путаницу.

Однако встретиться с доменом in-addr.arpa и переставленными байтами в IP адресе можно только тогда, когда мы общаемся с DNS непосредственно, используя, такие программы как host или просматривая пакеты с использованием tcpdump. При работе приложения, разборщик (gethostbyaddr) обычно воспринимает IP адрес и возвращает информацию о хосте. Перестановка байтов и добавление домена in-addr.arpa осуществляется разборщиком автоматически.

Пример

Давайте воспользуемся программой host, чтобы осуществить поиск указателя, и при этом просмотрим с использованием tcpdump как происходит обмен пакетами. Мы используем те же начальные установки как на рисунке 14.9, запустив программу host на хосте sun с тем же самым сервером DNS noao.edu. Мы указали IP адрес нашего хоста svr4:

sun % host 140.252.13.34
Name: svr4.tuc.noao.edu
Address: 140.252.13.34

Так как единственный аргумент в командной строке это IP адрес, программа host автоматически генерирует запрос указателя. На рисунке 14.12 показан вывод команды tcpdump.

1  0.0                      140.252.1.29.1610 > 140.252.1.54.53: 1+ PTR?
                           34.13.252.140.in-addr.arpa. (44)

2  0.332288 (0.3323)       140.252.1.54.53 > 140.252.1.29.1610: 1* 1/0/0 PTR
                           svr4.tuc.noao.edu. (75)

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

Из строки 1 видно, что идентификатор равен 1, установлен флаг требования рекурсии (знак плюс), и тип запроса это PTR. (Вспомним, что марка вопроса обозначает, что это запрос, а не отклик.) Размер данных составляет 44 байта, из которых 12 байт - DNS заголовок, 28 байт - 7 меток в имени домена, и 4 байта это тип запроса и класс запроса.

В отклике установлен бит авторитетного ответа (звездочка) и содержится одна запись ресурса (RR) ответа. Тип RR это PTR, а данные ресурса содержат имя домена.

От разборщика к серверу DNS в качестве запроса указателя передается не 32-битный IP адрес, а имя домена 34.13.252.140.in-addr.arpa.

Проверка неправильного имени хоста

Когда IP датаграмма прибывает на хост сервера, будь то UDP датаграмма или TCP сегмент с требованием установить соединение, все что доступно процессу сервера это IP адрес клиента и номер порта (UDP или TCP). Некоторые сервера требуют, чтобы IP адрес клиента имел запись указателя в DNS. В разделе "Примеры FTP" главы 27 мы рассмотрим пример, иллюстрирующий это, используя анонимный FTP с неизвестного IP адреса.

Другие серверы, как, например, сервер Rlogin (глава 26), требуют не только то, чтобы IP адрес клиента имел запись указателя, но и еще спрашивают DNS об IP адресе, соответствующем имени, возвращенном в PTR отклике, и требуют, чтобы один из возвращенных адресов совпадал с IP адресом в принятой датаграмме. Эта проверка осуществляется потому, что пункты в файле .rhosts (глава 26, раздел "Протокол Rlogin") содержат имя хоста, а не IP адрес; таким образом, сервер хочет убедиться, что имя хоста действительно соответствует входящему IP адресу.

Некоторые производители автоматически помещают эту проверку в программы разборщика, конкретно в функцию gethostbyaddr. При этом подобная проверка становится доступной для любой программы, использующей разборщик. Отпадает необходимость помещать эту проверку в каждое приложение.

Мы можем увидеть, как это происходит, с помощью библиотеки разборщика SunOS 4.1.3. Мы написали простую программу, которая осуществляет запрос указателя путем вызова функции gethostbyaddr. Также мы поместили записи в файл /etc/resolv.conf таким образом, чтобы использовать в качестве DNS сервера хост noao.edu, получить доступ к которому можно через SLIP канал с хоста sun. На рисунке 14.13 показан вывод команды tcpdump, полученный от SLIP канала, при вызове функции gethostbyaddr, в случае, когда получается имя, соответствующее IP адресу 140.252.1.29 (хост sun).

1  0.0                     sun.1812 > noao.edu.domain: 1+ PTR?
                          29.1.252.140.in-addr.arpa. (43)
2  0.339091 (0.3391)      noao.edu.domain > sun.1812: 1* 1/0/0 PTR
                          sun.tuc.noao.edu. (73)
3  0.344348 (0.0053)      sun.1813 > noao.edu.domain: 2+ A?
                          sun.tuc.noao.edu. (33)
4  0.669022 (0.3247)      noao.edu.domain > sun.1813: 2* 2/0/0 A
                          140.252.1.29 (69)

Рисунок 14.13 Вызов функции разборщика, которая осуществляет запрос указателя.

В строке 1 запрос указателя, в строке 2 отклик. Однако, функция разборщика автоматически посылает запрос об IP адресе в строке 3 на имя, возвращенное в строке 2. Отклик в строке 4 содержит две записи ответа, так как хост sun имеет два IP адреса. Если ни один из адресов не совпал с аргументом gethostbyaddr, отправляется сообщение системе, которая фиксирует событие, а функция возвращает ошибку приложению.

Записи ресурсов

Мы видели несколько различных типов записей ресурса (RR), а именно: IP адрес имеет тип A, а PTR обозначает запрос указателя. Также мы видели RR, которые возвращает DNS сервер: RR ответа, RR полномочий и RR дополнительной информации. Всего существует около 20 различных типов записей ресурсов, некоторые из которых мы сейчас опишем.

А

Запись А определяет IP адрес. Хранится как 32-битное двоичное значение.

PTR

Запись указателя используется для запросов указателя. IP адрес представляется в виде имени домена (последовательность меток) в домене in-addr.arpa.

CNAME

"Каноническое имя" (canonical name). Представляется как имя домена (последовательность меток). Имя домена, которое имеет каноническое имя, часто называется псевдонимом (alias). Они используются некоторыми FTP узлами, для того чтобы предоставить легкозапоминаемый псевдоним для какой-либо системы.

Например, сервер gated (мы упоминали о нем в разделе "Демоны маршрутизации в Unix" главы 10) доступен через анонимное FTP с сервера gated.cornell.edu. Однако, не существует системы, названной gated, это псевдоним для какой-то другой системы. Эта другая система является каноническим именем для gated.cornell.edu:

sun % host -t cname gated.cornell.edu
gated.cornell.edu      CNAME       COMET.CIT.CORNELL.EDU

Здесь мы использовали опцию -t, чтобы указать на один конкретный тип запроса.

HINFO

Информация о хосте: две символьные строки, указывающие на центральный процессор (CPU) и операционную систему. Не все хосты предоставляют записи HINFO для своих систем, и предоставляемая информация может быть устаревшей.

sun % host -t hinfo sun
sun.tuc.noao.edu      HINFO       Sun-4/25 Sun4.1.3

MX

Записи, посвященные обмену почтой, которые используются по следующему сценарию: (1) узел, который не подсоединен к Internet, может использовать узел, который подсоединен к Internet, в качестве своего почтового сервера. Два узла работают попеременно, обмениваясь любой прибывшей почтой, в основном с использованием протокола UUCP. (2) запись MX предоставляет возможность доставить почту на альтернативный хост, когда хост назначения недоступен. (3) записи MX позволяют организациям предоставлять виртуальные хосты, на которые можно отправлять почту, как, например, cs.university.edu, даже если хост с таким именем не существует. (4) Организации со шлюзами firewall могут использовать записи MX, чтобы ограничить доступ к внутренним системам.

Многие хосты, которые не подключены к Internet, имеют UUCP каналы к хостам, подключеным к Internet, как, например, UUNET. С помощью записи MX обеспечивается передача электронной почты с использованием стандартного обращения user@host (пользователь@хост). Например, фиктивный домен foo.com может иметь следующую MX запись:

sun % host -t mx foo.com
foo.com           MX      relay1.UU.NET
foo.com           MX      relay2.UU.NET

MX записи используются программами доставки почты на хостах, подключенных к Internet. В этом примере программа доставки почты говорит "если у тебя есть почта, которую необходимо послать на user@foo.com, пошли почту на relay1.uu.net или на relay2.uu.net".

В каждой записи MX есть 16-битное целое значение, которое называется значением предпочтительности. Если для одного пункта назначения существует несколько MX записей, они будут использованы по порядку, начиная с той записи, у которой наименьшее значение предпочтительности.

Записи MX используются, когда хост выключен или недоступен. В этом случае программа доставки почты использует MX записи только в том случае, если не может подсоединиться к пункту назначения с использованием TCP. Для объединенных сетей, с которыми экспериментировал автор, его основная система подключена к Internet через SLIP канал, и если она не работает, мы получим:

sun % host -tv mx sun
Query about sun for record types MX
Trying sun within tuc.noao.edu ...
Query done, 2 answers,authoritative status: no error
sun.tuc.noao.edu    86400    IN    MX     0 sun.tuc.noao.edu
sun.tuc.noao.edu    86400    IN    MX     10 noao.edu

Опцию -v позволяет посмотреть значения предпочтительности. (Благодаря этой опции в выводе появятся и все другие поля.) Второе поле, 86400, - это время жизни в секундах. Время жизни равно 24 часам (24х60х60). Третья колонка, IN, это класс (Internet). Мы видим, что непосредственная доставка на хост (первая запись MX) имеет наименьшее значение предпочтительности равное 0. Если это не работает (SLIP канал выключен), используется следующее более высокое значение предпочтительности (10), и делается попытка доставить почту на хост noao.edu. Если и это не сработает, отправитель отработает тайм-аут и повторит попытки позже.

В разделе "Примеры SMTP" главы 28 мы покажем примеры доставки почты SMTP с использованием записей MX.

NS

Запись имени сервера. Указывает на полномочные DNS серверы для домена. Представлена в виде имен доменов (последовательность меток). Мы рассмотрим примеры этих записей в следующем разделе.

Это общие типы RR. В следующих примерах мы увидим еще некоторые типы.

Кэширование

Чтобы уменьшить траффик DNS в Internet, все сервера DNS используют кэширование. В стандартных Unix реализациях кэш поддерживается сервером, а не разборщиком. Так как разборщик является частью каждого приложения, а приложения приходят и уходят, оставляя кэш в программах, которые живут все время, пока система работает (сервер DNS), имеет смысл поддерживать кэш именно на сервере. При этом кэш доступен любому приложению, которое использует сервер. Любые другие хосты в узле, которые используют этот сервер DNS, также пользуются кэшем сервера.

В примерах (рисунок 14.9), мы запускали клиентов на хосте sun, и обращались к DNS серверу на хост noao.edu через SLIP канал. Сейчас попробуем запустить DNS сервер на хосте sun. В этом случае, если просмотреть с использованием tcpdump траффик DNS в SLIP канале, мы увидим только запросы, которые не могут быть обработаны сервером в своем собственном кэше.

По умолчанию разборщик ищет сервер DNS на локальном хосте (UDP порт 53 или TCP порт 53). Мы удалили запись nameserver из файла разборщика, оставив только запись domain:

sun % cat /etc/resolv.conf
domain tuc.noao.edu

Отсутствие записи nameserver в этом файле приводит к тому, что разборщик пользуется сервером DNS на локальном хосте.

Затем мы запустили команду host следующим образом:

sun % host ftp.uu.net
ftp.uu.net          A     192.48.96.9

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

1  0.0                      sun.tuc.noao.edu.domain > NS.NIC.DDN.MIL.domain:
                           2 A? ftp.uu.net. (28)
2  0.559285 (0.5593)       NS.NIC.DDN.MIL.domain > sun.tuc.noao.edu.domain:
                           2- 0/5/5 (229)

3  0.564449 (0.0052)       sun.tuc.noao.edu.domain > ns.UU.NET.domain:
                           3+ A? ftp.uu.net. (28)
4  1.009476 (0.4450)       ns.UU.NET.domain > sun.tuc.noao.edu.domain:
                           3* 1/0/0 A ftp.UU.NET (44)

Рисунок 14.14 Вывод tcpdump для: host ftp.uu.net.

Появилась новая опция программы tcpdump. Мы получаем все данные, направляющиеся к или от UDP или TCP портов 53, с помощью опции -w. В этом случае весь символьный вывод сохраняется в файле для дальнейшей обработки. При этом tcpdump не пытается вызвать свой собственный разборщик, чтобы напечатать все имена, соответствующие IP адресам. После того как запущены все запросы, мы перезапустили tcpdump с опцией -r. В этом случае программа читает выходной файл и генерирует обычный вывод (который показан на рисунке 14.14). Это занимает несколько секунд, так как tcpdump вызывает свой разборщик.

Первое на что необходимо обратить внимание в выводе tcpdump, это то, что идентификаторы представляют собой небольшие целые числа (2 и 3). Это потому, что мы выключили сервер DNS и затем перестартовали его, чтобы очистить кэш. Когда сервер DNS стартует, он устанавливает идентификатор в 1.

Затем мы ввели запрос, в котором требуется получить IP адрес для хоста ftp.uu.net, DNS сервер установил соединение с одним из восьми корневых серверов, ns.nic.ddn.mil (строка 1). Это обычный запрос типа A, который мы уже видели ранее, однако обратите внимание, что флаг требования рекурсии не установлен. (Если флаг установлен, после идентификатора 2 должен быть напечатан знак плюс.) В наших предыдущих примерах говорилось, что разборщик устанавливает флаг необходимости рекурсии, однако здесь мы видим, что наш сервер DNS не установил флаг, когда он обратился к одному из корневых серверов. Это произошло потому, что от корневых серверов нельзя требовать рекурсивных ответов - они должны быть использованы только для того, чтобы найти адреса к другим полномочным серверам.

Из строки 2 видно, что отклик пришел назад без записи ресурса (RR) ответа, с пятью RR полномочий и пятью RR дополнительной информации. Знак минус, следующий за идентификатором 2, означает, что флаг "рекурсия возможна" (RA) не установлен - этот корневой сервер не ответит на рекурсивный запрос, даже если мы его об этом попросим.

Несмотря на то, что tcpdump не напечатал 10 записей ресурсов, которые были возвращены, мы можем исполнить команду host, чтобы посмотреть, что находится в кэше:

sun % host -v ftp.uu.net
Query about ftp.uu.net for record types A
Trying ftp.uu.net ...
Query done, 1 answer, status: no error
The following answer is not authoritative:
ftp.uu.net      19109    IN       A       192.48.96.9
Authoritative nameservers:
UU.NET                  170308    IN      NS      NS.UU.NET
UU.NET                  170308    IN      NS       UUNET.UU.NET
UU.NET                  170308    IN      NS       UUCP-GW-1.PA.DEC.COM
UU.NET                  170308    IN      NS       UUCP-GW-2.PA.DEC.COM
UU.NET                  170308    IN      NS      NS.EU.NET
Additional information:
NS.UU.NET               170347    IN      A        137.39.1.3
UUNET.UU.NET           170347    IN      A        192.48.96.2
UUCP-GW-1.PA.DEC.COM   170347   IN      A        16.1.0.18
UUCP-GW-2.PA.DEC.COM   170347   IN      A        16.1.0.19
NS.EU.NET               170347    IN      A        192.16.202.11

В этот раз мы указали опцию -v, чтобы увидеть не только записи A. Вывод показывает, что в домене uu.net присутствует пять полномочных DNS серверов. Пять RR с дополнительной информацией, которые были возвращены корневым серверам, содержат IP адреса этих пяти DNS серверов. Поэтому у нас нет необходимости снова устанавливать контакт с корневым сервером, чтобы найти адрес одного из этих серверов. Это еще одно из расширений реализации, сделанное в DNS.

Команда host определяет, что ответ не авторитетный. Это произошло из-за того, что ответ был получен из кэша нашего DNS сервера, а не в результате контакта с полномочным сервером.

Вернемся к строке 3 на рисунке 14.14; сервер DNS установил контакт с первым полномочным сервером (ns.uu.net) с тем же самым вопросом: какой IP адрес ftp.uu.net? На этот раз наш сервер установил флаг "рекурсия необходима". Ответ, возвращенный в строке 4 - это отклик с одним ответом RR.

Затем мы снова исполнили команду host, спрашивая о том же самом имени:

sun % host ftp.uu.net
ftp.uu.net          A          192.48.96.9

Это как раз то, что мы и ожидали, потому что ответ на команду host был получен из кэша сервера.

Мы исполнили команду host снова в поисках адреса для ftp.ee.lbl.gov:

sun % host ftp.ee.lbl.gov
ftp.ee.lbl.gov      CNAME     ee.lbl.gov
ee.lbl.gov          A          128.3.112.20

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

1 18.664971 (17.6555)      sun.tuc.noao.edu.domain > c.nyser.net.domain:
                           4 A? ftp.ee.lbl.gov. (32)
2 19.429412 ( 0.7644)      c.nyser.net.domain > sun.tuc.noao.edu.domain:
                           4 0/4/4 (188)

3 19.432271 ( 0.0029)      sun.tuc.noao.edu.domain > ns1.lbl.gov.domain:
                           5+ A? ftp.ee.lbl.gov. (32)
4 19.909242 ( 0.4770)      ns1.lbl.gov.domain > sun.tuc.noao.edu.domain:
                           5* 2/0/0 CNAME ee.lbl.gov. (72)

Рисунок 14.15 Вывод tcpdump для: host ftp.ee.lbl.gov.

Из строки 1 видно, что теперь наш сервер установил контакт с другим корневым сервером (c.nyser.net). Сервер DNS обычно циклически опрашивает различные серверы для зоны, при этом накапливается информация о времени отклика от того или иного сервера. И, в конце концов, будет использован тот сервер, время возврата от которого минимально.

Так как наш сервер установил контакт с корневым сервером, флаг "рекурсия необходима" не установлен. Корневой сервер не сбросил флаг "рекурсия возможна", как мы видели в строке 2 на рисунке 14.14. (Даже если так, DNS сервер все равно не должен запрашивать корневой сервер с помощью рекурсивного запроса.)

В строке 2 отклик приходит назад без ответа, однако с четырьмя RR полномочий и четырьмя RR дополнительной информации. Как мы можем догадаться, четыре RR полномочий это имена DNS серверов для ftp.ee.lbl.gov, а другие четыре RR содержат IP адреса этих четырех серверов.

Строка 3 - это запрос на сервер ns1.lbl.gov (первый из четырех DNS серверов, полученных в строке 2). Флаг "рекурсия необходима" установлен.

Отклик в строке 4 отличается от предыдущих откликов. Возвращено два RR ответа, и tcpdump сообщает, что первый - это RR CNAME. Каноническое имя для ftp.ee.lbl.gov - это ee.lbl.gov.

Это общепринятое использование записи CNAME. FTP узел для LBL всегда имеет имя, начинающееся с ftp, однако время от времени он может перемещаться с одного хоста на другой. Пользователям достаточно знать только имя ftp.ee.lbl.gov, а DNS сама установит соответствие с тем или иным хостом.

Вспомните, что когда мы запускали host, он печатал и CNAME и IP адрес, соответствующий каноническому имени. Это потому, что отклик (строка 4 на рисунке 14.15) содержит два RR ответа. Первый это CNAME, второй это запись A. Если запись A не была возвращена вместе с CNAME, наш сервер пошлет еще один запрос, спрашивая IP адрес ee.lbl.gov. Это еще одна оптимизация реализации: CNAME и запись A канонического имени возвращается в одном отклике.

UDP или TCP

Мы уже упоминали, что заранее известные номера портов для DNS серверов - UDP порт 53 и TCP порт 53. Это означает, что DNS поддерживает как UDP, так и TCP. Однако все примеры, которые мы просмотрели с использованием tcpdump, использовали UDP. Когда используется тот или иной протокол и почему?

Когда разборщик выдает запрос и возвращается отклик с установленным битом TC (обрезано - truncated), это означает, что размер отклика превысил 512 байт, только первые 512 байт были возвращены серверу. Разборщик обычно отправляет запрос снова, но уже с использованием TCP. При этом возвращается больше, чем 512 байт. (Вспомните описание максимального размера UDP датаграммы в разделе "Максимальный размер UDP датаграммы" главы 11.) Так как TCP делит поток данных на части, которые называются сегментами, он может передать любое количество пользовательских данных с использованием нескольких сегментов.

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

Так как DNS в основном использует UDP, и разборщик, и сервер DNS должны отработать свой собственный тайм-аут и осуществить повторную передачу. В отличие от других приложений Internet, которые используют UDP (TFTP, BOOTP и SNMP) и которые функционируют обычно в локальных сетях, DNS отправляет запросы и получает отклики обычно по глобальным сетям. Процент потерянных пакетов и разница в значениях времен возврата обычно значительно выше в глобальных сетях, нежели в локальных, при этом повышается важность надежной передачи и четкости работы алгоритма расчета тайм-аутов для клиентов DNS.

Еще один пример

Давайте рассмотрим еще один пример, который описывает несколько функций DNS, о которых мы рассказали раньше. Мы запустили клиента Rlogin, подсоединившись к Rlogin серверу в каком-то удаленном домене. На рисунке 14.16 показан обмен пакетами.

Рисунок 14.16 Обмен пакетами при старте Rlogin клиента и сервера.

Было осуществлено 11 шагов, при этом заранее никакой информации на клиенте или сервере кэшировано не было:

  1. Клиент стартует и вызывает свою функцию разборщика, чтобы конвертировать имя хоста, которое мы ввели вместо IP адреса. Запрос типа A отправляется на корневой сервер.
  2. Ответ от корневого сервера содержит DNS сервера для домена в котором находится Rlogin сервер.
  3. Разборщик клиента повторно отправляет запрос типа A на DNS сервер. Этот запрос обычно имеет установленный флаг "рекурсия необходима".
  4. Приходит отклик с IP адресом хоста.
  5. Клиент Rlogin устанавливает TCP соединение с сервером Rlogin. (В главе 18 этот процесс описывается более подробно.) TCP модули клиента и сервера обмениваются друг с другом тремя пакетами.
  6. Сервер Rlogin принимает соединение от клиента и вызывает свой разборщик, чтобы получить имя хоста клиента по IP адресу, который сервер получил от своего TCP. Это PTR запрос, выданный на корневой DNS сервер. Может быть, это не тот корневой сервер, к которому обратился клиент в шаге 1.
  7. Отклик корневого сервера содержит имя DNS сервера домена in-addr.arpa клиента.
  8. Разборщик сервера повторно отправляет PTR запрос к DNS серверу клиента.
  9. PTR отклик содержит FQDN хоста клиента.
  10. Разборщик сервера отправляет запрос типа A к DNS серверу клиента, спрашивая IP адрес, соответствующий имени, возвращенному в предыдущем шаге. Это может быть сделано автоматически с использованием функции сервера gethostbyaddr, как мы описали в разделе "Запросы указателя" этой главы, или сервер Rlogin осуществляет этот шаг самостоятельно. Также, DNS сервер клиента часто тот же самый, что и DNS сервер клиента in-addr.arpa, однако это необязательно.
  11. Отклик от DNS сервера клиента содержит запись A для хоста клиента. Сервер Rlogin сравнивает запись A с IP адресом клиента, потребовавшего открыть TCP соединение.

Кэширование может уменьшить количество пакетов, которыми произошел обмен в этом примере.

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

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

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

Все DNS запросы и отклики имеют один и тот же формат. Эти сообщения содержат записи ресурсов (RR) вопросов и, возможно, ответов, RR полномочий и RR дополнительной информации. Мы рассмотрели множество примеров, в которых показана конфигурация разборщика и некоторые принципы организации DNS: указатели на имена доменов (чтобы уменьшить размер сообщений), кэширование, домен in-addr.arpa (поиск имени по заданному IP адресу) и возвращаемые дополнительные RR (для того чтобы запрашивающий не выдавал повторный запрос).

Упражнения

  1. Классифицируйте DNS разборщик и DNS сервер как с точки зрения модели "клиент-сервер".
  2. Объясните назначение всех 75 байт в отклике на рисунке 14.12.
  3. В разделе "Примеры широковещательных запросов" главы 12 мы говорили, что приложения, которые понимают как IP адреса в виде десятичных цифр, разделенных точками, так и имена хостов, должны воспринимать сначала IP адреса, а затем имена хостов, в том случае, если получить IP адрес не удалось. Что произойдет, если порядок проверки будет изменен на обратный?
  4. Каждая UDP датаграмма имеет длину. Процессу, который получает UDP датаграмму, сообщается ее длина. Разборщик может выдать запрос с использованием TCP вместо UDP. TCP это поток байтов без каких-либо маркеров записи. Как приложение может узнать, сколько данных возвращено? Обратите внимание, что в DNS заголовке нет поля длины (рисунок 14.3). (Подсказка: обратитесь к RFC 1035.)
  5. Мы говорили, что сервер DNS должен знать IP адреса корневых серверов, и что эта информация доступна через анонимный FTP. К сожалению, не все системные администраторы обновляют свои DNS файлы, если изменяется список корневых серверов. (В списке корневых серверов иногда происходят изменения, однако нечасто.) Как Вы думаете, каким образом DNS обрабатывает подобную ситуацию?
  6. Получите файл, упомянутый в упражнении 8 главы 1, и определите, кто несет ответственность за обслуживание корневых серверов DNS. Как часто обновляется информация на корневых серверах?
  7. В чем заключается проблема обслуживания кэша на сервере DNS и какую роль в этом играет разборщик?
  8. При обсуждении рисунка 14.10 мы сказали, что DNS сервер сортирует записи A таким образом, что адреса, принадлежащие сети, в которой находится запрашивающий хост, становятся первыми. Кто должен сортировать записи A: сервер DNS или разборщик?


TFTP: простой протокол передачи данных

Глава 15 TFTP: простой протокол передачи данных

Введение

TFTP - это простой протокол передачи файлов. Он, как правило, используется при загрузке бездисковых систем (рабочие станции или X терминалы). В отличие от протокола передачи файлов (FTP - File Transfer Protocol), который мы опишем в главе 27 и который использует TCP, TFTP использует UDP. Это сделано для того, чтобы протокол был как можно проще и меньше. Реализации TFTP (и необходимого UDP, IP и драйвера устройства) могут поместиться в постоянной памяти (ПЗУ).

В этой главе приводится только краткое описание TFTP, потому что в следующей главе мы рассмотрим протокол Bootstrap, который использует TFTP. Мы использовали протокол TFTP, когда загружали из сети хост sun (см. рисунок 5.1). Он выдавал TFTP запрос, после того как получил свой IP адрес с использованием RARP.

RFC 1350 [Sollins 1992] является официальной спецификацией TFTP версия 2. Глава 12 [Stevens 1990] предоставляет полные исходные коды реализации TFTP клиента и сервера и описывает некоторую технику программирования, использованную в TFTP.

Протокол

Обмен между клиентом и сервером начинается с того, что клиент запрашивает сервер либо прочитать, либо записать файл для клиента. В стандартном варианте загрузки бездисковой системы первый запрос - это запрос на чтение (RRQ). На рисунке 15.1 показан формат пяти сообщений TFTP. (Коды операций 1 и 2 имеют одинаковый формат.)

Первые 2 байта TFTP сообщения это код операции (opcode). В запросе на чтение (RRQ) и в запросе на запись (WRQ) имя файла (filename) указывает файл на сервере, который клиент хочет либо считать, либо записать. Мы специально показали на рисунке 15.1, что это имя файла оканчивается нулевым байтом. Режим (mode) это ASCII строка: netascii или octet (любая комбинация больших или маленьких букв), которая также оканчивается байтом 0. netascii означает, что данные являются строками ASCII текста, причем каждая строка оканчивается 2-символьной последовательностью возврата каретки, за которой следует пропуск строки (обозначается - CR/LF).

Рисунок 15.1 Формат пяти TFTP сообщений.

И клиент и сервер должны иметь возможность осуществить преобразование между этим форматом и каким-либо другим (другой разделитель строк), который используется на локальном хосте. Передача octet обозначает, что данные будут передаваться в виде 8-битных байтов без интерпретации.

Каждый пакет данных содержит номер блока (block number), который затем используется в пакете подтверждении. В качестве примера скажем, что когда необходимо осуществить чтение файла, клиент посылает запрос на чтение (RRQ), указывая имя файла и режим. Если файл может быть прочитан клиентом, сервер отвечает пакетом данных с номером блока равным 1. Клиент посылает подтверждение (ACK) на номер блока 1. Сервер отвечает следующим пакетом данных с номером блока равным 2. Клиент подтверждает номер блока 2. Это продолжается до тех пор, пока файл не будет передан. Каждый пакет данных содержит 512 байт данных, за исключением последнего пакета, который содержит от 0 до 511 байт данных. Когда клиент получает пакет данных, который содержит меньше чем 512 байт, он считает, что получил последний пакет.

В случае запроса на запись (WRQ) клиент посылает WRQ, указывая имя файла и режим. Если файл может быть записан клиентом, сервер отвечает подтверждением (ACK) с номером блока равным 0. Клиент посылает первые 512 байт файла с номером блока равным 1, сервер отвечает ACK с номером блока равным 1.

Этот тип передачи данных называется протоколом с остановкой и ожиданием подтверждения (stop-and-wait) . Он используется только в простых протоколах, таких как TFTP. Мы увидим в разделе "Изменение размера окна" главы 20, что TCP предоставляет другую форму подтверждений, которая позволяет добиться более высокой пропускной способности. TFTP разработан таким образом, чтобы реализация была как можно проще, а не для того чтобы повысить пропускную способность.

Последний тип TFTP сообщений это сообщения об ошибках, код операции (opcode) равен 5. Это как раз то, чем сервер отвечает в том случае, если запрос на чтение или запись не может быть обработан. Ошибки чтения или записи в течении передачи файла также приводят к тому, что отправляется сообщение об ошибке, при этом передача прекращается. Номер ошибки (error number) содержит цифровой код ошибки, за которым следует сообщение об ошибке в ASCII формате, которое может содержать дополнительную информацию предоставляемую операционной системой.

Так как TFTP использует ненадежный UDP, то именно от TFTP зависит, как будут обработаны потерянные и дублированные пакеты. В случае потери пакета, отправитель отрабатывает тайм-аут и осуществляет повторную передачу. (Возможно появление проблемы, называемой "синдромом новичка" (sorcerer's apprentice syndrome), которая может возникнуть, если с обеих сторон будет отработан тайм-аут и осуществлена повторная передача. Раздел 12.2 [Stevens 1990] показывает, в результате чего может возникнуть подобная проблема.) Как и в большинстве UDP приложений, контрольная сумма TFTP сообщения не расчитывается, а это означает, что любое повреждение данных может быть определено только с помощью контрольной суммы UDP (см. главу 11, раздел "Контрольная сумма UDP").

Пример

Давайте посмотрим, как работает протокол TFTP. Мы запустим TFTP клиента на хосте bsdi и получим текстовый файл с хоста svr4:

bsdi % tftp svr4                      стартуем TFTP клиента
tftp> get test1.c                     получаем файл с сервера
Received 962 bytes in 0.3 seconds
tftp> quit                            разрываем соединение

bsdi % ls -l test1.c                  сколько байт в полученном файле?
-rw-r--r-- 1 rstevens staff 914 Mar 20 11:41 test1.c

bsdi % wc -l test1.c                  и сколько строк?
48 test1.c

Первый момент, который бросается в глаза, заключается в том, что файл в Unix содержит 914 байт, однако TFTP передает 962 байта. Воспользовавшись программой wc, мы увидим, что в файле 48 строк, таким образом, 48 символов новой строки в Unix были дополнены до 48 пар CR/LF, так как TFTP по умолчанию осуществляет передачу в режиме netascii.

На рисунке 15.2 показан обмен пакетами.

1  0.0                    bsdi.1106 > svr4.tftp: 19 RRQ "test1.c"

2  0.287080 (0.2871)     svr4.1077 > bsdi.1106: udp 516
3  0.291178 (0.0041)     bsdi.1106 > svr4.1077: udp 4

4  0.299446 (0.0083)     svr4.1077 > bsdi.1106: udp 454
5  0.312320 (0.0129)     bsdi.1106 > svr4.1077: udp 4

Рисунок 15.2 Обмен пакетами в случае TFTP.

В строке 1 показан запрос на чтение от клиента к серверу. Порт назначения UDP для TFTP - заранее известный порт 69, tcpdump просматривает TFTP пакеты и печатает RRQ и имя файла. Длина UDP данных печатается как 19 байт и получается следующим образом: 2 байта - код операции, 7 байт - имя файла, 1 байт равный 0, 8 байт для netascii и еще 1 байт равный 0.

Следующий пакет приходит от сервера (строка 2) и содержит 516 байт: 2 байта - код операции, 2 байта - номер блока и 512 байт данных. Строка 3 - подтверждение на эти данные: 2 байта - код операции и 2 байта - номер блока.

И последний пакет данных (строка 4) содержит 450 байт данных. 512 байт данных в строке 2 и эти 450 байт составляют 962 байта данных, полученные клиентом.

Обратите внимание, что tcpdump не печатает дополнительную информацию о протоколе TFTP для строк 2 - 5, как он это сделал, интерпретировав TFTP сообщение в строке 1. Это происходит потому, что номер порта сервера сменился между строками 1 и 2. Протокол TFTP требует, чтобы клиент отправил первый пакет (RRQ или WRQ) на заранее известный порт UDP сервера (69). Сервер находит какой-либо неиспользуемый динамически назначаемый порт (1077 на рисунке 15.2), который затем используется сервером для дальнейшего обмена пакетами между клиентом и сервером. Номер порта клиента (1106 в данном примере) не меняется. tcpdump не имеет представления о том, что порт 1077 на хосте svr4 используется TFTP сервером.

Причина, по которой меняется номер порта сервера, заключается в том, что сервер не должен захватывать заранее известный порт на большой промежуток времени, требуемый на передачу файла (что может занять от нескольких секунд до нескольких минут). Вместо этого, заранее известный порт остается доступным для других TFTP клиентов, которые могут послать туда свой запрос. Тогда как в это время передача осуществляется через другой порт.

Обратимся к рисунку 10.6. RIP сервер, которому необходимо послать клиенту более чем 512 байт, отправляет обе UDP датаграммы с заранее известного порта сервера. В случае TFTP (из-за отличий в протоколе), долговременного взаимодействия между клиентом и сервером не осуществляется (которое, как мы сказали, может занимать от секунд до минут). Если один процесс сервера будет использовать заранее известный порт все время, пока осуществляется передача файла, возникнет необходимость отказать всем последующим запросам, которые придут от других клиентов, или один процесс сервера должен иметь возможность осуществлять множественную передачу файлов нескольким клиентам в одно и то же время с одного и того же порта (69). Простейшее решение заключается в том, что сервер переходит на другой порт, после того как получил RRQ или WRQ. Клиент определяет новый порт, когда он получает первый пакет данных (строка 2 на рисунке 15.2), а затем посылать все последующие подтверждения (строки 3 и 5) на новый порт.

В разделе "Пример" главы 16 мы увидим, как TFTP используется при загрузке X терминалов.

Безопасность

Обратите внимание на то, что TFTP пакеты (рисунок 15.1) не содержат никаких данных об имени пользователя или пароле. Это брешь в секретности характерная для TFTP. Так как TFTP был разработан для использования в процессе загрузки, он не предоставляет возможности передать имя пользователя и пароль.

Эта характеристика TFTP была использована многими хакерами, чтобы получить копии файла паролей из Unix и затем расшифровать пароли. Чтобы предотвратить подобный доступ, большинство TFTP серверов в настоящее время регламентируют, какие файлы могут быть получены с использованием TFTP (как правило, файлы из директории /tftpboot в Unix системах). Эта директория содержит только загрузочные файлы, необходимые бездисковым системам.

Для дополнительной безопасности TFTP сервер, на Unix системе, обычно устанавливает свой пользовательский идентификатор (UID) и идентификатор группы (GID) в значения, которые не могут быть назначены реальному пользователю. Это позволяет доступ только к файлам, которые доступны для чтения и записи всем.

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

TFTP - это простой протокол, разработанный таким образом, чтобы помещаться в ПЗУ и быть использованным только в процессе загрузки бездисковых систем. Он использует небольшое количество форматов сообщений и протокол с остановкой и ожиданием подтверждения.

Чтобы позволить нескольким клиентам загружаться одновременно, TFTP сервер предоставляет несколько форм одновременной работы. Так как UDP не предоставляет уникального соединения между клиентом и сервером (как это делает TCP), TFTP сервер создает новый UDP порт для каждого клиента. Это позволяет разным клиентам выдавать датаграммы, которые будут демультиплексированы UDP модулем сервера, на основе номеров портов назначения, вместо того чтобы это делал сам сервер.

Протокол TFTP не предоставляет средства безопасности. Большинство реализаций позволяет доступ по протоколу TFTP только к файлам, которые необходимы при загрузке.

В главе 27 мы рассмотрим протокол передачи файлов (FTP - File Transfer Protocol), который разработан для общих целей, а также обеспечивает высокую пропускную способность при передаче файлов.

Упражнения

  1. Прочитайте требования к хостам Host Requirements RFC, чтобы посмотреть, что должен делать TFTP сервер, если он получит запрос, IP адрес назначения которого является широковещательным адресом.
  2. Как Вы думаете, что произойдет, когда номер блока TFTP изменится с 65535 на 0? Говорится ли что-нибудь по этому поводу в RFC 1350?
  3. Мы сказали, что TFTP отправитель осуществляет тайм-аут и повторную передачу, чтобы отработать потерю пакетов. Как это отразится на функционировании TFTP, когда он используется как часть процесса загрузки?
  4. Что является ограничивающим фактором по времени при передаче файла с использованием TFTP?


BOOTP: протокол загрузки

Глава 16 BOOTP: протокол загрузки

Введение

В главе 5 мы описали, как в момент загрузки бездисковая система может определить свой IP адрес с использованием RARP. При использовании RARP возникает две проблемы: (1) единственное, что можно узнать, это IP адрес, и (2) так как RARP использует широковещательный запрос на канальном уровне, запросы RARP не перенаправляются маршрутизаторами (поэтому RARP сервер должен находиться в каждой физической сети). В этой главе мы опишем альтернативный метод загрузки бездисковых систем, который называется протоколом загрузки (BOOTP - Bootstrap Protocol).

BOOTP использует UDP и обычно используется совместно с TFTP (глава 15). Официальная спецификация BOOTP приведена в RFC 951 [Croft and Gilmore 1985], пояснения даются в RFC 1542 [Wimer 1993].

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

BOOTP запросы и отклики инкапсулируются в UDP датаграммы, как показано на рисунке 16.1.

Рисунок 16.1 Инкапсуляция запросов и откликов BOOTP в UDP датаграмму.

На рисунке 16.2 приведен формат 300-байтного BOOTP запроса и отклика.

Рисунок 16.2 Формат BOOTP запроса и отклика.

Код операции (opcode) равен 1 для запроса и 2 для отклика. Поле типа аппаратного адреса (hardware type) равно 1 для Ethernet 10 Мбит/сек, это же значение находится в поле с таким же именем в ARP запросе или отклике (см. рисунок 4.3). Длина аппаратного адреса (hardware address length) равна 6 байтам, как и для Ethernet.

Счетчик пересылок (hop count) устанавливается клиентом в 0, однако может быть использован уполномоченным сервером (описано в разделе "BOOTP через маршрутизаторы" этой главы).

Идентификатор транзакции (transaction ID) - 32-битное целое число, которое устанавливается клиентом и возвращается сервером. Оно позволяет клиенту сопоставить отклик с запросом. Клиент устанавливает в это поле случайное число для каждого запроса.

В поле количество секунд (number of seconds) клиент записывает время, когда он предпринял первую попытку загрузиться. На основании значения этого поля вторичный сервер делает вывод о том, что первичный сервер не доступен. (Вторичный сервер делает подобный вывод если величина в поле количества секунд достигла определенного значения.)

Если клиент уже знает свой IP адрес, он заполняет поле IP адрес клиента (client IP address). Есле нет - клиент устанавливает это значение в 0. В последнем случае сервер вставляет в поле ваш IP адрес (your IP address) IP адрес клиента. Поле IP адрес сервера (server IP address) заполняется сервером. Если используется уполномоченный сервер (раздел "BOOTP через маршрутизаторы" этой главы), он заполняет IP адрес шлюза (gateway IP address).

Клиент должен установить свой аппаратный адрес клиента (client hardware address). Это то же значение, которое находиться в заголовке Ethernet и в поле UDP датаграммы, благодаря чему оно становится доступным любому пользовательскому процессу (например, серверу BOOTP), который получил датаграмму. Обычно процессу, работающему с UDP датаграммами, сложно или практически невозможно определить значение, находящееся в поле заголовка Ethernet датаграммы, в которой передается UDP датаграмма.

Имя хоста сервера (server hostname) это строка, которая заполняется сервером (не обязательно). Сервер также может заполнить поле имени загрузочного файла (boot filename). В это поле заносится полный путь к файлу, который используется при загрузке.

Область производителя (vendor-specific) используется для различных расширений BOOTP. В разделе "Информация производителя" этой главы описываются некоторые из этих расширений.

Когда клиент стартует с использованием BOOTP (код операции равен 1), запрос обычно рассылается с помощью широковещательного сообщения канального уровня, при этом IP адрес назначения в IP заголовке обычно установлен в 255.255.255.255 (ограниченный широковещательный запрос, глава 12, раздел "Широковещательные запросы"). IP адрес источника - 0.0.0.0, так как клиент еще не знает своего IP адреса. Обратитесь к рисунку 3.9, где показано, что 0.0.0.0 это разрешенный IP адрес источника, используемый в момент загрузки источника.

Номера портов

Для BOOTP выделено два заранее известных порта: 67 для сервера и 68 для клиента. Это означает, что клиент не выбирает неиспользуемый динамически назначаемый порт, а использует порт номер 68. Причина, по которой были выбраны два номера портов, вместо того чтобы использовать только один для сервера, заключается в том, что сервер может отправить отклик (хотя обычно он этого не делает) широковещательным образом.

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

Если клиент воспользуется заранее известным портом сервера (67), все сервера в сети будут вынуждены просматривать каждый широковещательный отклик. (Если все сервера были "разбужены", им придется проверить код операции, определить, что это отклик, а не запрос, и снова "уснуть".) Поэтому выбор был остановлен на том, как все сделано сейчас, то есть клиент имеет свой собственный единственный заранее известный порт, который отличается от заранее известного порта сервера.

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

Пример

Давайте рассмотрим, как работает BOOTP при загрузке X терминала. На рисунке 16.3 показан вывод команды tcpdump. (Имя клиента proteus, а имя сервера mercury. Вывод этой команды tcpdump был получен в другой сети, в отличие от всех других примеров, приведенных в тексте.)

1  0.0                        0.0.0.0.68 > 255.255.255.255.bootp:
                              secs:100 ether 0:0:a7:0:62:7c
2  0.355446 (0.3554)         mercury.bootp > proteus.68: secs:100 Y:proteus
                              S:mercury G:mercury ether 0:0:a7:0:62:7c
                              file "/local/var/bootfiles/Xncd19r"

3  0.355447 (0.0000)         arp who-has proteus tell 0.0.0.0
4  0.851508 (0.4961)         arp who-has proteus tell 0.0.0.0
5  1.371070 (0.5196)         arp who-has proteus tell proteus

6  1.863226 (0.4922)         proteus.68 > 255.255.255.255.bootp:
                              secs:100 ether 0:0:a7:0:62:7c
7  1.871038 (0.0078)         mercury.bootp > proteus.68: secs:100 Y:proteus
                              S:mercury G:mercury ether 0:0:a7:0:62:7c
                              file "/local/var/bootfiles/Xncd19r"

8  3.871038 (2.0000)         proteus.68 > 255.255.255.255.bootp:
                              secs:100 ether 0:0:a7:0:62:7c
9  3.878850 (0.0078)         mercury.bootp > proteus.68: secs:100 Y:proteus
                              S:mercury G:mercury ether 0:0:a7:0:62:7c
                              file "/local/var/bootfiles/Xncd19r"

10  5.925786 (2.0469)         arp who-has mercury tell proteus
11  5.929692 (0.0039)         arp reply mercury is-at 8:0:2b:28:eb:1d

12  5.929694 (0.0000)         proteus.tftp > mercury.tftp: 37 RRQ
                              "/local/var/bootfiles/Xncd19r"
13  5.996094 (0.0664)         mercury.2352 > proteus.tftp: 516 DATA block 1
14  6.000000 (0.0039)         proteus.tftp > mercury.2352: 4 ACK

                                 в этом месте удалено много строк

15  14.980472 (8.9805)        mercury.2352 > proteus.tftp: 516 DATA block 2463
16  14.984376 (0.0039)        proteus.tftp > mercury.2352: 4 ACK
17  14.984377 (0.0000)        mercury.2352 > proteus.tftp: 228 DATA block 2464
18  14.984378 (0.0000)        proteus.tftp > mercury.2352: 4 ACK

Рисунок 16.3 Пример использования BOOTP при загрузке X терминала.

В строке 1 мы видим, что запрос от клиента 0.0.0.0.68 направляется на 255.255.255.255.67. Единственные поля, которые заполнил клиент, это количество секунд и Ethernet адрес. Мы увидим, что клиент всегда устанавливает количество секунд в значение 100. Счетчик пересылок и идентификатор транзакции установлены в 0, поэтому они не присутствуют в выводе tcpdump. (Идентификатор транзакции, установленный в 0, означает, что клиент игнорирует это поле, иначе, если бы он хотел проверить это значение в отклике, он установил бы это поле в случайную величину.)

В строке 2 показан отклик от сервера. Поля, заполненные сервером, это IP адрес клиента (который tcpdump напечатал как имя proteus), IP адрес сервера (напечатан как имя mercury), IP адрес шлюза (напечатан как имя mercury) и имя загрузочного файла.

После получения BOOTP отклика клиент немедленно отправляет ARP запрос, чтобы посмотреть, не использует ли кто-либо в сети такой же IP адрес. Имя proteus, за которым следует who-has, соответствует IP адресу адресата (рисунок 4.3), а IP адрес отправителя устанавливается в 0.0.0.0. Он отправляет еще один идентичный ARP запрос через 0,5 секунды и еще один опять же через 0,5 секунды. В третьем ARP запросе (строка 5) клиент изменяет IP адрес отправителя, и устанавливает его в собственный IP адрес. Это так называемый "беспричинный" ARP запрос (глава 4, раздел "Беспричинный ARP").

В строке 6 показано, что клиент ожидает еще 0,5 секунды, после чего рассылает широковещательным запросом еще один BOOTP запрос. Единственное отличие между этим запросом и запросом, показанным в строке 1, заключается в том, что сейчас клиент помещает свой собственный IP адрес в IP заголовок. Он получает тот же самый отклик от того же самого сервера (строка 7). Клиент ждет еще 2 секунды и отправляет широковещательным образом еще один BOOTP запрос (строка 8) и снова получает тот же самый отклик от того же самого сервера.

Затем клиент ждет еще 2 секунды и отправляет ARP запрос на сервер mercury (строка 10). После того как получен ARP отклик, клиент незамедлительно отправляет TFTP запрос на чтение своего загрузочного файла (строка 12). Затем следуют 2464 пакета TFTP данных и подтверждений. Полный размер переданных данных составляет 512 х 2463 + 224 = 1261280 байт. Таким образом, в X терминал загружается операционная система. Мы удалили с рисунка 16.3 большинство строк, имеющих отношение к TFTP.

Необходимо обратить внимание на то, что этот пример TFTP обмена отличается от приведенного на рисунке 15.2 тем, что здесь клиент использует заранее известный порт TFTP (69) во время всей передачи. Так как один из трех партнеров использует порт 69, tcpdump знает, что пакеты это TFTP сообщения, поэтому он может интерпретировать каждый пакет с использованием TFTP протокола. Именно поэтому на рисунке 16.3 указано, какие из пакетов содержат данные, какие содержат подтверждения и каков номер блока для каждого пакета. Мы не могли получить эту дополнительную информацию на рисунке 15.2, потому что ни одна из сторон не использовала заранее известный порт TFTP для передачи данных. Обычно, TFTP клиент не может использовать заранее известный порт TFTP, так как этот порт используется сервером в многопользовательской системе. Однако здесь система только загружается, TFTP сервис не предоставляется, что позволяет клиенту использовать порт в течении всего времени передачи. Также из примера видно, что TFTP сервер на mercury не заботится о том, каков номер порта клиента - он отправляет на порт клиента данные вне зависимости от его номера.

На рисунке 16.3 мы видим, что 1261280 байт передано за 9 секунд. При этом скорость передачи составляет примерно 140000 байт в секунду. Несмотря на то, что это медленней, чем при передаче файлов с использованием FTP по Ethernet, это совсем неплохо для простого протокола с остановкой и ожиданием подтверждения, такого как TFTP.

После того как X терминал загрузился, осуществляется дополнительная передача файлов посредством TFTP, содержащих шрифты терминала, осуществляются некоторые запросы к DNS серверам и затем инициализация X протокола. Полное время загрузки на рисунке 16.3 составило почти 15 секунд, еще 6 секунд было истрачено на последующие шаги. Это означает, что бездисковый X терминал загрузился за 21 секунду.

Сервер BOOTP

BOOTP клиент обычно "живет" в постоянной памяти бездисковой системы. Однако нам интересно посмотреть, как обычно реализуется сервер.

Во-первых, сервер читает UDP датаграммы с заранее известного порта (67). Специальных средств не требуется. В этом заключается отличие от RARP сервера (см. главу 5, раздел "Реализация RARP сервера"), который, как мы говорили, читает Ethernet фреймы, у которых в поле типа установлено "RARP запрос" (RARP request). Протокол BOOTP также позволяет серверу легко получить аппаратный адрес клиента, поместив его в BOOTP пакет (см. рисунок 16.2).

Тут возникает интересная проблема: как сервер может послать отклик непосредственно клиенту? Отклик находится в UDP датаграмме, и сервер знает IP адрес клиента (который обычно считывается из конфигурационного файла на сервере). Однако, если клиент отправил UDP датаграмму на этот IP адрес (это обычный способ обработки вывода UDP), хост сервера, возможно, выдаст ARP запрос для этого IP адреса. Однако, клиент не может ответить на ARP запрос, так как он еще не знает своего IP адреса! (Проблема подробно описана в RFC 951.)

Существует два решения. Первое, которое обычно используется в Unix серверах, заключается в том, что сервер выдает ioctl (2) запрос в ядро, чтобы поместить определенный пункт в ARP кэш для этого клиента. (Это как раз то, что делает команда arp -s, глава 4, раздел "Команда arp".) Сервер может так поступить, так как он знает аппаратный адрес клиента и его IP адрес. Это означает, что когда сервер посылает UDP датаграмму (BOOTP отклик), ARP модуль сервера может найти IP адрес клиента в ARP кэше.

Альтернативное решение этой проблемы заключается в том, что сервер рассылает BOOTP отклик широковещательным запросом, вместо того чтобы посылать его непосредственно клиенту. Для того чтобы уменьшить количество широковещательных запросов в сети, это решение должно быть использовано только в том случае, когда сервер не может поместить пункт в свой ARP кэш. Обычно, для того чтобы ввести пункт в ARP кэш, необходимы права суперпользователя, а рассылка широковещательных откликов сервером не требует дополнительных привилегий.

BOOTP через маршрутизаторы

В разделе "Реализация RARP сервера" главы 5 мы сказали, что одним из неприятных свойств RARP является то, что он использует широковещательные запросы канального уровня, которые обычно не перенаправляются маршрутизаторами. А это означает, что необходимо иметь RARP сервер в каждой физической сети. BOOTP может работать через маршрутизатор, если маршрутизатор поддерживает этот протокол. (Большинство производителей маршрутизаторов добавляют поддержку этой характеристики.)

Обычно это необходимо для бездисковых маршрутизаторов, потому что если в качестве маршрутизатора используется многопользовательская система с диском, она может сама запустить BOOTP сервер. Однако все BOOTP сервера на основе Unix (приложение F) поддерживают этот режим, но, повторим снова, если Вы можете запустить BOOTP сервер в одной физической сети, нет необходимости перенаправлять запросы от другого сервера из другой сети.

Что произойдет, если маршрутизатор (также называемый "BOOTP агент" (BOOTP relay agent)) слушает BOOTP запросы на заранее известном порту сервера (67). Когда запрос принят, агент помещает свой IP адрес в поле IP адреса шлюза (gateway IP address) в BOOTP запросе и отправляет запрос на реальный BOOTP сервер. (Адрес, помещенный агентом в поле шлюза, это IP адрес интерфейса, на который был принят запрос.) Агент также увеличивает на единицу значение в поле пересылок. (Это делается для того, чтобы предотвратить зацикливание, если запрос будет повторно перенаправлен. В RFC 951 говориться, что запрос должен быть отброшен, если счетчик пересылок достигнет значения 3.) Так как исходящий запрос это датаграмма с персональным адресом (исходный запрос от клиента был широковещательным), он может пройти по любому маршруту к любому BOOTP серверу, проходя через другие маршрутизаторы. Реальный сервер получает запрос, формирует BOOTP отклик и отправляет его назад агенту, а не клиенту. Реальный сервер знает что запрос был перенаправлен, так как значение в поле шлюза ненулевое. Агент получает отклик и отправляет его клиенту.

Информация производителя

На рисунке 16.2 мы показали 64-байтную область производителя. В RFC 1533 [Alexander and Droms 1993] определен формат этой области. Область производителя содержит необязательную информацию для сервера, которую он может вернуть клиенту.

Если информация присутствует, в первые 4 байта этой области устанавливается IP адрес 99.130.83.99. Это означает, что в области присутствует дополнительная информация.

Вся остальная область занята списком пунктов. Каждый пункт начинается с 1-байтового поля признака. Два из пунктов состоят только из поля признака: признак равный 0 это байт заполнения, а признак равный 255 обозначает конец пунктов. Все байты, расположенные после первого конечного байта, должны быть установлены в значение 255.

Все остальные пункты, кроме этих 1-байтовых пунктов, состоят из единичного байта, за которым следует информация. На рисунке 16.4 показан формат некоторых пунктов в области производителя.

Рисунок 16.4 Формат некоторых пунктов в области производителя.

Маска подсети и значение времени это пункты с фиксированной длиной, потому что их значение всегда занимает 4 байта. Смещение времени - это количество секунд после полуночи 1 января 1900 года, UTC.

Пункт шлюза это один из пунктов с переменной длиной. Длина всегда кратна 4, а значение - это 32-битные IP адреса одного или нескольких шлюзов (маршрутизаторов), которыми пользуется клиент. Первый из возвращенных адресов должен быть адресом предпочтительного шлюза.

В RFC 1533 определено еще 14 пунктов. Возможно, наиболее важный из них - это IP адрес DNS сервера со значением признака равным 6. В других пунктах содержатся IP адрес сервера печати, IP адрес сервера времени и так далее. Обратитесь к RFC, где приведены все подробности.

В примере, приведенном на рисунке 16.3, мы не видим ICMP запроса маски адреса (глава 6, раздел "ICMP запрос и отклик маски адреса"), который может быть разослан клиентом широковещательным сообщением для определения собственной маски подсети. Его также не было в выводе tcpdump, поэтому мы можем предположить, что маска подсети клиента была передана в области производителя в отклике BOOTP.

Требования к хостам Host Requirements RFC рекомендуют, чтобы система, использующая BOOTP, получала свою маску подсети с использованием BOOTP, а не ICMP.

Размер области производителя ограничен 64 байтами. Это требование выдвигается некоторыми приложениями. Новый протокол, протокол динамической конфигурации хостов (DHCP - Dynamic Host Configuration Protocol), заменяет собой BOOTP. В DHCP эта область увеличена до 312 байт, как определено в RFC 1541 [Droms 1993].

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

BOOTP использует UDP и разработан в качестве альтернативы RARP для загрузки бездисковых систем, которым необходимо определить свой IP адрес. BOOTP также может вернуть дополнительную информацию, такую как IP адрес маршрутизатора, маску подсети клиента и IP адрес DNS сервера.

Так как BOOTP используется в процессе загрузки, бездисковые системы должны иметь следующие протоколы в своей постоянной памяти: BOOTP, TFTP, UDP, IP и драйвер устройства для локальной сети.

Реализация BOOTP сервера значительно проще чем RARP сервера, так как BOOTP запросы и отклики используют UDP датаграммы, а не специальные фреймы канального уровня. Маршрутизаторы могут также выступать в роли уполномоченных агентов для реальных BOOTP серверов, перенаправляя запросы клиентов на реальные серверы в удаленные сети.

Упражнения

  1. Мы сказали, что одно из преимуществ BOOTP над RARP заключается в том, что BOOTP может работать через маршрутизаторы, тогда как RARP, который использует широковещательные запросы канального уровня, не может. В разделе "BOOTP через маршрутизаторы" этой главы, мы определили специальные способы, с помощью которых BOOTP может работать через маршрутизаторы. Что произойдет, если добавить маршрутизатору возможность перенаправлять RARP запросы?
  2. Мы сказали, что BOOTP клиент должен использовать идентификатор транзакции, чтобы сопоставить отклики с запросами, в том случае, если несколько клиентов загружаются в одно и то же время с одного и того же сервера, который использует широковещательные отклики. Однако на рисунке 16.3 идентификатор транзакции установлен в 0, что указывает на то, что этот клиент игнорирует идентификатор транзакции. Как Вы думаете, сопоставляет ли этот клиент отклики с запросами?


TCP: Transmission Control Protocol

Глава 17 TCP: Transmission Control Protocol

Введение

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

Описанию TCP посвящена эта глава, и следующие семь глав. В главе 18 будет рассказано, как устанавливаются и разрываются TCP соединения, в главе 19 и главе 20 мы рассмотрим обычную передачу данных, как в диалоговом использовании (удаленный заход), так и передачу файлов. В главе 21 мы подробно рассмотрим, как TCP осуществляет тайм-ауты и повторные передачи. И в главе 22 и главе 23 мы рассмотрим некоторые таймеры TCP. И в заключение, в главе 24 мы рассмотрим новые характеристики и производительность TCP.

Основная спецификация TCP находится в RFC 793 [Postel 1981c], однако некоторые ошибки этого RFC исправлены в требованиях к хостам Host Requirements RFC.

TCP сервисы

Несмотря на то, что TCP и UDP используют один и тот же сетевой уровень (IP), TCP предоставляет приложениям абсолютно другие сервисы, нежели UDP. TCP предоставляет основанный на соединении надежный сервис потока байтов.

Термин "основанный на соединении" (connection-oriented) означает, что два приложения, использующие TCP (как правило, это клиент и сервер), должны установить TCP соединение друг с другом, после чего у них появляется возможность обмениваться данными. Типичная аналогия - это набор телефонного номера, ожидание ответа от удаленного абонента, когда он говорит "алло", после чего необходимо сказать, кто звонит. В главе 18 мы рассмотрим, как устанавливается соединение и как это соединение разрывается через некоторое время, когда работа завершена.

Всегда существуют две конечные точки, которые общаются друг с другом с помощью TCP соединения. Концепции, о которых мы говорили в главе 12, широковещательная рассылка и групповая рассылка, не имеют отношения к TCP.

TCP обеспечивает свою надежность благодаря следующему:

  • Данные от приложения разбиваются на блоки определенного размера, которые будут отправлены. Это полностью отличается от UDP, в котором каждая запись, которую осуществляет приложение, генерирует IP датаграмму этого размера. Блок информации, который передается от TCP в IP, называется сегментом (segment). (См. рисунок 1.7.) В разделе "Максимальный размер сегмента" главы 18 мы посмотрим, как TCP определяет нужный размер сегмента.
  • Когда TCP посылает сегмент, он устанавливает таймер, ожидая, что с удаленного конца придет подтверждение на этот сегмент. Если подтверждение не получено по истечении времени, сегмент передается повторно. В главе 21 мы рассмотрим стратегию TCP, которую он реализует для тайм-аутов и повторных передач.
  • Когда TCP принимает данные от удаленной стороны соединения, он отправляет подтверждение. Это подтверждение не отправляется немедленно, а обычно задерживается на доли секунды, что мы обсудим в разделе "Задержанные подтверждения" главы 19.
  • TCP осуществляет расчет контрольной суммы для своего заголовка и данных. Это контрольная сумма, рассчитываемая на концах соединения, целью которой является выявить любое изменение данных в процессе передачи. Если сегмент прибывает с неверной контрольной суммой, TCP отбрасывает его и подтверждение не генерируется. (Ожидается, что отправитель отработает тайм-аут и осуществит повторную передачу.)
  • Так как TCP сегменты передаются в виде IP датаграмм, а IP датаграммы могут прибывать беспорядочно, также беспорядочно могут прибывать и TCP сегменты. После получения данных TCP может по необходимости изменить их последовательность, в результате приложение получает данные в правильном порядке.
  • Так как IP датаграмма может быть продублирована, принимающий TCP должен отбрасывать продублированные данные.
  • TCP осуществляет контроль потока данных. Каждая сторона TCP соединения имеет определенное пространство буфера. TCP на принимающей стороне позволяет удаленной стороне посылать данные только в том случае, если получатель может поместить их в буфер. Это предотвращает от переполнения буферов медленных хостов быстрыми хостами.

Между двумя приложениями по TCP соединению осуществляется обмен потоком 8-битовых байтов. Автоматически TCP не вставляет записи маркеров. Это называется сервисом потока байтов (byte stream service). Если приложение на одном конце записало сначала 10 байт, затем 20 байт и затем еще 50 байт, приложение на другом конце соединения не может сказать какого размера была каждая запись. На другом конце эти 80 байт могут быть считаны, например, за 4 раза по 20 байт за каждый раз. Один конец соединения помещает поток байт в TCP, и точно так же идентичный поток байт появляется на другом конце.

TCP не интерпретирует содержимое байтов. TCP понятия не имеет о том, происходит ли обмен двоичными данными, ASCII символами, EBCDIC символами или чем-либо еще. Эта интерпретация потока байтов осуществляется приложениями на каждой стороне соединения.

Это представление потока байтов протоколом TCP напоминает представление файла в операционной системе Unix. Ядро Unix не интерпретирует байты, которые приложение читает или записывает - это полностью зависит от приложений. Ядро Unix не видит разницы между бинарными файлами и файлами, содержащими строки текста.

Заголовок TCP

Вспомним, что данные TCP инкапсулируются в IP датаграммы, как показано на рисунке 17.1.

Рисунок 17.1 Инкапсуляция TCP данных в IP датаграмму.

На рисунке 17.2 показан формат TCP заголовка. Обычно его размер составляет 20 байт, если не присутствуют опции.

Рисунок 17.2 TCP заголовок.

Каждый TCP сегмент содержит номер порта (port number) источника и назначения, с помощью которых идентифицируются отправляющее и принимающее приложения. Эти два значения вместе с IP адресом источника и назначения в IP заголовке уникально идентифицируют каждое соединение.

Комбинация IP адреса и номера порта иногда называется сокетом (socket) . Этот термин появился в исходной спецификации TCP (RFC 793), а позднее стал использоваться в качестве имени программного интерфейса Berkeley (глава 1, раздел "Интерфейсы прикладного программирования"). Эта пара сокетов (содержащая IP адрес клиента, номер порта клиента, IP адрес сервера и номер порта сервера) указывает две конечные точки, которые уникально идентифицируют каждое TCP соединение.

Номер последовательности (sequence number) идентифицирует байт в потоке данных от отправляющего TCP к принимающему TCP. Если мы представим поток байтов, текущий в одном направлении между двумя приложениями, TCP нумерует каждый байт номером последовательности. Номер последовательности представляет собой 32-битное беззнаковое число, которое переходит через 0, по достижению значения 232 - 1.

При установлении нового соединения, взводится флаг SYN. Поле номера последовательности (sequence number field) содержит исходный номер последовательности (ISN - initial sequence number), который выбирается хостом для данного соединения. Номер последовательности первого байта данных, который посылается этим хостом, будет равен ISN плюс один, потому что флаг SYN занимает собой номер последовательности. (Подробно мы опишем, как в действительности устанавливается соединение и как соединение разрывается, в следующей главе, где увидим, что флаг FIN также занимает собой номер последовательности.)

Так как каждый байт, который участвует в обмене, пронумерован, номер подтверждения (acknowledgment number) это следующий номер последовательности, который ожидает получить отправитель подтверждения. Это номер последовательности плюс 1 последнего успешно принятого байта данных. Это поле принимается в рассмотрение, только если флаг ACK (описан ниже) взведен.

Отправка ACK не стоит ничего (это означает, что на подтверждение не тратится сегмент), потому что 32-битное поле номера подтверждения всегда является частью заголовка, так же как и флаг ACK. Мы увидим, что когда соединение установлено, это поле всегда установлено и флаг ACK всегда взведен.

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

TCP может быть описан как протокол с изменяющимся окном без селективных или отрицательных подтверждений. (Изменение окна, используемое при передаче данных, описано в разделе "Изменение размера окна" главы 20.) Мы сказали, что в TCP нет селективных подтверждений, потому что номер подтверждения в TCP заголовке означает, что отправитель успешно принял, все байты за исключением этого байта. Таким образом, в настоящее время не существует возможности подтвердить отдельно выбранную часть потока данных. Например, если байты 1-1024 приняты нормально, а следующий сегмент содержит байты 2049-3072, принимающий не может подтвердить этот новый сегмент. Все что он может - это послать ACK с номером подтверждения 1025. Также нет никакого смысла посылать отрицательные подтверждения на сегмент. Например, если сегмент с байтами 1025-2048 прибыл, однако была определена ошибка в контрольной сумме, все что может послать принимающий TCP это ACK с номером подтверждения равным 1025. В разделе "Быстрая повторная передача и алгоритм быстрого восстановления" главы 21 мы увидим, как дублированные подтверждения могут помочь определить потерю пакетов.

Длина заголовка (header length) содержит длину заголовка в 32-битных словах. Это объясняется тем, что длина поля опций переменная. С 4-битным полем у TCP есть ограничение на длину заголовка в 60 байт. Без опций, однако, стандартный размер составляет 20 байт.

В TCP заголовке существуют 6 флаговых битов. Один или несколько из них могут быть установлены в единицу в одно и то же время. Здесь мы кратко опишем их использование, а позже опишем каждый флаг более подробно в соответствующих главах.

URG

Указатель срочности (urgent pointer) будет рассмотрен в разделе "Режим срочности (Urgent Mode)" главы 20.

ACK

Номер подтверждения необходимо принять в рассмотрение (acknowledgment).

PSH

Получатель должен передать эти данные приложению как можно скорее (глава 20, раздел "Флаг PUSH").

RST

Сбросить соединение (глава 18, раздел "Сегменты сброса (Reset)").

SYN

Синхронизирующий номер последовательности для установления соединения. Этот следующий флаги описаны в главе 18.

FIN

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

Контроль потока данных TCP осуществляется на каждом конце с использованием размера окна (window size) . Это количество байт, начинающееся с указанного в поле номера подтверждения, которое приложение собирается принять. Это 16-битовое поле ограничивает размер окна в 65535 байт. В разделе "Опция масштабирования окна" главы 24 мы увидим, что опция масштабирования окна позволяет изменять это значение, при этом могут быть использованы окна большего размера.

Контрольная сумма (checksum) охватывает собой весь TCP сегмент: TCP заголовок и TCP данные. Это обязательное поле, которое должно быть рассчитано и сохранено отправителем, а затем проверено получателем. Контрольная сумма TCP рассчитывается так же как контрольная сумма UDP, с использованием псевдозаголовка, как описано в разделе "Контрольная сумма UDP" главы 11.

Указатель срочности (urgent pointer) действителен только в том случае, если установлен флаг URG. Этот указатель является положительным смещением, которое должно быть прибавлено к полю номера последовательности сегмента, чтобы получить номер последовательности последнего байта срочных данных. Режим срочности TCP это способ, с помощью которого отправитель передает срочные данные на удаленный конец. Мы рассмотрим эту характеристику в разделе "Режим срочности (Urgent Mode)" главы 20.

Наиболее общее поле опций - это опция максимального размера сегмента (MSS - maximum segment size). На каждом конце соединения эта опция обычно указывается в первом сегменте, с которого начинается обмен (сегмент с установленным флагом SYN, который используется для установления соединения). Она указывает на максимальный размер сегмента, который может быть принят отправителем. Мы опишем опцию MSS более подробно в разделе "Максимальный размер сегмента" главы 18, а некоторые другие опции TCP в главе 24.

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

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

TCP предоставляет надежный, ориентированный на соединения, и ориентированный на поток байтов сервис транспортного уровня. Мы коротко рассмотрели все поля TCP заголовка, подробное описание полей приведено в следующих главах.

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

TCP используется множеством популярных приложений, таких Telnet, Rlogin, FTP и электронная почта (SMTP).

Упражнения

  1. Мы рассмотрели следующие форматы пакетов, каждый из которых имеет контрольную сумму в своем заголовке: IP, ICMP, IGMP, UDP и TCP. Для каждого опишите, какая часть IP датаграммы покрывается контрольной суммой, и где контрольная сумма обязательна, а где нет.
  2. Почему все протоколы Internet, которые мы обсуждали (IP, ICMP, IGMP, UDP, TCP), молча отбрасывают пакеты, которые прибывают с ошибкой контрольной суммы?
  3. TCP предоставляет сервис потока байтов, где не происходит разграничения записей в процессе передачи между отправителем и получателем. Как могут приложения осуществить вставку своих собственных маркеров записей?
  4. Почему номера портов источника и назначения находятся в начале TCP заголовка?
  5. Почему TCP заголовок имеет поле длины заголовка, тогда как UDP заголовок (рисунок 11.2) не имеет?


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

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

Введение

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).

Рисунок 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, например, показывал, как сервер времени закрывает соединение.)

Рисунок 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 показано как это происходит.

Рисунок 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.

Рисунок 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 подтвержден, соединение считается полностью закрытым.

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

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

sun % rsh bsdi sort < datafile

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

Рисунок 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) и ожидает прибытия другого запроса на соединение.

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

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

Рисунок 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.201662 (  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
8  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.

Рисунок 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 показаны изменения состояний.

Рисунок 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.

Рисунок 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

    0

    1

    0

    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

Глава 19 Интерактивный поток данных TCP

Введение

В предыдущей главе мы рассмотрели, как устанавливаются и разрываются TCP соединения. Теперь посмотрим, как с использованием TCP передаются данные.

Если проанализировать TCP траффик, как, например, это сделано в [Caceres et al. 1991], можно обнаружить, что примерно половина всех TCP сегментов составляет неинтерактивные данные (FTP, электронная почта, новости Usenet), а другая половина принадлежит интерактивным или диалоговым приложениям (Telnet и Rlogin, например). По количеству байт, примерно 90% составляют неинтерактивные данные, а 10% диалоговые данные. Cегменты, содержащие неинтерактивные данные, как правило, полного размера (512 байт пользовательских данных), тогда как интерактивные пакеты меньше. (Также было обнаружено, что 90% пакетов, используемых Telnet и Rlogin, содержат меньше чем 10 байт пользовательских данных.)

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

Интерактивный ввод

Давайте посмотрим, как осуществляется передача данных, при вводе интерактивной команды при Rlogin соединении. Большинство новичков в TCP/IP очень удивляются, когда обнаруживают, что каждое нажатие клавиши генерирует пакет данных. Другими словами, при нажатии клавиши от клиента серверу посылается 1 байт за один промежуток времени (а не строка за один раз). Более того, сервер Rlogin отражает эхом символы, которые введены клиентом. При этом генерируется 4 сегмента: (1) интерактивный ввод символа от клиента, (2) подтверждение получение символа от сервера, (3) эхо введенного символа от сервера и (4) подтверждение на эхо от клиента. На рисунке 19.1 показан обмен данными.

Рисунок 19.1 Один из возможных способов осуществить удаленное эхо введенного символа.

Обычно сегменты 2 и 3 объединяются - подтверждение введенного символа отправляется вместе с эхо. Мы опишем технику подобного комбинирования (которое называется задержанным подтверждением) в следующем разделе.

Rlogin не случайно выбран для иллюстрации примеров. Это приложение обычно посылает от клиента серверу по одному символу. При рассмотрении Telnet (глава 26), мы увидим, что там предусмотрена опция, которая позволяет отправить строку ввода от клиента серверу, что уменьшает загрузку сети.

На рисунке 19.2 показан поток данных, соответствующий вводу пяти символов date\n. (Мы не показали процесс установления соединения и удалили весь вывод, посвященный типу сервиса. BSD/386 устанавливает TOS для Rlogin соединения таким образом, минимизировать задержку.) В строке 1 от клиента серверу отправляется 1 символ (d). В строке 2 приходит подтверждение на этот символ и его эхо. (Два средних сегмента на рисунке 19.1 объединены.) В строке 3 подтверждается символ, отраженный эхом. Строки 4-6 соответствуют символу a, строки 7-9 соответствуют символу t, а строки 10-12 символу e. Секунды задержки между строками 3-4, 6-7, 9-10 и 12-13 вызваны "человеческим фактором" (скорость ввода символов пользователем).

Обратите внимание на отличие в строках 13-15. В данном случае от клиента серверу посылается один символ (Unix символ новой строки, который генерируется при нажатии клавиши RETURN), однако эхом возвращается два символа. Эти символы возврата каретки и пропуск строки (CR/LF), при этом курсор перемещается в крайне левую позицию следующей строки.

В строке 16 показан вывод команды date, запущенной на сервере. 30 байт соответствуют следующим 28 символам

Sat Feb 6 07:52:17 MST 1993

плюс пара CR/LF в конце.

1  0.0                    bsdi.1023 > svr4.login: P 0:1(1) ack 1 win 4096
2  0.016497 (0.0165)     svr4.login > bsdi.1023: P 1:2(1) ack 1 win 4096
3  0.139955 (0.1235)     bsdi.1023 > svr4.login: . ack 2 win 4096

4  0.458037 (0.3181)     bsdi.1023 > svr4.login: P 1:2(1) ack 2 win 4096
5  0.474386 (0.0163)     svr4.login > bsdi.1023: P 2:3(1) ack 2 win 4096
6  0.539943 (0.0656)     bsdi.1023 > svr4.login: . ack 3 win 4096

7  0.814582 (0.2746)     bsdi.1023 > svr4.login: P 2:3(1) ack 3 win 4096
8  0.831108 (0.0165)     svr4.login > bsdi.1023: P 3:4(1) ack 3 win 4096
9  0.940112 (0.1090)     bsdi.1023 > svr4.login: . ack 4 win 4096

10  1.191287 (0.2512)     bsdi.1023 > svr4.login: P 3:4(1) ack 4 win 4096
11  1.207701 (0.0164)     svr4.login > bsdi.1023: P 4:5(1) ack 4 win 4096
12  1.339994 (0.1323)     bsdi.1023 > svr4.login: . ack 5 win 4096

13  1.680646 (0.3407)     bsdi.1023 > svr4.login: P 4:5(1) ack 5 win 4096
14  1.697977 (0.0173)     svr4.login > bsdi.1023: P 5:7(2) ack 5 win 4096
15  1.739974 (0.0420)     bsdi.1023 > svr4.login: . ack 7 win 4096

16  1.799841 (0.0599)     svr4.login > bsdi.1023: P 7:37(30) ack 5 win 4096
17  1.940176 (0.1403)     bsdi.1023 > svr4.login: . ack 37 win 4096
18  1.944338 (0.0042)     svr4.login > bsdi.1023: P 37:44(7) ack 5 win 4096
19  2.140110 (0.1958)     bsdi.1023 > svr4.login: . ack 44 win 4096

Рисунок 19.2 Сегменты, генерируемые при вводе date в Rlogin соединение.

Следующие 7 байт, отправленные от сервера клиенту (строка 18), это приглашение, которое выглядит следующим образом: svr4 % . В строке 19 выдается подтверждение на эти 7 байт.

Обратите внимание на то, как в TCP генерируются подтверждения. В строке 1 отправляется байт данных с номером последовательности 0. В строке 2 приходит подтверждение с номером последовательности 1, то есть номер последовательности последнего успешно принятого байта плюс 1. (Это обычно называется номером последовательности следующего ожидаемого байта.) В строке 2 от сервера клиенту посылается байт данных с номером последовательности равным 1. Он подтверждается (ACK) клиентом в строке 3 путем отправки подтверждения с номером последовательности равным 2.

Задержанные подтверждения

На рисунке 19.2 необходимо обратить внимание на времена, которые мы рассмотрим в этом разделе. На рисунке 19.3 показана временная диаграмма обмена, приведенного на рисунке 19.2. (Мы удалили из временной диаграммы все объявления окна и добавили выражения, которые указывают на то, какие данные передаются.)

Семь подтверждений ACK, отправленные от bsdi к svr4, помечены как задержанные ACK (delayed ACK). Обычно TCP не отправляет ACK сразу по приему данных. Вместо этого он осуществляет задержку подтверждений в надежде на то, что в этом же направлении будут отправлены данные, таким образом ACK может быть отправлено вместе с данными. Большинство реализаций используют задержку равную 200 миллисекунд - таким образом, TCP задерживает ACK на время до 200 миллисекунд, чтобы посмотреть, не направляются ли данные в том же направлении, что и ACK.

Рисунок 19.3 Временная диаграмма потока данных для команды date, введенной в соединение rlogin.

Если рассмотреть разницу во времени между приемом данных bsdi и отправкой ACK, то она будет выглядеть случайной: 123,5; 65,6; 109,0; 132,3; 42,0; 140,3 и 195,8 миллисекунды. Давайте посмотрим на реальные времена (начинающиеся с 0), когда отправляются ACK: 139,9; 539,9; 940,1; 1339,9; 1739,9; 1940,1 и 2140,1 миллисекунды. (Мы пометили эти значения звездочкой слева на рисунке 19.3.) Разница между этими временами составляет несколько периодов по 200 миллисекунд. Это происходит из-за того, что TCP имеет таймер, который выключается каждые 200 миллисекунд, однако он выключается в фиксированные моменты времени - каждые 200 миллисекунд с того момента, когда ядро было загружено. Так как данные, которые должны быть подтверждены, приходят со случайными задержками (во времена 16,4; 474,3; 831,1 и так далее), TCP должен быть уведомлен о том, когда истекает следующий 200-миллисекундный таймер ядра. Это может произойти в любой момент от 1 до 200 миллисекунд в будущем.

Если мы посмотрим, сколько времени требуется svr4 на то, чтобы сгенерировать эхо для каждого полученного символа, времена будут следующими: 16,5; 16,3; 16,5; 16,4 и 17,3 миллисекунды. Так как это время меньше чем 200 миллисекунд, мы никогда не увидим задержанный ACK на этой стороне. Всегда существуют данные готовые к отправке, перед тем как истечет таймер задержанного ACK. (Конечно, мы можем увидеть задержанный ACK, если период ожидания, около 16 миллисекунд, совпадет с одним из таймеров в 200 миллисекунд. Однако, этого не произошло в примере.)

Мы видели тот же самый сценарий на рисунке 18.7, в случае с 500-миллисекундным таймером TCP, используемым для определения тайм-аута. Оба TCP таймера, и 200-миллисекундный и 500-миллисекундный, начинают отсчет с того момента, когда ядро было загружено. Когда TCP устанавливает таймер, он может выключиться в любой момент между 1-200 или 1-500 миллисекунд в будущем.

Требования к хостам Host Requirements RFC указывают на то, что TCP должен применять задержанные ACK, однако задержка должна быть меньше чем 500 миллисекунд.

Алгоритм Нагла

В предыдущем разделе мы видели, что обычно от клиента к серверу через Rlogin соединение передается 1 байт за один раз. При этом генерируются пакеты размером 41 байт: 20 байт - IP заголовок, 20 байт - TCP заголовок и 1 байт данных. Маленькие пакеты (называемые тиниграммами, от английского tiny - крошечный, маленький) - обычно не проблема для локальных сетей, так как большинство локальных сетей не перегружаются, однако они могут привести к перегрузке глобальной сети. Простое и элегантное решение было предложено в RFC 896 [Nagle 1984], которое сейчас называется алгоритмом Нагла (Nagle algorithm).

Из алгоритма следует, что в TCP соединении может присутствовать только один исходящий маленький сегмент, который еще не был подтвержден. Следующие маленькие сегменты могут быть посланы только после того, как было получено подтверждение. Вместо того чтобы отправляться последовательно, маленькие порции данных накапливаются и отправляются одним TCP сегментом, когда прибывает подтверждение на первый пакет. Красота этого алгоритма заключается в том, что он сам настраивает временные характеристики: чем быстрее придет подтверждение, тем быстрее будут отправлены данные. В медленных глобальных сетях, где необходимо уменьшить количество маленьких пакетов, отправляется меньше сегментов. (В разделе "Синдром "глупого" окна" главы 22 мы увидим, что определение "маленький" означает - меньше чем размер сегмента.)

На рисунке 19.3 мы видели, что для Ethernet время возврата на один отправленый байт, на который приходит подтверждение и эхо, составляет примерно 16 миллисекунд. Чтобы данные генерировались быстрее, мы должны печатать больше чем 60 символов в секунду. Это означает, что вряд ли можно использовать этот алгоритм при отправке данных между двумя хостами, находящимися в локальной сети.

Однако, положение меняется, когда время возврата (RTT) увеличивается, обычно это происходит в глобальных сетях. Давайте рассмотрим Rlogin соединение между хостом slip и хостом vangogh.cs.berkeley.edu. Чтобы выйти из нашей сети (рисунок 1.11), необходимо пройти два SLIP канала, а потом попасть в Internet. В этом случае мы ожидаем довольно большое время возврата. На рисунке 19.4 показана временная диаграмма потока данных, которые соответствуют быстрому набору символов клиентом (предположим, что за клавиатурой хорошая стенографистка). (Мы удалили всю информацию, соответствующую типу сервиса, однако оставили объявления размера окна.)

Рисунок 19.4 Поток данных с использованием rlogin между slip и vangogh.cs.berkeley.edu.

Первое, на что необходимо обратить внимание, на рисунках 19.3 и 19.4, то, что нет задержанных ACK от slip к vangogh. Это происходит потому, что в данном случае данные всегда готовы к отправке, перед тем как истечет таймер задержанного ACK.

Необходимо обратить внимание на различные размеры данных, которые отправляются слева направо: 1, 1, 2, 1, 2, 2, 3, 1 и 3 байта. Это объясняется тем что, что клиент собирает данные, которые необходимо послать, однако не посылает их, пока на ранее отправленные данные не приходит подтверждение. С использованием алгоритма Нагла с помощью девяти сегментов (а не 16) было отправлено 16 байт. Обработка сегментов 14 и 15 противоречит алгоритму Нагла, необходимо посмотреть на номера последовательности, чтобы определить, что произошло в действительности. Сегмент 14 это отклик на ACK, полученный в сегменте 12, так как подтвержденный номер последовательности равен 54. Однако перед тем как этот сегмент данных отправлен клиентом, от сервера прибывает сегмент 13. Сегмент 15 содержит подтверждение (ACK) на сегмент 13, номер последовательности 56. Таким образом, клиент игнорировал алгоритм Нагла, так как мы видим два последовательных сегмента данных, посланных от клиента к серверу.

Также на рисунке 19.4 необходимо обратить внимание на то, что существует один задержанный ACK, передаваемый однако от сервера к клиенту (сегмент 12). Мы предположили, что это задержанный ACK, потому что он не содержит данных. Сервер скорее всего был занят в этот момент времени, поэтому сервер Rlogin не мог отправить эхо символ, перед тем как истек таймер задержанного ACK.

И в заключение, обратимся к размеру данных и номерам последовательности в последних двух сегментах. Клиент посылает 3 байта данных (с номерами 18, 19 и 20), затем сервер подтверждает эти 3 байта (ACK 21 в последнем сегменте), однако посылает назад только один байт (с номером 59). Здесь происходит следующее: TCP сервер подтверждает 3 байта данных за один раз, однако эхо этих 3-х байт не будет готово к отправке назад, пока их не сгенерирует сервер Rlogin. Это показывает на то, что TCP может подтвердить принятые данные, до того как приложение считало и обработало эти данные. TCP подтверждение просто означает, что TCP корректно получил данные. Определить, что процесс сервера не считал эти 3 байта данных можно благодаря тому, что объявленный размер окна в последнем сегменте равен 8189, а не 8192.

Отключение алгоритма Нагла

Существуют моменты, когда необходимо отключить алгоритм Нагла. Классический пример - сервер X Window System (глава 30, раздел "X Window System"): маленькие пакеты (соответствующие передвижению мыши) должны быть переданы без задержки, что обеспечивает отклик в реальном времени для пользователя.

Однако здесь мы продемонстрируем другой пример, который позволяет представить необходимость выключения алгоритма - ввод одной из специальных клавиш терминала во время интерактивного терминального захода. Функциональные клавиши обычно генерируют несколько байт данных, которые часто начинаются с escape ASCII символа. TCP клиент получит 1 байт данных (от приложения) за один момент времени, пошлет этот первый байт (ASCII ESC), а оставшиеся байты задержит до того момента, пока не придет подтверждение на первый отправленный байт. Однако, когда сервер получает этот первый байт, он не генерирует эхо пока не будут получены все оставшиеся байты. В этом случае, обычно, включается алгоритм задержанного ACK на сервере, а это означает, что оставшиеся байты не будут отправлены в течении ближайших 200 миллисекунд. В результате пользователь получит заметную задержку вывода.

Сокеты API используют опцию сокета TCP_NODELAY, чтобы выключить алгоритм Нагла.

Требования к хостам Host Requirementts RFC указывают, что TCP должен применять алгоритм Нагла, однако должен существовать способ для приложения выключить его для определенного соединения.

Пример

Мы можем пронаблюдать функционирование алгоритма Нагла при нажатии клавиши, которая генерирует несколько байт. Было установлено Rlogin соединение от хоста slip к хосту vangogh.cs.berkeley.edu. Затем мы нажали функциональную клавишу F1, которая генерирует 3 байта: escape, левую квадратную скобку и M. Затем мы ввели функциональную клавишу F2, которая генерирует еще 3 байта. На рисунке 19.5 показан вывод tcpdump. (Информация, соответствующая типу сервиса и определению окна, удалена.)

                          нажимаем клавишу F1
1  0.0                    slip.1023 > vangogh.login: P 1:2(1) ack 2
2  0.250520 (0.2505)     vangogh.login > slip.1023: P 2:4(2) ack 2
3  0.251709 (0.0012)     slip.1023 > vangogh.login: P 2:4(2) ack 4
4  0.490344 (0.2386)     vangogh.login > slip.1023: P 4:6(2) ack 4
5  0.588694 (0.0984)     slip.1023 > vangogh.login: . ack 6

                          нажимаем клавишу F2
6  2.836830 (2.2481)     slip.1023 > vangogh.login: P 4:5(1) ack 6
7  3.132388 (0.2956)     vangogh.login > slip.1023: P 6:8(2) ack 5
8  3.133573 (0.0012)     slip.1023 > vangogh.login: P 5:7(2) ack 8
9  3.370346 (0.2368)     vangogh.login > slip.1023: P 8:10(2) ack 7
10  3.388692 (0.0183)     slip.1023 > vangogh.login: . ack 10

Рисунок 19.5 Работа алгоритма Нагла при вводе символов, которые генерируют несколько байт данных.

На рисунке 19.6 показана временная диаграмма соответствующая этому обмену. Внизу этого рисунка показано 6 байт, переданные от клиента к серверу с их номерами последовательности, и 8 байт, которые были возвращены эхом.

Когда первый байт ввода был прочитан клиентом rlogin и записан в TCP, он отправляется в сегменте 1. Это первый из 3 байт, сгенерированных при нажатии клавиши F1. Его эхо возвращено в сегменте 2, и только после этого отправляются следующие 2 байта (сегмент 3). Эхо для вторых 2 байт получено в сегменте 4, а подтверждение в сегменте 5.

Причина того, что эхо для первого байта заняло 2 байта (сегмент 2), заключается в том, что ASCII escape символ отражается эхом как 2 байта: символ "^" и левая квадратная скобка. Следующие 2 введенных байта, левая скобка и М, отражаются эхом как они есть.

Тот же самый обмен происходит, когда нажата следующая функциональная клавиша (сегменты 6-10). Как мы и ожидали, разница во времени между сегментами 5 и 10 (slip посылает подтверждение на эхо) это несколько периодов времени по 200 миллисекунд, так как оба подтверждения (ACK) задержаны.

Рисунок 19.6 Временная диаграмма для рисунка 19.5 (наблюдение за работой алгоритма Нагла).

Сейчас мы повторим тот же самый пример с использованием версии rlogin, которая модифицирована таким образом, что способна выключать алгоритм Нагла. На рисунке 19.7 показан вывод команды tcpdump. (Информация, посвященная типу сервиса и объявлению окна, удалена.)

                          нажимаем клавишу F1
1  0.0                    slip.1023 > vangogh.login: P 1:2(1) ack 2
2  0.002163 (0.0022)     slip.1023 > vangogh.login: P 2:3(1) ack 2
3  0.004218 (0.0021)     slip.1023 > vangogh.login: P 3:4(1) ack 2
4  0.280621 (0.2764)     vangogh.login > slip.1023: P 5:6(1) ack 4
5  0.281738 (0.0011)     slip.1023 > vangogh.login: . ack 2
6  2.477561 (2.1958)     vangogh.login > slip.1023: P 2:6(4) ack 4
7  2.478735 (0.0012)     slip.1023 > vangogh.login: . ack 6

                          нажимаем клавишу F2
8  3.217023 (0.7383)     slip.1023 > vangogh.login: P 4:5(1) ack 6
9  3.219165 (0.0021)     slip.1023 > vangogh.login: P 5:6(1) ack 6
10  3.221688 (0.0025)     slip.1023 > vangogh.login: P 6:7(1) ack 6
11  3.460626 (0.2389)     vangogh.login > slip.1023: P 6:8(2) ack 5
12  3.489414 (0.0288)     vangogh.login > slip.1023: P 8:10(2) ack 7
13  3.640356 (0.1509)     slip.1023 > vangogh.login: . ack 10

Рисунок 19.7 Отключение алгоритма Нагла в течении Rlogin сессии.

Будет более поучительно и информативно получить этот вывод и построить временную диаграмму, зная какие пакеты прошли по сети. Также этот пример требует внимательного изучения номеров последовательности, которые соответствуют текущим данным. Это показано на рисунке 19.8. Нумерация сегментов соответствует нумерации в выводе tcpdump, приведенном на рисунке 19.7.

Первое отличие, которое бросается в глаза, заключается в том, что все 3 байта отправляются, когда они готовы (сегменты 1, 2 и 3). Задержка не осуществляется - алгоритм Нагла выключен.

Следующий пакет, который мы видим в выводе tcpdump (сегмент 4), содержит 5 байт от сервера с ACK 4. Здесь произошла ошибка. Клиент немедленно отвечает с ACK 2 (незадержанный), а не ACK 6, так как он не ожидал прихода байта номер 5. Это означает, что сегмент данных был потерян. Это показано пунктирными линиями на рисунке 19.8.

Как мы узнали, что этот потерянный сегмент содержит байты 2, 3 и 4 вместе с ACK 3? Следующий байт, который мы ожидаем, это байт номер 2, как объявлено в сегменте номер 5. (Когда TCP получает данные в неправильном порядке, то есть с номерами последовательности, следующими за теми, которые мы ожидаем, он обычно отвечает подтверждением, содержащим номер последовательности следующего байта, который он ожидает получить.) Так как отсутствующий сегмент содержал байты 2, 3 и 4, это означает, что сервер должен получить сегмент 2, таким образом, отсутствующий сегмент должен быть указан как ACK 3 (номер последовательности следующего байта, который ожидается сервером к приему). И в завершение, обратите внимание на то, что при повторной передаче, сегмент 6, содержит данные из отсутствующего сегмента и сегмента 4. Это называется пересборкой пакетов (repacketization) , что мы обсудим более подробно в разделе "Пересборка пакетов" главы 21.

Возвращаясь к обсуждению выключения алгоритма Нагла, мы можем увидеть, что 3 байта, соответствующие следующей специальной клавише, которую мы нажали, отправляются как три отдельных сегмента (8, 9 и 10). Теперь сервер отражает эхом байт в сегменте 8 - первым (сегмент 11), а затем отражает эхом байты из сегментов 9 и 10 (сегмент 12).

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

Мы вернемся к этой теме в главе 21, которая посвящена тайм-аутам и повторным передачам.

Рисунок 19.8 Временная диаграмма для рисунка 19.7 (выключение алгоритма Нагла).

Объявления размера окна

На рисунке 19.4 мы видели, что slip объявляет окно размером 4096 байт, а vangogh объявляет окно 8192 байта. Большинство сегментов на этом рисунке содержат одно из этих двух значений.

Сегмент 5, однако, объявляет окно размером 4095 байт. Это означает, что в TCP буфере все еще находится 1 байт, не считанный приложением (клиент Rlogin). Следующий сегмент от клиента объявляет окно равное 4094 байта, а это означает, что 2 байта все еще должны быть считаны приложением.

Сервер обычно объявляет окно равное 8192 байтам, потому что TCP серверу нечего послать до тех пор, пока сервер Rlogin читает и принимает данные, а затем отражает их эхом. Данные от сервера посылаются после того, как сервер Rlogin считал ввод от клиента.

TCP клиент, с другой стороны, часто имеет данные, которые необходимо послать, когда прибудет подтверждение, поэтому он помещает символы в буфер, ожидая прихода ACK. Когда TCP клиент посылает буферизированные данные, Rlogin клиент не имеет возможности прочитать данные, полученные от сервера, поэтому клиент объявляет окно меньше чем 4096.

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

Диалоговые данные обычно передаются в сегментах с размером меньшим, чем максимальный размер сегмента. В случае Rlogin от клиента к серверу обычно передается один байт данных. Telnet позволяет посылать за один раз строку, однако большинство реализаций на сегодняшний день до сих пор посылают по одному символу.

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

При работе в медленных глобальных сетях часто используется алгоритм Нагла, что позволяет уменьшить количество маленьких сегментов. В случае использования алгоритма Нагла отправляется только один маленький неподтвержденный пакет в один момент времени. Однако существуют моменты, когда алгоритм Нагла должен быть отключен, как мы показали в примерах.

Упражнения

  1. Представьте себе приложение TCP клиента, который пишет маленькие заголовки приложения (8байт), за которыми следуют маленькие запросы (12 байт). Затем он ждет отклика от сервера. Что произойдет, если запрос отправлен с использованием двух записей (8 байт, затем 12 байт) вместо одной записи размером 20 байт?
  2. На рисунке 19.4 мы запустили tcpdump на маршрутизаторе sun. Это означает, что данные, показанные стрелочками справа налево, проходят через bsdi, а данные, показанные стрелочками слева направо, уже прошли через bsdi. Мы видим сегмент, идущий к slip, затем следующий сегмент, идущий от slip, разница во времени между ними следующая: 34,8; 26,7; 30,1; 28,1; 29,9 и 35,3 миллисекунды. Можно ли сказать, что два канала между sun и slip (Ethernet и CSLIP канал со скоростью 9600 бит/сек) вносят эти различия во времени? (Подсказка: см. главу 2, раздел "Вычисление загруженности последовательной линии".)
  3. Сравните время, требуемое для того чтобы отправить символы, соответствующие специальной клавише, и их подтверждения с использованием алгоритма Нагла (рисунок 19.6), и время, когда этот алгоритм выключен (рисунок 19.8).


Глава 20 Поток неинтерактивных данных TCP

Введение

В главе 15 мы видели, что TFTP использует протокол с ожиданием подтверждения (stop-and-wait). Отправитель блока данных требует подтверждения на этот блок, перед тем как будет отправлен следующий блок. В этой главе мы увидим, что TCP использует другую форму управления потоком данных, которая называется протоколом с изменяющимся окном (sliding window) . Это позволяет отправителю передать несколько пакетов, перед тем как он остановится и будет ждать подтверждения. При этом данные передаются быстрее, так как отправитель не должен останавливаться и ждать подтверждения каждый раз после отправки пакета.

Также мы увидим TCP флаг PUSH, который мы уже встречали в предыдущих примерах. Рассмотрим медленный старт, технику, используемую TCP для получения потока данных при установлении соединения, а затем рассмотрим пропускную способность при передаче неинтерактивных данных.

Обычный поток данных

Давайте начнем с рассмотрения односторонней передачи 8192 байт от хоста svr4 к хосту bsdi. Мы запустили нашу программу sock (как сервер) на bsdi:

bsdi % sock -i -s 7777

Флаги -i и -s сообщают о необходимости запустить программу в качестве сервера, не обрабатывающего данные (при этом данные читаются из сети и отбрасываются), номер порта сервера установлен в 7777. Клиент, соответственно, запускается следующим образом:

svr4 % sock -i -n8 bsdi 7777

Клиент осуществляет в сеть восемь записей размером 1024 байта каждая. На рисунке 20.1 показана временная диаграмма этого обмена. Мы оставили первые 3 сегмента вывода, чтобы показать значение MSS (максимальный размер сегмента) для каждой стороны.

Рисунок 20.1 Передача 8192 байт от svr4 к bsdi.

Во-первых, отправитель передает три сегмента данных (4-6). Следующий сегмент (7) подтверждает только первые два сегмента данных. Мы знаем об этом, потому что номер последовательности подтверждения равен 2049, а не 3073.

Сегмент 7 содержит ACK с номером 2049, а не 3073 по следующей причине. Когда пакет прибывает, он первоначально обрабатывается драйвером устройства, а затем помещается во входную очередь IP. Три сегмента 4, 5 и 6 прибывают один за другим и помещаются во входную очередь IP в том порядке, как они были приняты. IP затем передаст их в TCP в том же самом порядке. Когда TCP обрабатывает сегмент 4, в соединении генерируется задержанный ACK. TCP обрабатывает следующий сегмент (5), и теперь TCP имеет два сегмента, на которые необходимо сгенерировать подтверждение (ACK), поэтому генерируется подтверждение с номером 2049 (сегмент 7), а флаг задержанного ACK для этого соединения снимается. TCP обрабатывает следующий входной сегмент (6), а в соединение снова генерируется задержанный ACK. Перед тем как прибывает сегмент 9, выключается таймер задержанного ACK и генерируется подтверждение с номером 3073 (сегмент 8). В сегменте 8 окно объявляется размером 3072 байта, так как 1024 байта данных в приемном буфере TCP до сих пор не прочитаны приложением.

В случае сегментов 11-16 подтверждение осуществляется на каждый сегмент. Сегменты 11, 12 и 13 прибывают и помещаются во входную очередь IP. Когда сегмент 11 обрабатывается TCP, соединение помечается как использующее задержанное ACK. Когда обрабатывается сегмент 12, генерируется ACK (сегмент 14) на сегменты 11 и 12, а флаг задержанного ACK для данного соединения снимается. При обработке сегмента 13 соединение вновь помечается как использующее задержанное ACK, однако перед тем как задержанный ACK снимается по таймеру, обрабатывается сегмент 15, при этом ACK (сегмент 16) отправляется немедленно.

Очень важно обратить внимание на то, что ACK в сегментах 7, 14 и 16 подтверждает два принятых сегмента. В случае использования протокола TCP с изменяющимся окном, принимающая сторона не должна подтверждать каждый принятый пакет. В случае TCP, подтверждения накапливаются - они подтверждают, что получатель корректно принял все байты до номера последовательности подтверждения минус один. В этом примере три из ACK подтвердили 2048 байт данных, а два подтвердили 1024 байта данных. (За исключением ACK, появлявшихся при установлении и разрыве соединения.)

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

Для того чтобы показать, как все может измениться, на рисунке 20.2 показана еще одна временная диаграмма для того же самого обмена данными между теми же самыми хостами. Обмен был осуществлен через несколько минут после того, который был показан на рисунке 20.1.

Рисунок 20.2 Еще одна передача 8192 байт от svr4 к bsdi.

Здесь мы видим несколько отличий. Приемник не отправляет ACK с номером 3073; вместо этого он ожидает и отправляет ACK с номером 4097. Принимающий посылает только четыре ACK (сегменты 7, 10, 12 и 15): три из них на 2048 байт и один на 1024 байт. ACK на последние 1024 байта данных отправляется в сегменте 17, вместе с подтверждением на FIN. (Сравните сегмент 17 на этом рисунке с сегментами 16 и 18 на рисунке 20.1.)

Быстрый отправитель, медленный получатель

На рисунке 20.3 показана еще одна временная диаграмма, которая в данном случае показывает быстрого отправителя (Sparc) и медленного получателя (80386 с медленной Ethernet платой). Динамика снова изменилась.

Рисунок 20.3 Отправка 8192 байт от быстрого отправителя медленному получателю.

Отправитель передает четыре сегмента данных (4-7), чтобы заполнить окно объявленное получателем. Затем отправитель останавливается и ожидает подтверждения. Получатель отправляет подтверждение (сегмент 8), однако объявляет окно равное 0. Это означает, что получатель получил данные, однако все они находятся в TCP буферах получателя, потому что приложение не имеет возможности считать данные. Еще один ACK (называемый обновлением окна) посылается через 17,4 миллисекунды и объявляет о том, что получатель теперь может получить следующие 4096 байт. То, что выглядит как подтверждение (ACK), в действительности является обновлением окна, потому что здесь не происходит подтверждения каких-либо вновь полученных данных, а просто объявляется новый размер окна.

Отправитель передает свои последние четыре сегмента (10-13) и опять заполняет окно принимающего. Обратите внимание, что сегмент 13 содержит два флаговых бита: PUSH и FIN. После этого следуют еще два подтверждения от принимающего. Они подтверждают последние 4096 байт данных (байты от 4097 до 8192) и FIN (который имеет номер 8193).

Изменение размера окна

Протокол изменения размера окна, который мы рассмотрели в предыдущем разделе, может быть проиллюстрирован следующим образом.

Рисунок 20.4 Иллюстрация изменения окна TCP.

На этом рисунке мы пронумеровали байты с 1 по 11. Окно, которое объявляется принимающим, называется предлагаемым окном и покрывает собой байты с 4 по 9, что означает, что получатель подтвердил все байты до 3 включительно и объявляет размер окна равный 6. Обратитесь снова к главе 17, где мы говорили, что размер окна связан с подтвержденным номером последовательности. Отправитель рассчитывает свой возможный размер окна. Рассчитанное значение указывает, какое количество данных он может отправить немедленно.

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

  1. Окно закрывается, когда его левая граница совпадает с правой. Это происходит, когда данные отправлены и подтверждены.
  2. Окно открывается, когда его правая граница сдвигается вправо, при этом данные могут быть отправлены. Это происходит, когда принимающий процесс читает подтвержденные данные, освобождая тем самым место в приемном буфере TCP.
  3. Окно сжимается, когда его правая граница передвигается влево. Требования к хостам Host Requirements RFC очень не рекомендуют делать это, так как TCP соединение может быть установлено с хостом, который не поддерживает подобную опцию. В разделе "Синдром "глупого" окна" главы 22 показан пример, когда одна граница хочет сжать окно, передвинув его правый край влево, однако не может.

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

Рисунок 20.5 Перемещение границ окна.

Если приняты ACK, которые требуют перемещения левой границы окна влево, это дублированные ACK, они отбрасываются.

Если левая граница окна совпала с правой, это называется нулевым окном. При этом отправитель прекращает передачу данных.

Пример

На рисунке 20.6 показана динамика работы протокола TCP с изменением окна для передачи данных, показанной на рисунке 20.1.

Рисунок 20.6 Протокол изменения размера окна для рисунка 20.1.

Рассматривая эти рисунки, можно сделать следующие выводы.

Отправитель не должен передавать полное окно данных. Один сегмент от получателя подтверждает данные и раздвигает окно вправо. Это происходит из-за того, что размер окна связан с номером последовательности, которая была подтверждена. Размер окна может уменьшаться, как это показано для сегментов 7 и 8, однако правая граница окна не должна перемещаться влево. Получатель не должен ждать, пока окно заполнится перед отправкой ACK. Раньше мы видели, что большинство реализаций посылают ACK для каждых двух сегментов, которые были получены.

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

Размер окна

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

В 4.2BSD приемные и отправляющие буферы по умолчанию устанавливаются в 2048 байт каждый. В 4.3BSD оба были увеличены до 4096 байт. Как мы можем видеть из всех примеров, приведенных в тексте, SunOS 4.1.3, BSD/386 и SVR4 все еще используют по умолчанию размер буфера 4096 байт. Другие системы, такие как Solaris 2.2, 4.4BSD и AIX 3.2, используют по умолчанию большие размеры буферов, 8192 или 16384 байта.

API сокеты позволяют процессу устанавливать размеры отправляющего и приемного буферов. Размер принимающего буфера равен максимальному размеру объявленного окна для данного соединения. Некоторые приложения изменяют размеры буферов для увеличения производительности.

[Mogul 1993] показывает некоторые результаты передачи файла между двумя рабочими станциями, находящимися в сети Ethernet, с различными размерами приемных и передающих буферов. (Для одностороннего потока данных, который осуществляется при передаче файла, размер передающего буфера на стороне отправителя, и размер приемного буфера на стороне получателя имеют большое значение.) Размеры по умолчанию, составляющие 4096 байт для обоих буферов, не являются оптимальными для Ethernet. Увеличение пропускной способности примерно на 40% было получено просто путем увеличения обоих буферов до 16384 байт. Подобный результат также показан в [Papadopoulos and Parulkar 1993].

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

Пример

Установить размер буферов можно с помощью программы sock. Запустим сервер следующим образом:

bsdi % sock -i -s -R6144 5555

при этом размер приемного буфера (опция -R) устанавливается в 6144 байта. Затем стартуем клиента на хосте sun и осуществляем одну запись размером в 8192 байта:

sun % sock -i -n1 -w8192 bsdi 5555

На рисунке 20.7 показан результат.

Рисунок 20.7 Передача данных, при которой получатель объявляет размер окна равный 6144 байта.

Во-первых, обратите внимание на то, что в сегменте номер 2, получатель предлагает размер окна, равный 6144 байта. Так как размер окна увеличен, клиент немедленно посылает шесть сегментов (сегменты 4-9), после чего останавливается. В сегменте 10 все данные подтверждаются (байты с 1 по 6144), и объявляет размер окна равный всего лишь 2048, возможно потому, что принимающее приложение не может считать больше чем 2048 байт. Сегменты 11 и 12 завершают передачу данных от клиента, этот же последний сегмент данных содержит флаг FIN.

Сегмент 13 содержит тот же самый номер последовательности подтверждения как и сегмент 10, однако объявляет окно большего размера. Сегмент 14 подтверждает последние 2048 байт данных и FIN, а сегменты 15 и 16 просто объявляют окно большего размера. Сегменты 17 и 18 осуществляют нормальное закрытие.

Флаг PUSH

Мы видели флаг PUSH практически в каждом примере работы TCP, однако никогда не говорили о том, как он используется. Назначение этого флага заключается в том, что отправитель с его помощью предлагает получателю отправить все имеющиеся у него данные получающему процессу. Эти данные могут состоять из чего-либо угодно находящегося в сегменте, где взведен флаг PUSH, вместе с любыми другими данными, которые принимающий TCP собрал для принимающего процесса.

В исходной спецификации TCP предполагалось, что программный интерфейс может позволить отправляющему процессу сказать своему TCP, когда необходимо установить флаг PUSH. Для интерактивных приложений, например, когда клиент посылает команду серверу, клиент может установить флаг PUSH и ожидать отклика от сервера. (В упражнении 1 главы 19 мы представили, что клиент установил флаг PUSH, когда осуществлялась запись 12-байтного запроса.) Так как приложению клиента разрешено потребовать от своего TCP установить флаг, осуществляется уведомление TCP клиента о том, что процесс клиента не хочет, чтобы данные заполнили TCP буфер в ожидании дополнительных данных перед отправкой сегмента серверу. Точно так же, когда TCP модуль сервера получил сегмент с флагом PUSH, он получает уведомление о необходимости передать данные процессу сервера, а не ожидать прихода дополнительных данных.

В настоящее время, однако, большинство API не предоставляют приложению способа сообщить своему TCP о необходимости установить флаг PUSH. Вместо этого реализации TCP самостоятельно определяют, когда необходимо установить этот флаг.

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

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

Не существует возможности с использованием сокет API сказать TCP взвести PUSH флаг или сказать, был ли установлен флаг PUSH в принятых данных.

Реализации Berkeley игнорируют принятый PUSH флаг, потому что они обычно никогда не задерживают доставку принятых данных приложению.

Примеры

На рисунке 20.1 мы видели взведенный флаг PUSH для всех восьми сегментов данных (4-6, 9, 11-13 и 15). Это объясняется тем, что клиент осуществил восемь записей по 1024 байта каждая, и каждая запись опустошила отправляющий буфер.

Вернемся к рисунку 20.7. Мы ожидаем, что флаг PUSH будет установлен в сегменте 12, так как это последний сегмент данных. Почему флаг PUSH был установлен в сегменте 7, когда отправитель знал, что ему придется отправить еще некоторые байты данных? Причина заключается в том, что размер отправляющего буфера отправителя составляет 4096 байт, даже если мы осуществляем одну запись размером в 8192 байта.

Еще один момент, на который необходимо обратить внимание на рисунке 20.7, заключается в трех последовательных ACK, сегменты 14, 15 и 16. Мы видели два последовательных ACK на рисунке 20.3, однако это объяснялось тем, что получатель объявлял окно равное 0 (останавливал отправителя), поэтому когда окно открылось, потребовался еще один ACK, с ненулевым значением окна, чтобы перестартовать отправителя. На рисунке 20.7, однако, значение окна никогда не устанавливается в 0. Тем не менее, когда размер окна увеличивается до 2048 байт, отправляется еще один ACK (сегменты 15 и 16), чтобы осуществить обновление окна на удаленном конце. (Эти два окна, обновленные в сегментах 15 и 16, не нужны, так как с удаленного конца был принят FIN, после чего отправитель не будет посылать данные.) Большинство реализаций посылают это обновление окна, если размер окна достиг удвоенного значения максимального размера сегмента (2048 байт в данном примере, с MSS равным 1024) или 50% максимально возможного размера окна (2048 байт в этом примере, с максимальным окном 4096). Мы увидим это снова в разделе "Синдром "глупого" окна" главы 22, когда будем более подробно рассматривать синдром "глупого окна".

Другой пример, где появился флаг PUSH - рисунок 20.3. Причина, по которой мы видим этот флаг в первых четырех сегментах данных (4-7), заключается в том, что каждый из них генерируется TCP и передается в IP уровень. Однако, затем TCP должен остановиться, дождаться прихода ACK, чтобы отправить окно размером 4096 байт. Пока ожидается ACK, TCP берет последние 4096 байт данных от приложения. Когда окно открывается (сегмент 9), отправляющий TCP знает, что у него есть 4 сегмента, которые он может послать немедленно, поэтому он взводит флаг PUSH в последнем сегменте (13).

Медленный старт

Во всех примерах, которые мы рассмотрели, отправитель начинает свою работу, отправив несколько сегментов в сеть. Размер записей может достигать размера окна, объявленного получателем. При этом все будет в порядке, если два хоста находятся в одной и той же локальной сети, однако если между отправителем и получателем присутствуют маршрутизаторы или медленные каналы, могут возникнуть проблемы. Некоторые промежуточные маршрутизаторы должны будут поместить пакеты в очередь, которая, кстати сказать, может переполниться. [Jacobson 1988] показывает, как в подобных случаях может значительно понизиться пропускная способность TCP соединения.

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

При работе с медленным стартом отправляющему TCP добавляется еще одно окно: окно переполнения, которое называется cwnd. Когда устанавливается новое соединение с хостом, находящимся в другой сети, размер окна переполнения устанавливается равным размеру одного сегмента (размер сегмента объявлен удаленным концом). Каждый раз, когда принимается ACK, окно переполнения увеличивается на один сегмент. (Размер cwnd измеряется в байтах, однако при медленном старте размер всегда увеличивается на размер сегмента.) Отправитель может передать объем данных величиной до минимального размера окна переполнения и объявленного окна. С помощью окна переполнения, отправитель осуществляет управление потоком, тогда как с помощью объявленного окна потоком управляет получатель.

Отправитель начинает работу, отправив один сегмент и ожидая ACK на этот сегмент. Когда ACK получен, окно переполнения увеличивается с одного сегмента до двух, и в этом случае могут быть отправлены два сегмента. Когда каждый из этих двух сегментов подтвержден, окно переполнения увеличивается до 4. Таким образом, осуществляется экспотенциальное увеличение.

В определенной точке достигается максимум передачи для данного соединения (объединенной сети), в этом случае промежуточный маршрутизатор начинает отбрасывать пакеты. Это говорит о том, что размер окна переполнения отправителя стал слишком большим. Когда мы будем рассматривать алгоритмы тайм-аутов и повторных передач TCP в следующей главе, то увидим как обрабатывается подобная ситуация и что происходит с окном переполнения. А сейчас давайте посмотрим, как работает медленный старт.

Пример

На рисунке 20.8 показаны данные, которые отправляются от хоста sun на хост vangogh.cs.berkeley.edu. Эти данные проходят по медленному SLIP каналу, который в данном случае будет являться узким местом передачи. (Из этой временной диаграммы удалено все, что связанно с установлением соединения.)

Мы видим, что отправитель отправляет один сегмент с 512 байтами данных, а затем ожидает ACK. ACK получено через 716 миллисекунд, что определяет время возврата. Затем окно переполнения увеличивается до двух сегментов, и эти два сегмента отправляются. Когда ACK получен в сегменте 5, окно переполнения увеличивается до трех сегментов. Несмотря на то, что может быть послано три сегмента, отправляется только два, перед тем как не будет получен еще один ACK.

Мы вернемся к медленному старту в разделе "Алгоритм предотвращения переполнения" главы 21 и увидим, как реализована альтернативная техника, называемая предотвращением переполнения.

Пропускная способность для неинтерактивных данных

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

На рисунке 20.9 показана работа подобного соединения, отправитель находится слева, получатель - справа. На рисунке показано 16 моментов времени. Для простоты время показано дискретно. В верхней половине каждого рисунка мы показываем сегменты, переносящие данные слева направо, и пронумерованные как 1, 2, 3 и так далее. ACK двигаются в другом направлении и показаны в нижней половине каждого рисунка. Мы рисуем ACK меньше и указываем номера сегментов, которые подтверждаются.

Рисунок 20.8 Пример медленного старта.

Рисунок 20.9 Моменты времени 0-15, иллюстрирующие пропускную способность при передаче неинтерактивных данных.

В момент времени 0 отправитель посылает один сегмент. Так как отправитель работает с медленным стартом (окно переполнения установлено в один сегмент), он должен ждать подтверждения на этот сегмент, перед тем как продолжить работу.

В моменты времени 1, 2 и 3 сегмент проходит по одному промежутку времени вправо. В момент времени 4 получатель читает сегмент и генерирует подтверждение. В моменты времени 5, 6 и 7 подтверждение движется по одному промежутку времени влево, обратно к отправителю. Таким образом, время возврата (RTT) составляет 8 промежутков времени.

Мы специально нарисовали сегмент подтверждения (ACK) меньше чем сегмент данных, так как обычно он состоит из IP заголовка и TCP заголовка. Здесь мы показываем поток данных без учета направления в один и тот же момент времени. Также мы делаем предположение, что ACK двигается с той же самой скоростью, что и сегмент данных, что в действительности не всегда верно.

В-общем, время отправки пакета зависит от двух факторов: задержки прохождения (которая вызвана конечной скоростью света, временами ожидания и аппаратурой передачи) и задержки передачи, которая зависит от скорости среды передачи (количество бит, которое может быть передано в среде передачи за секунду). Для данного пути между двумя узлами задержка прохождения фиксированна, тогда как задержка передачи зависит от размера пакета. При небольших скоростях определяющими являются задержки передачи (в упражнении 2 главы 7 мы даже не принимали во внимание задержку прохождения), однако для скоростей равных гигабитам определяющей является задержка прохождения (см. рисунок 24.6).

Когда получатель принимает ACK, он может передать два сегмента (которые мы пронумеровали как 2 и 3) в моменты времени 8 и 9. Окно переполнения сейчас составляет два сегмента. Эти два сегмента двигаются вправо по направлению к приемнику, где генерируются подтверждения в моменты времени 12 и 13. Промежутки времени между подтверждениями (ACK) идентичны промежуткам между сегментами данных. Это поведение TCP называется самонастройкой по времени (self-clocking). Так как получатель может генерировать ACK только тогда, когда данные получены, по промежуткам между подтверждениями можно определить скорость прибытия данных к приемнику. (В действительности, организация очереди на маршруте возврата может изменить скорость прибытия ACK.)

На рисунке 20.10 показаны следующие 16 временных промежутков. Прибытие двух ACK увеличивает окно переполнения с 2 до 4 сегментов, и эти четыре сегмента отправляются в моменты времени 16-19. Первый ACK возвращается в момент времени 23. Четыре ACK увеличили окно переполнения с 4 сегментов до 8, и эти восемь сегментов передаются в моменты времени 24-31.

В момент времени 31 канал между отправителем и получателем полностью загружен. По нему нельзя передать больше данных, вне зависимости от величины окна переполнения или величины окна, объявленного получателем. В каждый момент времени получатель забирает сегмент из сети, а другой помещается в сеть отправителем. Канал заполнен большим количеством сегментов данных, в нем присутствует точно такое же количество подтверждений. Это идеальное состояние соединения.

Емкость канала в зависимости от полосы пропускания (Bandwidth-Delay Product)

Теперь мы можем ответить на вопрос, какого размера должно быть окно. В нашем примере, для достижения максимальной пропускной способности, отправитель должен выдать в сеть в один момент времени восемь пакетов и на них должно быть получено подтверждение. Окно, объявленное получателем, должно быть больше чем тот предел, который может быть передан отправителем.

Мы можем рассчитать емкость канала следующим образом:

емкость (биты) = ширина полосы (бит в секунду) х время возврата (секунда)

Обычно это называется емкостью канала в зависимости от полосы пропускания (bandwidth-delay product). Это значение может изменяться в очень широких пределах, в зависимости от скорости сети и времени возврата (RTT) между двумя концами. Например, для телефонной линии T1 (1544000 бит в секунду), находящейся в Соединенных Штатах (RTT примерно равно 60 миллисекунд), емкость канала составляет 11580 байт. Это значение в общем сопоставимо с размерами буферов, которые мы обсудили в разделе "Размер окна" этой главы, однако телефонная линия T3 (45000000 бит в секунду), так же пролегающая в Соединенных Штатах, уже имеет емкость канала равную 337500 байтам, что значительно больше чем максимально возможное окно TCP (65535 байт). Мы обсудим новую опцию масштабирования окна TCP в разделе "Опция масштабирования окна" главы 24 и увидим, как можно обойти существующие в настоящее время ограничения TCP.

Рисунок 20.10 Моменты времени 16-31 для примера пропускной способности при передаче неинтерактивных данных.

Значение 1544000 бит в секунду для телефонной линии T1 - это примерная скорость передачи битов. Скорость передачи данных в действительности составляет 1536000 бит в секунду, так как 1 бит из 193 используется для разделения на фреймы. Битовая скорость передачи телефонной линии T3 в действительности составляет 44736000 бит в секунду, а скорость передачи данных может достигать 44210000 бит в секунду. Для наших рассуждений мы будем использовать значение 1,544 Мбит/сек и 45 Мбит/сек.

Таким образом, ширина полосы или задержка могут оказывать влияние на емкость канала между отправителем и получателем. На рисунке 20.11 мы графически показали, как удвоение RTT удваивает емкость канала.

Рисунок 20.11 Удвоение RTT удваивает емкость канала.

В нижней части рисунка 20.11, с более длинным RTT, канал может содержать восемь сегментов вместо четырех.

Точно так же, на рисунке 20.12 показано, что удвоение полосы передачи также удваивает емкость канала.

Рисунок 20.12 Удвоение полосы передачи удваивает емкость канала.

В нижней части рисунка 20.12 мы предположили, что скорость сети удвоена, что, в свою очередь, позволяет нам посылать четыре сегмента за половину отрезка времени, показанного на верхней части рисунка. И снова емкость канала была увеличена в два раза. (Мы предположили, что сегменты в верхней части этого рисунка имеют тот же размер, то есть содержат такое же количество бит, как и сегменты, находящиеся в нижней половине.)

Переполнение

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

На рисунке 20.13 показана типичная ситуация, когда быстрый канал переполняет медленный. Мы считаем подобную ситуацию типичной, потому что большинство хостов подключены к локальным сетям, а также через маршрутизатор подключены к более медленным глобальным сетям. (Здесь как и прежде, мы предполагаем, что размер всех сегментов данных (9-20) в верхней части рисунка одинаков, и все размеры подтверждений в нижней части рисунка также одинаковы.)

На этом рисунке мы пометили маршрутизатор R1 как "узкое место", потому что именно в этой точке происходит переполнение. Маршрутизатор может принимать пакеты из локальной сети, которая находится слева от него, быстрее, чем они могут быть отправлены в глобальную сеть, которая находится справа. (Обычно R1 и R3 это один и тот же маршрутизатор, так же как R2 и R4, хотя это и не обязательно; могут быть использованы асимметричные маршруты.)

Рисунок 20.13 Переполнение, вызванное разными скоростями каналов.

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

На рисунке 20.13 мы предположили, что отправитель не использует медленный старт и посылает сегменты, которые мы пронумеровали 1-20, с такой скоростью, которая обеспечивается локальной сетью. (При этом сделано предположение, что принимающий хост объявил размер окна равный по меньшей мере 20 сегментам.) Промежутки между подтверждениями соответствуют ширине полосы самого медленного канала. Мы считаем, что маршрутизатор, являющийся узким местом, имеет достаточный размер буферов для всех 20 сегментов. Если это не так, маршрутизатор будет отбрасывать пакеты. Мы посмотрим, как избежать этого, когда будем говорить о предотвращении переполнения в разделе "Алгоритм предотвращения переполнения" главы 21.

Режим срочности (Urgent Mode)

TCP предоставляет режим срочности (urgent mode), который позволяет одному концу сообщить другому о том, что в обычный поток данных каким-либо образом были помещены "срочные данные". Удаленный конец, таким образом, уведомляется о том, что в поток данных помещены срочные данные, и уже от него зависит (от принимающего конца) как с ними поступить.

Уведомление о присутствии срочных данных в потоке осуществляется путем установки двух полей в TCP заголовке (рисунок 17.2). Устанавливается бит URG, а 16-битный указатель срочности содержит положительное смещение, которое должно быть добавлено к полю номера последовательности в TCP заголовке, чтобы получить номер последовательности последнего байта срочных данных.

До сих пор продолжаются дебаты на предмет того, куда должен указывать указатель срочности: или на последний байт срочных данных или на байт, следующий за последним байтом срочных данных. Исходная спецификация TCP позволяет использовать обе интерпретации, однако требования к хостам Host Requirements RFC указывают, какая из них верна: указатель срочности указывает на последний байт срочных данных.

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

TCP должен информировать получающий процесс о том, что принят указатель срочности; либо он уже принят, либо двигается в потоке данных. Затем принимающее приложение читает поток данных; оно должно быть в состоянии определить, когда появится указатель срочности. Приложение находится в "срочном режиме", все время, пока читает данные с текущей позиции до указателя срочности. После того как указатель срочности принят, приложение возвращается в нормальный режим.

Сам TCP может сказать очень немного о срочных данных. Не существует возможности точно определить, где в потоке начинаются срочные данные. Информация, которая посылается по соединению от TCP, сообщает в том, что режим срочности начался (бит URG в TCP заголовке), а также указывает на последний байт срочных данных. Вся остальная работа оставлена приложению.

К сожалению, большинство реализаций неверно называют режим срочности TCP данными, выходящими за полосу (out-of-band) . Если приложение действительно хочет выделить канал, выходящий за полосу, то самый простой способ добиться этого - создать второе TCP соединение. (Некоторые транспортные уровни предоставляют то, что большинство пользователей считают действительно данными, выходящими за полосу: логически выделенный канал данных, который, однако, использует то же самое соединение, что и стандартный поток данных. TCP этого не предоставляет.)

Путаница между режимом срочности TCP и данными, выходящими за полосу, также объясняется доминирующим программным интерфейсом, сокеты API устанавливают соответствие между режимом срочности TCP и тем, что сокеты называют данными, выходящими за полосу.

Для чего используется режим срочности? Рассмотрим два наиболее широко используемых приложения Telnet и Rlogin. Когда пользователь нажимает клавишу прерывания, используется режим срочности. (Мы покажем это в главе 26.) В случае FTP, когда пользователь прерывает передачу файла, также используется режим срочности. (Показано в примерах к главе 27.)

Telnet и Rlogin используют режим срочности в направлении от сервера к клиенту, потому что поток данных в этом направлении может быть остановлен клиентом TCP (путем объявления размера окна равного 0). Однако, если процесс сервера входит в режим срочности, сервер TCP немедленно отправляет указатель срочности и устанавливает флаг URG, даже если он не может послать какие-либо данные. Когда TCP клиент получает это уведомление, он, в свою очередь, уведомляет процесс клиента; таким образом, клиент может считать свой ввод от сервера, чтобы открыть окно и разрешить прохождение потока данных.

Что произойдет, если отправитель войдет в режим срочности несколько раз, перед тем как получатель обработает все данные до прихода первого указателя срочности? Указатель срочности все еще двигается в потоке данных, а его предыдущее положение для получателя потеряно. Для получателя существует только один указатель срочности и его значение перезаписывается, когда приходит новое значение указателя срочности с удаленного конца. Это означает, что если содержимое потока данных, которое формируется отправителем, когда он входит в режим срочности, важно для получателя, эти байты данных должны быть специально помечены (каким-либо образом) отправителем. Мы увидим, что Telnet помечает все свои командные байты в потоке данных, ставя перед ними байт 255.

Пример

Давайте посмотрим, как TCP посылает срочные данные, даже когда окно получателя закрыто. Мы стартуем программу sock на хосте bsdi и выдержим паузу в течении 10 секунд после того, как соединение будет установлено (опция -P), перед тем как начать читать из сети. Это позволяет удаленному концу заполнить отправляемое окно.

bsdi % sock -i -s -P10 5555

Затем мы стартуем клиента на хосте sun, указав ему установить отправляющий буфер размером 8192 байта (опция -S) и осуществить шесть записей в сеть, каждая размером 1024 байта (опция -n). Также мы указали -U5, что заставляет клиента записать 1 байт данных и войти в режим срочности перед записью в сеть пятого буфера. Также мы установили флаг отладки, чтобы посмотреть в каком порядке будут осуществлены записи:

sun % sock -v -i -n6 -S8192 -U5 bsdi 5555
connected on 140.252.13.33.1305 to 140.252.13.35.5555
SO_SNDBUF = 8192
TCP_MAXSEG = 1024
wrote 1024 bytes
wrote 1024 bytes
wrote 1024 bytes
wrote 1024 bytes
wrote 1 byte of urgent data
wrote 1024 bytes
wrote 1024 bytes

Мы установили размер буфера отправителя в 8192 байта, чтобы позволить отправляющему приложению немедленно записать все свои данные. На рисунке 20.14 показан вывод tcpdump для этого обмена. (Все имеющее отношение к установлению соединения удалено.) В строках 1-5 показано, как отправитель заполняет окно приемника четырьмя сегментами по 1024 байта. Затем отправитель останавливается, потому что окно приемника заполнено. (Подтверждение в строке 4 подтверждает данные, однако не перемещает правую границу окна.)

После того как приложение осуществило четыре обычные записи данных, приложение пишет 1 байт данных и входит в режим срочности. В строке 6 показан результат этой записи. Указатель срочности установлен в 4098. Указатель срочности отправляется с флагом URG, даже если отправитель не может послать какие-либо данные.

Пять из этих подтверждений (ACK) отправляются примерно в течение 13 миллисекунд (строки 6-10). Первое отправляется, когда приложение пишет 1 байт и входит в режим срочности. Следующие два отправляются, когда приложение осуществляет две последние записи, каждая по 1024 байта. (Даже если TCP не может отправить эти 2048 байт данных, каждый раз, когда приложение осуществляет запись, вызывается функция вывода TCP, и когда она видит, что осуществлен вход в режим срочности, то посылает еще одно уведомление о срочности.) Четвертый из этих ACK появляется, когда приложение со своей стороны закрывает соединение. (В этом случае снова вызывается функция вывода TCP.) Отправляющее приложение прекратило свою работу через несколько миллисекунд после старта - перед тем как принимающее приложение осуществило свою первую запись. TCP поставило в очередь все данные и отправляет их по возможности. (Именно поэтому мы указали размер буфера равный 8192 байта - таким образом, все данные могут поместиться в буфер.) Пятый из этих ACK сгенерирован на получение ACK в строке 4. Отправляющий TCP возможно уже поставил в очередь четвертый сегмент для вывода (строка 5), перед тем как прибыл этот ACK. Приход этого ACK от удаленной стороны также приводит к вызову подпрограммы вывода TCP.

1  0.0                    sun.1305 > bsdi.5555: P 1:1025(1024) ack 1 win 4096
2  0.073743 (0.0737)     sun.1305 > bsdi.5555: P 1025:2049(1024) ack 1 win 4096
3  0.096969 (0.0232)     sun.1305 > bsdi.5555: P 2049:3073(1024) ack 1 win 4096
4  0.157514 (0.0605)     bsdi.5555 > sun.1305: . ack 3073 win 1024
5  0.164267 (0.0068)     sun.1305 > bsdi.5555: P 3073:4097(1024) ack 1 win 4096

6  0.167961 (0.0037)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098
7  0.171969 (0.0040)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098
8  0.176196 (0.0042)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098
9  0.180373 (0.0042)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098
10  0.180768 (0.0004)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098

11  0.367533 (0.1868)     bsdi.5555 > sun.1305: . ack 4097 win 0
12  0.368478 (0.0009)     sun.1305 > bsdi.5555: . ack 1 win 4096 urg 4098

13  9.829712 (9.4612)     bsdi.5555 > sun.1305: . ack 4097 win 2048
14  9.831578 (0.0019)     sun.1305 > bsdi.5555: . 4097:5121(1024) ack 1 win 4096 urg 4098
15  9.833303 (0.0017)     sun.1305 > bsdi.5555: . 5121:6145(1024) ack 1 win 4096

16  9.835089 (0.0018)     bsdi.5555 > sun.1305: . ack 4097 win 4096
17  9.835913 (0.0008)     sun.1305 > bsdi.5555: FP 6145:6146(1) ack 1 win 4096
18  9.840264 (0.0044)     bsdi.5555 > sun.1305: . ack 6147 win 2048
19  9.842386 (0.0021)     bsdi.5555 > sun.1305: . ack 6147 win 4096
20  9.843622 (0.0012)     bsdi.5555 > sun.1305: F 1:1(0) ack 6147 win 4096
21  9.844320 (0.0007)     sun.1305 > bsdi.5555: . ack 2 win 4096

Рисунок 20.14 Вывод tcpdump для режима срочности TCP.

Затем получатель подтверждает последние 1024 байта данных (строка 11), а также объявляет размер окна равный 0. Отправитель отвечает еще одним сегментом, содержащим уведомление о срочности.

Получатель объявляет окно равное 2048 байт в строке 13, когда приложение активизируется и читает некоторые данные из приемного буфера. Отправляются следующие два сегмента размером 1024 байта (строки 14 и 15). В первом сегменте установлено уведомление о срочности, указатель срочности также находится в этом сегменте. Второй сегмент выключает уведомление срочности.

Когда получатель вновь открывает окно (строка 16), отправитель посылает последний байт данных (с номером 6145) и осуществляет нормальное закрытие соединения.

На рисунке 20.15 показаны номера последовательности 6145 байт данных, которые были отправлены. Мы видим, что номер последовательности байта, записанного при входе в режим срочности, равен 4097, однако значение указателя срочности на рисунке 20.14 равно 4098. Это является подтверждением того, что данная реализация (SunOS 4.1.3) устанавливает указатель срочности на 1 байт позади последнего байта срочных данных.

Рисунок 20.15 Пример того, как приложение пишет TCP сегменты в режиме срочности.

Этот рисунок также позволяет нам увидеть, как TCP осуществляет сборку пакетов из данных, которые записаны приложением. Один байт, который был выдан при входе в режим срочности, отправляется вместе со следующими 1023 байтами данных, находящимися в буфере. Следующий сегмент также содержит 1024 байта данных, а последний сегмент содержит 1 байт данных.

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

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

Фундаментальной концепцией эффективной передачи неинтерактивных данных является протокол изменяющегося окна TCP. Также мы увидели, что для того, чтобы получить максимально высокую скорость передачи данных с использованием TCP, необходимо поддерживать заполненным канал между отправителем и получателем. Мы оценили емкость этого канала как "емкость в зависимости от полосы пропускания" и увидели взаимосвязь между этим параметром и размером окна. Мы вернемся к этой концепции в разделе "Производительность TCP" главы 24, когда будем рассматривать производительность TCP.

Также мы рассмотрели флаг PUSH, который используется в TCP. Мы видим его в выводе практически всех отладочных программ, однако не можем управлять установкой этого флага. И последнее: мы рассмотрели срочные данные TCP, которые часто по ошибке называются "данными, выходящими за полосу". Режим срочности TCP это всего лишь уведомление от отправителя получателю о том, что были отправлены срочные данные, а также указание на номер последовательности последнего байта срочных данных. Программные интерфейсы для приложений, используемые для срочных данных, часто не очень оптимальны, что ведет к некоторой неразберихе.

Упражнения

  1. На рисунке 20.6 мы показали байт с номером 0 и байт с номером 8193. Для чего введены эти два байта?
  2. Забегая вперед, посмотрите на рисунок 22.1 и объясните установку флага PUSH хостом bsdi.
  3. В почтовой системе Usenet иногда появляются жалобы на пропускную способность равную 120000 бит/сек на канале 256000 бит/сек с задержкой 128 миллисекунд между Соединенными Штатами и Японией (используется 47%), и пропускную способность 33000 бит/сек, когда канал маршрутизируется через спутник (используется 13%). Какой размер окна присутствует в обоих случаях? (Представьте, что задержка, вызванная спутниковым каналом, составляет 500 миллисекунд.) Каким должен быть размер окна для спутникового канала?
  4. Если API предоставляет возможность отправляющему приложению сообщить своему TCP о необходимости взвести флаг PUSH и предоставляет возможность получателю определить, взведен ли флаг PUSH в принятом сегменте, может ли этот флаг использоваться как маркер записи?
  5. Почему на рисунке 20.3 сегменты 15 и 16 объединены?
  6. На рисунке 20.13 мы предположили, что подтверждения (ACK) возвращаются с такими же промежутками, как и промежутки между прохождением сегментов данных. Что произойдет, если ACK где-либо на обратном пути будут поставлены в очередь и придут к отправителю практически в одно и то же время?


Тайм-ауты и повторные передачи TCP

Глава 21 Тайм-ауты и повторные передачи TCP

Введение

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

Мы уже видели два примера тайм-аута и повторной передачи: (1) в примере, посвященном недоступности порта ICMP в разделе "ICMP ошибка недоступности порта" главы 6, мы видели, что TFTP клиент, использующий UDP, применяет простую стратегию тайм-аута и повторной передачи: он устанавливает период тайм-аута в 5 секунд и осуществляет повторную передачу каждые 5 секунд. (2) В примере ARP для несуществующего хоста (глава 4, раздел "Примеры ARP") мы видели, что когда TCP старается установить соединение, он повторно передает свои SYN, используя увеличенные задержки между каждой повторной передачей.

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

  1. Таймер повторной передачи (retransmission) используется в том случае, когда ожидается подтверждение от удаленного конца. В этой главе таймер повторной передачи рассматривается подробно, также рассматриваются соответствующие характеристики, такие как предотвращение переполнения.
  2. Устойчивый (persist) таймер, в течение которого сохраняется информация о размере окна передачи, даже если удаленный конец закрыл свое приемное окно. В главе 22 этот таймер будет описан более подробно.
  3. Таймер времени жизни (keepalive) определяет, когда можно считать, что удаленный конец вышел из строя или перезагрузился. Этот таймер описывается в главе 23.
  4. Таймер 2MSL определяет время, в течение которого соединение может быть в состоянии TIME_WAIT. Это описано в разделе "Диаграмма состояний передачи TCP" главы 18.

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

Простой пример использования тайм-аутов и повторных передач

Во-первых, давайте рассмотрим стратегию повторных передач, которая используется TCP. Мы установим соединение, отправим какие-нибудь данные, чтобы убедиться в том, что соединение функционирует, отсоединим кабель, отправим еще данные и посмотрим, как поступит TCP:

bsdi % telnet svr4 discard
Trying 140.252.13.34...
Connected to svr4.
Escape character is '^]'.
hello, world                             эту строку мы посылаем обычным образом
and hi                                   перед тем как отправить эту строку, отсоединяем кабель
Connection closed by foreign host.       вывод, после того как TCP ждал 9 минут

На рисунке 21.1 показан вывод команды tcpdump. (Мы удалили всю информацию, связанную с типом сервиса, которую устанавливает bsdi.)

1    0.0                     bsdi.1029 > svr4.discard: S 1747921409:1747921409(0)
                                                         win 4096 <mss 1024>
2    0.004811 ( 0.0048)     svr4.discard > bsdi.1029: S 3416685569:3416685569(0)
                                                         ack 1747921410
                                                         win 4096 <mss 1024>
3    0.006441 ( 0.0016)     bsdi.1029 > svr4.discard: . ack 1 win 4096

4    6.102290 ( 6.0958)     bsdi.1029 > svr4.discard: P 1:15(14) ack 1 win 4096
5    6.259410 ( 0.1571)     svr4.discard > bsdi.1029: . ack 15 win 4096

6   24.480158 (18.2207)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
7   25.493733 ( 1.0136)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
8   28.493795 ( 3.0001)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
9   34.493971 ( 6.0002)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
10   46.484427 (11.9905)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
11   70.485105 (24.0007)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
12  118.486408 (48.0013)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
13  182.488164 (64.0018)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
14  246.489921 (64.0018)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
15  310.491678 (64.0018)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
16  374.493431 (64.0018)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
17  438.495196 (64.0018)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
18  502.486941 (63.9917)     bsdi.1029 > svr4.discard: P 15:23(8) ack 1 win 4096
19  566.488478 (64.0015)     bsdi.1029 > svr4.discard: R 23:23(0) ack 1 win 4096

Рисунок 21.1 Простой пример тайм-аута и повторной передачи TCP.

Строки 1, 2 и 3 соответствуют обычному установлению TCP соединения. Строка 4 это передача "hello, world" (12 символов плюс символ возврата каретки и пропуска строки), а в строке 5 - подтверждение. Затем мы отсоединяем Ethernet кабель от svr4.

В строке 6 показано, как передается "and hi". Строки 7-18 это 12 повторных передач сегмента, а в строке 19 TCP прекращает попытки передачи и посылает сброс.

Обратите внимание на временные промежутки между последовательными повторными передачами: они происходили в моменты времени 1, 3, 6, 12, 24, 48 и 64 секунды. Дальше в этой главе мы увидим, что первый тайм-аут устанавливается в 1,5 секунды после первой передачи. (Причина, по которой он возник через 1,0136 секунды после первой передачи, а не точно через 1,5 секунды, была объяснена на рисунке 18.7.) После этого величина тайм-аута удваивается для каждой передачи, причем верхний предел составляет 64 секунды.

Подобное увеличение называется экспотенциальным наращиванием (exponential backoff). Сравните это с примером TFTP, который приведен в разделе "ICMP ошибка недоступности порта" главы 6, где каждая повторная передача осуществляется через 5 секунд после предыдущей.

Разница во времени между первой передачей пакета (строка 6, момент времени 24,480) и сбросом (строка 19, момент времени 566,488) составляет примерно 9 минут. Современные TCP реализации довольно настойчивы в попытках отправить данные!

В большинстве реализаций полная величина тайм-аута ненастраиваемая. Solaris 2.2 позволяет администратору изменить эту величину (переменная tcp_ip_abort_interval в разделе "Solaris 2.2" приложения E), а по умолчанию она составляет только 2 минуты, а не 9 минут, как это принято в большинстве реализаций.

Определение времени возврата

Основой тайм-аутов и повторных передач TCP является расчет времени возврата (RTT - round-trip time), соответствующего данному соединению. Мы ожидаем, что оно может изменяться со временем, так как может измениться маршрут, или загрузка сети. TCP должен отследить эти изменения и соответственно модифицировать тайм-ауты.

Во-первых, TCP должен рассчитать RTT между отправкой байта с конкретным номером последовательности и получением подтверждения на этот номер последовательности. Из предыдущей главы мы знаем, что обычно не существует полного соответствия между сегментами данных и подтверждениями (ACK). На рисунке 20.1 это означало, что один RTT, который может быть вычислен отправителем, является временем между передачей сегмента 4 (байты данных 1-1024) и получением сегмента 7 (ACK байт 1-2048), даже если этот ACK подтверждает дополнительно 1024 байта. Мы используем величину M, чтобы обозначить рассчитанный RTT.

Для определения RTT существует расширение к исходной спецификации TCP, которое называется хэшированная оценочная функция RTT (обозначается R)

R ¬ a R + (1 - a )M

где a это коэффициент хэширования с рекомендуемым значением 0,9. Хэширование - способ организации структур данных (хэш таблиц), обеспечивающий эффективный поиск и пополнение; положение элемента данных в хэш таблице определяется значением функции расстановки, отображающей множество возможных ключей элементов данных и множество индексов таблицы и обеспечивающей равномерное заполнение. Хэшированный RTT обновлялся каждый раз, когда осуществлялось новое вычисление. Девяносто процентов данных для каждого нового расчета берется из предыдущего расчета, а десять из нового.

Для подобной хэшированной оценочной функции, которая изменяется с изменением RTT, RFC 793 рекомендует, чтобы тайм-аут повторной передачи (RTO) устанавливался следующим образом

RTO = R b

где b это коэффициент изменения задержки с рекомендуемым значением равным 2.

[Jacobson 1988] подробно обсуждает проблемы, связанные с подобным подходом, в основном заключающиеся в том, что он не может применяться при широком диапазоне изменения RTT, и является причиной нежелательных повторных передач. Как замечает Jacobson, нежелательные повторные передачи увеличивают загрузку сети.

В этом случае необходимо добавить возможность отслеживать расхождения в расчетах RTT в дополнение к хэшированной функции оценки RTT. Расчет RTO основанный на среднем и расхождении дает значительно лучшие результаты при широком диапазоне изменений времен возврата, чем просто расчет RTO как постоянного кратного среднего значения. Рисунки 5 и 6 в [Jacobson 1988] показывают сравнение значений RTO в соответствии с RFC 793 для некоторых реальных времен возврата, с расчетами RTO, которые мы показали ранее, которые принимают во внимание изменение времен возврата.

Как описано у Jacobson, среднее отклонение является хорошим приближением к стандартному отклонению, однако рассчитывается значительно легче. (Расчет стандартного отклонения требует вычисления квадратного корня.) Таким образом, следующие уравнения могут быть применены к каждому расчету RTT M.

Err = M - A

A ¬ A + gErr

D ¬ D + h(| Err | - D)

RTO = A + 4D

где A это хэшированный RTT (оценочная функция среднего), а D это хэшированное среднее отклонение. Err это разница между только что рассчитанным значением и текущим значением оценочной функции RTT. Оба A и D используются для расчета следующего тайм-аута повторной передачи (RTO). Увеличение среднего (g) установлено в значение 1/8 (0,125). Увеличение отклонения (h) установлено в 0,25. Максимальное увеличение отклонения делает рост RTO быстрее при изменении RTT.

[Jacobson 1988] устанавливает 2D при расчете RTO, однако для следующих исследований [Jacobson 1990c] изменяет это значение на 4D, что соответствует реализации BSD Net/1.

Jacobson показывает способ осуществить эти вычисления с использованием целочисленной арифметики, именно так это обычно делается в стандартных реализациях. (Одна из причин заключается в том, что g, h и множитель 4 - это степени двух, поэтому все операции могут быть осуществлены с помощью сдвига, без умножений и делений.)

Сравнение исходного метода с методом Jacobsonа показывает, что расчеты хэшированного среднего одинаковы (a равно единица минус увеличение (g)), однако используются различные увеличения. Также, расчет RTO по Jacobsonу зависит от обоих значений - хэшированного RTT и хэшированного среднего отклонения, тогда как оригинальный метод использует умножение хэшированных RTT.

В следующем разделе мы увидим, как устанавливаются эти оценочные функции, когда будем рассматривать примеры.

Алгоритм Карна

В процессе повторной передачи пакета могут возникнуть проблемы. Скажем, пакет передан, тайм-аут отработан, RTO экспотенциально увеличен, как показано в разделе "Простой пример использования тайм-аутов и повторных передач" этой главы, пакет передан повторно с большим RTO и получено подтверждение. Соответствует ли это подтверждение первой передаче или второй? Это называется проблемой двусмысленности повторной передачи (retransmission ambiguity problem) .

[Karn and Partridge 1987] указывает, что когда применяется тайм-аут и повторная передача, мы не можем обновить оценочные функции RTT, когда, в конце концов, прибывает подтверждение на повторно переданные данные. Это потому, что мы не знаем, которой передаче соответствует подтверждение (ACK). (Возможно, первая передача была задержана, но не была отброшена, или был задержан ACK на первую передачу.)

Так как данные были повторно переданы и к RTO было применено экспотенциальное наращивание, мы повторно используем экспотенциально увеличенный RTO для следующей передачи. Новый RTO не рассчитывается до тех пор, пока не будет получено подтверждение на сегмент, который не отправлялся повторно.

Пример RTT

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

С помощью программы sock отправлено 32768 байт данных с хоста slip на discard сервис хоста vangogh.cs.berkeley.edu:

slip % sock -D -i -n32 vangogh.cs.berkeley.edu discard

Обратимся к рисунку, находящемуся на внутренней стороне обложки. Из рисунка видно, что slip подсоединен к Ethernet 140.252.1 двумя SLIP каналами, а затем через Internet к пункту назначения. Так как используется два SLIP канала (со скоростью 9600 бит в секунду), мы ожидаем появления определенных задержек.

Команда, приведенная выше, осуществит 32 записи по 1024 байта. Так как MTU между slip и bsdi составляет 296, то будет сгенерировано 128 сегментов, каждый из которых будет содержать 256 байт пользовательских данных. Полное время передачи займет примерно 45 секунд, и мы увидим один тайм-аут и три повторные передачи.

Пока осуществляется эта передача, мы запустим tcpdump на хосте slip, чтобы увидеть все сегменты, которые были переданы и приняты. В дополнение мы указали опцию -D, чтобы включить отладку сокетов (см. приложение A, раздел "Опция отладки сокета"). Кроме того, у нас была возможность запустить модифицированную версию программы trpt(8), которая позволяет напечатать некоторые переменные в блоке управления соединением, имеющие отношение к временам задержки, медленному старту и предотвращению переполнения.

Из-за того, что вывод достаточно большой, мы не можем показать его целиком, однако рассмотрим его по частям, в процессе изучения главы. На рисунке 21.2 показана передача данных и подтверждений в течение первых 5 секунд. Мы немного модифицировали этот вывод от предыдущего вывода команды tcpdump. Мы только оценили моменты времени, когда пакет отправлялся или принимался хостом, на котором запущена программа tcpdump, на этом рисунке мы хотели показать, что пакет двигается по сети (так как это соединение в локальной сети не похоже на распределенный Ethernet), и показать, когда принимающий хост, возможно, генерирует подтверждения. (Мы удалили все объявления окна. slip всегда объявляет окно размером 4096, а vangogh всегда объявляет окно 8192.)

Рисунок 21.2 Обмен пакетами и расчет RTT.

Также обратите внимание на то, что на этом рисунке мы пронумеровали сегменты 1-13 и 15 в том порядке, в котором они были отправлены или приняты хостом slip. Это соответствует выводу tcpdump, который был получен для этого хоста.

Определение времени возврата

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

Большинство реализаций TCP, происходящих от Berkeley, рассчитывают только одно значение RTT для соединения за один раз. Если в тот момент, когда отправляется сегмент данных, таймер для данного соединения уже используется, время для этого сегмента не засекается.

Установка времени осуществляется путем увеличения счетчика каждый раз, когда запускается 500-миллисекундный таймер TCP. Это означает, что для сегмента, подтверждение на который прибывают через 550 миллисекунд после того, как сегмент был отправлен, может быть принято RTT как равное одному тику (500 миллисекунд), так и RTT равное двум тикам (1000 миллисекунд).

В дополнение к этому счетчику тиков для каждого соединения, запоминается начальный номер последовательности данных в сегменте. Когда принимается подтверждение, содержащее этот номер последовательности, таймер выключается. Если данные не были повторно переданы, когда прибыл ACK, хэшированное RTT и хэшированное среднее отклонение обновляется на основе новых значений.

Таймер для соединения, показанного на рисунке 21.2, стартует, когда передается сегмент 1, и выключается, когда прибывает подтверждение на него (сегмент 2). Несмотря на то, что его RTT равен 1,061 секунды (из вывода команды tcpdump), исследование отладочной информации сокета показывает, что за этот период произошло 3 тика часов TCP, а это обозначает, что RTT равен 1500 миллисекунд.

Следующий сегмент, для которого засекли время, сегмент номер 3. Когда, через 2,4 миллисекунды, передается сегмент номер 4, он не может быть отслежен по времени, так как таймер для этого соединения уже используется. Когда прибывает сегмент 5, подтверждая данные, на которые было засечено время, его RTT рассчитывается равным 1 тику (500 миллисекунд), даже несмотря на то, что, как мы видели из вывода команды tcpdump, его RTT равен 0,808 секунды.

Таймер стартует снова, когда передается сегмент 6, и выключается, когда прибывает подтверждение на него, через 1,015 секунды (сегмент 10). Полученный RTT равен 2 тикам часов. Сегменты 7 и 9 не могут быть оценены по времени, так как таймер занят. Также, когда принимается сегмент 9 (ACK 769), ничего не обновляется, так как подтверждение не подтверждает байты, на которые засекли время.

На рисунке 21.3 показана взаимосвязь между реальными RTT, которые мы можем определить из вывода команды tcpdump, и счетчиком тиков часов.

Рисунок 21.3 Расчет RTT и тики часов.

Вверху мы показали тики часов, каждые 500 миллисекунд. Внизу - моменты времени, полученные из вывода команды tcpdump, и то, когда таймер соединения включался и выключался. Мы знаем, что между отправкой сегмента 1 и получением сегмента 2 прошло 3 тика, что заняло 1,061 секунды, таким образом, мы предполагаем, что первый тик возник в момент времени 0,03. (Первый тик должен произойти между 0,00 и 0,061.) На рисунке показано, что второй RTT был оценен равным 1 тику, а третий - 2 тикам.

В этом примере было передано 128 сегментов и получено 18 значений RTT. На рисунке 21.4 показаны измеренные RTT (взятые из вывода tcpdump) вместе с RTO, используемого в TCP для установки тайм-аутов (взято из отладочной информации сокета). Момент времени 0 (на рисунке 21.2) по оси OX соответствует отправке первого сегмента данных, а не отправке первого SYN.

Рисунок 21.4 Рассчитанные RTT и RTO TCP для этого примера.

Первые три точки, которые соответствующие измеренным RTT, соответствуют трем RTT, которые мы показали на рисунке 21.2. Пропуски в значениях RTT около моментов времени 10, 14 и 21 вызваны повторными передачами, которые здесь имели место (что будет показано позже в этой главе). Алгоритм Карна не позволяет обновить наши оценки до тех пор, пока еще один сегмент не будет передан и подтвержден. Также обратите внимание на то, что для этой реализации рассчитанные RTO TCP всегда кратны 500 миллисекундам.

Расчет оценочных функций RTT

Давайте посмотрим, как устанавливаются и обновляются оценочные функции RTT (хэшированный RTT и хэшированное среднее отклонение), и как рассчитывается тайм-аут для каждой передачи.

Переменные A и D устанавливаются в 0 и 3 секунды соответственно. Исходный тайм-аут для передачи рассчитывается с использованием формулы

RTO = A + 2D = 0 + 2 x 3 = 6 секунд

(Коэффициент 2D используется только для этого первоначального расчета. Затем при расчете RTO к A прибавляется 4D, как было показано ранее.) Это RTO для передачи первоначального SYN.

В случае если исходный SYN потерян, осуществляется тайм-аут и повторная передача. На рисунке 21.5 показаны первые четыре строки вывода команды tcpdump.

1  0.0                    slip.1024 > vangogh.discard: S 35648001:35648001(0)
                                                        win 4096 <mss 256>
2  5.802377 (5.8024)     slip.1024 > vangogh.discard: S 35648001:35648001(0)
                                                        win 4096 <mss 256>
3  6.269395 (0.4670)     vangogh.discard > slip.1024: S 1365512705:1365512705(0)
                                                        ack 35648002
                                                        win 8192 <mss 512>
4  6.270796 (0.0014)     slip.1024 > vangogh.discard: . ack 1 win 4096

Рисунок 21.5 Тайм-аут и повторная передача исходного SYN.

Когда тайм-аут возникает позже, через 5,802 секунды, текущий RTO рассчитывается следующим образом

RTO = A + 4D = 0 + 4 x 3 = 12 секунд

Затем к RTO равному 12 применяется экспотенциальное наращивание. Так как это первый тайм-аут, используется множитель 2, при этом значение следующего тайм-аута будет равно 24 секундам. Следующий тайм-аут рассчитывается с использованием множителя 4, значения тайм-аута становится 48 секунд: 12 x 4. (Эти исходные RTO для первого SYN, 6 секунд и затем 24 секунды, как раз то, что мы видели на рисунке 4.5.)

ACK прибывает через 467 миллисекунд после повторной передачи. Значения A и D не обновляются, потому что алгоритм Карна определяет двусмысленность передачи. Следующий отправляемый сегмент - это ACK в строке 4, однако время для него не засекается, так как это всего лишь подтверждение. (Время устанавливается только для сегментов, содержащих данные.)

Когда отправляется первый сегмент данных (сегмент 1 на рисунке 21.2), RTO не меняется, опять же в соответствии с алгоритмом Карна. Текущее значение, равное 24 секундам, повторно используется до тех пор, пока не будет осуществлено измерение RTT. Это означает, что RTO для момента времени равного 0 на рисунке 21.4 равно в действительности 24, однако мы не берем во внимание эту точку.

Когда прибывает подтверждение на этот первый сегмент данных (сегмент 2 на рисунке 21.2), получено 3 тика часов, и наши показатели устанавливаются следующим образом

A = M + 0,5 = 1,5 + 0,5 = 2

D = A/2 = 1

(Значение M равное 1,5 соответствует 3-м тикам часов.) Предыдущие установки A и D в 0 и 3 были сделаны для расчета первоначального RTO. Эти установки предназначены для первого расчета оценочных функций, с использованием первого измерения RTT M. RTO рассчитывается следующим образом

RTO = A + 4D = 2 + 4 x 1 = 6 секунд

Когда прибывает ACK на второй сегмент данных (сегмент 5 на рисунке 21.2), отсчитан 1 тик часов (0,5 секунды), и наши показатели обновляются следующим образом

Err = M - A = 0,5 - 2 = -1,5

A = A + gErr = 2 - 0,125 x 1,5 = 1,8125

D = D + h(|Err| - D) = 1 + 0,25 x (1,5 - 1) = 1,125

RTO = A + 4D = 1,8125 + 4 x 1,125 = 6,3125

Существует несколько тонкостей в представлении Err, A и D, при расчетах с фиксированной точкой, которая и используется в действительности (однако мы показали для простоты с плавающей точкой). Эта разница дает RTO равное 6 секундам (а не 6,3125), как раз столько, сколько было показано на рисунке 21.4 для момента времени 1,871.

Медленный старт

Мы описали алгоритм медленного старта в разделе "Медленный старт" главы 20, а также видели его в действии на рисунке 21.2.

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

Пример переполнения

Давайте теперь рассмотрим передачу сегментов данных. На рисунке 21.6 показана зависимость стартового номера последовательности сегмента от времени, когда сегмент был отправлен. Это позволит нам более наглядно представить процесс передачи данных. Обычно точки, соответствующие данным, должны двигаться вверх вправо, при этом наклон соответствует скорости передачи. Повторные передачи показаны отклонением графика вниз вправо.

В начале раздела "Пример RTT" этой главы мы сказали, что полное время передачи составляло примерно 45 секунд, однако на этом рисунке мы показали только 35 секунд. (Потому что, именно 35 секунд потребовалось для передачи только сегментов данных.) Первый сегмент данных не передавался в течении 6,3 секунды после отправки первого SYN, потому что первый SYN был потерян при передаче и передан повторно (рисунок 21.5). После того как последний сегмент данных и FIN были отправлены (момент времени 34,1 на рисунке 21.6), потребовалось еще примерно 4,0 секунды на то, чтобы принять последние 14 ACK от получателя перед тем, как от получателя был получен FIN.

Рисунок 21.6 Отправка 32768 байт данных от slip на vangogh.

На рисунке 21.6 мы сразу видим три повторные передачи в моменты времени 10, 14 и 21. Во всех трех случаях только один сегмент передается повторно, потому что только одна точка оказалась ниже предыдущих.

Давайте рассмотрим первый из этих "скачков вниз" (в момент времени 10). Вывод команды tcpdump мы рассмотрим вместе с рисунком 21.7.

Рисунок 21.7 Обмен пакетами в процессе повторной передачи в районе 10-секундной метки.

Мы удалили все объявления окна, за исключением сегмента 72, который мы обсудим ниже. slip всегда объявляет окно равное 4096, а vangogh объявляет окно равное 8192. Сегменты на этом рисунке пронумерованы как продолжение рисунка 21.2, где первый сегмент данных по соединению был с номером 1. Как и на рисунке 21.2, сегменты пронумерованы в соответствии с тем, как они отправлялись или принимались на хосте slip, где была запущена программа tcpdump. Мы также удалили несколько сегментов, которые не имели отношения к нашему обсуждению (44, 47 и 49, а также все ACK от vangogh).

Случилось так, что сегмент 45 либо потерялся, либо пришел поврежденным - мы не можем сказать этого определенно, основываясь на выводе команды. Все что мы можем увидеть на хосте slip это то, что было подтверждено все, за исключением байта 6657 (сегмент 58), за которым следовали следующие восемь ACK с тем же самым номером последовательности. Прием сегмента 62, третьего из дублированных ACK, вызвал повторную передачу данных, начиная с номера последовательности 6657 (сегмент 63). В действительности, реализации, происходящие от Berkeley, подсчитывают количество принятых дублированных ACK, и когда принимается третий, они подразумевают, что сегмент потерян, и повторно передают только один сегмент, начиная с этого номера последовательности. Подобное поведение является частью алгоритма быстрой повторной передачи (fast retransmit), который следует за алгоритмом быстрого восстановления данных (fast recovery algorithm), которые мы обсудим в разделе "Быстрая повторная передача и алгоритм быстрого восстановления" этой главы.

Обратите внимание на то, что после повторной передачи (сегмент 63) отправитель продолжает обычную передачу данных (сегменты 67, 69 и 71). TCP не ожидает того, что удаленный конец подтвердит повторную передачу.

Давайте посмотрим, что происходит на принимающем конце. Когда обычные данные приходят последовательно (сегмент 43), принимающий TCP передает 256 байт данных пользовательскому процессу. Однако следующий принятый сегмент (сегмент 46) не в порядке; стартовый номер последовательности данных (6913) не является следующим ожидаемым номером последовательности (6657). TCP сохраняет 256 байт данных и отвечает посредством ACK с самый большим номером последовательности, который был принят успешно, плюс один (6657). Следующие семь сегментов, принятых vangogh (48, 50, 52, 54, 55, 57 и 59), также не в порядке. Данные сохраняются принимающим TCP, и генерируются дублированные ACK.

Таким образом, для TCP не существует способа сообщить удаленному концу, что сегмент отсутствует. Помимо этого TCP не может подтвердить поврежденные данные. Все что может сделать vangogh в подобном случае - это продолжать посылать ACK с номером 6657.

Когда прибывают отсутствующие данные (сегмент 63), принимающий TCP имеет в своем буфере байты данных 6657-8960. Он передает эти 2304 байта пользовательскому процессу. Все 2304 байта подтверждены в сегменте 72. Также обратите внимание на то, что этот ACK объявляет окно равное 5888 (8192 - 2304), так как пользовательский процесс не имеет возможности прочитать 2304 байта, которые уже готовы для него.

Если мы рассмотрим более подробно вывод команды tcpdump для моментов времени 14 и 21 на рисунке 21.6, то увидим, что они были вызваны получением трех дублированных ACK, а это указывает на то, что пакет был потерян. Во всех этих случаях только один пакет был передан повторно.

Мы продолжим рассмотрение этого примера в разделе "Пример переполнения (продолжение)" этой главы, после того как рассмотрим более подробно алгоритм предотвращения переполнения.

Алгоритм предотвращения переполнения

Медленный старт, который мы описали в разделе "Медленный старт" главы 20, это способ первоначально установить поток данных по соединению. Однако, в это же самое время мы достигнем предела у промежуточного маршрутизатора, при котором пакеты будут отбрасываться. Предотвращение переполнения это способ, позволяющий предотвратить потерю пакетов. Подробности можно найти в [Jacobson 1988].

Предположение, на котором строится этот алгоритм, заключается в том, что из-за различных повреждений теряется очень малое число пакетов (значительно меньше чем 1%), поэтому потеря пакетов сигнализирует о том, что в каком-либо месте сети между источником и назначением появилось переполнение. Существуют два признака, по которым можно определить, что пакеты теряются: появление тайм-аутов и получение дублированных ACK. (Мы видели последнее в разделе "Пример переполнения" этой главы. Если же использовать тайм-аут как показатель возникновения переполнения, то нам потребуется хороший алгоритм расчета RTT, примерно такой, как описан в разделе "Определение времени возврата".)

Предотвращение переполнения и медленный старт это независимые друг от друга алгоритмы, более того, работающие с различными объектами. Однако, когда возникает переполнение, мы хотим замедлить скорость передачи пакетов по сети, а затем использовать медленный старт, чтобы начать все с начала. На практике эти алгоритмы используются вместе.

Предотвращение переполнения и медленный старт требуют, чтобы для каждого соединения были определены две переменные: окно переполнения, cwnd, и размер порога медленного старта, ssthresh. Вместе алгоритмы работают следующим образом:

Инициализация заданного соединения устанавливает cwnd в один сегмент, а ssthresh в 65535 байт. Подпрограмма вывода TCP определит, какое из значений меньше: cwnd или окно, объявленное получателем и никогда не пошлет больше минимального значения. Предотвращение переполнения это способ контролировать поток данных, со стороны отправителя, тогда как объявление окна это способ контролировать поток данных, со стороны получателя. Первый основан на оценке отправителем того, насколько переполнена сеть, тогда как последний связан с величиной доступного буферного пространства у получателя для данного соединения. Когда возникает переполнение (на что указывает тайм-аут или получение дублированных ACK), одна половина текущего размера окна (меньшее значение из величин cwnd и размера окна, объявленного получателем, но по меньшей мере два сегмента) сохраняется в ssthresh. Более того, если мы узнали о переполнении с помощью тайм-аута, cwnd устанавливается в один сегмент (то есть осуществляется медленный старт). Когда новые данные подтверждены удаленным концом, cwnd увеличивается, однако способ этого увеличения зависит от того, работает ли алгоритм медленного старта или предотвращения переполнения. Если cwnd меньше или равно ssthresh, используется медленный старт; иначе используется предотвращение переполнения. Медленный старт продолжается до тех пор, пока мы не достигнем половины пути до того момента где были, когда возникло переполнение (то есть, до того момента пока мы не запишем половину размера окна, которое доставило нам проблемы в шаге 2), после чего используется алгоритм предотвращения переполнения. Медленный старт требует, чтобы cwnd начиналась с одного сегмента и увеличивалась на один сегмент каждый раз при приеме ACK. Как указано в разделе "Медленный старт" главы 20, это открывает окно экспотенциально: посылается один сегмент, затем два, затем четыре и так далее. Предотвращение переполнения требует, чтобы cwnd увеличивалась на 1/cwnd плюс меньшая дробная часть размера сегмента (размер сегмента, поделенный на 8) каждый раз, когда прибывает ACK. (Это ошибка реализации, которая присутствовала во всех релизах 4.3BSD и даже в 4.4BSD. Но этой ошибки нет в будущих реализациях [Floyd 1994]. Обратите внимание на то, что в примерах ниже в главе используется этот термин, потому что примеры исполнялись на реализации с ошибкой [см. рисунок 21.9 и рисунок 21.11]). Это увеличение посредством сложения, по сравнению с экспотенциальным увеличением при медленном старте. Мы хотим увеличивать cwnd по крайней мере на один сегмент за каждый промежуток времени равный времени возврата (вне зависимости от того, сколько ACK было принято за этот RTT), тогда как медленный старт увеличивает cwnd на количество ACK принятых за время возврата. Прибавление меньшей дробной части размера сегмента позволяет быстрее открывать большие окна.

Релиз 4.3BSD Tahoe, описанный в [Leffler et al. 1989], осуществляет медленный старт, только если удаленный конец находится в другой сети. Это было изменено в релизе 4.3BSD Reno таким образом, что медленный старт осуществляется всегда.

На рисунке 21.8 приведено описание медленного старта и предотвращения переполнения. Мы показали размер cwnd и ssthresh в блоках сегментов, тогда как в действительности их размер измеряется в байтах.

Рисунок 21.8 Медленный старт и предотвращение переполнения.

На этом рисунке мы предположили, что переполнение возникает, когда cwnd имеет значение равное 32 сегментам. ssthresh устанавливается в 16 сегментов, а cwnd устанавливается в 1 сегмент. Один сегмент посылается в момент времени 0 и предполагается, что ACK на него возвращается в момент времени 1, cwnd увеличивается до 2 сегментов. Затем отправляется два сегмента и предполагается, что их ACK прибывают в момент времени 2, cwnd увеличивается до 4 сегментов (по одному разу на каждый ACK). Этот экспотенциальный рост продолжается до тех пор, пока cwnd не будет равно ssthresh, а именно после того как 8 ACK были приняты между моментами времени 3 и 4. С этой точки увеличение cwnd линейное, причем максимальный увеличение - это увеличение на один сегмент за период равный времени возврата.

Как видно из этого рисунка, термин "медленный старт" не вполне корректен. Это, скорее, замедление передачи пакетов, которое вызвано переполнением, однако скорость увеличения количества пакетов, отправленных в сеть, увеличивается в течение медленного старта. Скорость увеличения не уменьшается до тех пор, пока не будет достигнуто значение ssthresh, когда начинает действовать предотвращение переполнения.

Быстрая повторная передача и алгоритм быстрого восстановления

В тексте не приводится достаточно жесткого разграничения между алгоритмом быстрой повторной передачи и быстрого восстановления. Это два абсолютно независимых алгоритма. Алгоритм быстрой повторной передачи вступает в действие, когда TCP определяет потерю сегмента и номер потерянного сегмента по наличию небольшого количества последователььных дублированных подтверждений (ACK). Потерянный сегмент передается повторно. Алгоритм быстрого востановления говорит, что после быстрой повторной передачи необходимо осуществить предотвращение переполнения, а не медленный старт. Алгоритм быстрой повторной передачи впервые появился в 4.3BSD Tahoe, однако за ним неправильно следовал медленный старт. Алгоритм быстрого восстановления впервые был применен в 4.3BSD Reno.

В 1990 году [Jacobson 1990b] алгоритм предотвращения переполнения был модифицирован. Мы уже видели эти модификации в действии в примере переполнения (раздел "Пример переполнения" этой главы).

Перед тем как познакомиться с этими изменениями представьте, что TCP требует сгенерировать немедленное подтверждение (дублированный ACK), когда принят поврежденный сегмент. Этот дублированный ACK не должен быть задержан. Цель дублированного ACK заключается в том, чтобы сообщить удаленному концу о том, что сегмент был получен поврежденным, и сообщить, какой ожидается номер последовательности.

Так как мы не знаем, было ли дублирование ACK вызвано потерей сегмента или просто тем, что изменился порядок следования сегментов, мы ожидаем прихода небольшого количества дублированных ACK. Это означает, что если сегменты просто пришли в другом порядке, будет получено только один или два дублированных ACK перед тем, как перемешанные сегменты будут обработаны, после чего будет сгенерирован новый ACK. Если же последовательно пришло три или более дублированных ACK, это уже является признаком того, что сегмент был потерян (раздел "Пример переполнения"). Мы осуществляем повторную передачу отсутствующего сегмента, не ожидая истечения таймера повторной передачи. Также мы осуществляем предотвращение переполнения, но не медленный старт.

На рисунке 21.7 мы видели, что медленный старт не осуществлялся после того, как было принято три дублированных ACK. Вместо этого отправитель осуществил повторную передачу, за которой следует три сегмента новых данных (сегменты 67, 69 и 71), перед тем как были получены подтверждения на повторную передачу (сегмент 72).

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

Алгоритм функционирует следующим образом.

  1. Когда принят третий дублированный ACK, ssthresh устанавливается равным половине текущего окна переполнения, cwnd.

    Передается отсутствующий сегмент.

    Значение cwnd устанавливается в значение ssthresh плюс утроенное значение размера сегмента.

  2. Каждый раз когда прибывает дублированный ACK, cwnd увеличивается на размер сегмента, и передается пакет (если это разрешено новым значением cwnd).
  3. Когда прибывает следующий ACK, который подтверждает новые данные, устанавливается cwnd равное ssthresh (значение, установленное в шаге 1). Это должно быть ACK на повторную передачу из шага 1, пришедшее через одно время возврата после повторной передачи. Этот ACK должен также подтверждать все промежуточные сегменты, отправленные между потерянным пакетом и получением первого дублированного ACK. Этот шаг осуществляется при предотвращении переполнения, так как мы наполовину замедлили скорость, с которой производилась передача в тот момент, когда пакет был потерян.

Мы увидим, что произойдет с переменными cwnd и ssthresh, в следующем разделе.

Пример переполнения (продолжение)

Просматривая соединение с использованием tcpdump и отладочной опции сокетов (которую мы описали в разделе "Пример RTT"), мы можем увидеть значения cwnd и ssthresh при передаче каждого сегмента. Если максимальный размер сегмента (MSS) равен 256 байт, исходные значения cwnd и ssthresh будут равны 256 и 65535 соответственно. Каждый раз, когда принимается ACK, cwnd увеличивается на значение MSS и принимает значения 512, 768, 1024, 1280 и так далее. Предположим, что переполнения не происходит, поэтому окно переполнения достигнет значения окна, объявленного получателем, а это, в свою очередь, будет означать, что объявленное окно ограничивает поток данных.

Однако нам интересно посмотреть, что произойдет в случае возникновения переполнения. Рассмотрим тот же пример из раздела "Пример RTT". В этом примере переполнение появлялось четыре раза. Был тайм-аут при передаче исходного SYN, который предназначался для установления соединения (рисунок 21.5), после чего, в процессе передачи данных, было потеряно три пакета (рисунок 21.6).

На рисунке 21.9 показаны значения двух переменных cwnd и ssthresh, когда осуществлялась повторная передача исходного SYN, за которым следовало семь первых сегментов данных. (Мы показали обмен исходными сегментами данных и их ACK на рисунке 21.2.) Переданные байты данных показаны в формате вывода команды tcpdump: 1:257(256), что означает байты с 1 по 256.

Когда возникает тайм-аут при передаче SYN, ssthresh устанавливается в свое минимальное значение (512 байт, что равно, в этом примере, двум сегментам). cwnd устанавливается в один сегмент (256 байт), чтобы войти в фазу медленного старта.

Когда получены SYN и ACK, с этими переменными ничего не происходит, так как новые данные не были подтверждены.

Когда прибывает ACK 257, мы все еще находимся в режиме медленного старта, так как cwnd меньше либо равно ssthresh, поэтому cwnd увеличивается на 256. То же самое происходит, когда прибывает ACK 513.

Когда прибывает ACK 769, мы больше не находимся в режиме медленного старта, однако переходим в режим предотвращения переполнения. Новое значение cwnd рассчитывается следующим образом

cwnd ¬ cwnd + (разм.сегмента x разм.сегмента)/cwnd + разм.сегмента/8

Это больше на 1/cwnd, чем то, что мы показали ранее, принимая во внимание то, что cwnd рассчитывается в действительности в байтах, а не в сегментах. Для этого примера мы рассчитаем

cwnd ¬ 768 + (256 x 256)/768 + 256/8

Номер сегмента (рисунок 21.2) | Действие |  |  | Переменная
 | Отправлено | Получено | Комментарий | cwnd | ssthresh
 |  |  | инициализация | 256 | 65535
 | SYN |  |  |  |
 | SYN |  | тайм-аут повторная передача | 256 | 512
 |  | SYN, ACK |  |  |
 | ACK |  |  |  |
1 | 1:257(256) |  |  |  |
2 |  | ACK 257 | медленный старт | 512 | 512
3 | 257:513(256) |  |  |  |
4 | 513:769(256) |  |  |  |
5 |  | ACK 513 | медленный старт | 768 | 512
6 | 769:1025(256) |  |  |  |
7 | 1025:1281(256) |  |  |  |
8 |  | ACK 769 | предотвращение переполнения | 885 | 512
9 | 1281:1537(256) |  |  |  |
10 |  | ACK 1025 | предотвращение переполнения | 991 | 512
11 | 1537:1793(256) |  |  |  |
12 |  | ACK 1281 | предотвращение переполнения | 1089 | 512

Рисунок 21.9 Пример предотвращения переполнения.

что равно 885 (с использованием целочисленной арифметики). Когда прибывает следующий ACK 1025, мы рассчитаем

cwnd ¬ 885 + (256 x 256)/885 + 256/8

что равно 991.

Это суммарное увеличение cwnd продолжается до появления первой повторной передачи, которая происходит в районе 10-секундной метки на рисунке 21.6. Рисунок 21.10 это график для тех же самых данных, которые приведены на рисунке 21.6, но сюда добавлены значения cwnd.

Первые шесть значений cwnd на этом рисунке - это значения, которые мы рассчитали для рисунка 21.9. На этом рисунке невозможно указать разницу между экспотенциальным увеличением в течение медленного старта и суммарным увеличением в течение предотвращения переполнения, потому что фаза медленного старта проходит очень быстро.

Нам необходимо объяснить, что происходит в трех точках, когда возникает повторная передача. Вспомним, что каждая повторная передача возникает из-за того, что были приняты три дублированных ACK (это указывает на то, что пакет был потерян). Здесь вступает в действие алгоритм быстрой повторной передачи, описанный в разделе "Быстрая повторная передача и алгоритм быстрого восстановления". ssthresh сразу же устанавливается в половину размера окна, который соответствовал тому моменту, когда была осуществлена повторная передача, однако cwnd позволяет продолжать увеличение до тех пор, пока принимаются дублированные ACK, так как каждый дублированный ACK означает, что сегмент все еще находится в сети (принимающий TCP буферизировал его, ожидая прибытия отсутствующих данных).

Рисунок 21.10 аналогичен рисунку 21.9 и показывает значения cwnd и ssthresh. Номера сегментов в первой колонке соответствуют рисунку 21.7.

Рисунок 21.10 Значения cwnd и номера отправляемых последовательностей, при передаче данных.

Номер сегмента (рисунок 21.7) | Действие |  |  | Переменная
 | Отправлено | Получено | Комментарий | cwnd | ssthresh
58 |  | ACK 6657 | подтверждение новых данных | 2426 | 512
59 | 8705:8961(256) |  |  |  |
60 |  | ACK 6657 | дублированный ACK номер 1 | 2426 | 512
61 |  | ACK 6657 | дублированный ACK номер 2 | 2426 | 512
62 |  | ACK 6657 | дублированный ACK номер 3 | 1792 | 1024
63 | 6657:6913(256) |  | повторная передача |  |
64 |  | ACK 6657 | дублированный ACK номер 4 | 2048 | 1024
65 |  | ACK 6657 | дублированный ACK номер 5 | 2304 | 1024
66 |  | ACK 6657 | дублированный ACK номер 6 | 2560 | 1024
67 | 8961:9217(256) |  |  |  |
68 |  | ACK 6657 | дублированный ACK номер 7 | 2816 | 1024
69 | 9217:9473(256) |  |  |  |
70 |  | ACK 6657 | дублированный ACK номер 8 | 3072 | 1024
71 | 9473:9729(256) |  |  |  |
72 |  | ACK 8961 | подтверждение новых данных | 1280 | 1024

Рисунок 21.11 Пример предотвращения переполнения (продолжение).

Значения cwnd увеличиваются постоянно, от последнего значения на рисунке 21.9 для сегмента 12 (1089), до первого значения на рисунке 21.11 для сегмента 58 (2426). Значение ssthresh осталось тем же самым (512), так как за этот период не было осуществлено повторных передач.

Когда прибыли первые два дублированные ACK (сегменты 60 и 61), они просто подсчитываются (третий дублированный ACK не прибыл), а cwnd остается неизменным. (Эта неизменяющаяся часть рисунка 21.10, предваряет первую повторную передачу.) Однако когда прибывает третий ACK, ssthresh устанавливается в значение равное половине cwnd. cwnd устанавливается в значение ssthresh плюс размер сегмента, умноженный на количество дублированных ACK (1024 + 3 x 256). Затем осуществляется повторная передача.

Прибывает еще пять дублированных ACK (сегменты 64-66, 68 и 70), при этом cwnd каждый раз увеличивается на размер сегмента. Наконец, прибывает новый ACK (сегмент 72), и cwnd устанавливается в значение ssthresh (1024), при этом стартует стандартный алгоритм предотвращения переполнения. Так как cwnd меньше или равно ssthresh (они равны), к cwnd добавляется размер сегмента, при этом получается значение 1280. Когда прибывает следующий ACK (который не показан на рисунке 21.11), cwnd больше чем ssthresh, поэтому cwnd устанавливается в значение 1363.

В течение фазы быстрой повторной передачи и быстрого восстановления мы передаем новые данные после получения дублированных ACK в сегментах 66, 68 и 70, однако не после получения дублированных ACK в сегментах 64 и 65. Причина этого заключается в значении cwnd по сравнению с количеством неподтвержденных байтов данных. Когда прибывает сегмент 64, cwnd становится равным 2048, однако мы имеем 2304 неподтвержденных байта (девять сегментов: 46, 48, 50, 52, 54, 55, 57, 59 и 63). Мы ничего не можем послать. Когда прибывает сегмент 65, cwnd становится равным 2304, поэтому мы все еще ничего не можем отправить. Однако, когда прибывает сегмент 66, cwnd становится равным 2560, теперь мы можем послать новый сегмент данных. Точно так же, когда прибывает сегмент 68, cwnd становится равным 2816, что больше чем 2560 байт неподтвержденных данных, таким образом, мы можем послать еще один новый сегмент данных. То же самое происходит, когда прибывает сегмент 70.

Когда в момент времени 14,3 происходит следующая повторная передача (см. рисунок 21.10), которая также вызвана приемом трех дублированных ACK, мы видим такое же увеличение cwnd как если бы прибыл еще один дублированный ACK, после чего происходит уменьшение до 1024.

Повторная передача в момент времени 21,1 на рисунке 21.10 также происходит при приходе дублированных ACK. Мы получили еще три дублированных ACK после повторной передачи, поэтому мы видим три дополнительных увеличения cwnd, после чего следует уменьшение до 1280. Для остальной передачи cwnd увеличивается линейно с окончательным значением 3615.

Показатели на маршрут

Более новые реализации TCP хранят большинство из показателей, которые мы описали в этой главе в записях таблицы маршрутизации. Предположим, что TCP соединение закрыто, при этом по соединению было отправлено достаточное количество данных, чтобы получить статистику, и запись в таблице маршрутизации для определенного пункта назначения не является маршрутом по умолчанию. При выполнении этих условий в записи таблицы маршрутизации сохраняется следующая информация (эта информация будет использована при следующих обращениях к этой записи): хэшированный RTT, хэшированное среднее отклонение и порог медленного старта. Понятие "достаточное количество данных" - означает 16 окон данных. При этом можно получить 16 примеров RTT, что позволяют фильтру хэшированного RTT получить значение с отклонением в пределах 5% от реального.

Помимо этого, администратор может воспользоваться командой route(8) , чтобы установить показатели для заданного маршрута: три показателя, упомянутых в предыдущем параграфе, а также MTU, емкость исходящего канала в зависимости от полосы пропускания (раздел "Пропускная способность для неинтерактивных данных" главы 20) и емкость входящего канала в зависимости от полосы пропускания.

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

ICMP ошибки

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

Текущие реализации (производные от Berkeley) обрабатывают эти ICMP ошибки следующим образом:

  • При получении подавления источника, окно переполнения, cwnd, устанавливается в один сегмент, чтобы осуществить медленный старт, однако порог медленного старта, ssthresh, не изменяется, таким образом, окно будет открыто (ограничивается размером окна и временем возврата), пока не возникнет переполнение.
  • Получение ошибки о недоступности хоста или недоступности сети игнорируется, так как эти две ошибки воспринимаются как "кратковременные". Это может произойти из-за того, что промежуточный маршрутизатор вышел из строя, и потребуется несколько минут на то, чтобы протокол маршрутизации установил альтернативный маршрут. В течение этого периода может возникнуть любая из этих двух ICMP ошибок, однако соединение не должно быть разорвано. Вместо этого TCP старается отправить данные, на которые была сгенерирована ошибка, однако, они могут быть отброшены по тайм-ауту. (Обратитесь к рисунку 21.1, на котором показано, что TCP не разрывает соединение в течение 9 минут.) Текущие реализации Berkeley фиксируют возникшие ICMP ошибки, и если соединение разорвано по тайм-ауту, ICMP ошибка транслируется в более понятный код ошибки, который выглядит как "connection timed out" (соединение закрыто по тайм-ауту).

Более ранние реализации BSD некорректно разрывали соединение, когда приходила ICMP ошибка о недоступности хоста или недоступности сети.

Пример

Мы можем посмотреть, как ICMP ошибка о недоступности хоста обрабатывается на нашем SLIP канале, когда он погашен в середине соединения. Мы установили соединение с хоста slip на хост aix. (На рисунке, находящемся на внутренней стороне обложки, мы видим, что это соединение проходит через SLIP канал.) После установления соединения и передачи какого-то количества данных, SLIP канал между маршрутизаторами sun и netb погашен. При этом запись, соответствующая маршруту по умолчанию в sun (которую мы показывали на рисунке 9.2), будет удалена. Ожидается, что sun затем будет отвечать на IP датаграммы, направляемые в Ethernet сеть 140.252.1, ошибкой ICMP о недоступности хоста. Мы хотим посмотреть, как TCP обрабатывает эти ICMP ошибки.

Здесь приведена интерактивная сессия на хосте slip:

slip % sock aix echo                         запускаем программу sock
test line                                    вводим эту строку
test line                                     она отображается эхом
                                             в этом месте SLIP канал выключен
another line                                 затем вводим эту строку и смотрим повторные передачи
                                             здесь SLIP канал восстановлен
another line                                  и происходит обмен строкой и ее эхом
line number 3
line number 3
the last line
                                             SLIP канал погашен и не восстановлен
read error: No route to host                  TCP в конце концов закрывает соединение

На рисунке 21.12 показан соответствующий вывод команды tcpdump, полученный на маршрутизаторе bsdi. (Установление соединения и все объявления окна удалены.) Мы подсоединились к эхо серверу на хосте aix и напечатали "test line" (строка 1). Она отразилась эхом (строка 2), и эхо было подтверждено (строка 3). Затем мы погасили SLIP канал.

Затем ввели "another line" (строка 3) и ожидаем, что TCP отработает тайм-аут и повторно передаст сообщение. И действительно, эта строка отправляется шесть раз перед тем, как будет получен отклик. Строки 4-13 показывают первую передачу и следующие четыре повторные передачи, каждая из которых генерирует ICMP ошибку о недоступности хоста с маршрутизатора sun. Это как раз то, что мы ожидали: IP датаграммы, двигаются от slip к маршрутизатору bsdi (который имеет маршрут по умолчанию, указывающий на sun) и затем на sun, где и находится поврежденный канал.

Пока происходят эти повторные передачи, SLIP канал восстанавливается, и повторная передача в строке 14 достигают пункта назначения. Строка 15 это эхо от aix, а строка 16 - подтверждение на эхо.

Можно сделать вывод, что TCP игнорирует ICMP ошибку о недоступностии хоста и продолжает осуществлять повторные передачи. Мы также видим ожидаемое экспотенциальное наращивание времени в каждом тайм-ауте при повторной передаче: первый возникает через 2,5 секунды, затем умножается на 2 (при этом длительность тайм-аута составляет 5 секунд), затем на 4 (10 секунд), затем на 8 (20 секунд) и затем на 16 (40 секунд).

После чего мы напечатали третью строку ввода ("line number 3") и увидели, что она отправлена в строке 17, отражена эхом в строке 18, и эхо подтверждено в строке 19.

Сейчас мы хотим посмотреть, что произойдет, когда TCP осуществляет повторную передачу, а затем прекращает попытки после получения ICMP ошибки о недоступности хоста. Для этого мы снова выключаем SLIP канал. После того как канал выключен, мы печатаем "the last line" и видим, что она передается 13 раз перед тем, как TCP прекращает попытки. (Строки 30-43 удалены из вывода. В них показаны дополнительные повторные передачи.)

Необходимо обратить внимание на одну деталь которая, заключается в том, что сообщение об ошибке печатается нашей программой sock, когда она в конце концов прекращает работу: "No route to host". (Нет маршрута к хосту.) Это соответствует ошибке операцинной системы Unix, которая, в свою очередь соответствует ICMP ошибке о недоступности хоста (рисунок 6.12). Значит, TCP сохраняет ICMP ошибку, которую он получил по соединению, и когда он в конце концов прекращает свою работу, то печатает эту ошибку вместо "Connection timed out" (соединение закрыто по тайм-ауту).

И в заключение, обратите внимание на то, что временные промежутки между повторными передачами в строках 22-46 сравнимы с промежутками в строках 6-14. Это происходит из-за того, что TCP обновил свои оценочные функции, когда третья строка, которую мы напечатали, была отправлена и подтверждена без повторных передач в строках 17-19. Исходный тайм-аут повторной передачи в настоящее время равен 3 секундам, при этом последующие значения будут равны 6, 12, 24, 48 и затем верхний предел 64.

1     0.0                      slip.1035 > aix.echo: P 1:11(10) ack 1
2     0.212271 (  0.2123)     aix.echo > slip.1035: P 1:11(10) ack 11
3     0.310685 (  0.0984)     slip.1035 > aix.echo: . ack 11

                               здесь SLIP канал выключен

4   174.758100 (174.4474)     slip.1035 > aix.echo: P 11:24(13) ack 11
5   174.759017 (  0.0009)     sun > slip: icmp: host aix unreachable

6   177.150439 (  2.3914)     slip.1035 > aix.echo: P 11:24(13) ack 11
7   177.151271 (  0.0008)     sun > slip: icmp: host aix unreachable

8   182.150200 (  4.9989)     slip.1035 > aix.echo: P 11:24(13) ack 11
9   182.151189 (  0.0010)     sun > slip: icmp: host aix unreachable

10   192.149671 (  9.9985)     slip.1035 > aix.echo: P 11:24(13) ack 11
11   192.150608 (  0.0009)     sun > slip: icmp: host aix unreachable

12   212.148783 ( 19.9982)     slip.1035 > aix.echo: P 11:24(13) ack 11
13   212.149786 (  0.0010)     sun > slip: icmp: host aix unreachable

                               здесь SLIP канал включен

14   252.146774 ( 39.9970)     slip.1035 > aix.echo: P 11:24(13) ack 11
15   252.439257 (  0.2925)     aix.echo > slip.1035: P 11:24(13) ack 24
16   252.505331 (  0.0661)     slip.1035 > aix.echo: . ack 24

17   261.977246 (  9.4719)     slip.1035 > aix.echo: P 24:38(14) ack 24
18   262.158758 (  0.1815)     aix.echo > slip.1035: P 24:38(14) ack 38
19   262.305086 (  0.1463)     slip.1035 > aix.echo: . ack 38

                               здесь SLIP канал выключен

20   458.155330 (195.8502)     slip.1035 > aix.echo: P 38:52(14) ack 38
21   458.156163 (  0.0008)     sun > slip: icmp: host aix unreachable

22   461.136904 (  2.9807)     slip.1035 > aix.echo: P 38:52(14) ack 38
23   461.137826 (  0.0009)     sun > slip: icmp: host aix unreachable

24   467.136461 (  5.9986)     slip.1035 > aix.echo: P 38:52(14) ack 38
25   467.137385 (  0.0009)     sun > slip: icmp: host aix unreachable

26   479.135811 ( 11.9984)     slip.1035 > aix.echo: P 38:52(14) ack 38
27   479.136647 (  0.0008)     sun > slip: icmp: host aix unreachable

28   503.134816 ( 23.9982)     slip.1035 > aix.echo: P 38:52(14) ack 38
29   503.135740 (  0.0009)     sun > slip: icmp: host aix unreachable

здесь удалено 14 строк вывода

44  1000.219573 ( 64.0959)     slip.1035 > aix.echo: P 38:52(14) ack 38
45  1000.220503 (  0.0009)     sun > slip: icmp: host aix unreachable

46  1064.201281 ( 63.9808)     slip.1035 > aix.echo: R 52:52(0) ack 38
47  1064.202182 (  0.0009)     sun > slip: icmp: host aix unreachable

Рисунок 21.12 Обработка TCP полученной ICMP ошибки о недоступности хоста.

Пересборка пакетов

Когда TCP отрабатывает тайм-ауты и осуществляет повторные передачи, он не должен повторно передавать идентичные сегменты (если исходно было 10 пакетов по одному байту, то при повторной передаче можно передать 1 пакет размером десять байт). Вместо этого, TCP разрешено осуществлять пересборку пакетов (repacketization), отправляя сегменты большего размера, что может увеличить производительность. (В действительности, сегменты большего размера не могут превосходить по размеру MSS, объявленный удаленным получателем.) Протокол TCP может себе это позволить, потому что он идентифицирует данные, которые были отправлены и подтверждены, по номерам байтов, а не по номерам сегментов.

Мы можем легко посмотреть, как это происходит. Воспользуемся программой sock, чтобы подсоединиться к discard серверу, и напечатаем одну строку. Затем мы отсоединяем Ethernet кабель и вводим вторую строку. Пока эта вторая строка будет повторно передаваться, мы вводим третью строку. При этом ожидается, что следующая повторная передача будет содержать обе и вторую, и третью строки.

bsdi % sock svr4 discard
hello there                       первая строка отправлена нормально
                                  отсоединяем Ethernet кабель
line number 2                     эта строка будет повторно передаваться
and 3                             вводим эту строку перед тем, как вторая отправлена нормально
                                  подсоединяем обратно Ethernet кабель

На рисунке 21.13 показан вывод команды tcpdump. (Мы удалили установление соединения, прерывание соединения и все объявления окна.)

1   0.0                     bsdi.1032 > svr4.discard: P 1:13(12) ack 1
2   0.140489 ( 0.1405)     svr4.discard > bsdi.1032: . ack 13

                           здесь отсоединен Ethernet кабель

3  26.407696 (26.2672)     bsdi.1032 > svr4.discard: P 13:27(14) ack 1
4  27.639390 ( 1.2317)     bsdi.1032 > svr4.discard: P 13:27(14) ack 1
5  30.639453 ( 3.0001)     bsdi.1032 > svr4.discard: P 13:27(14) ack 1

                           здесь напечатана третья строка

6  36.639653 ( 6.0002)     bsdi.1032 > svr4.discard: P 13:33(20) ack 1
7  48.640131 (12.0005)     bsdi.1032 > svr4.discard: P 13:33(20) ack 1
8  72.640768 (24.0006)     bsdi.1032 > svr4.discard: P 13:33(20) ack 1

                           здесь Ethernet кабель подсоединен обратно

9  72.719091 ( 0.0783)     svr4.discard > bsdi.1032: . ack 33

Рисунок 21.13 Пересборка пакетов TCP.

В строках 1 и 2 показана первая строка ("hello there"), которая отправлена и подтверждена. Затем мы отсоединяем Ethernet кабель и печатаем "line number 2" (14 байт, включая символ новой строки). Эти байты передаются в строке 3, а затем повторно передаются в строках 4 и 5.

Перед тем как осуществляется повторная передача в строке 6, мы печатаем "and 3" (6 байт, включая символ новой строки) и видим, что повторная передача на этот раз содержит 20 байт: обе строки, которые мы напечатали. Подтверждение, которое прибывает в строке 9, подтверждает все 20 байт.

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

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

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

Наш подробный пример, в котором было потеряно три пакета, позволил нам рассмотреть большинство алгоритмов TCP в действии: медленный старт, предотвращение переполнения, быструю повторную передачу и быстрое восстановление. Мы также имели возможность рассчитать оценочные функции RTT TCP вместе с окном переполнения и порогом медленного старта, а также сравнить эти значения с реальными значениями, полученными из вывода отладочных программ.

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

Упражнения

  1. На рисунке 21.5 первый тайм-аут был рассчитан как 6 секунд, а следующий как 12 секунд. Если ACK для первоначального SYN не прибыл после того, как истечет 12-секундный тайм-аут, какова будет величина следующего тайм-аута?
  2. В рассуждениях, которые следуют за рисунком 21.5, мы сказали, что временные промежутки между тайм-аутами рассчитываются как 6, 24 и затем 48 секунд, как мы видели на рисунке 4.5. Однако, если мы рассмотрим TCP соединение на несуществующий хост с системы SVR4, тайм-ауты будут 6, 12, 24 и 48 секунд. Что происходит?
  3. Сравните производительность скользящего окна TCP с TFTP протоколом с ожиданием подтверждения следующим образом. В этой главе мы передали 32768 байт примерно за 35 секунд (рисунок 21.6) по каналу со средним RTT, находящимся в районе 1,5 секунды (рисунок 21.4). Рассчитайте, сколько понадобится времени TFTP для того, чтобы осуществить ту же передачу.
  4. В разделе "Быстрая повторная передача и алгоритм быстрого восстановления" мы сказали, что прием дублированного ACK вызывается, как правило, тем, что сегмент был потерян или пришел не в свое вовремя (беспорядочно). В разделе "Пример переполнения" мы видели, что генерация дублированных ACK вызывается потерей сегмента. Нарисуйте рисунок, который доказывал бы, что приход сегмента "не в свое время" также генерирует дублированные ACK.
  5. На рисунке 21.6 между моментами времени 28,8 и 29,8 находится очень заметный пропуск. Является ли это повторной передачей?
  6. В разделе "Алгоритм предотвращения переполнения" мы сказали, что релиз 4.3BSD Tahoe осуществляет только медленный старт, если пункт назначения находится в другой сети. Как Вы думаете, что означает "другая сеть"? (Подсказка: см. приложение E.)
  7. В разделе "Обычный поток данных" главы 20 мы сказали, что TCP подтверждает каждый второй сегмент. Однако на рисунке 21.2 мы видели, что получатель подтверждает каждый сегмент. Почему?
  8. Действительно ли полезны показатели на маршрут, с господствующим маршрутом по умолчанию?


Устойчивый таймер TCP

Глава 22 Устойчивый таймер TCP

Введение

Мы видели, что TCP получатель осуществляет управление потоком данных, указывая количество данных, которые он хочет получить от отправителя: размер окна. Что происходит, когда размер окна становится равным 0? Обычно это останавливает отправителя, который прекращает передавать данные до тех пор, пока размер окна станет ненулевым.

Именно так разворачивались события на рисунке 20.3. Когда отправитель получает сегмент 9, открывающий окно, которое было закрыто сегментом 8, он немедленно начинает посылать данные. TCP должен предпринять какие-либо действия в том случае, если подтверждение, открывающее окно (сегмент 9) было потеряно. Подтверждения передаются ненадежно - другими словами, TCP не подтверждает сами подтверждения, он подтверждает только сегменты, содержащие данные.

Если подтверждение потеряно, на каждом конце соединения будут ждать действий от удаленного конца: получатель ожидает получить данные (так как он отправил отправителю ненулевое окно), а отправитель надеется получить обновление окна, которое позволит ему продолжить передачу. Чтобы выйти из подобного тупика, отправитель использует устойчивый (persist) таймер, в соответствии с которым осуществляется периодический опрос получателя на предмет, не был ли увеличен размер окна. Сегменты, которые при этом посылает отправитель, называются пробами окна (window probes) . В этой главе мы рассмотрим пробы окна и устойчивый таймер. Также мы рассмотрим синдром "глупого окна", который непосредственно связан с устойчивым таймером.

Пример

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

Программа sock позволяет установить паузу с помощью опции -P, при этом пауза вставляется между моментом, когда сервер принимает запрос на соединение, и моментом, когда будет осуществлено первое чтение. Мы запустим сервер следующим образом:

svr4 % sock -i -s -P100000 5555

После запуска этой команды сервер будет "спать" 100000 секунд (27,8 часов) перед тем, как начать читать из сети. Клиент запущен на хосте bsdi и осуществляет записи по 1024 байта на порт сервера 5555. На рисунке 22.1 показан вывод команды tcpdump. (Мы удалили все имеющее отношение к установлению соединения.)

1    0.0                 bsdi.1027 > svr4.5555: P 1:1025(1024) ack 1 win 4096
2    0.191961 ( 0.1920) svr4.5555 > bsdi.1027: . ack 1025 win 4096
3    0.196950 ( 0.0050) bsdi.1027 > svr4.5555: . 1025:2049(1024) ack 1 win 4096
4    0.200340 ( 0.0034) bsdi.1027 > svr4.5555: . 2049:3073(1024) ack 1 win 4096
5    0.207506 ( 0.0072) svr4.5555 > bsdi.1027: . ack 3073 win 4096
6    0.212676 ( 0.0052) bsdi.1027 > svr4.5555: . 3073:4097(1024) ack 1 win 4096
7    0.216113 ( 0.0034) bsdi.1027 > svr4.5555: P 4097:5121(1024) ack 1 win 4096
8    0.219997 ( 0.0039) bsdi.1027 > svr4.5555: P 5121:6145(1024) ack 1 win 4096
9    0.227882 ( 0.0079) svr4.5555 > bsdi.1027: . ack 5121 win 4096
10    0.233012 ( 0.0051) bsdi.1027 > svr4.5555: P 6145:7169(1024) ack 1 win 4096
11    0.237014 ( 0.0040) bsdi.1027 > svr4.5555: P 7169:8193(1024) ack 1 win 4096
12    0.240961 ( 0.0039) bsdi.1027 > svr4.5555: P 8193:9217(1024) ack 1 win 4096
13    0.402143 ( 0.1612) svr4.5555 > bsdi.1027: . ack 9217 win 0

14    5.351561 ( 4.9494) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
15    5.355571 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

16   10.351714 ( 4.9961) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
17   10.355670 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

18   16.351881 ( 5.9962) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
19   16.355849 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

20   28.352213 (11.9964) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
21   28.356178 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

22   52.352874 (23.9967) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
23   52.356839 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

24  100.354224 (47.9974) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
25  100.358207 ( 0.0040) svr4.5555 > bsdi.1027: . ack 9217 win 0

26  160.355914 (59.9977) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
27  160.359835 ( 0.0039) svr4.5555 > bsdi.1027: . ack 9217 win 0

28  220.357575 (59.9977) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
29  220.361668 ( 0.0041) svr4.5555 > bsdi.1027: . ack 9217 win 0

30  280.359254 (59.9976) bsdi.1027 > svr4.5555: . 9217:9218(1) ack 1 win 4096
31  280.363315 ( 0.0041) svr4.5555 > bsdi.1027: . ack 9217 win 0

Рисунок 22.1 Пример устойчивого таймера при пробах окна нулевого размера.

В сегментах 1-13 осуществляется обычная передача данных от клиента к серверу, при этом заполняется окно размером 9216 байт. Сервер объявил окно равное 4096 и имеет размер буфера сокета по умолчанию равный 4096, однако в действительности принимает 9216 байт. Это является формой взаимодействия между кодами TCP/IP и потоковой подсистемой в SVR4.

В сегменте 13 сервер подтверждает предыдущие четыре сегмента данных, однако объявляет окно равное 0, приостанавливая тем самым передачу данных от клиента. Клиент вынужден установить свой устойчивый таймер. Если клиент не получил обновление окна до истечения таймера, он осуществляет пробу пустого окно, чтобы проверить не было ли потеряно обновление окна. Так как процесс сервера спит, 9216 байт данных буферизированы TCP и ожидают, что будут переданы приложению.

Обратите внимание на промежутки времени между пробами окна, которые осуществляет клиент. Первая (сегмент 14) происходит через 4,949 секунды после получения окна нулевого размера. Следующая (сегмент 16) - через 4,996 секунды. Затем промежутки между пробами становятся приблизительно равными 6, 12, 24, 48 и 60 секунд после предыдущей пробы.

Почему промежутки всегда на какую-то долю секунды меньше чем 5, 6, 12, 24, 48 и 60? Пробы осуществляются в соответствии с 500-миллисекундным таймером TCP. Когда таймер истекает, отправляется проба окна, а отклик принимается примерно через 4 миллисекунды. Получение отклика, вновь устанавливает таймер, однако время до следующего тика часов составляет примерно 500 минус 4 миллисекунды.

При расчете устойчивого таймера используется обычное экспотенциальное наращивание TCP. Первый тайм-аут рассчитывается как 1,5 секунды для стандартного соединения по локальной сети. Затем это значение умножается на 2 для второго тайм-аута (значение равное 3 секундам). Множитель равный 4-м дает следующее значение равное 6, множитель равный 8-ми дает значение 12, и так далее. Однако, устойчивый таймер всегда находится в диапазоне между 5 и 60 секундами, и именно эти значения мы видим на рисунке 22.1.

Проба окна содержит 1 байт данных (номер последовательности 9217). TCP всегда позволяет послать 1 байт данных, после того как окно было закрыто. Однако подтверждения, возвращающиеся с номером окна равным 0, не подтверждают этот байт. (Эти ACK получены для всех байтов с номером меньше чем 9216, и на байт с номером 9216.) Поэтому, этот байт будет передан повторно.

Характеристика устойчивого состояния отличается от тайм-аутов при повторной передаче, описанных в главе 21, тем, что TCP никогда не прекращает отправку проб окна. Эти пробы окна отправляются с 60-секундными интервалами до тех пор, пока окно не будет открыто, или приложение решит, что соединение должно быть разорвано.

Синдром "глупого" окна

При использовании окон в схемах управления потоком данных (таких как используется в TCP) могут возникнуть условия известные как синдром "глупого" окна (SWS - silly window syndrome). В этом случае, по соединению, осуществляется обмен небольшим количеством данных вместо обмена сегментами полного размера [Clark 1982].

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

  1. Получатель не должен объявлять маленькие окна. Стандартный алгоритм определяет, что получатель не должен объявлять окно больше, чем объявлено в настоящий момент (которое может быть равным 0), до тех пор, пока окно не будет увеличено хотя бы на один сегмент полного размера (принятый MSS) или на половину размера буфера получателя, в зависимости от того, какое значение меньше.
  2. Отправитель может избежать синдрома глупого окна, приостанавливая передачу, если одно из следующих условий неверно: (a) может быть отправлен сегмент полного размера, (b) можно отправить по меньшей мере половину от максимального размера окна, объявленного удаленным концом, или (c) можно послать все что у нас есть, а также мы не ожидаем подтверждения (нет неподтвержденных данных), или для этого соединения выключен алгоритм Нагла (глава 19, раздел "Алгоритм Нагла").

    Условие (b) имеет отношение к хостам, которые всегда объявляют очень маленькие окна, возможно даже меньше чем размер сегмента. Условие (c) позволяет избежать отправки маленьких сегментов, когда у нас есть неподтвержденные данные, которые ожидают того, чтобы быть подтвержденными, и включен алгоритм Нагла. Если приложение осуществляет маленькие записи (меньше чем размер сегмента), условие (c) позволяет избежать синдрома глупого окна.

    Эти условия также позволяют ответить на вопрос: спасает ли алгоритм Нагла от отправки маленьких сегментов, пока существуют неподтвержденные данные и что означает слово "маленький"? Из условия (a) мы видим, что "маленький" означает - количество байтов меньше чем размер сегмента. Условие (b) имеет отношение только к старым, примитивным хостам.

Условие (b) в шаге 2 требует, чтобы отправитель отслеживал максимальный размер окна, объявленный удаленным концом. Здесь отправителем делается попытка догадаться о размере буфера получателя на удаленном конце. Несмотря на то, что размер буфера получателя может уменьшаться в процессе установления соединения, на практике это происходит редко.

Пример

Сейчас мы подробно рассмотрим, как можно избежать синдрома глупого окна и как работает устойчивый таймер. Мы воспользуемся программой sock с отправляющего хоста, sun, который сделает в сеть шесть записей размером 1024 байта:

sun % sock -i -n6 bsdi 7777

Принимающий процесс на хосте bsdi сделает несколько пауз, а именно, перед осуществлением первого считывания пауза продлиться 4 секунды, а между каждым следующим считыванием пауза составит 2 секунды. Получатель считывает данные по 256 байт:

bsdi % sock -i -s -P4 -p2 -r256 7777

Первая пауза позволяет заполнить буфер приемника, что заставляет его остановить передатчик. Так как получатель затем осуществляет маленькие считывания из сети, мы ожидаем, что получатель применит процедуру предотвращения синдрома глупого окна.

На рисунке 22.2 показана временная диаграмма для передачи 6144 байт данных. (Мы удалили все связанное с установлением соединения.)

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

Рисунок 22.2 Временная диаграмма, показывающая, как получатель предотвращает появление синдрома глупого окна.

Время | Номер сегмента | Действие |  |  | Буфер приемника
 | (рисунок 22.2) | Отправляющий TCP | Принимающий TCP | Приложение | данные | свободно
0,000 | 1 | 1:1025(1024) |  |  | 1024 | 3072
0,002 | 2 | 1025:2049(1024) |  |  | 2048 | 2048
0,003 | 3 | 2049:3073(1024) |  |  | 3072 | 1024
0,005 | 4 | 3073:4097(1024) |  |  | 4096 | 0
0,170 | 5 |  | ACK 4097, win 0 |  |  |
3,99 |  |  |  | читает 256 | 3840 | 256
5,151 | 6 | 4097:4098(1) |  |  | 3841 | 255
5,17 | 7 |  | ACK 4098, win 0 |  |  |
5,99 |  |  |  | читает 256 | 3585 | 511
7,99 |  |  |  | читает 256 | 3329 | 767
9,99 |  |  |  | читает 256 | 3073 | 1023
10,151 | 8 | 4098:4099(1) |  |  | 3074 | 1022
10,170 | 9 |  | ACK 4099, win 0 |  |  |
11,99 |  |  |  | читает 256 | 2818 | 1278
13,99 |  |  |  | читает 256 | 2562 | 1534
15,151 | 10 | 4099:4100(1) |  |  | 2563 | 1533
15,170 | 11 |  | ACK 4100, win 1533 |  |  |
15,172 | 12 | 4100:5124(1024) |  |  | 3587 | 509
15,370 | 13 |  | ACK 5124, win 509 |  |  |
15,99 |  |  |  | читает 256 | 3331 | 765
17,99 |  |  |  | читает 256 | 3075 | 1021
19,99 |  |  |  | читает 256 | 2819 | 1277
20,151 | 14 | 5124:5633(509) |  |  | 3328 | 768
20,170 | 15 |  | ACK 5633, win 0 |  |  |
21,99 |  |  |  | читает 256 | 3072 | 1024
23,99 |  |  |  | читает 256 | 2816 | 1280
25,151 | 16 | 5633:5634(1) |  |  | 2817 | 1279
25,170 | 17 |  | ACK 5634, win 1279 |  |  |
25,171 | 18 | 5634:6145(511) |  |  | 3328 | 768
25,174 | 19 |  | ACK 6146, win 767 |  |  |
25,99 |  |  |  | читает 256 | 3072 | 1024
27,99 |  |  |  | читает 256 | 2816 | 1280
29,99 |  |  |  | читает 256 | 2560 | 1536
31,99 |  |  |  | читает 256 | 2304 | 1792
33,99 |  |  |  | читает 256 | 2048 | 2048
35,99 |  |  |  | читает 256 | 1792 | 2304
37,99 |  |  |  | читает 256 | 1536 | 2560
39,99 |  |  |  | читает 256 | 1280 | 2816
39,99 | 20 |  | ACK 6146, win 2816 |  |  |
41,99 |  |  |  | читает 256 | 1024 | 3072
43,99 |  |  |  | читает 256 | 768 | 3328
45,99 |  |  |  | читает 256 | 512 | 3584
47,99 |  |  |  | читает 256 | 256 | 3840
49,99 |  |  |  | читает 256 | 0 | 4096
51,99 |  |  |  | читает 256 (EOF) | 0 | 4096
51,991 | 21 |  | ACK 6146, win 4096 |  |  |
51,992 | 22 | ACK 2 |  |  |  |

Рисунок 22.3 Как получатель избавляется от синдрома глупого окна.

В первой колонке на рисунке 22.3 приводится относительный момент времени соответствующий каждому действию. Величины времени с тремя цифрами справа от десятичной точки взяты из вывода команды tcpdump (рисунок 22.2). Величины времени со значением 99 справа от десятичной точки - это предполагаемая продолжительность функционирования принимающего хоста. (Наличие этих относительных времен на принимающем конце и содержащих 99 в позиции сотых долей секунд указывает на их взаимосвязь с сегментами 20 и 22 на рисунке 22.2, только два события на приемнике, которые мы видим в выводе команды tcpdump, произошли на принимающем хосте по тайм-ауту. Все другие пакеты, отправленные bsdi, отправлены по приему сегмента от отправителя. Также необходимо принять во внимание, что это могло бы поместить исходную 4-секундную паузу непосредственно перед моментом времени 0, когда отправитель передает первый сегмент данных. Это примерно тогда, когда получатель берет управление после приема подтверждения на свой SYN при установлении соединения.)

Размер буфера приемника увеличивается, когда он получает данные от получателя, и уменьшается, когда приложение считывает данные из буфера. Нам необходимо проследить за объявлениями окна, которые отправляются получателем отправителю, и за тем, что содержат эти объявления окна. Таким образом, мы можем увидеть, как получатель избегает синдрома глупого окна.

Первые четыре сегмента данных и соответствующие ACK (сегменты 1-5) показывают, что отправитель заполнил буфер приемника. В этой точке отправитель останавливается, однако у него все еще остались данные, которые необходимо отправить. Он устанавливает свой устойчивый таймер в минимальное значение равное 5 секундам.

Когда таймер истек, посылается 1 байт данных (сегмент 6). Принимающее приложение считало 256 байт из приемного буфера (в момент времени 3,99), таким образом, байт принимается и подтверждается (сегмент 7). Однако, объявленное окно все еще равно 0, так как у приемника нет места равного одному сегменту полного размера или половине своего буфера. Именно таким образом, получатель избегает появления синдрома глупого окна.

Устойчивый таймер отправителя сбрасывается и устанавливается снова через 5 секунд (момент времени 10,151). Снова отправляется и подтверждается один байт (сегменты 8 и 9). И снова, размер свободного места в буфере приемника (1022 байта) заставляет его объявить окно равное 0.

Когда устойчивый таймер отправителя истекает в следующий раз (в момент времени 15,151), отправляется и подтверждается еще один байт (сегменты 10 и 11). В это время получатель имеет в буфере 1533 байта свободного пространства, поэтому объявляется окно ненулевого размера. Отправитель немедленно пользуется этим и отправляет 1024 байта (сегмент 12). Подтверждение на эти 1024 байта (сегмент 13) объявляет окно равное 509 байт. Это противоречит тому, что мы видели раньше с объявлениями маленьких окон.

Здесь произошло следующее: сегмент 11 объявил окно размером 1533 байта, однако отправитель отправил только 1024 байта. Если подтверждение в сегменте 13 объявит окно равное 0, это будет противоречить принципу TCP, который заключается в том, что окно не может изменяться путем перемещения его правого края влево (глава 20, раздел "Изменение размера окна"). Именно поэтому должно быть объявлено маленькое окно размером в 509 байт.

Затем необходимо обратить внимание на то, что отправитель отправляет это маленькое окно не сразу. Это уже меры, которые принимает отправитель, чтобы избавиться от синдрома глупого окна. Отправитель ожидает истечения еще одного устойчивого таймера (момент времени 20,151) и только тогда посылает 509 байт. Даже если все равно отправляется маленький сегмент размером 509 байт данных, отправитель ожидает 5 секунд, чтобы посмотреть, не придет ли ACK и не объявит ли большее окно. После прихода 509 байт данных в буфере приемника остается всего лишь 768 байт свободного места, поэтому подтверждение (сегмент 15) объявляет окно равное 0.

Устойчивый таймер снова включается в момент времени 25,151, а отправитель передает 1 байт. В буфере получателя этот момент 1279 байт свободного пространства, окно такого размера объявляется в сегменте 17.

Отправителю необходимо отправить только 511 дополнительных байт, которые он немедленно отправляет после получения объявления окна размером 1279 байт (сегмент 18). Этот сегмент также содержит флаг FIN. Получатель подтверждает данные и FIN, объявляя окно размером 767. (См. упражнение 2 в конце главы.)

Так как отправляющее приложение выдало сигнал закрытия, после того как сделало шесть записей размером 1024 байта, соединение со стороны отправителя переходит из состояния ESTABLISHED в состояние FIN_WAIT_1, затем в состояние FIN_WAIT_2 (рисунок 18.12).

Оно остается в этом состоянии до получения FIN с удаленного конца. Для этого состояния не существует таймера (обратитесь к нашему обсуждению в конце раздела "Диаграмма состояний передачи TCP" главы 18), поэтому FIN, который был отправлен в сегменте 18, подтверждается в сегменте 19. Именно поэтому мы не видим дальнейшей передачи от отправителя до тех пор, пока он не получил FIN (сегмент 21).

Получающее приложение продолжает читать 256 байт данных каждые 2 секунды из приемного буфера. Почему ACK отправлен в момент времени 39,99 (сегмент 20)? Размер свободного места в буфере получателя увеличился с момента последнего объявления окна размером 767 (сегмент 19) до 2816, когда приложение осуществило чтение в момент времени 39,99. Таким образом, в приемном буфере появилось 2049 байт свободного пространства. Обратитесь к первому правилу, с которого мы начали этот раздел, в соответствии с которым получатель отправляет обновление окна, потому что количество свободного места увеличилось на величину, равную половине размера приемного буфера. Это означает, что получающий TCP каждый раз проверяет, нужно ли послать обновления окна, когда приложение считывает данные из приемного буфера TCP.

Последнее чтение, осуществленное приложением, происходит в момент времени 51,99, и приложение получает уведомление о конце файла, так как буфер пуст. Два последних сегмента (21 и 22) завершают разрыв соединения.

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

Устойчивый таймер TCP устанавливается одним концом соединения, когда у него есть данные, которые необходимо отправить, однако отправка была остановлена, потому что другой конец соединения объявил окно нулевого размера. Отправитель отправляет пробы окна, при этом интервал повторной передачи рассчитывается так, как это делалось в главе 21. Отправка проб закрытого окна продолжается постоянно.

С помощью примеров, работы устойчивого таймера, мы видели, как TCP борется с синдромом глупого окна. Это необходимо для того, чтобы предотвратить объявление маленьких окон или отправку маленьких пакетов. В примерах мы видели, как происходит предотвращение синдрома глупого окна на обоих концах, то есть у отправителя и получателя.

Упражнения

  1. На рисунке 22.3 обратите внимание на моменты времени соответствующие подтверждениям (сегменты 5, 7, 9, 11, 13, 15 и 17): 0,170; 5,170; 10,170; 15,170; 15,370; 20,170 и 25,170. Также обратите внимание на разницу во времени между получением данных и отправкой подтверждений: 164,5; 18,5; 18,7; 18,8; 198,3; 18,5 и 19,1 миллисекунды. Объясните, что бы это могло означать.
  2. На рисунке 22.3 в момент времени 25,174 объявляется окно размером 767, однако в приемном буфере есть место для 768 байт. Чем объясняется разница в 1 байт?


TCP таймер "оставайся в живых"

Глава 23 TCP таймер "оставайся в живых"

Введение

Большинство новичков в TCP/IP, как правило, бывают очень удивлены, когда узнают, что по свободному TCP соединению не передаются данные. А это именно так, то есть если ни один из процессов на концах TCP соединения не посылает данные другому процессу, обмена между двумя TCP модулями не осуществляется. Например, не осуществляется опросов, как это происходит в других сетевых протоколах. Другими словами, мы можем запустить процесс клиента, который установит TCP соединение с сервером, а затем уйти на несколько часов, дней, недель или месяцев, а соединение будет держаться. Промежуточные маршрутизаторы могут выходить из строя и перезагружаться, телефонные линии могут обрыватьься и восстанавливаться, однако если хосты на концах соединения не будут перезагружены, соединение будет оставаться установленным.

При этом подразумевается, что ни одно из приложений - клиент или сервер - не имеет таймеров на прикладном уровне, которые позволяют определить отсутствие активности по соединению, и прекратить работу приложения. Обратитесь к концу раздела "BGP: протокол граничных маршрутизаторов" главы 10, где показано, что BGP посылает пробы приложениям на удаленном конце каждые 30 секунд. Это прикладной таймер, который действует независимо от TCP таймера "оставайся в живых".

Однако существуют моменты, когда сервер хочет узнать, что случилось с хостом клиента: или он вышел из строя и был выключен, или вышел из строя и перезагрузился. Таймер "оставайся в живых" (keepalive timer) это характеристика большинства реализаций, которая предоставляет эту возможность.

Таймеры "оставайся в живых" не являются частью TCP спецификации. Требования к хостам Host Requirements RFC приводят три причины, по которым их не следует использовать: (1) они могут привести к тому, что абсолютно нормальное соединение будет разорвано из-за непродолжительного сбоя, (2) они занимают определенную ширину полосы, и (3) они стоят денег, так как обмен пакетами между сетями имеет определенную цену. Тем не менее, большинство реализаций имеют таймер "оставайся в живых".

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

Опция "оставайся в живых" может вызвать разрыв устойчивого соединения между двумя процессами из-за временной потери соединения между двумя конечными системами. Например, если проба "оставайся в живых" отправлена в тот момент, когда промежуточный маршрутизатор вышел из строя и перезагружается, TCP подумает, что вышел из строя хост клиента, что, естественно, не так.

Характеристика "оставайся в живых" предназначена для того, чтобы приложение сервера могло оценить поведение клиентов и имело возможность определить, что клиент вышел из строя. Большинство версий Telnet и Rlogin серверов по умолчанию включают опцию "оставайся в живых".

Можно привести пример, однозначно доказывающий необходимость характеристики "оставайся в живых". Пользователи персональных компьютеров часто заходят терминалами на хост с помощью Telnet. В конце рабочего дня они просто выключают питание компьютера, не закрыв соединения. При этом остается полуоткрытое соединение. На рисунке 18.16 мы показали, что отправка данных по полуоткрытому соединению приводит к возврату сброса (reset), однако это происходит в том случае когда данные отправляются клиентом. Если клиент исчез, оставив полуоткрытое соединение на конце сервера, а сервер ожидает каких-либо данных от клиента, он будет ждать вечно. Характеристика "оставайся в живых" предназначена для того, чтобы помочь серверу определить наличие полуоткрытых соединений.

Описание

В этом описании мы будем называть конец, на котором включается опция "оставайся в живых", сервером, а другой конец - клиентом. Ничто не запрещает клиенту установить эту опцию, однако она устанавливается именно на сервере. Опция может быть установлена на обоих концах соединения, если каждому концу необходимо знать, работает ли удаленный конец. (В главе 29 мы увидим, что когда NFS использует TCP, оба, и клиент, и сервер, устанавливают эту опцию. А в главе 26, когда мы будем рассматривать Rlogin и Telnet, мы увидим, что эту опцию устанавливает только сервер, но не клиент.)

Если в заданном соединении не осуществляются какие-либо действия в течение 2 часов, сервер отправляет клиенту пробный сегмент. (Мы увидим, что представляет из себя пробный сегмент, в примере, который приведен ниже.) Хост клиента должен находиться в одном из четырех состояний.

  1. Хост клиента включен и доступен для сервера. TCP клиент откликается нормально, и сервер знает, что удаленный конец все еще включен. TCP сервер перезапустит таймер "оставайся в живых" еще на 2 часа. Если до истечения следующих 2 часов по соединению будет осуществлен какой-либо обмен, таймер снова сбросится и установится в 2 часа.
  2. Хост клиента вышел из строя и либо выключен, либо перезагружается. В обоих случаях его TCP не отвечает. Сервер не получит отклик на свою пробу и отработает тайм-аут равный 75 секундам. Всего сервер отправляет 10 таких проб, с интервалом в 75 секунд, и если он так и не получил отклика, сервер считает, что хост клиента выключен, и разрывает соединение.
  3. Хост клиента вышел из строя и перезагрузился. В этом случае сервер получит отклик на свою пробу "оставайся в живых", однако отклик будет игнорирован и сервер разорвет соединение.
  4. Хост клиента запущен и работает, однако недоступен для сервера. Для TCP это то же самое, что и пункт 2. В обоих случаях можно сказать, что на пробы не получен отклик.

Сервер не должен беспокоиться о том, что хост клиента был выключен и затем перезагружен. (Имеется ввиду shutdown, а не выход хоста из строя.) Когда система выключена оператором, все процессы клиента корректно завершают свою работу, при этом TCP клиент отправляет FIN для соединения. При получении FIN, TCP сервер выдает метку конца файла процессу сервера, что позволяет серверу корректно закрыть соединение.

В первом сценарии приложение сервера не подозревает о том, что были отправлены пробы "оставайся в живых". Все это делается на TCP уровне. Для приложения абсолютно безразлично, имели ли место сценарии 2, 3 или 4. Во всех трех случаях приложению сервера возвращается ошибка от его собственного TCP. (Обычно сервер осуществляет чтение из сети, ожидая данные от клиента. Если характеристика "оставайся в живых" возвратила ошибку, она передается серверу как код возврата на операцию чтения.) В случае второго сценария ошибка выглядит примерно следующим образом: "соединение закрыто по тайм-ауту" (connection timed out), а в случае третьего сценария мы можем ожидать "соединение сброшено удаленным концом" (connection reset by peer). Четвертый сценарий может выглядеть как, если соединение разорвано по тайм-ауту, однако может быть возвращена и другая ошибка, в зависимости от того, какая принята ICMP ошибка по этому соединению. Мы рассмотрим все четыре сценария в следующих разделах.

Вечный вопрос, который задают люди, изучающие опцию "оставайся в живых", заключается в том, может ли быть изменено значение 2-часового тайм-аута. Обычно требуется значительно меньше времени, где-то несколько минут. Как мы показали в приложении Е, это значение обычно может быть изменено, однако во всех системах, описанных в этом приложении, интервал "оставайся в живых" является системным значением, поэтому его изменение окажет влияние на всех пользователей.

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

Примеры "оставайся в живых"

Сейчас мы просмотрим сценарии 2, 3 и 4 из предыдущего раздела, чтобы рассмотреть обмен пакетами при использовании опции "оставайся в живых".

Удаленный конец вышел из строя

Давайте посмотрим, что произойдет, когда хост сервера вышел из строя и не перезагрузился. Чтобы имитировать эту ситуацию, мы поступим следующим образом:

  • Установим соединение между клиентом (программа sock на хосте bsdi) и стандартным эхо сервером на хосте svr4. Клиент включает опцию "оставайся в живых" с помощью опции -K.
  • Убедимся, что данные могут проходить по соединению.
  • Посмотрим, что TCP модуль клиента отправляет пакеты "оставайся в живых" каждые 2 часа, и увидим, что они подтверждаются TCP модулем сервера.
  • Отсоединим Ethernet кабель от сервера и оставим его отсоединенным до тех пор, пока пример не будет завершен. Это заставит клиента думать, что хост сервера вышел из строя.
  • Мы ожидаем, что клиент отправит 10 проб "оставайся в живых" с интервалом в 75 секунд, перед тем как решит, что соединение умерло.

Здесь приводится интерактивный вывод клиента:

bsdi % sock -K svr4 echo              опция -K для включения "оставайся в живых"
hello, world                          убедимся, что соединение работает
hello, world                           отражение эхом
                                      отсоединяем Ethernet кабель после 4 часов
read error: Connection timed out      это произошло примерно через 6 часов 10 минут после начала эксперимента

На рисунке 23.1 показан вывод tcpdump. (Мы удалили все посвященное установлению соединения и объявлению окна.)

1      0.0                     bsdi.1055 > svr4.echo: P 1:14(13)ack 1
2      0.006105 (   0.0061)   svr4.echo > bsdi.1055: P 1:14(13)ack14
3      0.093140 (   0.0870)   bsdi.1055 > svr4.echo: . ack 14

4   7199.972793 (7199.8797)   arp who-has svr4 tell bsdi
5   7199.974878 (   0.0021)   arp reply svr4 is-at 0:0:c0:c2:9b:26
6   7199.975741 (   0.0009)   bsdi.1055 > svr4.echo: . ack 14
7   7199.979843 (   0.0041)   svr4.echo > bsdi.1055: . ack 14

8  14400.134330 (7200.1545)   arp who-has svr4 tell bsdi
9  14400.136452 (   0.0021)   arp reply svr4 is-at 0:0:c0:c2:9b:26
10  14400.137391 (   0.0009)   bsdi.1055 > svr4.echo: . ack 14
11  14400.141408 (   0.0040)   svr4.echo > bsdi.1055: . ack 14

12  21600.318309 (7200.1769)   arp who-has svr4 tell bsdi
13  21675.320373 (  75.0021)   arp who-has svr4 tell bsdi
14  21750.322407 (  75.0020)   arp who-has svr4 tell bsdi
15  21825.324460 (  75.0021)   arp who-has svr4 tell bsdi
16  21900.436749 (  75.1123)   arp who-has svr4 tell bsdi
17  21975.438787 (  75.0020)   arp who-has svr4 tell bsdi
18  22050.440842 (  75.0021)   arp who-has svr4 tell bsdi
19  22125.432883 (  74.9920)   arp who-has svr4 tell bsdi
20  22200.434697 (  75.0018)   arp who-has svr4 tell bsdi
21  22275.436788 (  75.0021)   arp who-has svr4 tell bsdi

Рисунок 23.1 Пакеты "оставайся в живых", которые определяют, что хост вышел из строя.

В строках 1, 2 и 3 отправляется строка "hello, world" от клиента к серверу и обратно. Первая проба "оставайся в живых" появляется через 2 часа (7200 секунд) в строке 4. Первое на что необходимо обратить внимание - это ARP запрос и ARP отклик перед отправкой TCP сегмента в строке 6. На пробу "оставайся в живых" в строке 6 приходит отклик с удаленного конца (строка 7). Тот же обмен пакетами происходит через 2 часа в строках 8-11.

Если бы мы могли видеть все поля в пробах "оставайся в живых" (строки 6 и 10), то обязательно обратили бы внимание на то, что поле номера последовательности на единицу меньше чем следующий отправляемый номер последовательности, который должен быть отправлен (в данном примере 13, тогда как должен быть 14). Так как в сегменте нет данных, tcpdump не печатает поле номера последовательности. (Программа tcpdump печатает номер последовательности для пустых сегментов, только в том случае если они содержат флаги SYN, FIN или RST.) Именно прием этих неверных номеров последовательности заставляет TCP модуль сервера отвечать подтверждениями на пробы "оставайся в живых". В отклике клиенту сообщается следующий номер последовательности, которую ожидает сервер (14).

Некоторые более старые реализации, основанные на 4.2BSD, не отвечают откликом на пробы "оставайся в живых", если сегмент не содержит данных. Некоторые системы могут быть сконфигурированы так, чтобы посылать в пробе один байт данных, чтобы получить на него отклик. Этот байт не принесет никакого вреда, потому что это не ожидаемый байт (байт, который получатель уже ранее получил и подтвердил), поэтому он отбрасывается получателем. Другие системы посылают сначала сегмент в стиле 4.3BSD (без данных) в качестве пробы, и если отклик не получен, переключаются на сегменты в стиле 4.2BSD.

Затем мы отсоединили кабель и ожидаем, что на следующую пробу (а именно через 2 часа) отклик не будет получен. Когда появляется следующая проба, мы никогда не увидим TCP сегменты в кабеле, потому что хост не отвечает на ARP запросы. Все же мы видим, что клиент отправляет 10 проб, с промежутком в 75 секунд, перед тем как прекратить попытки. Из нашего интерактивного скрипта мы видим, что код ошибки, возвращенный процессу клиента от TCP модуля, транслируется в сообщение "Connection timed out" (соединение закрыто по тайм-ауту).

Удаленный конец вышел из строя и перезагрузился

В этом примере мы увидим, что произойдет, если сервер выйдет из строя и перезагрузится. Первоначальный сценарий такой же как и раньше, однако после того, как мы убедились, что соединение функционирует, мы отсоединили сервер от Ethernet, перезагрузили его и затем вновь подсоединили его к Ethernet. Мы ожидаем, что следующая проба "оставайся в живых" сгенерирует сброс (reset) от сервера, потому что сервер сейчас ничего не знает об этом соединении. Ниже приводится интерактивная сессия:

bsdi % sock -K svr4 echo                опция -K для включения "оставайся в живых"
hi there                                проверяем, что соединение функционирует
hi there                                 это отражается эхом с удаленного конца
                                        здесь сервер перезагружен, пока отсоединен кабель Ethernet
read error: Connection reset by peer

На рисунке 23.2 показан вывод команды tcpdump. (Мы удалили все связанное с установлением соединения и объявлением окна.)

1     0.0                       bsdi.1057 > svr4.echo: P 1:10(9) ack 1
2     0.006406 (   0.0064)     svr4.echo > bsdi.1057: P 1:10(9) ack 10
3     0.176922 (   0.1705)     bsdi.1057 > svr4.echo: . ack 10

4  7200.067151 (7199.8902)     arp who-has svr4 tell bsdi
5  7200.069751 (   0.0026)     arp reply svr4 is-at 0:0:c0:c2:9b:26
6  7200.070468 (   0.0007)     bsdi.1057 > svr4.echo: . ack 10
7  7200.075050 (   0.0046)     svr4.echo > bsdi.1057: R 1135563275:1135563275(0)

Рисунок 23.2 Пример "оставайся в живых", когда удаленный хост вышел из строя и перезагрузился.

Мы установили соединение и послали 9 байт данных от клиента к серверу (строки 1-3). Два часа спустя клиент отправил первую пробу "оставайся в живых", отклик от сервера содержит сброс. Приложение клиента печатает сообщение об ошибке "Connection reset by peer" (соединение сброшено удаленным концом).

Удаленный конец недоступен

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

Чтобы имитировать эту ситуацию, мы установили TCP соединение с нашего хоста slip через SLIP канал с дозвоном на хост vangogh.cs.berkeley.edu, а затем погасили канал. Во-первых, приведем вывод интерактивной сессии:

slip % sock -K vangogh.cs.berkeley.edu echo
testing                                         вводим эту строку
testing                                          строка отражена эхом
                                                где-то в это время погашен SLIP канал
read error: No route to host

На рисунке 23.3 показан вывод команды tcpdump, который получен с маршрутизатора bsdi. (Установление соединения и объявления окна удалены.)

1      0.0                      slip.1056 > vangogh.echo: P 1:9(8)ack1
2      0.277669 (   0.2777)     vangogh.echo > slip.1056: P 1:9(8)ack9
3      0.424423 (   0.1468)    slip.1056 > vangogh.echo: . ack 9

4   7200.818081 (7200.3937)    slip.1056 > vangogh.echo: . ack 9
5   7201.243046 (   0.4250)    vangogh.echo > slip.1056: . ack 9

6  14400.688106 (7199.4451)    slip.1056 > vangogh.echo: . ack 9
7  14400.689261 (   0.0012)    sun > slip: icmp: net vangogh unreachable
8  14475.684360 (  74.9951)    slip.1056 > vangogh.echo: . ack 9
9  14475.685504 (   0.0011)    sun > slip: icmp: net vangogh unreachable

14 строк удалено

24  15075.759603 (  75.1008)    slip.1056 > vangogh.echo: R 9:9(0)ack9
25  15075.760761 (   0.0012)    sun > slip: icmp: net vangogh unreachable

Рисунок 23.3 Пример "оставайся в живых", когда удаленный конец недоступен.

Мы начинаем этот пример так же, как и предыдущий: в строках 1-3 убеждаемся, что соединение функционирует. На первую пробу "оставайся в живых", отправляемую через 2 часа, успешно получен отклик (строки 4 и 5), однако перед тем, как будет отправлена следующая, еще через 2 часа, мы выключили SLIP соединение между маршрутизаторами sun и netb. (Обратитесь к топологии, приведенной на внутренней стороне обложки.)

На пробу "оставайся в живых" в строке 6 генерируется ICMP ошибка о недоступности сети от маршрутизатора sun. Как мы описали в разделе "ICMP ошибки" главы 21, это всего лишь "мягкая" ошибка для принимающего TCP на хосте slip. Он фиксирует, что была принята ICMP ошибка, однако получение ошибки не разрывает соединение. Отправляются еще 9 проб "оставайся в живых", с интервалом в 75 секунд, перед тем, как хост прекращает свои попытки. Ошибка, возвращаемая приложению, генерирует другое сообщение: "No route to host" (нет маршрута к хосту). На рисунке 6.12 мы видели, что это соответствует ICMP ошибке о недоступности сети.

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

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

Она функционирует посредством отправки пробных пакетов по соединению, после того как соединение не использовалось в течение 2 часов. Могут возникнуть четыре различных сценария: удаленный конец функционирует и откликается, удаленный конец вышел из строя, удаленный конец вышел из строя и перезагрузился и удаленный конец в настоящее время недоступен. В нашем примере мы рассмотрели все эти сценарии и видели различные ошибки, возвращаемые для последних трех условий.

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

Упражнения

  1. Приведите список достоинств характеристики "оставайся в живых".
  2. Приведите список недостатков характеристики "оставайся в живых".


Будущее TCP и его производительность

Глава 24 Будущее 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 показана топология для данного примера.

Рисунок 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>
3  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.

Рисунок 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 байт.

Рисунок 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 миллион байт, который необходимо отправить.

Рисунок 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.220114 ( 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 | 0
заполнение (до минимального размера Ethernet) | 0 | 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 и рассчитали время транзакции между двумя хостами, с чем мы можем это сравнить, чтобы определить его эффективность?


SNMP: протокол управления сетью

Глава 25 SNMP: протокол управления сетью

Введение

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

Управление сетью в объединенных сетях TCP/IP строится на взаимодействии между станцией управления сетью (менеджер) и элементами сети. Элементами сети могут быть любые объекты, которые используют семейство протоколов TCP/IP: хосты, маршрутизаторы, X терминалы, терминальные сервера, принтеры и так далее. На элементах сети должно быть запущено программное обеспечение, которое называется агентом. Станции управления это обычно рабочие станции с цветным монитором и графическим дисплеем, которые отображают то, что происходит с элементами (которые из них работают, а которые нет, объем траффика по различным каналам за единицу времени и так далее).

Обмен данными, как правило, двусторонний: менеджер просит агента сообщить ему определенное значение (например, "сколько было сгенерировано ICMP ошибок о недоступности порта?"), или агент сообщает менеджеру о каком-либо важном событии ("подключенный интерфейс не работает"). У менеджера должна быть возможность установить переменные в агенте ("измени значение TTL по умолчанию на 64"), помимо того, что менеджер должен иметь возможность считывать переменные от агента.

Управление сетями TCP/IP состоит из трех частей.

  1. Информационная база управления (MIB - Management Information Base), которая указывает, какие переменные в элементах сети необходимо обслуживать (информация, которая может быть запрошена и установлена менеджером). RFC 1213 [McCloghrie and Rose 1991] определяет вторую версию, которая называется MIB-II.
  2. Установка общей структуры и схемы идентификации, используемой для обращения к переменным в MIB. Это называется структурой информации управления (SMI - Structure of Management Information) и описывается в RFC 1155 [Rose and McCloghrie 1990]. Например, SMI указывает, что счетчик (Counter) это неотрицательное целое число, которое изменяется от 0 до 4294967295 и затем снова возвращается в 0.
  3. Протокол, который функционирует между менеджером и элементом, называется простым протоколом управления сетью (SNMP - Simple Network Management Protocol). RFC 1157 [Case et al. 1990] описывает этот протокол. Там же подробно описан формат пакетов, с помощью которых осуществляется обмен. Несмотря на то, что в качестве транспортных протоколов могут быть использованы разные протоколы, обычно с SNMP используется UDP.

Эти RFC определяют то, что в настоящее время называется SNMPv1, или просто SNMP, что мы и обсудим в этой главе. В течение 1993 года были опубликованы дополнительные RFC, которые описывают SNMP Version 2 (SNMPv2), который мы опишем в разделе "SNMP версии 2" этой главы.

В этой главе мы рассмотрим протокол, который используется для общения между менеджером и клиентом, во-первых, а затем посмотрим какие переменные использует агент. Мы опишем информационную базу данных, поддерживаемую агентом (MIB), рассмотрим группы, которые мы уже описали в тексте: IP, UDP, TCP и так далее. Рассмотрим примеры, соответствующие каждому описанию, а в процессе рассмотрения будем обращаться к концепции протоколов, описанных в предыдущих главах.

Протокол

SNMP определяет всего пять типов сообщений, которыми обмениваются менеджер и клиент.

Получить значение одной или нескольких переменных: оператор get-request. Получить следующую переменную после этой или несколько указанных переменных: оператор get-next-request. (Мы опишем то, что имеем в виду под словом "следующий" позже в этой главе.) Установить значение одной или нескольких переменных: оператор set-request. Выдать значение одной или нескольких переменных: оператор get-response. Это сообщение возвращается агентом менеджеру в ответ на операторы get-request, get-next-request и set-request. Уведомить менеджера, когда что-либо произошло с агентом: оператор trap.

Первые три сообщения отправляются от менеджера к агенту, а последние два от агента к менеджеру. (Мы будем называть первые три оператора как get, get-next и set.) На рисунке 25.1 приведены все пять операторов.

Так как четыре из пяти SNMP сообщений реализуются простой последовательностью запрос-отклик (менеджер отправляет запрос, а агент возвращает отклик), SNMP используют UDP. Это означает, что запрос от менеджера может не прибыть к агенту, а отклик от агента может не прибыть к менеджеру. В этом случае менеджер, возможно, отработает тайм-аут и осуществит повторную передачу.

Рисунок 25.1 Пять операторов SNMP.

Менеджер отправляет эти три запроса на UDP порт 161. Агент отправляет ловушки (trap) на UDP порт 162. Так как используются два разных порта, одна система может выступать в роли менеджера и агента одновременно. (См. упражнение 1 в конце главы.)

На рисунке 25.2 показан формат пяти SNMP сообщений, инкапсулированных в UDP датаграмму.

Рисунок 25.2 Формат пяти SNMP сообщений.

На этом рисунке мы указали в байтах только размер IP и UDP заголовков. Это объясняется тем, что для SNMP сообщений используется кодирование - называемое ASN.1 и BER, которые мы опишем позже в этой главе - в зависимости от типа переменных и их значений.

Значение поля version равно 0. Это значение в действительности равно номеру версии минус единица. (Версия SNMP, которую мы описываем, называется SNMPv1.)

На рисунке 25.3 показано значение для типа блока данных протокола (PDU type). (PDU - это блок данных протокола - Protocol Data Unit, обычно называемый "пакет".)

PDU type | Имя
0 | get-request
1 | get-next-request
2 | set-request
3 | get-response
4 | trap

Рисунок 25.3 Типы PDU сообщений SNMP.

Сообщество (community) это строка символов, в которой содержится пароль в открытом виде. Пароль используется при общении между менеджером и агентом. Обычное значение - 6-символьная строка public.

В операторах get, get-next и set менеджер устанавливает идентификатор запроса (request ID), который возвращается агентом в сообщении get-response. Мы видели этот тип переменной в других UDP приложениях. (Вспомните поле идентификации (identification) DNS на рисунке 14.3 и поле идентификатора транзакции (transaction ID) на рисунке 16.2.) Это позволяет клиенту (менеджеру в данном случае) сопоставить отклики от сервера (агент) с запросами, которые были отправлены клиентом. Это поле также позволяет менеджеру выдать несколько запросов одному или нескольким агентам, а затем отсортировать полученные отклики.

Статус ошибки (error status) это целое число, которое возвращается агентам и указывает на ошибку. На рисунке 25.4 показаны значения, имена и описания ошибок.

статус ошибки | Имя | Описание
0 | noError | все в порядке
1 | tooBig | клиент не может поместить отклик в одно SNMP сообщение
2 | noSuchName | оператор указывает на несуществующую переменную
3 | badValue | в операции установки использовано недопустимое значение или сделана ошибка в синтаксисе
4 | readOnly | менеджер попытался изменить переменную, которая помечена как "только для чтения"
5 | genErr | неопознанная ошибка

Рисунок 25.4 Значения статуса ошибки SNMP.

Если возникла ошибка, индекс ошибки (error index) это целое смещение, указывающее на то, в какой переменной произошла ошибка. Это значение устанавливается агентом только для ошибок noSuchName (нет такого имени), badValue (неверное значение) и readOnly (только для чтения).

Список имен переменных и значений следует в get, get-next и set запросах. Раздел значений игнорируется в операторах get и get-next.

Для оператора trap (PDU type равен 4) формат SNMP сообщения изменяется. Мы опишем поля заголовка ловушки, когда будем описывать этот оператор в разделе "Ловушки" этой главы.

Структура управляющей информации

SNMP использует небольшое количество различных типов данных. В этой главе мы рассмотрим эти типы, однако не будем рассматривать то, как эти данные в действительности кодируются (для хранения данных используются битовые шаблоны).

  • INTEGER (целое число). Некоторые переменные объявляются как целые без ограничений (например, MTU для интерфейса), некоторые определены с конкретными значениями (например, флаг IP о перенаправлении установлен в 1, если перенаправление включено, или в 2, если перенаправление выключено), а другие определены с их минимальными и максимальными значениями (например, номера портов TCP и UDP находятся в диапазоне от 0 до 65535).
  • OCTET STRING (восьмеричная строка). Строка из 0 или нескольких 8-битных байт. Каждый байт имеет значение от 0 до 255. В кодировании BER, используемом для этих типов данных и для следующего, счетчик количества байт в строке находится перед самой строкой. Эти строки не заканчиваются нулевыми значениями.
  • DisplayString. Строка из 0 или нескольких 8-битных байт, причем каждый байт должен быть символом из набора ASCII NVT (глава 26, раздел "Протокол Telnet"). Все переменные этого типа в MIB-II должны содержать не больше чем 255 символов. (Строка нулевой длины допустима.)
  • OBJECT IDENTIFIER (идентификатор объекта). Мы опишем их в следующем разделе.
  • NULL (ноль). Означает, что у соответствующей переменной нет значения. Используется, например, в качестве всех значений для переменных в запросах get или get-next, пока эти переменные запрашиваются, а не устанавливаются.
  • IpAddress (IP адрес). OCTET STRING (восьмеричная строка) длиной 4, с 1 байтом на каждый байт IP адреса.
  • PhysAddress (физический адрес). OCTET STRING (восьмеричная строка), содержит физический адрес (например, 6-байтный Ethernet адрес).
  • Counter (счетчик). Неотрицательное целое число, значение которого увеличивается монотонно от 0 до значения 232-1 (4.294.967.295) и затем вновь возвращается в 0.
  • Gauge (критерий). Неотрицательное целое число в диапазоне от 0 до 232-1, значение которого может увеличиваться или уменьшаться, однако изменения прекращаются по достижении максимального значения. Это означает, что если значение достигнет величины 232-1, критерий будет оставаться в этом значении до тех пор, пока не будет сброшен. Примером может служить переменная MIB tcpCurrEstab: это количество TCP соединений, находящихся в настоящий момент в состоянии ESTABLISHED (установлено) или CLOSE_WAIT (ожидание закрытия).
  • TimeTicks (тики времени). Счетчик, который считает время в сотых долях секунды с какой-либо исходной точки. Различные переменные могут указывать начало счета с различных исходных точек, исходная точка используемая для каждой переменной этого типа и указывается, когда переменная объявляется в MIB. Например, переменная sysUpTime это количество сотых долей секунды, в течение которых агент был включен.
  • SEQUENCE (последовательность). Напоминает структуру в языке программирования С. Например, мы рассмотрим, как в MIB определяется последовательность (SEQUENCE), которая называется UdpEntry и которая содержит информацию об активности конечных точек UDP агента. (Под словом "активность" мы подразумеваем порты, которые используются в настоящее время приложением.) В этой структуре есть две записи:
  1. udpLocalAddress, типа IpAddress, содержащая локальный IP адрес.
  2. udpLocalPort, типа INTEGER, в диапазоне от 0 до 65535, которая содержит локальный номер порта.
  • SEQUENCE OF (последовательность чего). Это определение вектора со всеми элементами, которые имеют тот же самый тип данных. Если каждый элемент имеет простой тип данных, такой как целое, мы имеем простой вектор (одномерный массив). Однако мы увидим, что SNMP использует эти типы данных с каждым элементом вектора, который является последовательностью (SEQUENCE) (структура). Поэтому мы можем считать их двумерными массивами или таблицей. Например, таблица слушающих процессов (listener) UDP называется udpTable, и является последовательностью (SEQUENCE OF) 2-элементной структуры (SEQUENCE) UdpEntry, которую мы только что описали. На рисунке 25.5 показан двумерный массив.

Рисунок 25.5 Таблица listener UDP (udpTable), которая представлена как двумерный массив SNMP.

Количество строк в этой таблице не определяется SNMP, однако мы увидим, что менеджер, используя оператор get-next (раздел "Простые примеры" этой главы), может определить, что получена последняя строка таблицы. Также, в разделе "Примеры идентификации" мы увидим, как менеджер указывает, какую строку таблицы он хочет получить или установить.

Идентификаторы объектов

Идентификатор объекта это тип данных, указывающий на полномочно названный объект. Под словом "полномочно" мы подразумеваем, что эти идентификаторы не назначаются случайным образом, а назначаются некоторыми организациями, которые несут ответственность за группу идентификаторов.

Идентификатор объекта это последовательность целых десятичных чисел, разделенных точками. Эти целые числа представляют собой древовидную структуру, напоминающую DNS (рисунок 14.1) или файловую систему Unix. В вершине, откуда начинается дерево идентификаторов объектов, существует корень без названия.

На рисунке 25.6 показана структура дерева. Все переменные в MIB начинаются с идентификатора объекта 1.3.6.1.2.1.

У каждого узла в дереве также существует текстовое имя. Имя, соответствующее идентификатору объекта 1.3.6.1.2.1, это iso.org.dod.internet.mgmt.mib. Подобная форма записи имен используется для удобства чтения. Имена переменных MIB используемые при обмене пакетами между менеджером и агентом (рисунок 25.2), это цифровые идентификаторы объектов, начинающиеся с 1.3.6.1.2.1.

Рисунок 25.6 Идентификаторы объектов в информационной базе управления.

Помимо идентификаторов объектов mib, приведенных на рисунке 25.6, мы также привели еще один iso.org.dod.internet.private.enterprises (1.3.5.1.4.1). В этом месте находится MIB различных производителей. В Assigned Numbers RFC приведен список около 400 идентификаторов, зарегистрированных ниже этого узла.

Введение в Информационную Базу Управления (MIB)

Информационная база управления (MIB - Management Information Base) это информационная база данных, которая обслуживается агентом, а менеджер может запросить информацию из этой базы или записать информацию в эту базу. Мы рассмотрим то, что называется MIB-II и описано в RFC 1213 [McCloghrie and Rose 1991].

Как показано на рисунке 25.6, MIB поделена на группы: system, interfaces, at (трансляция адресов), ip и так далее.

В этом разделе мы опишем только переменные, находящиеся в группе UDP. Это простая группа с небольшим количеством переменных и одной таблицей. В следующих разделах, на примере этой группы мы покажем, как осуществляется идентификация, построение лексикографического порядка и некоторые простые примеры этих характеристик. После чего, в разделе "Информационная база управления (продолжение)", мы вернемся к MIB и опишем некоторые другие группы в MIB.

На рисунке 25.6 мы показали группу под названием udp, находящуюся ниже mib. На рисунке 25.7 показана структура группы UDP.

Рисунок 25.7 Древовидная структура таблицы IP адресов.

Как мы говорили ранее, группа UDP содержит четыре переменные, и одну таблицу из двух переменных. На рисунке 25.8 приведены четыре переменные.

Имя | Тип данных | R/W | Описание
udpInDatagrams | Counter |  | Количество UDP датаграмм, доставленных пользовательским процессам.
udpNoPorts | Counter |  | Количество доставленных UDP датаграмм, для которых не оказалось пользовательского процесса на порте назначения.
udpInErrors | Counter |  | Количество недоставленных UDP датаграмм не по причине того, что на порте назначения не оказалось приложения (например, ошибка контрольной суммы UDP).
udpOutDatagrams | Counter |  | Количество отправленных UDP датаграмм.

Рисунок 25.8 Переменные в группе udp.

Мы будем использовать этот формат при описании всех переменных MIB в этой главе. Колонка, помеченная как "R/W", пуста, если переменная предназначена только для чтения или содержит точку (·), если переменную можно читать и записывать. Мы всегда будем включать эту колонку, даже если все переменные в группе только для чтения (как мы видели в группе udp), чтобы напомнить, что ни одна из переменных не может быть установлена менеджером. В случае если тип данных - INTEGER (целый) с ограничением, мы будем указывать верхний и нижний пределы, как сделано для номера порта UDP на следующем рисунке.

На рисунке 25.9 описываются две переменные в udpTable.

Таблица слушающего процесса (listener) UDP, index = <udpLocalAddress>.<udpLocalPort>
Имя | Тип данных | R/W | Описание
udpLocalAddress | IpAddress |  | Локальный IP адрес слушающего процесса. 0.0.0.0 указывает, что слушающий процесс воспринимает датаграммы с любого интерфейса.
udpLocalPort | [0..65535] |  | Локальный номер порта слушающего процесса.

Рисунок 25.9 Переменные в udpTable.

Каждый раз, когда мы описываем переменные в SNMP таблице, первая строка рисунка содержит значение "index", используемое для обращения к каждой строке таблицы. Мы покажем некоторые примеры того, как это делается, в следующем разделе.

Диаграммы зависимостей

Здесь приводится взаимосвязь между первыми тремя счетчиками, описанными на рисунке 25.8. Диаграммы зависимостей [Case and Partridge 1989] иллюстрируют взаимосвязь между различными MIB переменными в заданной группе. На рисунке 25.10 показана диаграмма зависимостей для UDP группы.

Рисунок 25.10 Диаграмма зависимостей для группы UDP.

Эта диаграмма показывает, что количество UDP датаграмм, доставленных приложению (udpInDatagrams), можно получить как количество UDP датаграмм, доставленных от IP к UDP, минус udpInErrors, минус udpNoPorts. Количество UDP датаграмм, доставленных в IP (udpOutDatagrams), это количество, переданное в UDP от приложения. Можно сделать вывод, что udpInDatagrams не включает в себя udpInErrors или udpNoPorts.

Эти диаграммы были использованы в процессе разработки MIB, для проверки того, что все пути данных для пакета были учтены. [Rose 1994] показывает диаграммы зависимостей для всех групп в MIB.

Примеры идентификации

Каждая переменная в MIB должна быть идентифицирована, когда SNMP обращается к ней, чтобы получить или установить ее значение. Во-первых, обращение осуществляется только к листовым узлам (листовой узел в древовидной структуре - любой самый удаленный элемент от корня). SNMP не работает с целыми рядами или колонками таблицы. Возвращаясь к рисунку 25.7, мы видим, что листовыми узлами дерева, являются те четыре, что мы описали на рисунке 25.8, и два на рисунке 25.9. Узлы mib, udp, udpTable и udpEntry не являются листовыми узлами.

Простые переменные

На то, что эта переменная простая, указывает ".0", добавленный к идентификатору объекта переменной. Например, к счетчику udpInDatagrams показанному на рисунке 25.8, c идентификатором объекта 1.3.6.1.2.1.7.1, можно обратиться как 1.3.6.1.2.1.7.1.0. Текстовое имя при подобном обращении будет iso.org.dod.internet.mgmt.mib.udp.udpInDatagrams.0.

Однако обращения к этой переменной обычно делаются в сокращенном виде, udpInDatagrams.0, мы повторим, что name (имя) переменной, которое появляется в сообщении SNMP (рисунок 25.2), это идентификатор объекта 1.3.6.1.2.1.7.1.0.

Таблицы

Рассмотрим идентификацию пунктов таблицы более подробно. Давайте вернемся к таблице слушающего процесса UDP (рисунок 25.7).

Для каждой таблицы в MIB указан один или несколько индексов. Для таблицы слушающего процесса UDP, MIB определяет индекс как комбинацию двух переменных udpLocalAddress (локальный IP адрес) и udpLocalPort (локальный UDP порт), индекс в данном случае - целое число. (Мы показали этот индекс в верхней строке на рисунке 25.9.)

Представьте себе, что в таблице слушающего процесса UDP есть три строки: первая для IP адреса 0.0.0.0 и порта 67, вторая для 0.0.0.0 и порта 161 и третья для 0.0.0.0 и порта 520. На рисунке 25.11 показана эта таблица.

udpLocalAddress | udpLocalPort
0.0.0.0 | 67
0.0.0.0 | 161
0.0.0.0 | 520

Рисунок 25.11 Простая таблица слушающего процесса UDP.

Из таблицы видно, что система готова принимать UDP датаграммы с любого интерфейса для портов 67 (BOOTP сервер), 161 (SNMP) и 520 (RIP). К трем строкам в таблице можно обратиться, как показано на рисунке 25.12.

Лексикографический порядок

Порядок в MIB основан на расположении идентификаторов объектов. Все записи в MIB таблице расположены в лексикографическом порядке в соответствии с их идентификаторами объектов. Это означает, что шесть переменных, приведенных на рисунке 25.12, расставлены в MIB так, как это показано на рисунке 25.13. В результате можно декларировать два правила.

Строка (ряд) | Идентификатор объекта | Сокращенное имя | Значение
1 | 1.3.6.1.2.1.7.5.1.1.0.0.0.0.67 1.3.6.1.2.1.7.5.1.2.0.0.0.0.67 | udpLocalAddress.0.0.0.0.67 udpLocalPort.0.0.0.0.67 | 0.0.0.0 67
2 | 1.3.6.1.2.1.7.5.1.1.0.0.0.0.161 1.3.6.1.2.1.7.5.1.2.0.0.0.0.161 | udpLocalAddress.0.0.0.0.161 udpLocalPort.0.0.0.0.161 | 0.0.0.0 161
3 | 1.3.6.1.2.1.7.5.1.1.0.0.0.0.520 1.3.6.1.2.1.7.5.1.2.0.0.0.0.520 | udpLocalAddress.0.0.0.0.520 udpLocalPort.0.0.0.0.520 | 0.0.0.0 520

Рисунок 25.12 Пример идентификации строк в таблице слушающего процесса UDP.

Колонка | Идентификатор объекта (в лексикографическом порядке) | Сокращенное имя | Значение
1 | 1.3.6.1.2.1.7.5.1.1.0.0.0.0.67 1.3.6.1.2.1.7.5.1.1.0.0.0.0.161 1.3.6.1.2.1.7.5.1.1.0.0.0.0.520 | udpLocalAddress.0.0.0.0.67 udpLocalAddress.0.0.0.0.161 udpLocalAddress.0.0.0.0.520 | 0.0.0.0 0.0.0.0 0.0.0.0
2 | 1.3.6.1.2.1.7.5.1.2.0.0.0.0.67 1.3.6.1.2.1.7.5.1.2.0.0.0.0.161 1.3.6.1.2.1.7.5.1.2.0.0.0.0.520 | udpLocalPort.0.0.0.0.67 udpLocalPort.0.0.0.0.161 udpLocalPort.0.0.0.0.520 | 67 161 520

Рисунок 25.13 Лексикографический порядок таблицы слушающего процесса UDP.

  1. Так переменная udpLocalAddress всегда появляется перед появлением в этой же таблице следующей переменной (udpLocalPort), это означает, что доступ к таблице осуществляется в порядке колонка-строка. Это является результатом лексикографического упорядочивания идентификаторов объектов, а не имен, предназначенных для чтения человеком.
  2. Порядок расположения строк в таблице зависит от значений индексов для таблицы. На рисунке 25.13, 67 лексикографически меньше чем 161, который, в свою очередь, лексикографически меньше чем 520.

На рисунке 25.14 показан порядок колонка-строка для нашего примера таблицы слушающего процесса UDP.

Рисунок 25.14 Таблица слушающего процесса UDP, показанная в порядке колонка-ряд.

Мы еще увидим этот порядок колонка-строка, когда будем использовать оператор get-next в следующем разделе.

Простые примеры

В этой главе мы покажем некоторые простые примеры того, как можно получить значения переменных от SNMP агента. Программное обеспечение, используемое для опроса агента, называется snmpi и взято из системы ISODE. Оба кратко описаны в [Rose 1994].

Простые переменные

Мы запрашиваем маршрутизатор на предмет двух простых переменных из UDP группы:

sun % snmpi -a gateway -c secret

snmpi> get udpInDatagrams.0 udpNoPorts.0
udpInDatagrams.0=616168 udpInDatagrams.0=616168
udpNoPorts.0=33

snmpi> quit

Опция -a указывает на агента, с которым мы хотим пообщаться, а опция -c указывает SNMP сообщество. Это пароль, устанавливаемый клиентом (snmpi в данном случае), и если сервер (агент на системе gateway) распознает имя сообщества, он ответит на запрос менеджера. Агент может позволить клиентам, принадлежащим к одному сообществу, только чтение своих переменных, а клиентам из другого сообщества чтение и запись.

Программа выводит приглашение snmpi>, после чего мы можем, например, ввести команду get, которая будет преобразована в SNMP сообщение get-request. Затем мы вводим quit. (Во всех следующих примерах последняя команда quit удалена.) На рисунке 25.15 показаны две строки вывода tcpdump для этого примера.

1  0.0                   sun.1024 > gateway.161: GetRequest (42)
                        1.3.6.1.2.1.7.1.0 1.3.6.1.2.1.7.2.0

2  0.348875 (0.3489)    gateway.161 > sun.1024: GetResponse (46)
                        1.3.6.1.2.1.7.1.0=616168
                        1.3.6.1.2.1.7.2.0=33

Рисунок 25.15 Вывод tcpdump для простого запроса SNMP.

Запрос о двух переменных посылается в одной UDP датаграмме, отклик также прибывает в одной UDP датаграмме.

Мы показали переменные в виде соответствующих им идентификаторов объектов, потому что именно это было отправлено в SNMP сообщениях. Мы должны были указать эти две переменные как 0. Обратите внимание на то, что в отклике всегда возвращается имя переменной (идентификатора объекта). Ниже мы увидим, что это необходимо для работы оператора get-next.

Оператор get-next

Функционирование оператора get-next основано на лексикографическом порядке MIB. Мы начнем следующий пример с запроса следующего идентификатора объекта после udp (не указывая объект, так как это не листовой объект). При этом будет возвращен первый объект группы UDP. Затем мы запросим следующую запись, будет возвращена вторая запись. И наконец, мы повторим это еще раз, чтобы получить третью запись:

sun % snmpi -a gateway -c secret

snmpi> next udp
udpInDatagrams.0=616318

snmpi> next udpInDatagrams.0
udpNoPorts.0=33

snmpi> next udpNoPorts.0
udpInErrors.0=0

Этот пример показывает, почему оператор get-next должен возвращать имя переменной: мы спрашиваем агента о следующей переменной, и агент возвращает ее имя и значение.

При использовании оператора get-next менеджер осуществляет циклический опрос всех переменных, поддерживаемых агентом (цикл стартует с начала MIB). Другое использование этого оператора - просмотр таблиц.

Доступ к таблице

Мы можем убедиться в том, что таблица имеет организацию колонка-строка, воспользовавшись программой, которая отправляет запросы, для того чтобы пройти через таблицу слушающего процесса UDP. Мы начнем с того, что спросим следующую переменную после udpTable. Так как это не листовой объект, мы не можем указать объект, однако оператор get-next все равно возвращает следующий объект в таблице. Затем мы продолжим движение по таблице, так как агент возвращает следующую переменную, в соответствии с порядком столбец-строка:

sun % snmpi -a gateway -c secret

snmpi> next udpTable
udpLocalAddress.0.0.0.0.67=0.0.0.0

snmpi> next udpLocalAddress.0.0.0.0.67
udpLocalAddress.0.0.0.0.161=0.0.0.0

snmpi> next udpLocalAddress.0.0.0.0.161
udpLocalAddress.0.0.0.0.520=0.0.0.0

snmpi> next udpLocalAddress.0.0.0.0.520
udpLocalPort.0.0.0.0.67=67

snmpi> next udpLocalPort.0.0.0.0.67
udpLocalPort.0.0.0.0.161=161

snmpi> next udpLocalPort.0.0.0.0.161
udpLocalPort.0.0.0.0.520=520

snmpi> next udpLocalPort.0.0.0.0.520
snmpInPkts.0=59             здесь мы закончили просмотр таблицы слушающего процесса UDP

Мы видим, что порядок, возвращенный в данном примере, соответствует порядку, приведенному на рисунке 25.14.

Как менеджер может определить, что он достиг конца таблицы? Так как ответ на оператор get-next содержит имя следующего пункта в MIB после таблицы, менеджер может сказать, когда имя изменилось. В нашем примере последний пункт в таблице слушающего процесса UDP следует за переменной snmpInPkts.

Информационная база управления (продолжение)

А сейчас мы вернемся к описанию MIB. Опишем следующие группы: system (идентификация системы), if (интерфейсы), at (трансляция адресов), ip, icmp и tcp. Также определены дополнительные группы.

Группа system

Группа system довольно проста; она состоит из семи простых переменных (таблиц в этой группе нет). На рисунке 25.16 приведены их имена, типы данных и описания.

Имя | Тип данных | R/W | Описание
sysDescr | DisplayString |  | Текстовое описание пункта.
sysObjectID | ObjectID |  | Идентификатор поставщика в поддереве 1.3.6.1.4.1.
sysUpTime | TimeTicks |  | Время в сотых долях секунд с того момента, когда часть системы, отвечающая за управление сетевое, была перестартована.
sysContact | DisplayString | · | Имя человека, к которому необходимо обратиться, и как его можно найти.
sysName | DisplayString | · | Полное имя домена узла (FQDN) .
sysLocation | DisplayString | · | Физическое расположение узла.
sysServices | [0..127] |  | Значение, указывающее на то, какие сервисы предоставляются узлом. Это сумма уровней OSI модели, поддерживаемых узлом. Следующие значения складываются вместе, в зависимости от того, какие сервисы поддерживаются: 0х01 (физический), 0х02 (канальный), 0х04 (сетевой), 0х08 (точка-точка), 0х40 (прикладной).

Рисунок 25.16 Простые переменные группы system.

Мы можем отправить запрос маршрутизатору netb, для того чтобы получить некоторые из этих переменных:

sun % snmpi -a netb -c secret

snmpi> get sysDescr.0 sysObjectID.0 sysUpTime.0 sysServices.0
sysDescr.0="Epilogue Technology SNMP agent for Telebit NetBlazer"
sysObjectID.0=1.3.6.1.4.1.12.42.3.1
sysUpTime.0=22 days, 11 hours, 23 minutes, 2 seconds (194178200 timeticks)
sysServices.0=0xc<internet, transport>

Идентификатор объекта системы находится в группе internet.private.enterprises (1.3.6.1.4.1), в соответствии с рисунком 25.6. Из Assigned Numbers RFC мы можем определить, что следующий идентификатор объекта (12) назначен производителю (Epilogue).

Также мы можем видеть, что переменная sysServices является суммой 4 и 8: этот элемент сети (netb) поддерживает IP уровень (маршрутизация) и транспортный уровень (точка-точка).

Группа interface

Только одна простая переменная определена для этой группы: количество интерфейсов в системе. Рисунок 25.17.

Имя | Тип данных | R/W | Описание
ifNumber | INTEGER |  | Количество сетевых интерфейсов в системе.

Рисунок 25.17 Простая переменная в группе if.

В этой группе также определена таблица, состоящая из 22 строк. Каждая строка в таблице определяет характеристики каждого интерфейса, как показано на рисунке 25.18.

Таблица интерфейсов, индекс = < IfIndex >
Имя | Тип данных | R/W | Описание
ifIndex | INTEGER |  | Индекс интерфейса, находится в диапазоне между единицей и ifNumber.
ifDescr | DisplayString |  | Текстовое описание интерфейса.
ifType | INTEGER |  | Тип, например: 6 = Ethernet, 7 = 802.3 Ethernet, 9 = 802.5 Token ring, 23 = PPP, 28 = SLIP и многие другие переменные.
ifMtu | INTEGER |  | MTU интерфейса (максимальный блок передачи).
ifSpeed | Gauge |  | Скорость в битах в секунду.
ifPhysAddress | PhysAddress |  | Физический адрес или строка нулевой длины для интерфейсов без физического адреса (например, последовательные каналы).
ifAdminStatus | [1..3] | · | Желательное состояние интерфейса: 1 = активен, 2 = выключен, 3 = тестируется.
ifOperStatus | [1..3] |  | Текущее состояние интерфейса: 1 = активен, 2 = выключен, 3 = тестируется.
ifLastChange | TimeTicks |  | Значение sysUpTime на момент, когда интерфейс вошел в текущее состояние функционирования.
ifInOctets | Counter |  | Полное количество принятых байтов, включая символы построения фреймов.
ifInUcastPkts | Counter |  | Количество персональных пакетов, доставленных к верхним уровням.
ifInNUcastPkts | Counter |  | Количество неперсональных (широковещательных или групповых) пакетов, доставленных к верхним уровням.
ifInDiscards | Counter |  | Количество принятых и отброшенных пакетов, даже если в пакете не была обнаружена ошибка (переполнение буферов).
ifInErrors | Counter |  | Количество пакетов принятых и отброшенных по причине ошибок.
ifInUnknownProtos | Counter |  | Количество принятых и отброшенных пакетов по причине того, что они принадлежали неизвестному протоколу.
ifOutOctets | Counter |  | Количество переданных байт, включая символы построения фреймов.
ifOutUcastPkts | Counter |  | Количество персональных пакетов, принятых от верхних уровней.
ifOutNUcastPkts | Counter |  | Количество неперсональных (широковещательных или групповых) пакетов, принятых от верхних уровней.
ifOutDiscards | Counter |  | Количество исходящих пакетов, которые были отброшены, даже если в пакетах не была обнаружена ошибка (переполнение буферов).
ifOutErrors | Counter |  | Количество исходящих пакетов, отброшенных по причине ошибок.
ifOutQLen | Gauge |  | Количество пакетов, находящихся в выходной очереди.
ifSpecific | ObjectID |  | Ссылка на определение MIB конкретно для этого типа среды передачи.

Рисунок 25.18 Переменные в таблице интерфейсов: ifTable.

Мы можем запросить хост sun на предмет некоторых из этих переменных для всех его интерфейсов. Мы ожидаем увидеть три интерфейса (см. главу 3, раздел "Команда ifconfig"), если активизирован SLIP интерфейс:

sun % snmpi -a sun

snmpi> next ifTable

          во-первых, мы видим,

чему равен индекс первого интерфейса
ifIndex.1=1

snmpi> get ifDescr.1 ifType.1 ifMtu.1 ifSpeed.1 ifPhysAddress.1
ifDescr.1="le0"
ifType.1=ethernet-csmacd (6)
ifMtu.1=1500
ifSpeed.1=10000000
ifPhysAddress.1=0x08:00:20:03:f6:42

snmpi> next ifDescr.1 ifType.1 ifMtu.1 ifSpeed.1 ifPhysAddress.1
ifDescr.2="sl0"
ifType.2=propPointToPointSerial (22)
ifMtu.2=552
ifSpeed.2=0
ifPhysAddress.2=0x00:00:00:00:00:00

snmpi> next ifDescr.2 ifType.2 ifMtu.2 ifSpeed.2 ifPhysAddress.2
ifDescr.3="lo0"
ifType.3=softwareLoopback (24)
ifMtu.3=1536
ifSpeed.3=0
ifPhysAddress.3=0x00:00:00:00:00:00

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

Тип интерфейса для SLIP канала сообщается как последовательное соединение точка-точка, а не SLIP. Также, не сообщается скорость SLIP канала.

Очень важно понять взаимосвязь между оператором get-next и порядком расположения информации в таблице, а именно колонка-строка. Когда мы задаем next ifDescr.1, возвращается следующая строка таблицы для этой переменной, а не следующую переменную в этой же строке. Однако если бы таблицы хранились в порядке строка-колонка, мы могли подобным образом перейти к следующему появлению заданной переменной.

Группа at

Группа трансляции адресов поддерживается во всех системах, однако ее ценность значительно уменьшилась, после того как стала использоваться MIB-II. С использованием MIB-II, каждая группа сетевых протоколов (например, IP) содержит свою собственную таблицу трансляции адресов. Для IP это ipNetToMediaTable.

В группе at определена только одна таблица из трех строк, как показано на рисунке 25.19.

Мы можем использовать новую команду, существующую в программе snmpi, чтобы получить содержимое таблицы в целом. Мы запросим маршрутизатор с именем kinetics (который предоставляет маршруты между TCP/IP сетью и сетью AppleTalk), выдать полный ARP кэш. Этот вывод будет находиться в лексикографическом порядке в виде пунктов, находящихся в таблице:

Таблица трансляции адресов, индекс = <atIfIndex>.1.<atNetAddress>
Имя | Тип данных | R/W | Описание
atIfIndex | INTEGER | · | Номер интерфейса: ifIndex.
atPhysAddress | PhysAddress | · | Физический адрес. Установка этого параметра в строку с нулевой длиной приводит к тому, что пункт считается некорректным.
atNetAddress | NetworkAddress | · | IP адрес

Рисунок 25.19 Таблица трансляции адресов: atTable.

sun % snmpi -a kinetics -c secret dump at

atIfIndex.1.1.140.252.1.4=1
atIfIndex.1.1.140.252.1.22=1
atIfIndex.1.1.140.252.1.183=1
atIfIndex.2.1.140.252.6.4=2
atIfIndex.2.1.140.252.6.6=2

atPhysAddress.1.1.140.252.1.4=0xaa:00:04:00:f4:14
atPhysAddress.1.1.140.252.1.22=0x08:00:20:0f:2d:38
atPhysAddress.1.1.140.252.1.183=0x00:80:ad:03:6a:80
atPhysAddress.2.1.140.252.6.4=0x00:02:16:48
atPhysAddress.2.1.140.252.6.6=0x00:02:3c:48

atNetAddress.1.1.140.252.1.4=140.252.1.4
atNetAddress.1.1.140.252.1.22=140.252.1.22
atNetAddress.1.1.140.252.1.183=140.252.1.183
atNetAddress.2.1.140.252.6.4=140.252.6.4
atNetAddress.2.1.140.252.6.6=140.252.6.6

С использованием tcpdump можно увидеть следующее. Для того чтобы получить полную таблицу, snmpi, во-первых, выдает get-next для имени таблицы (at в данном примере), чтобы получить первый пункт. Затем печатает первый пункт и выдает get-next. Это продолжается до тех пор, пока не будет получена вся таблица целиком.

На рисунке 25.20 показана подобная таблица.

atIfIndex | AtPhysAddress | atNetAddress
1 | 0xaa:00:04:00:f4:14 | 140.252.1.4
1 | 0x08:00:20:0f:2d:38 | 140.252.1.22
1 | 0x00:80:ad:03:6a:80 | 140.252.1.183
2 | 0x00:02:16:48 | 140.252.6.4
2 | 0x00:02:3c:48 | 140.252.6.6

Рисунок 25.20 Пример таблицы at (ARP кэш).

Физические адреса AppleTalk с номером интерфейса - 2 имеют 32-битные значения, а не 48-битные, как у привычных Ethernet адресов. Также обратите внимание на то, что существуют запись для нашего маршрутизатора (netb находится по адресу 140.252.1.183), так как kinetics и netb находятся на одном и том же Ethernet кабеле (140.252.1), и kinetics должен использовать ARP, чтобы послать нам SNMP отклик.

Группа ip

Группа ip определяет большое количество переменных и три таблицы. На рисунке 25.21 приведены простые переменные.

Имя | Тип данных | R/W | Описание
ipForwarding | [1..2] | · | 1 означает, что система перенаправляет IP датаграммы, а 2 означает, что не перенаправляет.
ipDefaultTTL | INTEGER | · | Значение TTL по умолчанию, когда его не предоставляет транспортный уровень.
ipInReceives | Counter |  | Полное количество IP датаграмм, полученных со всех интерфейсов.
ipInHdrErrors | Counter |  | Количество IP датаграмм, отброшенных из-за ошибок в заголовке (например, ошибка контрольной суммы, несовпадение номера версии, истечение TTL и так далее).
ipInAddrErrors | Counter |  | Количество IP датаграмм, отброшенных из-за неправильного адреса назначения.
IpForwDatagrams | Counter |  | Количество IP датаграмм, для которых была сделана попытка перенаправить.
ipInUnknownProtos | Counter |  | Количество локально адресованных IP датаграмм, у которых было неверное поле протокола.
ipInDiscards | Counter |  | Количество принятых IP датаграмм, отброшенных из-за недостаточного размера буфера.
ipInDelivers | Counter |  | Количество IP датаграмм, доставленных соответствующему модулю протокола.
ipOutRequests | Counter |  | Полное количество IP датаграмм, переданных в IP уровень для передачи. Сюда не включены те, которые были посчитаны в ipForwDatagrams.
ipOutDiscards | Counter |  | Количество исходящих IP датаграмм, которые были отброшены из-за отсутствия места в буфере.
ipOutNoRoutes | Counter |  | Количество IP датаграмм, которые были отброшены из-за того, что не был найден маршрут.
ipReasmTimeout | INTEGER |  | Максимальное количество секунд, в течение которого принятые фрагменты ждали повторной сборки.
ipReasmReqds | Counter |  | Количество принятых IP фрагментов, которые должны быть собраны.
ipReasmOKs | Counter |  | Количество успешно собранных IP датаграмм.
ipReasmFails | Counter |  | Количество сбоев в алгоритме повторной сборки IP.
ipFragOKs | Counter |  | Количество успешно фрагментированных IP датаграмм.
ipFragFails | Counter |  | Количество IP датаграмм, которые необходимо фрагментировать, однако это не было сделано, потому что был установлен флаг "не фрагментировать".
ipFragCreates | Counter |  | Количество IP фрагментов, которые были получены при фрагментации.
ipRoutingDiscards | Counter |  | Количество пунктов маршрутизации, выбранных для уничтожения, даже если они существовали и были верны.

Рисунок 25.21 Простые переменные группы ip.

Первая таблица в группе ip это таблица IP адресов. Она содержит по одной строке для каждого IP адреса в системе. Каждая строка содержит пять переменных, описанных на рисунке 25.22.

Таблица IP адресов, индекс = <ipAdEntAddr>
Имя | Тип данных | R/W | Описание
ipAdEntAddr | IpAddress |  | IP адрес для этой строки.
ipAdEntIfIndex | INTEGER |  | Соответствующий номер интерфейса: ifIndex.
ipAdEntNetMask | IpAddress |  | Маска подсети для этого IP адреса.
ipAdEntBcastAddr | [0..1] |  | Значение младших битов в широковещательном IP адресе. Обычно равно 1.
ipAdEntReasmMaxSize | [0..65535] |  | Размер максимальной принятой IP датаграммы для этого интерфейса, которая может быть повторно собрана.

Рисунок 25.22 Таблица IP адресов: ipAddrTable.

Мы можем запросить хост sun, чтобы получить таблицу IP адресов:

sun % snmpi -a sun dump ipAddrTable

ipAdEntAddr.127.0.0.1=127.0.0.1
ipAdEntAddr.140.252.1.29=140.252.1.29
ipAdEntAddr.140.252.13.33=140.252.13.33

ipAdEntIfIndex.127.0.0.1=3          loopback

интерфейс, lo0
ipAdEntIfIndex.140.252.1.29=2       SLIP интерфейс,

sl0
ipAdEntIfIndex.140.252.13.33=1      Ethernet интерфейс,

le0

ipAdEntNetMask.127.0.0.1=255.0.0.0
ipAdEntNetMask.140.252.1.29=255.255.255.0
ipAdEntNetMask.140.252.13.33=255.255.255.224

ipAdEntBcastAddr.127.0.0.1=1        все три

используют единичный бит
ipAdEntBcastAddr.140.252.1.29=1     для

широковещательного адреса
ipAdEntBcastAddr.140.252.13.33=1

ipAdEntReasmMaxSize.127.0.0.1=65535
ipAdEntReasmMaxSize.140.252.1.29=65535
ipAdEntReasmMaxSize.140.252.13.33=65535

Номера интерфейсов можно сравнить с выводом, полученным на рисунке 25.18, а IP адреса и маски подсетей можно сравнить со значениями, полученными в выводе команды ifconfig в разделе "Команда ifconfig" главы 3.

Следующая таблица, приведенная на рисунке 25.23, это таблица IP маршрутизации. (Обратитесь к описанию таблиц маршрутизации, приведенному в разделе "Принципы маршрутизации" главы 9.) В качестве индекса для получения доступа к каждой строке таблицы, используется IP адрес назначения.

На рисунке 25.24 приведена таблица маршрутизации для хоста sun, полученная с помощью команды dump ipRouteTable программы snmpi. Мы удалили все пять показателей маршрутизации, так как все они равны -1, а в заголовках колонок удалили префикс ipRoute для каждого имени переменной.

Таблица маршрутизации IP, индекс = <ipRouteDest>
Имя | Тип данных | R/W | Описание
ipRouteDest | IpAddress | · | IP адрес назначения. Значение 0.0.0.0 указывает на пункт по умолчанию.
ipRouteIfIndex | INTEGER | · | Номер интерфейса: ifIndex.
ipRouteMetric1 | INTEGER | · | Первичный показатель маршрута. Значение показателя зависит от протокола маршрутизации (ipRouteProto). Значение -1 означает, что маршрут не используется.
ipRouteMetric2 | INTEGER | · | Альтернативный показатель маршрута.
ipRouteMetric3 | INTEGER | · | Альтернативный показатель маршрута.
ipRouteMetric4 | INTEGER | · | Альтернативный показатель маршрута.
ipRouteNextHop | IpAddress | · | IP адрес маршрутизатора следующей пересылки.
ipRouteType | INTEGER | · | Тип маршрута: 1=другой, 2=недействующий маршрут, 3=прямой, 4=непрямой.
ipRouteProto | INTEGER |  | Протокол маршрутизации: 1=другой, 4=ICMP перенаправление, 8=RIP, 13=OSPF, 14=BGP и другие.
ipRouteAge | INTEGER | · | Количество секунд, которое прошло с того момента, когда маршрут был последний раз обновлен или определен как корректный.
ipRouteMask | IpAddress | · | Маска, которая должна быть добавлена по логическому И к IP адресу назначения, перед тем как она будет сравнена с ipRouteDest.
ipRouteMetric5 | INTEGER | · | Альтернативный показатель маршрута.
ipRouteInfo | ObjectID |  | Ссылка на конкретное определение MIB для этого протокола маршрутизации.

Рисунок 25.23 Таблица IP маршрутизации: ipRouteTable.

Dest | IfIndex | NextHop | Type | Proto | Mask
0.0.0.0 | 2 | 140.252.1.183 | непрямой (4) | другой (1) | 0.0.0.0
127.0.0.1 | 3 | 127.0.0.1 | прямой (3) | другой (1) | 255.255.255.255
140.252.1.183 | 2 | 140.252.1.29 | прямой (3) | другой (1) | 255.255.255.255
140.252.13.32 | 1 | 140.252.13.33 | прямой (3) | другой (1) | 255.255.0.0
140.252.13.65 | 1 | 140.252.13.35 | непрямой (4) | другой (1) | 255.255.255.255

Рисунок 25.24 Таблица IP маршрутизации маршрутизатора sun.

Для сравнения здесь приводится таблица маршрутизации IP в формате вывода команды netstat (см. главу 9, раздел "Принципы маршрутизации"). На рисунке 25.24 таблица маршрутизации приводится в лексикографическом порядке:

sun % netstat -rn
Routing tables
Destination     Gateway          Flags   Refcnt   Use     Interface
140.252.13.65   140.252.13.35   UGH     0         115    le0
127.0.0.1       127.0.0.1        UH      1         1107   lo0
140.252.1.183   140.252.1.29    UH       0        86      sl0
default         140.252.1.183   UG       2        1628    sl0
140.252.13.32   140.252.13.33   U        8        68359   le0

И последняя таблица для группы ip это таблица трансляции адресов, приведенная на рисунке 25.25. Как мы говорили раньше, группа at в настоящее время практически не используется (как устаревшая), и эта таблица заменяет ее.

Таблица трансляции IP адресов, индекс = <ipNetToMediaIfIndex>.<ipNetToMediaNetAddress>
Имя | Тип данных | R/W | Описание
ipNetToMediaIfIndex | INTEGER | · | Соответствующий интерфейс: ifIndex.
ipNetToMediaPhysAddress | PhysAddress | · | Физический адрес.
ipNetToMediaNetAddress | IpAddress | · | IP адрес.
ipNetToMediaType | [1..4] | · | Тип сопоставления: 1=другой, 2=неиспользуемый, 3=динамический, 4=статический.

Рисунок 25.25 Таблица трансляции IP адресов: ipNetToMediaTable.

Здесь мы приводим ARP кэш системы sun:

sun % arp -a
svr4 (140.252.13.34) at 0:0:c0:c2:9b:26
bsdi (140.252.13.35) at 0:0:c0:6f:2d:40

и соответствующий SNMP вывод:

sun % snmpi -a sun dump ipNetToMediaTable

ipNetToMediaIfIndex.1.140.252.13.34=1
ipNetToMediaIfIndex.1.140.252.13.35=1
ipNetToMediaPhysAddress.1.140.252.13.34=0x00:00:c0:c2:9b:26
ipNetToMediaPhysAddress.1.140.252.13.35=0x00:00:c0:6f:2d:40
ipNetToMediaNetAddress.1.140.252.13.34=140.252.13.34
ipNetToMediaNetAddress.1.140.252.13.35=140.252.13.35
ipNetToMediaType.1.140.252.13.34=dynamic(3)
ipNetToMediaType.1.140.252.13.35=dynamic(3)

Группа icmp

Группа icmp состоит из четырех общих счетчиков (общее количество входящих и исходящих ICMP сообщений и количество входящих и исходящих ICMP сообщений с ошибками) и 22-х счетчиков для различных типов ICMP сообщений: 11 счетчиков на входящие сообщения и 11 счетчиков на исходящие сообщения. Это показано на рисунке 25.26.

Для ICMP сообщений с дополнительными кодами (обратитесь к рисунку 6.3, на котором приведены 15 различных кодов для сообщения о недостижимости пункта назначения) отдельный счетчик для каждого SNMP кода не поддерживается.

Группа tcp

На рисунке 25.27 описаны простые переменные группы tcp. Многие из них соответствуют состояниям TCP, которые показаны на рисунке 18.12.

Имя | Тип данных | R/W | Описание
icmpInMsgs | Counter |  | Полное количество принятых ICMP сообщений.
icmpInErrors | Counter |  | Количество принятых ICMP сообщений с ошибками (например, ошибочная контрольная сумма ICMP).
icmpInDestUnreachs | Counter |  | Количество принятых ICMP сообщений о недоступности источника.
icmpInTimeExcds | Counter |  | Количество принятых ICMP сообщений об истечении времени.
icmpInParmProbs | Counter |  | Количество принятых ICMP сообщений о проблемах с параметром.
icmpInSrcQuenchs | Counter |  | Количество принятых ICMP сообщений о подавлении источника.
icmpInRedirects | Counter |  | Количество принятых ICMP сообщений о перенаправлении.
icmpInEchos | Counter |  | Количество принятых ICMP сообщений с эхо запросом.
icmpInEchoReps | Counter |  | Количество принятых ICMP сообщений с эхо откликом.
icmpInTimestamps | Counter |  | Количество принятых ICMP сообщений с запросом временной марки.
icmpInTimestampReps | Counter |  | Количество принятых ICMP сообщений с откликом временной марки.
icmpInAddrMasks | Counter |  | Количество принятых ICMP сообщений с запросом маски адреса.
icmpInAddrMaskReps | Counter |  | Количество принятых ICMP сообщений с откликом маски адреса.
icmpOutMsgs | Counter |  | Полное количество исходящих ICMP сообщений.
icmpOutErrors | Counter |  | Количество ICMP сообщений, которые не были отправлены из-за проблем внутри ICMP (переполнение буферов).
icmpOutDestUnreachs | Counter |  | Количество посланных ICMP сообщений о недоступности пункта назначения.
icmpOutTimeExcds | Counter |  | Количество посланных ICMP сообщений об истечении времени.
icmpOutParmProbs | Counter |  | Количество посланных ICMP сообщений о проблемах с параметром.
icmpOutSrcQuenchs | Counter |  | Количество посланных ICMP сообщений о подавлении источника.
icmpOutRedirects | Counter |  | Количество посланных ICMP сообщений о перенаправлении.
icmpOutEchos | Counter |  | Количество посланных ICMP сообщений с эхо запросом.
icmpOutEchoReps | Counter |  | Количество посланных ICMP сообщений с эхо откликом.
icmpOutTimestamps | Counter |  | Количество посланных ICMP сообщений с запросом временной марки.
icmpOutTimestampReps | Counter |  | Количество посланных ICMP сообщений с откликом временной марки.
icmpOutAddrMasks | Counter |  | Количество посланных ICMP сообщений с запросом маски адреса.
icmpOutAddrMaskReps | Counter |  | Количество посланных ICMP сообщений с откликом маски адреса.

Рисунок 25.26 Простые переменные группы icmp.

Мы можем запросить некоторые из этих переменных для системы sun:

sun % snmpi -a sun

snmpi> get tcpRtoAlgorithm.0 tcpRtoMin.0 tcpRtoMax.0 tcpMaxConn.0
tcpRtoAlgorithm.0=vanj(4)
tcpRtoMin.0=200
tcpRtoMax.0=12800
tcpMaxConn.0=-1

Система SunOS 4.1.3 использует алгоритм тайм-аута и повторной передачи, разработанный Van Jacobson, при этом используемые тайм-ауты находятся в диапазоне от 200 миллисекунд до 12,8 секунд, и не существует фиксированного предела для количества TCP соединений. (Верхняя граница диапазона, составляющая 12,8 секунды, неверна, так как большинство реализаций используют верхний предел в 64 секунды, как мы видели в главе 21.)

Группа tcp имеет одну таблицу, таблицу TCP соединений, показанную на рисунке 25.28. Она содержит по одной строке для каждого соединения. Каждая строка содержит пять переменных: состояние соединения, локальный IP адрес, локальный номер порта, удаленный IP адрес и удаленный номер порта.

Имя | Тип данных | R/W | Описание
tcpRtoAlgorithm | INTEGER |  | Алгоритм, используемый для расчета величин тайм-аутов и повторных передач: 1=нет, 2=постоянный RTO, 3=MIL-STD-1778 (приложение B), 4=алгоритм Van Jacobson.
tcpRtoMin | INTEGER |  | Минимальное значение тайм-аута повторной передачи, в миллисекундах.
tcpRtoMax | INTEGER |  | Максимальное значение тайм-аута повторной передачи, в миллисекундах.
tcpMaxConn | INTEGER |  | Максимальное количество TCP соединений. Значение -1 обозначает, что эта величина определяется динамически.
tcpActiveOpens | Counter |  | Количество переходов от состояния CLOSED к состоянию SYN_SENT.
tcpPassiveOpens | Counter |  | Количество переходов от состояния LISTEN к состоянию SYN_RCVD.
tcpAttemptFails | Counter |  | Количество переходов от состояния SYN_SENT или SYN_RCVD к состоянию CLOSED, плюс количество переходов от состояния SYN_RCVD к состоянию LISTEN.
tcpEstabResets | Counter |  | Количество переходов от состояния ESTABLISHED или CLOSE_WAIT к состоянию CLOSED.
tcpCurrEstab | Gauge |  | Количество соединений, находящихся в настоящее время в состоянии ESTABLISHED или CLOSE_WAIT.
tcpInSegs | Counter |  | Полное количество принятых сегментов.
tcpOutSegs | Counter |  | Полное количество отправленных сегментов, за исключением тех, которые содержали только повторно передаваемые байты.
tcpRetransSegs | Counter |  | Полное количество повторно переданных сегментов.
tcpInErrs | Counter |  | Полное количество сегментов, принятых с ошибками (например, неверная контрольная сумма).
tcpOutRsts | Counter |  | Полное количество сегментов, посланных с установленным флагом RST.

Рисунок 25.27 Простые переменные в группе tcp.

индекс = <tcpConnLocalAddress>.<tcpConnLocalPort>.<tcpConnRemAddress>.<tcpConnRemPort>
Имя | Тип данных | R/W | Описание
tcpConnState | [1..12] | · | Состояние соединения: 1=CLOSED, 2=LISTEN, 3=SYN_SENT, 4=SYN_RCVD, 5=ESTABLISHED, 6=FIN_WAIT_1, 7=FIN_WAIT_2, 8=CLOSE_WAIT, 9=LAST_ACK, 10=CLOSING, 11=TIME_WAIT, 12=удаление TCB. Единственное значение, которое может установить менеджер, это значение 12 (немедленное прекращение соединения).
tcpConnLocalAddress | IpAddress |  | Локальный IP адрес. 0.0.0.0 указывает на то, что слушающий процесс готов принять соединение с любого интерфейса.
tcpConnLocalPort | [0..65535] |  | Локальный номер порта.
tcpConnRemAddress | IpAddress |  | Удаленный IP адрес.
tcpConnRemPort | [0..65535] |  | Удаленный номер порта.

Рисунок 25.28 Таблица TCP соединений: tcpConnTable.

Давайте посмотрим эту таблицу на системе sun. Мы показали только часть таблицы, так как очень много серверов (в данном случае слушающие процессы) слушает запросы на соединения. Перед тем как получить таблицу, было установлено два TCP соединения:

sun % rlogin gemini IP адрес gemini равен 140.252.1.11

и

sun % telnet localhost IP адрес должен быть 127.0.0.1

Единственный слушающий сервер, как мы показали, это FTP сервер на порте 21:

sun % snmpi -a sun dump tcpConnTable

tcpConnState.0.0.0.0.21.0.0.0.0.0=listen(2)
tcpConnState.127.0.0.1.23.127.0.0.1.1415=established(5)
tcpConnState.127.0.0.1.1415.127.0.0.1.23=established(5)
tcpConnState.140.252.1.29.1023.140.252.1.11.513=established(5)

tcpConnLocalAddress.0.0.0.0.21.0.0.0.0.0=0.0.0.0
tcpConnLocalAddress.127.0.0.1.23.127.0.0.1.1415=127.0.0.1
tcpConnLocalAddress.127.0.0.1.1415.127.0.0.1.23=127.0.0.1
tcpConnLocalAddress.140.252.1.29.1023.140.252.1.11.513=140.252.1.29

tcpConnLocalPort.0.0.0.0.21.0.0.0.0.0=21
tcpConnLocalPort.127.0.0.1.23.127.0.0.1.1415=23
tcpConnLocalPort.127.0.0.1.1415.127.0.0.1.23=1415
tcpConnLocalPort.140.252.1.29.1023.140.252.1.11.513=1023

tcpConnRemAddress.0.0.0.0.21.0.0.0.0.0=0.0.0.0
tcpConnRemAddress.127.0.0.1.23.127.0.0.1.1415=127.0.0.1
tcpConnRemAddress.127.0.0.1.1415.127.0.0.1.23=127.0.0.1
tcpConnRemAddress.140.252.1.29.1023.140.252.1.11.513=140.252.1.11

tcpConnRemPort.0.0.0.0.21.0.0.0.0.0=0
tcpConnRemPort.127.0.0.1.23.127.0.0.1.1415=1415
tcpConnRemPort.127.0.0.1.1415.127.0.0.1.23=23
tcpConnRemPort.140.252.1.29.1023.140.252.1.11.513=513

Для команды rlogin на хост gemini присутствует только один пункт, так как gemini это удаленный хост. Мы видим только клиентскую часть соединения (локальный порт 1023), однако показаны оба конца Telnet соединения (порт клиента 1415 и порт сервера 23), так как соединение проходит через loopback интерфейс. Также мы можем видеть, что слушающий FTP сервер имеет локальный IP адрес 0.0.0.0, что указывает на то, что он примет соединение с любого интерфейса.

Дополнительные примеры

Сейчас мы вернемся к некоторым проблемам, которые были рассмотрены раньше в этой книге, и попробуем использовать SNMP, для того чтобы понять, что все-таки произошло.

MTU интерфейса

Вернемся к нашему эксперименту, описанному в разделе "ICMP ошибки о недоступности" главы 11, в котором мы пытались определить MTU SLIP канала от netb к sun. Сейчас мы можем использовать SNMP, чтобы получить этот MTU. Во-первых, мы получим номер интерфейса (ipRouteIfIndex) для SLIP канала (140.252.1.29) из таблицы IP маршрутизации. Для этого, мы попадаем в таблицу интерфейсов и получаем MTU (вместе с описанием и типом) канала SLIP:

sun % snmpi -a netb -c secret

snmpi> get ipRouteIfIndex.140.252.1.29
ipRouteIfIndex.140.252.1.29=12
snmpi> get ifDescr.12 ifType.12 ifMtu.12
ifDescr.12="Telebit NetBlazer dynamic dial virtual interface"
ifType.12=other(1)
ifMtu.12=1500

Мы видим, что даже для канала, который является SLIP, MTU установлено в Ethernet значение равное 1500, возможно для того, чтобы избежать фрагментации.

Таблицы маршрутизации

Давайте обратимся к нашему обсуждению сортировки адресов, которая осуществляется DNS, приведенному в разделе "Простой пример" главы 14. Мы показали, как первый IP адрес, возвращенный DNS сервером, был одним из тех, что принадлежат к той же подсети, что и клиент. Также мы упомянули, что использование другого IP адреса, вполне возможно, будет работать, однако в этом случае все будет менее эффективно. Давайте посмотрим, что произойдет при использовании альтернативного IP адреса. Мы будем использовать SNMP, чтобы посмотреть пункты в таблице маршрутизации, и попробуем объединить вместе несколько концепций, о которых мы рассказывали в предыдущих главах и которые имели отношение к IP маршрутизации.

Хост gemini имеет несколько интерфейсов, два из которых Ethernet интерфейсы. Во-первых, убедимся, что можем получить соединение Telnet на оба адреса:

sun % telnet 140.252.1.11 daytime
Trying 140.252.1.11 ...
Connected to 140.252.1.11.
Escape character is '^]'.
Sat Mar 27 09:37:24 1993
Connection closed by foreign host.

sun % telnet 140.252.3.54 daytime
Trying 140.252.3.54 ...
Connected to 140.252.3.54.
Escape character is '^]'.
Sat Mar 27 09:37:35 1993
Connection closed by foreign host.

Мы видим, что нет никакой разницы в соединениях между двумя адресами. Сейчас воспользуемся traceroute, чтобы посмотреть, используются ли различные маршруты к каждому адресу:

sun % traceroute 140.252.1.11
traceroute to 140.252.1.11 (140.252.1.11), 30 hops max, 40 byte packets
   1 netb (140.252.1.183) 299 ms 234 ms 233 ms
   2 gemini (140.252.1.11) 233 ms 228 ms 234 ms

sun % traceroute 140.252.3.54
traceroute to 140.252.3.54 (140.252.3.54), 30 hops max, 40 byte packets
   1 netb (140.252.1.183) 245 ms 212 ms 234 ms
   2 swnrt (140.252.1.6) 233 ms 229 ms 234 ms
   3 gemini (140.252.3.54) 234 ms 233 ms 234 ms

Мы видим, что при использовании адреса подсети 140.252.3 появляется дополнительная пересылка (маршрутизатор swnrt - это R3 на рисунке 3.6). Давайте посмотрим, почему осуществляется эта дополнительная пересылка.

На рисунке 25.29 показаны настройки систем. На основе вывода команды traceroute мы можем сказать, что хост gemini и маршрутизатор swnrt оба подсоединены к двум сетям: 140.252.1 и 140.252.3.

Рисунок 25.29 Топология систем, которые используются в примере.

Обратитесь к рисунку 4.6, где мы объясняли, как маршрутизатор netb использует уполномоченного агента ARP, для того чтобы создалось впечатление, как будто sun непосредственно подключен к Ethernet 140.252.1. В наших рассуждениях мы не принимаем во внимание модемы, которые включены в SLIP канал между sun и netb, так как это не влияет на наши рассуждения.

На рисунке 25.29 мы показали путь Telnet данных с использованием пунктирных стрелок, в том случае, когда был указан адрес 140.252.3.54. Как мы можем узнать, что возвращающиеся пакеты проходят непосредственно от gemini к netb, а не возвращаются тем же самым путем, по которому они пришли? Воспользуемся версией команды traceroute со свободной маршрутизацией от источника, которая описана в разделе "Опция IP маршрутизации от источника" главы 8:

sun % traceroute -g 140.252.3.54 sun
traceroute to sun (140.252.13.33), 30 hops max, 40 byte packets
   1 netb (140.252.1.183) 244 ms 256 ms 234 ms
   2 * * *
   3 gemini (140.252.3.54) 285 ms 227 ms 234 ms
   4 netb (140.252.1.183) 263 ms 259 ms 294 ms
   5 sun (140.252.13.33) 534 ms 498 ms 504 ms

Когда используется свободная маршрутизация от источника, маршрутизатор swnrt никогда не отвечает. Если посмотреть на ранний вывод команды traceroute, без маршрутизации от источника, мы увидим, что swnrt является второй пересылкой. Причиной этого может являться то, что маршрутизатор не генерирует ICMP ошибки об истечении времени, когда в датаграмме установлена свободная маршрутизация от источника. Мы видим из этого вывода команды traceroute, что путь возврата от gemini (TTL 3, 4 и 5) проходит непосредственно к netb, а не проходит через маршрутизатор swnrt.

Вопрос, на который нам должен помочь ответить SNMP, заключается в том, что делает пункт таблицы маршрутизации в netb таким, что сеть назначения установлена в 140.252.3? Все дело в том, что netb отправляет пакеты к swnrt, а не непосредственно к gemini. Мы используем команду get, чтобы получить значение маршрутизатора следующей пересылки для этого пункта назначения:

sun % snmpi -a netb -c secret get ipRouteNextHop.140.252.3.0

ipRouteNextHop.140.252.3.0=140.252.1.6

Пункт таблицы маршрутизации говорит netb послать пакеты на swnrt, что, как мы видим, и происходит.

Почему gemini отправляет пакеты назад непосредственно через netb? Потому что адрес назначения для пакетов, возвращающихся от gemini, установлен в 140.252.1.29, а сеть 140.252.1 подключена непосредственно.

То, что мы видим в этом примере, является результатом политических решений о маршрутизации. Маршрут по умолчанию к сети 140.252.3 проходит через маршрутизатор swnrt, потому что gemini является просто хостом с несколькими интерфейсами, но не выполняет функции маршрутизатора. Это как раз пример хоста с несколькими интерфейсами, который не хочет быть маршрутизатором.

Ловушки (Traps)

Все примеры, которые мы рассмотрели в этой главе, иллюстрируют передачу информации от менеджера к агенту. Однако у агента существует способ сообщить менеджеру о возникновении какого-либо события, о котором менеджер должен знать (см. рисунок 25.1). В этом случае агент посылает менеджеру ловушки (trap). Ловушки отправляются на UDP порт 162 менеджера.

На рисунке 25.2 мы показали формат ловушки PDU. Мы просмотрим все поля в этом сообщении, когда ниже будем рассматривать вывод команды tcpdump.

Определены шесть ловушек, седьмая используется производителем, чтобы установить собственную ловушку. На рисунке 25.30 описываются значения типа ловушки (trap type) в сообщении ловушки (рисунок 25.2).

тип ловушки | Имя | Описание
0 | coldStart | Агент инициализировал себя сам.
1 | warmStart | Агент повторно инициализировал себя сам.
2 | linkDown | Состояние интерфейса изменилось с "активизировано" на "выключено" (рисунок 25.18). Первая переменная в сообщении указывает на интерфейс.
3 | linkUp | Состояние интерфейса изменилось с состояния "выключено" на состояние "активизировано" (рисунок 25.18). Первая переменная в сообщении указывает на интерфейс.
4 | authenticationFailure | Сообщение было получено от SNMP менеджера из неверного сообщества.
5 | egpNeighborLoss | EGP узел изменил свое состояние на "выключено". Первая переменная в сообщении содержит IP адрес узла.
6 | enterpriseSpecific | Обратитесь к полю специализированных кодов за информацией об этой ловушке.

Рисунок 25.30 Типы ловушек.

Мы можем увидеть некоторые ловушки с помощью tcpdump. Стартуем SNMP агента на системе sun и посмотрим, как он генерирует ловушку coldStart. (Агент знает о необходимости посылать ловушки на хост bsdi. Однако на bsdi не стартован менеджер для обработки ловушек, вместо него запущен tcpdump, что позволяет увидеть генерируемые пакеты. Обратитесь к рисунку 25.1, откуда видно, что ловушки посылаются от агента к менеджеру, однако менеджер не посылает подтверждений, поэтому нет необходимости, чтобы менеджер обрабатывал ловушки.) Затем, с использованием программы snmpi, мы посылаем запрос, в котором указано неверное имя сообщества. В ответ на это должна быть сгенерирована ловушка authenticationFailure. На рисунке 25.31 показан вывод.

1  0.0                  sun.snmp > bsdi.snmp-trap: C=traps Trap(28)
                       E:unix.1.2.5 [140.252.13.33] coldStart 20

2  18.86 (18.86)       sun.snmp > bsdi.snmp-trap: C=traps Trap(29)
                       E:unix.1.2.5 [140.252.13.33] authenticationFailure 1907

Рисунок 25.31 Вывод команды tcpdump, соответствующий генерации ловушек SNMP агентом.

Во-первых, необходимо обратить внимание на то, что обе UDP датаграммы, отправленные от SNMP агента (порт 161, печатается как имя snmp), имеют порт назначения 162 (печатается как имя snmp-trap).

Выражение C=traps это имя сообщества в сообщении ловушки. Это опция конфигурации, которая используется агентом в случае ISODE SNMP.

Следующее выражение, Trap(28) в строке 1 и Trap(29) в строке 2, это тип PDU (PDU type) и длина.

Следующее поле вывода для обеих строк - E:unix.1.2.5. Это enterprise: идентификатор системы объекта (sysObjectID). Он находится под узлом 1.3.6.1.4.1 в дереве, приведенном на рисунке 25.6 (iso.org.dod.internet.private.enterprises), таким образом, идентификатор объекта агента равен 1.3.6.1.4.1.4.1.2.5. Его сокращенное имя unix.agents.fourBSD-isode.5. Последняя цифра (5) это номер версии релиза агента ISODE. Это значение указывает на то, какое программное обеспечение агента сгенерировало ловушку.

Следующее поле вывода команды tcpdump это IP адрес агента (140.252.13.33).

Тип ловушки печатается как coldStart в строке 1 и как authenticationFailure в строке 2. Они соответствуют значениям типа ловушки равным 0 и 4 соответственно (рисунок 25.30). Так как эти ловушки не являются специализированными (enterprise), специфичный код (specific code) должен быть равен 0 и поэтому не печатается.

Далее следует поле временной марки (timestamp), которое печатается как 20 и 1907. Это значение TimeTicks, соответствующее количеству сотых долей секунд с момента инициализации агента. В случае ловушки холодного старта, она генерируется через 200 миллисекунд после того, как агент инициализирован. Вывод tcpdump указывает на то, что вторая ловушка появилась через 18,86 секунды после первой, чему соответствует напечатанное значение 1907 сотых долей секунд минус 200 миллисекунд.

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

ASN.1 и BER

Формальная спецификация SNMP использует абстрактную форму записи (ASN.1 - Abstract Syntax Notation 1), и кодирование бит в SNMP сообщениях (рисунок 25.2) на основе основных правил кодирования (BER - Basic Encoding Rules). В отличие от большинства публикаций, описывающих SNMP, мы специально оставили обсуждение ASN.1 и BER на самый конец. Если рассказать о них в самом начале, читатель может неправильно понять реальное назначение SNMP - управление сетью. В этом разделе мы дадим только краткий обзор этих двух тем. Глава 8 [Rose 1990] описывает ASN.1 и BER более подробно.

ASN.1 это формальный язык, который описывает данные и характеристики данных. Он не определяет то, как эти данные хранятся или кодируются. Все поля в MIB и SNMP сообщениях описываются с использованием ASN.1. Например, ASN.1 определение типа данных IpAddress из SMI выглядит следующим образом:

IpAddress ::=
     [APPLICATION 0] -- in network-byte order
          IMPLICIT OCTET STRING (SIZE(4))

Точно так же, в MIB мы находим следующее определение простых переменных:

udpNoPorts OBJECT-TYPE
                   SYNTAX Counter
                   ACCESS read-only
                   STATUS mandatory
                   DESCRIPTION
                         "The total number of received UDP datagrams for which there
                          was no application at the destination port."
                   ::= { udp 2 }

Определение таблиц, использующих SEQUENCE и SEQUENCE OF, более сложное.

С использованием подобных ASN.1 определений существует множество способов закодировать данные в поток битов при передаче. SNMP использует BER. Для представления маленьких целых чисел, таких как 64, с использованием BER требуется 3 байта. Один байт содержит значение целого, следующий байт говорит, сколько байтов используется, чтобы хранить целое (1), и последний байт содержит двоичное значение.

К счастью, подробности ASN.1 и BER важны только для разработчиков SNMP. Они не обязательны для понимания того, как осуществляется управление сетью.

SNMP версии 2 (SNMP Version 2)

В течение 1993 года было опубликовано 11 RFC, которые определяли новые стандарты SNMP. Первый из них, RFC 1441 [Case et al. 1993], является введением в SNMP версии 2 (SNMPv2). Две книги также описывают SNMPv2 [Stallings 1993; Rose 1994]. В настоящее время существуют две доступные реализации (см. приложение В.3 публикации [Rose 1994]), однако коммерческие реализации, возможно, не будут широко доступны до 1994 года.

В этом разделе мы опишем основные отличия SNMPv1 от SNMPv2.

Новый тип пакетов get-bulk-request позволяет менеджеру эффективно обрабатывать большие блоки данных. Еще один новый тип пакетов inform-request позволяет одному менеджеру посылать информацию другому менеджеру. Определены два новых MIB: MIB SNMPv2 и MIB SNMPv2-M2M (менеджер-менеджер). SNMPv2 предоставляет улучшенную секретность по сравнению с SNMPv1. В SNMPv1 имя сообщества передается от менеджера к агенту в виде открытого пароля. SNMPv2 предоставляет аутентификацию и расширенную секретность.

Все производители начинают реализовывать агентов, совместимых с SNMPv2, а также появляются управляющие станции, которые могут использовать оба стандарта. [Routhier 1993] описывает расширения и улучшения SNMPv1, которые призваны обеспечить совместимость и поддержку SNMPv2.

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

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

Все переменные идентифицируются идентификаторами объекта, используется иерархическая схема присвоения имен, которая состоит из длинных строк чисел, которые обычно сокращаются в простые имена, для простоты чтения. Конкретные переменные строятся с помощью добавления переменной к идентификатору объекта.

Большинство SNMP переменных содержится в таблицах, с фиксированными номерами колонок, однако с переменными номерами строк. Фундаментальным для SNMP является схема идентификации, используемая для того, чтобы идентифицировать каждую строку в таблице (в том случае, когда мы не знаем, сколько строк содержится в таблице), и лексикографический порядок (порядок колонка-строка). SNMP оператор get-next является основным для любого SNMP менеджера.

Мы описали следующие группы SNMP переменных: система, интерфейс, трансляция адресов, IP, ICMP, TCP и UDP. Затем показали два примера, один из которых позволил определить MTU интерфейса, а другой - просмотреть таблицу маршрутизации маршрутизатора.

Мы завершили главу рассмотрением SNMP ловушек. Это способ, предоставляющий агенту возможность уведомить менеджера о том, что произошло какое-либо событие, которое будет интересно менеджеру. Здесь же кратко описаны ASN.1 и BER. Две последние темы, скорее всего, являются наиболее сложными аспектами SNMP, однако они необходимы только для разработчиков.

Упражнения

  1. Мы сказали, что использование двух различных портов (161 и 162) позволяет системе запускать как менеджера, так и агента. Что произойдет, если будет использоваться один и тот же порт?
  2. Как Вы можете получить полный список таблицы маршрутизации с использованием get-next?


Telnet и Rlogin: удаленный терминал

Глава 26 Telnet и Rlogin: удаленный терминал

Введение

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

В сетях TCP/IP существуют два приложения, позволяющие осуществить терминальный заход.

  1. Telnet - стандартное приложение, которое присутствует практически в каждой реализации TCP/IP. Оно может быть использовано для связи между хостами, работающими пол управлением различных операционных систем. Telnet использует согласование опций клиента и сервера, чтобы определить, какие характеристики присутствуют с той и с другой стороны.
  2. Программа Rlogin происходит из Berkeley Unix и была разработана для того, чтобы работать только между Unix системами, однако впоследствии эта программа была перенесена и на другие операционные системы.

В этой главе рассматривается и Telnet, и Rlogin. Мы начнем с Rlogin, потому что это приложение работает проще.

Telnet это одно из старейших приложений Internet. Оно появилось в 1969 году в ARPANET. Имя этого приложения является сокращением от "сетевой коммуникационный протокол" (telecommmunications network protocol).

Приложения удаленного терминала используют стандарт клиент-сервер. На рисунке 26.1 показано типичное общение Telnet клиента и Telnet сервера. (Точно такой же рисунок можно нарисовать для Rlogin клиента и Rlogin сервера.)

Рисунок 26.1 Описание работы Telnet клиент-сервер.

Существует несколько моментов, на которые необходимо обратить внимание на этом рисунке.

  • Клиент Telnet взаимодействует и с пользователем, находящимся за терминалом, и с протоколами TCP/IP. Обычно, все что пользователь вводит с клавиатуры отправляется по TCP соединению, а все что приходит по соединению попадает на терминал.
  • Сервер Telnet обычно взаимодействует с так называемыми псевдотерминальными устройствами (pseudo-terminal), по крайней мере в Unix системах. Это делает его похожим на командный интерпритатор (shell), который запускается на сервере, или на любую программу, которая запускается из shellа, так как именно они общаются с терминальными устройствами. Некоторые приложения, например, полноэкранные редакторы, считают, что они общаются с терминальным устройством. И действительно, заставить shell на сервере считать, что он общается с терминалом, является наиболее сложным аспектом в программировании сервера удаленного терминала.
  • Устанавливается только одно TCP соединение. Так как существуют моменты, когда Telnet клиент должен общаться с Telnet сервером (и наоборот), должны существовать способы, позволяющие отделить команды, которые посылаются по соединению, от пользовательских данных. Ниже мы рассмотрим, как Telnet и Rlogin справляются с этой задачей.
  • На рисунке 26.1, квадратиками нарисованными пунктирными линиями, показаны терминал и псевдотерминальные драйверы, вместе с реализацией TCP/IP, которые обычно являются частью ядра операционной системы. Однако, Telnet клиент и сервер - это, как правило, пользовательские приложения.
  • На хосте сервера, показан командный интерпретатор, который запускается при входе пользователя в систему (login shell). Это необходимо для получения терминального захода на сервер. Необходимо иметь открытый бюджет на этой системе, чтобы получить терминальный заход, с использованием Telnet или Rlogin.

Интересно будет сравнить сложность Telnet и Rlogin, основываясь на количестве строк в исходных текстах программ, клиента и сервера каждого приложения. На рисунке 26.2 показаны размеры стандартных Telnet и Rlogin клиентов и серверов, так, как они распространяются в различных версиях Berkeley (рисунок 1.10).

Рисунок 26.2 Сравнение размеров исходных текстов Telnet/Rlogin/клиент/сервер.

Надо отметить, что из-за появления новых опций, реализация Telnet постоянно растет, тогда как Rlogin остается простым и стабильным.

При заходе удаленным терминалом не предусматривается передача большого количества данных. Как мы уже упоминали ранее, по соединению между двумя конечными системами обычно передается очень много маленьких пакетов. В соответствии с [Paxson 1993] соотношение байт, отправленных клиентам (пользовательский ввод с терминала), к количеству байт, отправленных назад серверам, составляет примерно 1:20. Такое соотношение получается из-за того, что мы вводим короткие команды, которые, в свою очередь, могут генерировать очень большой вывод.

Протокол Rlogin

Rlogin появился в 4.2BSD и был предназначен для захода удаленным терминалом между Unix хостами. Поэтому Rlogin проще, чем Telnet, так как он не требует определение параметров, которые для одной и той же операционной системы известны заранее и для клиента, и для сервера. Через несколько лет Rlogin был перенесен на не-Unix системы.

RFC 1282 [Kantor 1991] содержит спецификацию протокола Rlogin. Однако, как и в случае с RFC посвященным RIP (Routing Information Protocol), он был написан после того, как Rlogin уже использовался в течении нескольких лет. Глава 15 [Stevens 1990] описывает программирование клиента и сервера удаленного терминала и предоставляет полные исходные тексты Rlogin клиента и сервера. Главы 25 и 26 [Comer and Stevens 1993] сдержат подробности разработки и исходные тексты Telnet клиента.

Запуск приложения

Rlogin использует одно TCP соединение между клиентом и сервером. После того как TCP соединение установлено, между клиентом и сервером осуществляется следующая последовательность действий.

  1. Клиент отправляет серверу четыре строки: (a) нулевой байт, (b) имя пользователя на хосте клиента, заканчивающееся нулевым байтом, (с) имя пользователя на хосте сервера, заканчивающееся нулевым байтом, (d) тип терминала пользователя, за которым следует слэш (/), затем следует скорость терминала, и все это заканчивается нулевым байтом. Необходимо отправить именно два имени пользователя, потому что пользователи не обязаны иметь одинаковые имена на разных хостах.

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

  2. Сервер отвечает нулевым байтом.
  3. У сервера есть опция, с помощью которой он просит ввести пароль. Это осуществляется как обычный обмен данными по Rlogin соединению - специальные протоколы не применяются. Сервер отправляет клиенту строку (которую клиент отображает на терминале), чаще всего эта строка выглядит как Password:. Если клиент не вводит пароль в течение определенного времени (обычно 60 секунд), сервер закрывает соединение. В домашней директории на сервере можно создать файл (который называется .rhosts) в котором будут содержаться имя хоста и имя пользователя. Если зайти терминалом с указанного хоста с указанным именем пользователя, то не выдается приглашение ввести пароль. В некоторых публикациях, посвященных безопасности компьютерных систем, как, например, [Curry 1992], очень не рекомендуют пользоваться этой характеристикой, потому что в этом случае в секретности появляется порядочная брешь. Все что вводится в ответ на приглашение сервера ввести пароль, передается в виде открытого текста. Символы введенного пароля посылаются так, как они есть. Каждый, кто может прочитать пакеты в сети, может прочитать любой пароль. Последние реализации Rlogin клиента, например в 4.4BSD, используют Kerberos для шифрации при передаче по сети. Естественно требуются, чтобы и сервер поддерживал Kerberos. ([Curry 1992] описывает Kerberos более подробно.)
  4. Сервер обычно отправляет запрос клиенту, спрашивая размер окна терминала (это будет описано позже).

Клиент посылает за один раз серверу 1 байт, каждый байт сервер отражает эхо-откликом. Мы это видели в разделе "Интерактивный ввод" главы 19. Обычно используется алгоритм Нагла (глава 19, раздел "Алгоритм Нагла"), поэтому несколько входных байтов отправляются по медленным сетям как один TCP сегмент. Функционально все довольно просто: то, что вводит пользователь, отправляется на сервер, а то, что сервер отправляет клиенту, отображается на терминале.

Существуют команды, которые могут быть отправлены от клиента к серверу и от сервера к клиенту. Давайте, во-первых, опишем сценарий, по которому работают эти команды.

Управление потоком

По умолчанию управление потоком обычно осуществляет Rlogin клиент. Клиент распознает ASCII символы STOP и START (Control-S и Control-Q), которые вводятся пользователем, и останавливает или стартует вывод на терминал.

Если это не сделано, каждый раз, когда мы вводим Control-S, чтобы остановить вывод на терминал, символ Control-S отправляется по сети к серверу, и сервер прекращает писать в сеть, однако данные (размер данных может достигать размера окна) могут быть уже выданы сервером в сеть и будут отображены на терминале, перед тем как вывод будет остановлен. Сотни или тысячи байт данных могут прокрутиться на экране, перед тем как вывод будет остановлен. На рисунке 26.3 показан подобный сценарий.

Рисунок 26.3 Функционирование Rlogin соединения в случае, если сервер поддерживает обмен STOP/START.

Для интерактивных пользователей подобная задержка отклика на ввод символа Control-S нежелательна.

Однако, иногда приложения, запущенные на сервере, должны интерпретировать каждый байт ввода, и они не хотят, чтобы клиент использовал символы Control-S и Control-Q каким-то особенным образом. (Редактор emacs является примером приложения, которое использует эти два символа как свои собственные команды.) В подобном случае сервер может сообщить клиенту, поддерживает ли он контроль потока данных или нет.

Прерывание от клиента

Проблема, напоминающая управление потоком данных, возникает, когда пользователь вводит символ прерывания (обычно DELETE или Control-C), чтобы прекратить процесс, запущенный на сервере. Сценарий подобен тому, который мы показали на рисунке 26.3. В этом случае также одно полное окно данных в канале между сервером и клиентом будет передано клиенту, до тех пор пока символ прерывания проделает свой путь по соединению в другом направлении. Мы хотим, чтобы символ прерывания остановил или прервал вывод данных на экран так быстро, как это возможно.

Достаточно редко поток данных от клиента к серверу может быть остановлен контролем потока данных. В этом направлении передаются только вводимые с клавитуры символы. Поэтому нет необходимости отправлять эти специальные символы (Control-S или прерывание) от клиента к серверу с использованием режима срочности TCP.

Изменения размера окна

Если существует возможность поделить дисплей на окна, мы можем динамически менять размер окна, в процессе работы приложения. Некоторые приложения (обычно те, которые манипулируют с целыми окнами, такие как полноэкранные редакторы) должны знать об этих изменениях. Большинство Unix систем каким-либо образом сообщают приложению об изменении размера окна.

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

Команды от сервера к клиенту

Давайте кратко рассмотрим четыре команды, которые сервер Rlogin может отправить клиенту по TCP соединению. Проблема заключается в том, что используется одно TCP соединение, поэтому сервер должен пометить байты команд так, чтобы клиент интерпретировал их именно как команды, а не отображал эти байты на терминале. Для этого используется режим срочности TCP (глава 20, раздел "Режим срочности (Urgent Mode)").

Когда сервер отправляет команду клиенту, он входит в режим срочности, при этом последний байт срочных данных это и есть байт команды от сервера. Когда клиент получает уведомление о режиме срочности, он читает из соединения и сохраняет данные до тех пор, пока не будет получен байт с командой (последний байт срочных данных). Данные, которые сохранил клиент, могут быть выданы на терминал или проигнорированы, в зависимости от команды. На рисунке 26.4 описываются четыре командных байта.

Байт | Описание
0х02 | Сбросить вывод. Клиент отбрасывает все данные, принятые от сервера, до командного байта (последний байт срочных данных). Клиент также отбрасывает все данные, сбуферизированные и предназначенные для вывода на терминал. Сервер посылает эту команду, когда пользователь ввел символ прерывания.
0х10 | Клиент прекращает контролировать поток данных.
0х20 | Клиент возобновляет контроль потока данных.
0х80 | Клиент немедленно отвечает отправкой серверу текущего размера окна и уведомляет сервер, если в будущем размер окна изменится. Сервер обычно отправляет эта команду немедленно после установления соединения.

Рисунок 26.4 Команды Rlogin, передаваемые от сервера клиенту.

Одна из причин, по которой описанные выше команды посылаются с использованием режима срочности TCP, заключается в том, что первая команда (flush output) должна быть отправлена клиенту, даже если поток данных от сервера к клиенту остановлен контролем потока данных TCP. Ситуация когда передача информации от сервера к клиенту остановлена управлением потоком данных может возникнуть, так как процессы, запущенные на сервере, обычно генерируют вывод быстрее, чем этот он может быть отображен на терминале клиента. Однако, достаточно редко может возникнуть ситуация, когда передача информациии от клиента к серверу будет остановлена управлением потоком данных, так как в этом направлении поток данных содержит только символы, вводимые пользователем.

Обратитесь к рисунку 20.14, где показано, что уведомление о срочности проходит по соединению, даже если размер окна был установлен в 0. (Мы увидим еще один пример этого в следующем разделе.) Оставшиеся три команды не критичны по времени, однако они используют ту же технику для упрощения реализации.

Команды от клиента к серверу

В настоящее время определена только одна команда, передаваемая от клиента к серверу: это отправка серверу текущего размера окна. Изменение размера окна не отправляется серверу пока клиент не получит от сервера команду 0x80 (рисунок 26.4).

И снова, так как используется одно TCP соединение, клиент должен иметь возможность пометить свои команды, чтобы они не были переданы приложению, запущенному на сервере. Клиент помечает команды с помощью отправки 2-х байт равных 0xff, за которыми следуют два специальных флаговых байта.

Для команды, управляющей размером окна, два флаговых байта это ASCII символы s. Затем следуют четыре 16-битных значения (в порядке сетевых байтов): количество символов в строке (обычно 25), количество символов в столбце (обычно 80), количество пикселей по оси X и количество пикселей по оси Y. Часто два последних 16-битных значения равны 0, так как большинство приложений, запускаемых Rlogin сервером, определяют размер экрана в символах, а не в пикселях.

Подобная форма представления команд называется командами в полосе (in-band signaling), так как командные байты передаются в обычном потоке данных. Байты, выделяющие команды из потока данных (0xff) выбраны таким образом, нажатие какой-либо клавиши не могло сгенерировать подобные байты. Естественно у подобной формы представления команд есть свои недостатки. Если мы сможем сгенерировать два последовательных байта равных 0xff с клавиатуры, за которыми будут следовать два ASCII символа s, следующие 8 введенных байт будут восприняты как размеры окна.

Команды Rlogin от сервера к клиенту, показанные на рисунке 26.4, называются командами, выходящими за полосу (out-of-band signaling), именно так эта техника называется в большинстве API. Обратитесь к режиму срочности TCP (глава 20, раздел "Режим срочности (Urgent Mode)"). Режим срочности не имеет отношения к данным, выходящим за полосу, так как командные байты передаются в обычном потоке данных, при этом их местоположение в потоке данных определяется с использованием указателя срочности.

Так как команды в полосе (in-band signaling) передаются от клиента к серверу, сервер должен просматривать каждый байт, принятый от клиента, в поисках двух последовательных байтов 0xff. В случае команд выходящих за полосу (out-of-band signaling) , которые передаются от сервера к клиенту, клиенту нет необходимости просматривать данные, которые он получает от сервера, до тех пор пока сервер не перейдет в режим срочности. Даже в режиме срочности клиенту необходимо только просмотреть байт, на который указывает указатель срочности. Так как соотношение количества байт, передаваемых в двух направлениях (от клиента к серверу и от сервера к клиенту), составляет примерно 1:20, то возникает необходимость использовать команды в полосе (in-band signaling) для небольшого потока данных (от клиента к серверу), и команды, выходящие за полосу (out-of-band signaling), для более загруженного потока данных (от сервера к клиенту).

Способы прекращения работы клиента

Обычно, все, что вводит пользователь Rlogin, отправляется на сервер. Однако иногда возникает необходимость пообщаться непосредственно с программой клиента Rlogin. При этом серверу отправлять ничего не нужно. Это делается путем ввода символа тильда (~) в первой позиции строки, за которым может следовать один из следующих четырех символов:

  1. Точка прекращает работу клиента.
  2. Символ конца файла (обычно Control-D) прекращает работу клиента.
  3. Символ подавления (в управлении задачами обычно Control-Z) приостанавливает работу клиента.
  4. Символ задержанного подавления (в управлении задачами обычно Control-Y) задерживает только ввод клиента. Все вводимое с клавиатуры интерпретируется программой, запущенной на хосте клиента, однако все отправленное Rlogin сервером клиенту появляется на терминале. Это может быть использовано, когда на сервере запущено задание, которое займет много времени, и необходимо знать, когда и что оно выдаст, однако на клиенте необходимо запустить другую программу.

Две последние команды поддерживаются, только если клиент является Unix системой, которая поддерживает управление работами.

Примеры Rlogin

Мы рассмотрим два примера: первый показывает протокол клиент-сервер в начале Rlogin сессии, а второй показывает, что произойдет при вводе клавиши прерывания, для того чтобы остановить процесс, работающий на сервере и генерирующий много вывода. На рисунке 19.2 показан обычный поток данных по Rlogin сессии.

Исходный протокол клиент-сервер

На рисунке 26.5 показана временная диаграмма для Rlogin соединения от хоста bsdi к серверу svr4. (Мы удалили все связанное с установлением TCP соединения, объявлением окна, а также информацию о типе сервисов.)

Протокол, описанный в предыдущих разделах, можно увидеть в сегментах 1-9. Клиент отправляет один нулевой байт (сегмент 1), за которым следуют три строки (сегмент 3). В этом примере три строки это: rstevens (имя клиента), rstevens (имя на сервере) и ibmpc3/9600 (тип терминала и скорость). Сервер проверяет полномочность этой информации и отвечает нулевым байтом (сегмент 5).

Затем сервер отправляет команду с запросом окна (сегмент 7). Эта команда отправляется с использованием режима срочности TCP. Здесь мы видим реализацию (SVR4), которая использует старую, однако более распространенную интерпретацию, когда указатель срочности указывает на номер последовательности последнего байта срочных данных плюс один. Клиент отвечает 12 байтами данных: 2 байта 0xff, 2 байта s и четыре 16-битных значения.

Следующие четыре сегмента от сервера (10, 12, 14 и 16) это приветствие операционной системы. За ними следует 7-байтное приглашение от shellа: "svr4%" (сегмент 18).

Данные, которые вводит клиент, отправляются по 1 байту за один раз, как показано на рисунке 19.2. Соединение может быть закрыто с любой стороны. Если мы введем команду, которая заставит shell, запущенный на сервере, прекратить свою работу, сервер со своей стороны осуществит активное закрытие. Если мы введем escape последовательность Rlogin клиенту (обычно тильда), за которой следует точка или символ конца файла, клиент осуществит активное закрытие.

Номер порта клиента на рисунке 26.5 равен 1023, это значение находится внутри диапазона, который управляется IANA (глава 1, раздел "Номера портов"). Протокол Rlogin требует, чтобы клиент имел номер порта меньше чем 1024, эти порты называются зарезервированные порты. В Unix системах клиент не может получить зарезервированный порт, если только процесс не имеет привилегии суперпользователя. Это является частью определения полномочий между клиентом и сервером, что позволяет пользователю получить доступ к системе, не вводя пароль. [Stevens 1990] обсуждает эти зарезервированные порты и полномочия, используемые между клиентом и сервером, более подробно.

Рисунок 26.5 Временная диаграмма Rlogin соединения.

Кнопка прерывания клиента

Давайте рассмотрим еще один пример, в котором TCP использует режим срочности, когда поток данных остановлен с помощью символа прерывания. В этом примере сведены вместе несколько алгоритмов TCP, которые мы обсуждали ранее: режим срочности, предотвращение "глупого" окна, управление потоком данных с помощью окон и устойчивого таймера. Мы стартовали клиента на хосте sun. Затем зашли терминалом на bsdi, вывели на терминал большой текстовый файл, а затем остановили вывод, нажав Control-S. Когда вывод остановлен, мы нажали клавишу прерывания (DELETE), чтобы завершить работу программы:

sun % rlogin bsdi
                                             приветствие от операционной системы
bsdi % cat /usr/share/misc/termcap            вывод большого файла на терминал
                                             большой вывод на терминал
                                             мы печатаем Control-S, чтобы остановить вывод,
                                             и ожидаем до тех пор, пока вывод не остановится

^?                                           вводим символ прерывания, он отображается эхом
bsdi %                                        затем появляется приглашение

Ниже описаны состояния клиента, сервера и соединения.

  1. Вывод на терминал остановлен путем ввода Control-S.
  2. Rlogin клиент приостановил вывод на терминал, однако выходной буфер терминала будет заполнен.
  3. Rlogin клиент не может читать из сети, так как приемный буфер TCP клиента полон.
  4. TCP клиент объявляет размер окна равный 0, чтобы остановить отправителя (TCP модуль Rlogin сервера), когда приемный буфер полон.
  5. TCP модуль сервера отправляет сообщение о заполнении буфера, когда его вывод остановлен сообщением от клиента о том, что размер окна равен 0.
  6. Сервер Rlogin остановлен, так как отправляющий буфер полон. Таким образом, сервер Rlogin не может читать данные от приложения, которое запущено на сервере (cat).
  7. Приложение cat останавливается, когда его выходной буфер наполняется.
  8. Затем мы вводим символ прерывания, чтобы остановить работу приложения cat на сервере. Это отправляется от TCP клиента к TCP серверу, потому что поток данных в этом направлении не остановлен с помощью контроля потока данных.
  9. Приложение cat получает символ прерывания и прекращает свою работу. При этом его выходной буфер (из которого читал сервер Rlogin) очищается, после чего "просыпается" сервер Rlogin. Сервер Rlogin входит в режим срочности и посылает клиенту команду "очистить вывод" (0x02).

На рисунке 26.6 показан поток данных от сервера к клиенту. (Номера последовательности взяты из временной диаграммы, которую мы покажем чуть ниже.)

Рисунок 26.6 Поток данных от сервера к клиенту в примере Rlogin.

Затемненная часть отправляющего буфера это неиспользуемая часть буфера размером в 4096 байт. На рисунке 26.7 приведена временная диаграмма для этого примера.

В сегментах 1-3 сервер отправляет клиенту сегменты полного размера. Подтверждение (ACK) в сегменте 4 всего лишь объявляет окно равное 1024, потому что вывод остановлен: так как клиент не может писать на терминал, он не может читать из сети. Сегмент 5 не полного размера, а ACK в сегменте 6 объявляет только оставшееся пространство в приемном буфере, размер премного буфера составляет 4096 байт. Клиент должен объявить окно размером 349 байт, потому что если он объявит окно равное 0 (как мы ожидаем, основываясь на алгоритме избежания "глупого окна", глава 22, раздел "Синдром "глупого" окна"), это приведет к тому, что правая граница окна сдвинется влево, что не должно произойти (глава 20, раздел "Изменение размера окна"). Так как сервер не может отправить буфер полного размера, когда он принимает сегмент 6, он прибегает к алгоритму предотвращения "глупого окна", не посылая ничего, при этом устанавливается устойчивый таймер (5 секунд). Когда таймер истекает, отправляется 349 байт (сегмент 7), и так как вывод клиента все еще остановлен, подтверждение в сегменте 8 объявляет окно равное 0.

В этот момент мы вводим символ прерывания, который передается в сегменте 9. Все еще объявлено окно равное 0. Когда Rlogin сервер получает символ прерывания, он передает его приложению (cat), и приложение прекращает свою работу. Так как приложение было остановлено вводом символа прерывания с терминала, его вывод сбрасывается и это передается серверу Rlogin. Это заставляет сервер посылать команду "очистить вывод" клиенту с использованием режима срочности TCP. Мы видим это в сегменте 10. Обратите внимание, что командный байт 0x02 имеет номер последовательности 30146 (указатель срочности минус один). Перед командным байтом находится 3419 байт (номера последовательности 26727:30145), они буферизированы сервером и сервер собирается их отправить.

Сегмент 10, с уведомлением срочности, содержит следующий байт данных, который передается от сервера клиенту (номер последовательности 26727). Он не содержит командный байт "очистить вывод". Сервер может послать этот единственный байт в сегменте 10 (глава 22, раздел "Пример"), так как отправитель всегда может проверить, закрыто ли окно, отправив 1 байт данных. TCP модуль клиента немедленно отвечает сегментом 11 с нулевым окном, однако прием уведомления о срочности в сегменте 10 заставляет TCP модуль клиента уведомить Rlogin клиента, что удаленный конец соединения вошел в режим срочности.

Рисунок 26.7 Пример Rlogin, когда клиент останавливает вывод и затем прерывает работу программы на сервере.

Как только Rlogin клиент получает уведомление о срочности от своего TCP, он начинает считывание данных, которые уже ожидают этого, окно открывается (сегмент 13). Данные, буферизированные сервером, отправляются (сегменты 14, 15, 17 и 18). Последний сегмент содержит последний байт срочных данных (номер последовательности 30146), который содержит командный байт, передаваемый от сервера клиенту. Когда клиент считывает этот байт, он отбрасывает все данные, которые он прочитал в сегментах 14, 15, 17 и 18, и очищает свою выходную очередь на терминал. Следующие 2 байта в сегменте 19 содержат эхо символа прерывания: "^?". Последний сегмент, который мы показали (21), содержит приглашение shellа от клиента.

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

Протокол Telnet

Telnet был разработан, для того чтобы работать между хостами работающими под управлениием любых операционных систем, а также с любыми терминалами. Его спецификация, приведенная в RFC 854 [Postel and Reynolds 1983a], определяет терминал, который может являться наиболее общим, и который называется виртуальным сетевым терминалом (NVT - network virtual terminal). NVT это воображаемое устройство, находящееся на обоих концах соединения, у клиента и сервера, с помощью которого устанавливается соответствие между их реальными терминалами. Таким образом, операционная система клиента должна определять соответствие между тем типом терминала, за которым работает пользователь, с NVT. В свою очередь, сервер должен устанавливать соответствие между NVT и теми типами терминалов, которые он (сервер) поддерживает.

NVT это символьное устройство с клавиатурой и принтером. Данные, введенные пользователем с клавиатуры, отправляются серверу, а данные, полученные от сервера, поступают на принтер. По умолчанию клиент отражает эхом на принтер все, что ввел пользователь, однако, ниже мы увидим что, существуют опции, которые позволяют изменить подобное поведение.

NVT ASCII

Термин NVT ASCII означает 7-битный вариант U.S. ASCII набора символов, который используется в семействе протоколов Internet. Каждый 7-битный символ отправляется как 8-битный байт со старшим битом установленным в 0.

Конец строки передается как двухсимвольная последовательность - CR (возврат каретки - carriage return), затем следует LF (пропуск строки - linefeed). Мы показываем это как \r\n. Возврат каретки передается как двухсимвольная последовательность CR, за которой следует NUL (нулевой байт). Мы показываем это как \r\0.

В следующих главах мы увидим, что FTP, SMTP, Finger и Whois используют NVT ASCII для ввода команд клиента и откликов сервера.

Команды Telnet

Telnet использует команды в полосе (in-band signaling) в обоих направлениях. Байт 0xff (255 десятичный) называется IAC, "интерпретировать как команду". Следующий байт является командным байтом. Для того чтобы послать байт данных равный 255, отправляются два последовательных байта равных 255. (В предыдущем параграфе мы сказали, что поток данных имеет формат NVT ASCII, то есть используются 7-битные значения, а это означает, что байт данных равный 255 не может быть отправлен посредством Telnet. Существует двоичная опция Telnet, описанная в RFC 856 [Postel and Reynolds 1983b], которая, позволяет передавать 8-битные данные.) На рисунке 26.8 приведены команды Telnet.

Имя | Код (десятичный) | Описание
EOF | 236 | конец файла
SUSP | 237 | подавить текущий процесс (управление задачами)
ABORT | 238 | прекратить процесс
EOR | 239 | конец записи
SE | 240 | конец подопции
NOP | 241 | пустая операция
DM | 242 | маркер данных
BRK | 243 | прерывание
IP | 244 | прервать процесс
AO | 245 | прекратить вывод
AYT | 246 | вы здесь?
EC | 247 | escape символ
EL | 248 | стереть строку
GA | 249 | идем дальше
SB | 250 | начало подопции
WILL | 251 | обсуждение опции (рисунок 26.9)
WONT | 252 | обсуждение опции
DO | 253 | обсуждение опции
DONT | 254 | обсуждение опции
IAC | 255 | байт данных 255

Рисунок 26.8 Команды Telnet, предваряемые IAC (255).

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

Обсуждение опций

Несмотря на то, что при начале работы Telnet подразумевается, что на каждом конце находится NVT, первый обмен данными, который происходит по Telnet соединению, являет собой обсуждение опций. Обсуждение опций это симметричный процесс - каждая сторона может послать запрос другой.

Каждая сторона может послать один из четырех различных запросов для любой заданной опции.

  1. WILL. Отправитель хочет включить эту опцию для себя.
  2. DO. Отправитель хочет, чтобы получатель включил эту опцию.
  3. WONT. Отправитель хочет выключить эту опцию для себя.
  4. DONT. Отправитель хочет, чтобы получатель выключил опцию.

Так как правила Telnet позволяют стороне принять или отклонить запрос на включение опции (случаи 1 и 2), однако требуют, чтобы она всегда удовлетворяла запрос на выключение опции (случаи 3 и 4), из этих четырех возможных случаев может получиться шесть комбинаций, которые приведены на рисунке 26.9.

 | Отправитель |  | Получатель | Описание
1. | WILL | ® ¬ | DO | отправитель хочет включить опцию получатель говорит ДА
2. | WILL | ® ¬ | DONT | отправитель хочет включить опцию получатель говорит НЕТ
3. | DO | ® ¬ | WILL | отправитель хочет, чтобы получатель включил опцию получатель говорит ДА
4. | DO | ® ¬ | WONT | отправитель хочет, чтобы получатель включил опцию получатель говорит НЕТ
5. | WONT | ® ¬ | DONT | отправитель хочет выключить опцию получатель должен сказать ДА
6. | DONT | ® ¬ | WONT | отправитель хочет, чтобы получатель выключил опцию получатель должен сказать ДА

Рисунок 26.9 Шесть сценариев обсуждения опции Telnet.

Обсуждение опции занимает 3 байта: IAC байт, за которым следует байт WILL, DO, WONT или DONT, затем ID байт, указывающий на ту опцию, которую необходимо включить или выключить. В настоящее время, таким образом, может быть обсуждено 40 опций. Assigned Numbers RFC содержат значения для байт опций, а соответствующее RFC описывает сами опции. На рисунке 26.10 показаны коды опций, которые мы увидим в этой главе.

ID опции (десятичный) | Имя | RFC
1 | эхо | 857
3 | запрещение команды go ahead | 858
5 | статус | 859
6 | маркер времени | 860
24 | тип терминала | 1091
31 | размер окна | 1073
32 | скорость терминала | 1079
33 | удаленный контроль потоком данных | 1372
34 | линейный режим (linemode) | 1184
36 | переменные окружения | 1408

Рисунок 26.10 Коды опций Telnet, обсуждаемые в этой главе.

Обсуждение опции Telnet, как и многое другое в протоколе Telnet, процесс симметричный. Каждая сторона может начать процесс обсуждения опции. Однако заход удаленным терминалом не является симметричным процессом. Клиент решает свои задачи, а сервер свои. Мы увидим, что некоторые опции Telnet применимы только к клиенту (например, требование включить линейный режим (linemode)), а некоторые предназначены только для сервера.

Обсуждение подопций

Некоторые опции требуют большего количества информации, нежели просто "включить" (enable) или "выключить" (disable). Например, установка типа терминала: для того чтобы клиент мог идентифицировать тип терминала, он должен отправить ASCII строку. Чтобы обработать эти опции, применяется обсуждение подопций.

RFC 1091 [VanBokkelen 1989] определяет подопции, которые необходимо обсудить для установки типа терминала. Во-первых, одна сторона (обычно клиент) просит включить опцию, отправляя 3-байтовую последовательность

<IAC, WILL, 24>

где 24 (десятичное) это идентификатор опции типа терминала. Если получатель (сервер) говорит ДА, его ответ будет выглядеть как

<IAC, DO, 24>

Затем сервер посылает

<IAC, SB, 24, 1, IAC, SE>

спрашивая о типе терминала клиента. SB это команда, которая сообщает о начале подопций (suboption-begin). Следующий байт равный 24 указывает на то, что это подопция типа терминала. (SB всегда следует за номером опции, к которой относятся подопции.) Следующий байт равный 1 означает "отправьте ваш тип терминала". Перед командой конец подопций (suboption-end) должен опять стоять IAC, так же как и перед командой SB. Клиент отвечает командой

<IAC, SB, 24, 0, 'I', 'B', 'M', 'P', 'C', IAC, SE>

в случае, если его тип терминала ibmpc. Четвертый байт равный 0 означает "у меня следующий тип терминала". ("Официальный" список приемлемых типов терминалов находится в Assigned Numbers RFC, однако для Unix систем приемлем любой тип терминала, поддерживаемый сервером. Обычно это терминалы, поддерживаемые базами termcap или terminfo.) Типы терминалов, указываемые в подопциях Telnet, пишутся большими буквами и обычно преобразуются в маленькие буквы уже сервером.

Полудуплексный, символ за один раз, строка за один раз или линейный режим (Linemode)?

Существуют четыре режима, в которых функционирует большинство Telnet клиентов и серверов.

  1. Полудуплексный.

    Это режим по умолчанию, который, однако, редко используется в настоящее время. NVT по умолчанию это полудуплексное устройство, которое требует исполнения команды GO AHEAD (GA) от сервера, перед тем как будет принят ввод от пользователя. Ввод пользователя отображается локальным эхом от NVT клавиатуры на NVT принтер, таким образом, от клиента к серверу посылаются только полные строки.

    Таким образом, обеспечивается минимальная поддержка терминала, однако подобным образом невозможно обеспечить полнодуплексную связь с хостами, которые поддерживают полнодуплексную форму общения, что является нормой на сегодняшний день. RFC 857 [Postel and Reynolds 1983c] определяет опцию ECHO (эхо), а RFC 858 [Postel and Reynolds 1983d] определяет опцию SUPPRESS GO AHEAD (запрещение команды go ahead). Комбинация этих двух опций предоставляет поддержку для следующего режима, символ за один раз, с удаленным эхом.

  2. Символ за один раз.

    Именно таким образом работает Rlogin. Каждый вводимый символ отправляется серверу отдельно от других. Сервер отражает эхом большинство символов, если только у приложения на сервере не отключено отражение эхом.

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

    Для того чтобы сервер мог войти в этот режим, у него должна быть включена опция SUPPRESS GO AHEAD. Обсуждение этой опции осуществляется следующим образом: клиент посылает DO SUPPRESS GO AHEAD (требуя от сервера, чтобы тот включил опцию), или сервер посылает WILL SUPPRESS GO AHEAD клиенту (спрашивая о возможности включить эту опцию для самого себя). Затем сервер осуществляет WILL ECHO, спрашивая о возможности включить отражение эхом.

  3. Строка за один раз.

    Часто это называется "kludge line mode", потому что его реализация приходит от чтения между строк в RFC 858. Этот RFC декларирует, что должны присутствовать обе опции ECHO и SUPPRESS GO AHEAD, чтобы обеспечить ввод символа за один раз с удаленным эхом. Таким образом, если какая-либо из этих опций не включена, Telnet находится в режиме строка за один раз. В следующем разделе мы увидим пример того, как происходит обсуждение этого режима, и как он может быть отключен, когда программе сервера необходимо читать каждое нажатие клавиши. (То есть когда программа должна читать каждый символ, введенный пользователем, а не целую строку символов.)

  4. Линейный режим (linemode).

    В данном случае этот термин означает реальную опцию linemode, определенную в RFC 1184 [Borman 1990]. Эта опция обсуждается клиентом и сервером и корректирует все недостатки в режиме строка за один раз. Новые реализации поддерживают эту опцию.

На рисунке 26.11 показаны режимы функционирования по умолчанию между различными Telnet клиентами и серверами. Выражение "char" означает символ за один раз, "kludge" означает строка за один раз и "linemode" означает реальный линейный режим RFC 1184.

Клиент | Сервер
 | SunOS 4.1.3 | Solaris 2.2 | SVR4 | AIX 3.2.2 | BSD/386 | 4.4BSD
SunOS 4.1.3 | char | char | char | char | kludge | kludge
Solaris 2.2 | char | char | char | char | kludge | kludge
SVR4 | char | char | char | char | kludge | kludge
AIX 3.2.2 | char | char | char | char | kludge | kludge
BSD/386 | char | char | char | char | linemode | linemode
4.4BSD | char | char | char | char | linemode | linemode

Рисунок 26.11 Режимы функционирования по умолчанию между различными Telnet клиентами и серверами.

Только две реализации на этом рисунке поддерживают реальный линейный режим. Это BSD/386 и 4.4BSD. Только эти два сервера делают попытку обсудить режим передачи строки за раз, если клиент не поддерживает реальный линейный режим. Все клиенты и серверы, показанные на этом рисунке, поддерживают режим передачи строки за раз, однако они не выбирают этот режим по умолчанию, если только этот режим не предлагается сервером.

Сигнал синхронизации (Synch)

Telnet использует команду Data Mark в качестве сигнала синхронизации (DM на рисунке 26.8), который передается в виде срочных данных TCP. Команда DM это метка синхронизации в потоке данных, которая сообщает принимающему о необходимости вернуться в обычный режим работы. Он может быть отправлен в любом направлении по Telnet соединению.

Когда один конец принимает уведомление о том, что другой конец вошел в режим срочности, он начинает читать из потока данных, отбрасывая все данные кроме Telnet команд. Последний байт срочных данных это DM байт. Причина, по которой используется режим срочности TCP, заключается в том, что он позволяет посылать Telnet команды по соединению, даже если поток TCP данных остановлен управлением потока данных TCP.

Мы увидим примеры использования сигнала синхронизации Telnet в следующем разделе.

Управление клиентом

Как и в случае Rlogin клиента, Telnet клиент так же позволяет пообщаться с ним, вместо того чтобы отправлять пользовательский ввод серверу. Стандартный символ, позволяющий осуществить переход в режим управления клиентом (escape), это Control-] (control и правая квадратная скобка, что обычно печатается как "^]"). При этом клиент выводит приглашение, обычно выглядящее как "telnet>". В ответ на это приглашение можно вводить команды, что позволяет сменить характеристики сессии или напечатать какую-либо информацию. Команда help поддерживается большинством Unix клиентов и отображает все доступные команды.

Мы увидим примеры того, как можно управлять клиентом, а также как стоит пользоваться командами в следующем разделе.

Примеры Telnet

Сейчас мы рассмотрим обсуждение опций Telnet, вместе с тремя различными режимами функционирования: символ за один раз, реальный линейный режим и режим передачи строки за один раз. Также мы увидим, что происходит, когда пользователь прекращает работу процесса на сервере с использованием символа прерывания.

Режим символ за один раз

Мы начнем с основного режима символ за один раз (character-at-a-time), который напоминает Rlogin. Каждый символ, который мы вводим на терминале, отправляется на сервер без каких-либо модификаций, а сервер отражает этот символ эхом. Однако если мы запустим более новую версию клиента (BSD/386), который старается включить некоторые новые опции, то увидим, что сервер, работающий под управлением SVR4, не позволит включить эти опции.

Для того чтобы увидеть, как происходит обсуждение между клиентом и сервером, мы включим опцию клиента, которая позволяет отобразить все обсуждаемые опции, а также запустим tcpdump, чтобы получить временную диаграмму обмена пакетами. На рисунке 26.12 показана диалоговая сессия.

bsdi % telnet                           стартуем клиента без опций в командной строке

telnet> toggle options                  сообщаем клиенту о необходимости отобразить
Will show option processing.             всю обработку опций

telnet> open svr4                       сейчас устанавливаем соединение с сервером
Trying 140.252.13.34...
Connected to svr4.
Escape character is '^]'.

SENT DO SUPPRESS GO AHEAD                 1. (номера строк, которые мы обсудим ниже)
SENT WILL TERMINAL TYPE                   2.
SENT WILL NAWS                            3.
SENT WILL TSPEED                          4.
SENT WILL LFLOW                           5.
SENT WILL LINEMODE                        6.
SENT WILL ENVIRON                         7.
SENT DO STATUS                            8.
RCVD DO TERMINAL TYPE                     9.
RCVD WILL SUPPRESS GO AHEAD              10.
RCVD DONT NAWS                           11.
RCVD DONT TSPEED                         12.
RCVD DONT LFLOW                          13.
RCVD DONT LINEMODE                       14.
RCVD DONT ENVIRON                        15.
RCVD WONT STATUS                         16.
RCVD IAC SB TERMINAL-TYPE SEND          17.
SENT IAC SB TERMINAL-TYPE IS "IBMPC3"   18.
RCVD WILL ECHO                           19.
SENT DO ECHO                             20.
RCVD DO ECHO                             21.
SENT WONT ECHO                           22.

UNIX(r) System V Release 4.0 (svr4)

RCVD DONT ECHO                           23.
login: rstevens                         вводим имя
Password:                                и пароль, который сервер не отображает эхом,
                                        после чего появляется приветствие от
                                        операционной системы...
                                        и затем приглашение от shell

Рисунок 26.12 Начальное обсуждение опций Telnet клиентом и сервером.

Строки, в которых происходит обсуждение опций, пронумерованы (они начинаются с SENT или RCVD). Давайте рассмотрим обсуждение опций более подробно.

  1. Клиент начинает с обсуждения опции SUPPRESS GO AHEAD. Эта опция начинается с DO, так как команда GO AHEAD обычно отправляется от сервера клиенту, и клиент хочет, чтобы сервер включил опцию. (Это немного непонятно, так как включение опции исключает отправку команд GA.) Сервер соглашается на включение этой опции в строке 10.
  2. Клиент хочет отправить свой тип терминала как указано в RFC 1091 [VanBokkelen 1989]. Это является обычным для Unix клиентов. Опция начинается с WILL, так как клиент хочет включить эту опцию для себя.
  3. NAWS означает "обсуждение размера окна", как определено в RFC 1073 [Waitzman 1988]. Если сервер согласен (чего не происходит в строке 11), клиент посылает подопцию с количеством строк и колонок своего терминала. Если размер окна изменяется, клиент пошлет эту подопцию позже. (То же самое мы видели в случае Rlogin с командой 0x80 на рисунке 26.4.)
  4. Опция TSPEED позволяет отправителю (обычно клиенту) сообщить свою скорость терминала, как определено в RFC 1079 [Hedrick 1988b]. Если сервер согласен (чего не происходит в строке 12), то клиент отправляет подопцию, в которой сообщает свою скорость передачи и скорость приема.
  5. LFLOW обозначает "локальное управление потоком данных", как определено в RFC 1372 [Hedrick and Borman 1992]. Клиент отправляет эту опцию серверу, сообщая, хочет ли он включить или выключить управление потоком данных. Если сервер согласен (чего не происходит в строке 13), сервер посылает подопцию клиенту, в которой сообщается, что переключение между клиентом и сервером будет осуществляться с помощью Control-S и Control-Q. (Это напоминает то, что мы видели в случае Rlogin команд 0x10 и 0x20 на рисунке 26.4.) Как мы уже говорили, обсуждая Rlogin, пользователь получает лучшее управление потоком данных, когда оно осуществляется клиентом, а не сервером.
  6. LINEMODE это реальный линейный режим, о котором мы уже упоминали в разделе "Протокол Telnet". Вся обработка символов введенных с терминала осуществляется Telnet клиентом (удалить символ, удалить строку и так далее), а серверу отправляются завешенные строки. Ниже мы увидим, как это делается. Эта опция отклонена в строке 14.
  7. Опция ENVIRON позволяет клиенту отправить переменные окружения серверу, как определено в RFC 1408 [Borman 1993a]. При этом переменные окружения с хоста клиента могут быть автоматически установлены на хосте сервера. Сервер отказался активизировать эту опцию в строке 15. ( В соответствии с соглашением, переменные окружения в Unix - это имена из заглавных букв, после которых через знак равно, следует значение переменной, однако это всего лишь соглашение.) По умолчанию, Telnet клиент BSD/386 посылает только две переменные, а именно DISPLAY и PRINTER, если они определены и если опция включена. Пользователь Telnet может указать дополнительные переменные окружения, которые необходимо отправить.
  8. Опция STATUS (RFC 859 [Postel and Reynolds 1983e]) позволяет одному участнику обмена спросить другой о его текущем состоянии опций Telnet. В этом примере клиент просит сервер включить опцию (DO). Если сервер согласен (чего не происходит в строке 16), клиент может в подопции попросить сервер послать статус его опций.
  9. Это первый отклик от сервера. Сервер согласен включить опцию типа терминала. (Почти каждый Unix сервер поддерживает эту опцию.) Клиент, однако, не может послать свой тип терминала, пока сервер не попросит его об этом с помощью подопции (строка 17).
  10. Сервер согласен запретить отправку команды GO AHEAD.
  11. Сервер не позволяет клиенту отправить свой размер окна.
  12. Сервер не позволяет клиенту отправить свою скорость терминала.
  13. Сервер не позволяет клиенту осуществлять управление потоком данных.
  14. Сервер не позволяет клиенту включить опцию линейного режима.
  15. Сервер не позволяет клиенту послать переменные окружения.
  16. Сервер не будет посылать информацию о состоянии своих опций.
  17. Это подопция, с помощью которой сервер просит клиента послать тип терминала.
  18. Клиент посылает свой тип терминала в виде 6-символьной строки IBMPC3.
  19. Сервер просит клиента позволить ему осуществлять отражение эхом. Это первый момент, когда сервер инициирует обсуждение опции.
  20. Клиент позволяет серверу осуществлять отражение эхом.
  21. Сервер просит клиента осуществлять отображение эхом. Эта команда может показаться лишней, так как подобное обсуждение было предпринято в двух предыдущих строках. Это всего лишь еще один способ, применяемый в большинстве Unix систем, с помощью которого Telnet сервер определяют, является ли их клиент хостом 4.2BSD или более поздним релизом BSD. Если клиент ответит WILL ECHO, он, возможно, является хостом с более поздним релизом чем 4.2BSD и не сможет корректно поддерживать режим срочности TCP. (В этом случае режим срочности не используется.)
  22. Клиент отвечает WONT ECHO, из чего следует, что это хост не 4.2BSD.
  23. Сервер отвечает на принятое WONT ECHO с DONT ECHO.

На рисунке 26.13 показана временная диаграмма для этого обмена клиент-сервер. (Мы удалили все, связанное с установлением соединения.)

В сегменте 1 содержатся строки 1-8 (рисунок 26.12). Каждая опция занимает 3 байта в сегменте, состоящем из 24 байт. Обсуждение опций начинает клиент. Мы видим, что в одном TCP сегменте может находиться несколько Telnet опций.

Сегмент 3 соответствует строке 9 на рисунке 26.12, команда DO TERMINAL TYPE. Сегмент 5 содержит восемь откликов от сервера на предложенные опции, строки 10-17 (рисунок 26.12). Длина этого сегмента составляет 27 байт, потому что строки 10-16 это стандартные опции, каждая требует 3 байта вместе с подопцией (строка 17), которая требует 6 байт. 12 байт в сегменте 6 соответствуют строке 18, клиент посылает подопцию со своим типом терминала.

Сегмент 8 (53 байта) - это комбинация двух команд Telnet с 47 байтами данных, которые выводятся на терминал. Первые 6 байт - это две команды от сервера: WILL ECHO и DO ECHO (строки 19 и 21). Следующие 47 байт выглядят так:

\r\n\r\nUNIX(r) System V Release 4.0 (svr4) \r\n\r\0\r\n\r\0

Первые 4 байта генерируют две пустые строки, перед тем как эта строка появится в выводе. 2-байтовая последовательность \r\n - это символ новой строки в Telnet. 2-байтовая последовательность \r\0 соответствует символу возврата каретки. Этот сегмент показывает, что данные и команды могут находиться в одном и том же сегменте. Telnet клиент и Telnet сервер должны просматривать каждый полученный байт в поисках IAC байта и затем обрабатывать то, что следует за этим байтом.

Рисунок 26.13 Начальное обсуждение опций Telnet клиентом и сервером.

В сегменте 9 содержатся две последние опции от клиента: строки 20 и 22. Отклик в сегменте 10 соответствует строке 23 - это последняя опция от сервера.

С этого момента по соединению начинается обмен данными. Ничего не мешает тому, чтобы продолжить обсуждение опций, однако этого не происходит. Сегмент 12 содержит приглашение login: от сервера. Сегмент 14 это первый символ, который мы вводим в качестве нашего имени, его эхо возвращается в сегменте 15. Подобный тип интерактивного траффика мы видели в разделе "Интерактивный ввод" главы 19 в случае Rlogin: клиент посылает один символ за один раз, а сервер осуществляет эхо.

Обсуждение опций на рисунке 26.12 инициировано клиентом, однако в нашей книге мы не раз использовали Telnet клиента, чтобы подсоединиться к стандартным серверам, таким как сервер времени или эхо сервер, для того чтобы продемонстрировать различные характеристики TCP. Когда мы рассматривали обмен пакетами в этих примерах, как, например, на рисунке 18.1, то никогда не видели, чтобы клиент инициировал обсуждение опций. Почему? Unix Telnet клиент никогда не начинает обсуждение опций, если указан номер порта, отличный от стандартного порта Telnet (23). Это позволяет Telnet клиенту с использованием стандартного NVT обмениваться данными с другими, не-Telnet серверами. Мы пользовались этим с daytime, echo и discard серверами и будем использовать это с FTP и SMTP серверами в следующих главах.

Линейный режим (Linemode)

Чтобы увидеть опцию линейного режима Telnet в действии, мы запустим клиента на хосте bsdi и подключимся к серверу 4.4BSD на vangogh.cs.berkeley.edu. И BSD/386, и 4.4BSD поддерживают эту опцию.

Мы не будем рассматривать все пакеты или обсуждаемые опции и подопции, потому что оно подобно тому, что мы привели в предыдущем примере. Вместо этого рассмотрим особенности обсуждения этой опции.

  1. Сервер 4.4BSD поддерживает больше опций, доступных к обсуждению, чем BSD/386: размер окна, локальное управление потоком данных, состояние опций, прием переменных окружения и скорость терминала.
  2. Сервер 4.4BSD старается обсудить более новые опции, чем те, которые поддерживаются клиентом BSD/386: аутентификация (позволяет избежать отправки пользовательского пароля в виде открытого текста по соединению).
  3. Клиент посылает опцию WILL LINEMODE, сервер отвечает DO LINEMODE, так как он поддерживает эту опцию. Клиент отправляет серверу 16 специальных символов в качестве подопции. Это текущие значения специальных символов терминала на хосте клиента: символ прерывания, символ конца файла и так далее. Сервер отправляет подопции клиенту, которые сообщают ему, как обрабатывать все входные строки и как осуществлять все функции редактирования (удаление символа, удаление строки и так далее). Клиент отправляет серверу только завершенные строки. Сервер также сообщает клиенту, как транслировать любые символы прерывания или сигнальные символы в соответствующие символы Telnet. Например, если символ прерывания - Control-C, и мы вводим Control-C, чтобы прервать текущий процесс на сервере, клиент посылает серверу Telnet команду "прервать процесс" (<IAC, IP>).
  4. Интересно посмотреть, что происходит, когда мы вводим пароль. В случае Rlogin или режима Telnet один символ за раз, когда сервер осуществляет эхо, он не отражает эхом символы пароля. В случае линейного режима, однако, эхо осуществляется самим клиентом. Он справляется с этой задачей с помощью следующего обмена:
    (a)
    Сервер отправляет WILL ECHO, сообщая клиенту, что сервер будет осуществлять отражение эхом.
    (b)
    Клиент отвечает DO ECHO.
    (c)
    Сервер отправляет строку Password: клиенту, а клиент выводит эту строку на терминал.
    (d)
    Мы вводим пароль, клиент посылает его серверу при нажатии клавиши RETURN. Пароль не отражается эхом, так как клиент считает, что это сделает сервер.
    (e)
    Сервер посылает 2-байтовую последовательность CR, LF, чтобы переместить курсор, так как символ RETURN, который завершил ввод пароля, не был отображен эхом.
    (f)
    Сервер посылает WONT ECHO.
    (g)
    Клиент отвечает DONT ECHO. Клиент восстанавливает отражение эхом.

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

vangogh % date

по Telnet соединению с использованием линейного режима. (Мы удалили всю информацию о типе сервиса и объявления окна.)

Рисунок 26.14 Отправка команды от клиента к серверу с использованием линейного режима Telnet.

Если мы сравним это с той же самой командой, вводимой в Rlogin (рисунок 19.2), то увидим, что линейный режим Telnetа использует два сегмента (один с данными и один для подтверждения, что в целом составляет 86 байт, включая IP и TCP заголовки), тогда как Rlogin использует 15 сегментов (5 с введенными данными, 5 с отраженными эхом данными, 5 с подтверждениями, всего 611 байт). Нетрудно догадаться, в чью пользу это сравнение!

Что если на сервере мы запустим приложение, которое требует использовать режим единственного символа? (Например, редактор vi.) В этом случае будет происходить следующее.

  1. Когда приложение стартует на сервере и изменяет режим своего псевдотерминала, Telnet сервер уведомляется о том, что требуется режим единственного символа. Сервер посылает WILL ECHO клиенту вместе с подопцией линейного режима, которая сообщает клиенту о том, что не нужно составлять полные строки, а вместо этого необходимо посылать один символ за один раз.
  2. Клиент отвечает DO ECHO и подтверждает подопцию линейного режима.
  3. Приложение запускается на сервере. Каждый символ, который мы печатаем, отправляется серверу сам по себе (естественно используется алгоритм Нагла), при этом сервер не требует отражения эхом.
  4. Когда приложение завершает работу и восстанавливает режим псевдотерминала, об этом уведомляется Telnet сервер. Сервер отправляет WONT ECHO клиенту вместе с подопцией линейного режима, которая сообщает клиенту о необходимости снова составлять полные строки.
  5. Клиент отвечает DONT ECHO и подтверждает подопцию линейного режима.

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

На рисунке 26.15 показаны различные режимы, которые мы видели для Telnet и Rlogin.

Приложение | Клиент посылает |  | Эхо клиента? | Пример
 | символ за раз | строка за раз
Rlogin | · |  | нет |
Telnet, символ за раз | · |  | нет |
Telnet, линейный режим |  | · | да | обычные команды
Telnet, линейный режим |  | · | нет | ввод пароля
Telnet, линейный режим | · |  | нет | редактор vi

Рисунок 26.15 Сравнение режимов Rlogin и Telnet в действии.

Режим строка за раз (Kludge Line Mode)

Мы видели на рисунке 26.11, что более новые сервера, которые поддерживают опцию линейного режима, входят в режим строка за раз, если клиент не поддерживает линейный режим. Мы также упомянули, что все клиенты и серверы на этом рисунке поддерживают режим строка за раз, однако это не было установкой по умолчанию и должно быть специально включено сервером или пользователем. Давайте посмотрим, как включается режим строка за раз с использованием опций Telnet.

Во-первых, мы опишем, как сервер BSD/386 обсуждает этот режим, когда клиент не поддерживает реальный линейный режим.

  1. Когда клиент отказывает на требование сервера включить линейный режим, сервер посылает опцию DO TIMING MARK. RFC 860 [Postel and Reynolds 1983f] определяет эту опцию Telnet. Она предназначена для того, чтобы оба участника обмена могли синхронизироваться друг с другом. Мы увидим, как это происходит, позже в этом разделе, когда будем рассматривать прерывание от пользователя. Использование этой опции заключается в том, чтобы определить, поддерживает ли клиент режим строка за раз.
  2. Клиент отвечает WILL TIMING MARK, что указывает на то, что он поддерживает опцию строка за раз.
  3. Сервер посылает опцию WONT SUPPRESS GO AHEAD вместе с опцией WONT ECHO, сообщая о том, что он хочет выключить эти две опции. Мы упоминали ранее, что режим символ за раз подразумевает, что обе опции SUPPRESS GO AHEAD и ECHO включены, таким образом, выключение этих опций стартует режим строка за раз.
  4. Клиент отвечает DONT SUPPRESS GO AHEAD и DONT ECHO.
  5. Сервер посылает приглашение login:, мы вводим имя. Ввод отправляется серверу в виде полной строки и отражается локальным эхом клиентом.
  6. Сервер посылает строку Password: вместе с опцией WILL ECHO. При вводе пароля отражение эхом выключается, так как Telnet клиент считает, что сервер ответит эхом. Клиент отвечает DO ECHO.
  7. Мы вводим пароль. Он отправляется клиентом на сервер в виде полной строки.
  8. Эхо снова включается, после того как сервер отправил WONT ECHO, на что клиент отвечает DONT ECHO.

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

Мы упоминали ранее, что все клиенты и сервера на рисунке 26.11, которые помечены как "char", поддерживают режим строки за раз, однако по умолчанию стартуют в режиме символ за раз. Мы можем увидеть, как происходит обсуждение, когда мы сообщаем клиенту о необходимости войти в линейный режим:

                                   клиент это sun, сервер это svr4
svr4 %                              вводим Control-], чтобы пообщаться с Telnet клиентом
                                   (эхо выключено)

telnet> status                     проверяем, находимся ли мы в режиме символ за раз
Connected to svr4.tuc.noao.edu
Operating in character-at-a-time mode.
Escape character is '^]'.

telnet> toggle options             просмотрим обработку опций
Will show option processing.

telnet> mode line                  переключимся в режим строки за раз
SENT dont SUPPRESS GO AHEAD        клиент отправляет эти две опции
SENT dont ECHO
RCVD wont SUPPRESS GO AHEAD        и сервер отвечает на обе WONT
RCVD wont ECHO

Telnet сессия помещается в режим строки за раз, при этом обе опции SUPPRESS GO AHEAD и ECHO выключены.

Если мы запустим приложение, такое как редактор vi, на сервере, то будем иметь те же проблемы, что мы имели с опцией линейного режима. Серверу необходимо сообщить клиенту о необходимости переключиться из режима строка за раз в режим символ за раз, пока работает это приложение, а затем переключиться обратно, когда оно закончит свою работу. Используется следующая техника.

  1. Telnet сервер знает, что он должен перейти в режим символ за раз, потому что приложение меняет режим на своем псевдотерминале, о чем уведомляется сервер. Сервер посылает WILL SUPPRESS GO AHEAD и WILL ECHO, это помещает клиента в режим символ за раз.
  2. Клиент отвечает DO SUPPRESS GO AHEAD и WILL ECHO.
  3. Приложение запускается на сервере.
  4. Когда приложение прекращает свою работу и изменяет режим своего псевдотерминала, Telnet сервер помещает клиента обратно в режим строка за раз. Он посылает WONT SUPPRESS GO AHEAD и WONT ECHO.
  5. Клиент отвечает DONT SUPPRESS GO AHEAD и DONT ECHO, указывая на то, что он вернулся в режим строка за раз.

На рисунке 26.16 показаны различные установки опций SUPPRESS GO AHEAD и ECHO для режимов символ за раз и строка за раз.

Режим | SUPPRESS GO AHEAD | ECHO | Пример
символ за раз | вкл. | вкл. | редактор vi в режиме строка за раз
строка за раз | выкл. | выкл. | обычные команды
строка за раз | выкл. | вкл. | ввод пароля

Рисунок 26.16 Установки Telnet опций при режиме строка за раз.

Линейный режим: символ прерывания от клиента

Давайте посмотрим, что делает Telnet, когда клиент вводит символ прерывания. Мы открыли сессию между клиентом bsdi и сервером vangogh.cs.berkeley.edu. На рисунке 26.17 показана временная диаграмма того, что происходит, когда вводится символ прерывания. (Мы удалили объявления окна и тип сервиса.)

В сегменте 1 символ прерывания (обычно Control-C или DELETE) конвертируется в команду Telnet "прерывание процесса" (IP - interrupt process): <IAC, IP>. Следующие 3 байта, <IAC, DO, TM>, содержат опцию Telnet DO TIMING MARK. Эта метка отправляется клиентом и на нее должен быть получен ответ либо WILL, либо WONT. В любом случае все данные получены от сервера, перед тем как выдается отклик (за исключением команд Telnet). Это метка синхронизации от клиента к серверу и обратно. Сегмент 1 не посылается с использованием режима срочности TCP.

Рисунок 26.17 Введение символа прерывания в процессе функционирования линейного режима.

Требование к хостам Host Requirements RFC указывает, что IP команда должна быть послана с использованием сигнала синхронизации Telnet. Если это сделано, за <IAC, IP> должно следовать <IAC, DM>, с указателем срочности, указывающим на DM байт. Большинство Unix Telnet клиентов имеют опцию, которая отправляет IP команду с сигналом синхронизации, однако эта опция по умолчанию выключена (как мы здесь и видели).

Сегмент 2 это отклик сервера на опцию DO TIMING MARK. Он следует в сегментах 3 и 4 за сигналом синхронизации Telnet: <IAC, DM>. Указатель срочности в сегменте 3 указывает на DM байт, который отправляется в сегменте 4.

Если в очередь поставлено полное окно данных или оно в пути от сервера к клиенту, все эти данные будут отброшены клиентом после отправки команды прерывания процесса в сегменте 1. Даже если управление потоком данных TCP запретило серверу отправлять данные в сегментах 2, 3 и 4, указатель срочности все равно посылается. Это аналогично тому, что мы видели в случае Rlogin на рисунке 26.7.

Почему сигнал синхронизации посылается в виде двух сегментов (3 и 4)? Мы рассмотрели это в разделе "Режим срочности (Urgent Mode)" главы 20, это связано с указателем срочности TCP. Требование к хостам Host Requirements RFC говорит, что он должен указывать на последний байт срочных данных, тогда как большинство Berkeley реализаций указывают на 1 байт позади последнего байта срочных данных. (Обратитесь к рисунку 26.6, на котором указатель срочности указывает на 1 байт позади командного байта.) Сервер Telnet специально пишет первый байт сигнала синхронизации как срочные данные, зная, что указатель срочности будет (неверно) указывать на следующий байт, который он запишет (метка данных, DM), однако эта первая запись с байтом IAC посылается немедленно, вместе с указателем срочности, который следует в следующем сегменте за DM байтом.

Последний сегмент данных, сегмент 6, это следующее приглашение shellа от сервера.

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

В этой главе показано функционирование приложений Rlogin и Telnet. Оба предоставляют заход удаленным терминалом с хоста клиента на хост сервера, а также позволяют запускать программы на сервере.

Между этими приложениями существуют отличия. Rlogin подразумевает, что на обоих концах соединения присутствуют Unix хосты, поэтому существует только одна опция. Это простой протокол. Telnet не ограничивает, какая операционная система используется на обоих концах соединения. Telnet разработан для того, чтобы функционировать между различными операционными системами.

Чтобы поддерживать разнородное окружение, Telnet предоставляет обсуждение опций между клиентом и сервером. Это позволяет общаться клиентам и серверам, а также позволяет добавлять новые опции.

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

На рисунке 26.18 сравниваются различные характеристики, предоставляемые Rlogin и Telnet.

Характеристика | Rlogin | Telnet
транспортный протокол | Одно TCP соединение. Используется режим срочности. | Одно TCP соединение. Используется режим срочности.
пакетный режим | Всегда символ за один раз, удаленное эхо. | Общий режим по умолчанию - символ за один раз, удаленное эхо. Поддерживается режим строки за раз с эхом от клиента. Новые опции для реального линейного режима с эхом от клиента. Если приложение на сервере требует режим символ за один раз - он поддерживается всегда.
управление потоком данных | Обычно осуществляется клиентом, однако может быть выключено сервером. | Обычно осуществляется сервером, иногда позволяется делать клиенту.
тип терминала | Присутствует всегда. | Опционально, обычно поддерживается.
скорость терминала | Присутствует всегда. | Опционально.
размер окна | Опционально поддерживается большинством серверов. | Опционально.
переменные окружения | Не поддерживается. | Опционально.
автоматический login | По умолчанию. Может появиться приглашение ввести пароль, который посылается в виде открытого текста. Более новые версии поддерживают Kerberos. | По умолчанию необходимо ввести имя и пароль, который отправляется в виде открытого текста. Более новыми версиями предоставляется опция аутентификации.

Рисунок 26.18 Краткое описание характеристик, предоставляемых Rlogin и Telnet.

Telnet и Rlogin серверы обычно устанавливают TCP опцию "оставайся в живых" (keepalive) (глава 23), если она поддерживается реализацией TCP сервера, чтобы определить момент выхода из строя хоста клиента. Оба приложения также используют режим срочности TCP, чтобы посылать команды сервера клиенту, даже если поток данных в этом направлении был остановлен управлением потока данных.

Упражнения

  1. Найдите все задержанные ACK на рисунке 26.5.
  2. Почему был отправлен сегмент номер 12 на рисунке 26.7?
  3. Мы говорили, что Rlogin клиент должен использовать зарезервированный порт (глава 1, раздел "Номера портов"). (Обычно, Rlogin клиент использует зарезервированный порт в диапазоне 512-1023.) Как это ограничение отражается на хосте? Существует ли способ обойти это?
  4. Прочитайте RFC 1097, описывающий Telnet опцию subliminal-message.


FTP: протокол передачи файлов

Глава 27 FTP: протокол передачи файлов

Введение

FTP это еще одно широко используемое приложение. Оно является стандартом Internet для передачи файлов. Необходимо различать передачу файлов, именно то, что предоставляет FTP, и доступ к файлам, что предоставляется такими приложениями как NFS (Network File System, глава 29). Передача файлов заключается в копировании целого файла из одной системы в другую. Чтобы использовать FTP, необходимо иметь открытый бюджет на сервере, или можно воспользоваться так называемым анонимным FTP (anonymous FTP).

Как и Telnet, FTP был создан для того, чтобы работать между хостами работающими под управлением различных операционных систем, использующих различные структуры файлов и, возможно, различные наборы символов. Telnet, однако, обеспечивает связь между разнородными системами, заставляя каждого участника соединения работать с одним и тем же стандартом: NVT, использующий 7-битный ASCII. FTP сглаживает различия между системами с использованием другого подхода. FTP поддерживает ограниченное количество типов файлов (ASCII, двоичное и так далее) и структуру файлов (поток байтов или ориентированный на запись).

RFC 959 [Postel and Reynolds 1985] является официальной спецификацией FTP. Этот RFC описывает историю и развитиие передачи файлов в течение времени.

Протокол FTP

FTP отличается от других приложений тем, что он использует два TCP соединения для передачи файла.

  1. Управляющее соединение устанавливается как обычное соединение клиент-сервер. Сервер осуществляет пассивное открытие на заранее известный порт FTP (21) и ожидает запроса на соединение от клиента. Клиент осуществляет активное открытие на TCP порт 21, чтобы установить управляющее соединение. Управляющее соединение существует все время, пока клиент общается с сервером. Это соединение используется для передачи команд от клиента к серверу и для передачи откликов от сервера. Тип IP сервиса для управляющего соединения устанавливается для получения "минимальной задержки", так как команды обычно вводятся пользователем (рисунок 3.2).
  2. Соединение данных открывается каждый раз, когда осуществляется передача файла между клиентом и сервером. (Оно также открывается и в другие моменты, как мы увидим позже.) Тип сервиса IP для соединения данных должен быть "максимальная пропускная способность", так как это соединение используется для передачи файлов.

На рисунке 27.1 показано общение клиента и сервера по двум соединениям.

Рисунок 27.1 Процессы, участвующие в передаче файлов.

Из рисунка видно, что интерактивный пользователь обычно не видит команды и отклики, которые передаются по управляющему соединению. Эти детали оставлены двум интерпретаторам протокола. Квадратик, помеченный как "пользовательский интерфейс", это именно то, что видит интерактивный пользователь (полноэкранный интерфейс, основанный на меню, командные строки и так далее). Интерфейс конвертирует ввод пользователя в FTP команды, которые отправляются по управляющему соединению. Отклики, возвращаемые сервером по управляющему соединению, конвертируются в формат, удобный для пользователя.

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

Представление данных

Протокол FTP предоставляет различные способы управления передачей и хранения файлов. Необходимо сделать выбор по четырем пунктам.

  1. Тип файла.

    2. (а)

    ASCII файлы.
    (По умолчанию) Текстовый файл передается по соединению данных как NVT ASCII. При этом требуется, чтобы отправитель конвертировал локальный текстовый файл в NVT ASCII, а получатель конвертировал NVT ASCII в текстовый файл. Конец каждой строки передается в виде NVT ASCII символа возврата каретки, после чего следует перевод строки. Это означает, что получатель должен просматривать каждый байт в поисках пары символов CR, LF. (Мы видели тот же сценарий в случае передачи ASCII файла с помощью TFTP в разделе "Протокол" главы 15.)

    (b)

    EBCDIC файлы.
    Альтернативный способ передачи текстовых файлов, когда на обоих концах системы EBCDIC.

    (c)

    Двоичные или бинарные файлы. (Image.)
    Данные передаются как непрерывный поток битов.

    (d)

    Локальный тип файлов.
    Способ передачи бинарных файлов между хостами, которые имеют различный размер байта. Количество битов в байте определяется отправителем. Для систем, которые используют 8-битные байты, локальный тип файла с размером байта равным 8 эквивалентен бинарному типу файла.

  2. Управление форматом. Применяется только для ASCII и EBCDIC файлов.

    3. (a)

    Nonprint. (По умолчанию)
    Файл не содержит информацию вертикального формата.

    (b)

    Telnet format control.
    Файл содержит управляющие символы вертикального формата Telnet, которые интерпретируются принтером.

    (c)

    Fortran carriage control.
    Первый символ каждой строки это Fortran символ управления формата.

  3. Структура.

    (a)

    Структура файла.
    (По умолчанию) Файл воспринимается в виде непрерывного потока байтов. Файл не имеет внутренней структуры.

    (b)

    Структура записи.
    Эта структура используется только в случае текстовых файлов (ASCII или EBCDIC).

    (c)

    Структура страницы.
    Каждая страница передается с номером страницы, что позволяет получателю хранить страницы в случайном порядке. Предоставляется операционной системой TOPS-20. (Требование к хостам Host Requirements RFC не рекомендует использовать эту структуру.)

  4. Режим передачи. Указывает на то, как файл передается по соединению данных.

    (a)

    Режим потока.
    (По умолчанию) Файл передается как поток байтов. Для файловой структуры конец файла указывает на то, что отправитель закрывает соединение данных. Для структуры записи специальная 2-байтовая последовательность обозначает конец записи и конец файла.

    (b)

    Режим блоков.
    Файл передается как последовательность блоков, перед каждым из них стоит один или несколько байт заголовков.

    (c)

    Сжатый режим.
    Простое кодирование неоднократно встречающихся повторяющихся байт. В текстовых файлах обычно сжимаются пустые строки или строки из пробелов, а в бинарных строки из нулевых байт. (Этот режим поддерживается редко. Существуют более оптимальные способы сжатия файлов для FTP.)

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

Самые распространенные Unix реализации FTP клиента и сервера предоставляют следующий выбор:

  • Тип: ASCII или двоичный.
  • Управление форматом: только nonprint.
  • Структура: только файловая структура.
  • Режим передачи: только потоковый режим.

Это ограничивает нас одним из двух режимов: ASCII или двоичный.

Подобная реализация отвечает минимальным требованиям к хостам Host Requirements RFC. (RFC также требует обеспечить поддержку для структуры записи, однако только если операционная система поддерживает это, а Unix, как правило, не поддерживает.)

Большинство не-Unix реализаций предоставляет FTP возможности, которые позволяют обрабатывать их собственные форматы файлов. Требование к хостам Host Requirements RFC говорит: "Протокол FTP включает множество характеристик, некоторые из которых распространены не очень широко. Однако, для каждой характеристики в FTP существует по меньшей мере одна реализация."

Команды FTP

Команды и отклики передаются по управляющему соединению между клиентом и сервером в формате NVT ASCII. В конце каждой строки команды или отклика присутствует пара CR, LF.

Единственные команды Telnet (начинающиеся с IAC), которые могут быть отправлены клиентом серверу - это команда прерывания процесса (<IAC, IP>) и Telnet сигнал синхронизации (<IAC, DM> в режиме срочности). Мы увидим, что эти две команды Telnet используются для прекращения передачи файла или для того, чтобы отправить серверу запрос в процессе передачи. Если сервер получает от клиента команду с Telnet опцией (WILL, WONT, DO или DONT), он отвечает либо DONT, либо WONT.

Команды состоят из 3 или 4 байт, а именно из заглавных ASCII символов, некоторые с необязательными аргументами. Клиент может отправить серверу более чем 30 различных FTP команд. На рисунке 27.2 показаны некоторые наиболее широко используемые команды, большинство из которых мы рассмотрим в этой главе.

Команда | Описание
ABOR | прервать предыдущую команду FTP и любую передачу данных
LIST список файлов | список файлов или директорий
PASS пароль | пароль на сервере
PORT n1,n2,n3,n4,n5,n6 | IP адрес клиента (n1.n2.n3.n4) и порт (n5 x 256 + n6)
QUIT | закрыть бюджет на сервере
RETR имя файла | получить (get) файл
STOR имя файла | положить (put) файл
SYST | сервер возвращает тип системы
TYPE тип | указать тип файла: A для ASCII, I для двоичного
USER имя пользователя | имя пользователя на сервере

Рисунок 27.2 Распространенные FTP команды.

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

FTP отклики

Отклики состоят из 3-циферных значений в формате ASCII, и необязательных сообщений, которые следуют за числами. Подобное представление откликов объясняется тем, что программному обеспечению необходимо посмотреть только цифровые значения, чтобы понять, что ответил процесс, а дополнительную строку может прочитать человек. Поэтому пользователю достаточно просто прочитать сообщение (причем нет необходимости запоминать все цифровые коды откликов).

Каждая из трех цифр в коде отклика имеет собственный смысл. (В главе 28 мы увидим, что протокол передачи почтовых сообщений - SMTP, использует те же соглашения для своих команд и откликов.) На рисунке 27.3 показаны значения первых и вторых цифр в коде отклика.

Отклик | Описание
1yz | Положительный предварительный отклик. Действие началось, однако необходимо дождаться еще одного отклика перед отправкой следующей команды.
2yz | Положительный отклик о завершении. Может быть отправлена новая команда.
3yz | Положительный промежуточный отклик. Команда принята, однако необходимо отправить еще одну команду.
4yz | Временный отрицательный отклик о завершении. Требуемое действие не произошло, однако ошибка временная, поэтому команду необходимо повторить позже.
5yz | Постоянный отрицательный отклик о завершении. Команда не была воспринята и повторять ее не стоит.
x0z | Синтаксическая ошибка.
x1z | Информация.
x2z | Соединения. Отклики имеют отношение либо к управляющему, либо к соединению данных.
x3z | Аутентификация и бюджет. Отклик имеет отношение к логированию или командам, связанным с бюджетом.
x4z | Не определено.
x5z | Состояние файловой системы.

Рисунок 27.3 Значения первой и второй цифр в 3-циферном коде отклика.

Третья цифра дает дополнительное объяснение сообщению об ошибке. Ниже приведены некоторые типичные отклики с возможными объясняющими строками.

  • 125 Соединение данных уже открыто; начало передачи.
  • 200 Команда исполнена.
  • 214 Сообщение о помощи (для пользователя).
  • 331 Имя пользователя принято, требуется пароль.
  • 425 Невозможно открыть соединение данных.
  • 452 Ошибка записи файла.
  • 500 Синтаксическая ошибка (неизвестная команда).
  • 501 Синтаксическая ошибка (неверные аргументы).
  • 502 Нереализованный тип MODE.

Обычно каждая FTP команда генерируют отклик в одну строку. Например, команда QUIT сгенерирует следующий отклик:

221 Goodbye.

Если необходим отклик в несколько строк, первая строка содержит дефис вместо пробела после 3-циферного кода отклика, а последняя строка содержит тот же самый 3-циферный код отклика, за которым следует пробел. Например, команда HELP сгенерирует следующий отклик:

214- The following commands are recognized (* =>'s unimplemented).
   USER    PORT    STOR    MSAM*     RNTO    NLST    MKD      CDUP
   PASS    PASV    APPE    MRSQ*     ABOR    SITE    XMKD     XCUP
   ACCT*   TYPE    MLFL*   MRCP*     DELE    SYST    RMD      STOU
   SMNT*   STRU    MAIL*   ALLO      CWD     STAT    XRMD     SIZE
   REIN*   MODE    MSND*   REST      XCWD    HELP    PWD      MDTM
   QUIT    RETR    MSOM*   RNFR      LIST    NOOP    XPWD
214 Direct comments to ftp-bugs@bsdi.tuc.noao.edu.

Управление соединением

Использовать соединение данных можно тремя способами.

  1. Отправка файлов от клиента к серверу.
  2. Отправка файлов от сервера к клиенту.
  3. Отправка списка файлов или директорий от сервера к клиенту.

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

Мы сказали, что управляющее соединение остается в активизированном состоянии все время, пока установлено соединение клиент-сервер, однако соединение данных может выключаться и включаться по необходимости. Как выбираются номера портов для соединения данных, и кто осуществляет активное открытие, а кто пассивное открытие?

Во-первых, как было сказано ранее, распространенный режим передачи (в случае Unix это единственный режим передачи) - это потоковый режим. В этом режиме конец файла обозначает закрытие соединения данных. Из этого следует, что для передачи каждого файла или списка директории требуется новое соединение данных. Обычная процедура выглядит следующим образом:

  1. Создание соединения данных осуществляется клиентом, потому что именно клиент выдает команды, которые требуют передать данные (получить файл, передать файл или список директории).
  2. Клиент обычно выбирает динамически назначаемый номер порта на хосте клиента для своего конца соединения данных. Клиент осуществляет пассивное открытие с этого порта.
  3. Клиент посылает этот номер порта на сервер по управляющему соединению с использованием команды PORT.
  4. Сервер принимает номер порта с управляющего соединения и осуществляет активное открытие на этот порт хоста клиента. Сервер всегда использует порт 20 для соединения данных.

На рисунке 27.4 показано состояние соединений, пока осуществляется шаг номер 3. Мы предполагаем, что динамически назначаемый порт клиента для управляющего соединения имеет номер 1173, а динамически назначаемый порт клиента для соединения данных имеет номер 1174. Команда, посылаемая клиентом - PORT, а ее аргументы это шесть десятичных цифр в формате ASCII, разделенные запятыми. Четыре первых числа - это IP адрес клиента, на который сервер должен осуществить активное открытие (140.252.13.34 в данном примере), а следующие два - это 16-битный номер порта. Так как 16-битный номер порта формируется из двух цифр, его значение в этом примере будет 4 x 256 + 150 = 1174.

На рисунке 27.5 показано состояние соединений, когда сервер осуществляет активное открытие на конец клиента соединения данных. Конечная точка сервера это порт 20.

Рисунок 27.4 Команда PORT, передаваемая по управляющему соединению FTP.

Рисунок 27.5 FTP сервер осуществляет активное открытие соединения данных.

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

Если клиент не выдает команду PORT, сервер осуществляет активное открытие на тот же самый номер порта, который использовался клиентом для управляющего соединения (1173 в данном примере). В этом случае все работает корректно, так как номера порта сервера для двух соединений различны: один 20, другой 21. Тем не менее, в следующем разделе мы посмотрим, почему современные реализации не поступают таким образом.

Примеры FTP

Сейчас мы рассмотрим некоторые примеры использования FTP: как осуществляется управление соединением данных, как передаются текстовые файлы с использованием NVT ASCII, как в FTP используется сигнал синхронизации Telnet для прекращения процесса передачи. В завершение мы рассмотрим "анонимный FTP" (anonymous FTP).

Управление соединением: динамически назначаемый порт

Давайте, рассмотрим управление FTP соединением на примере простой FTP сессии, в течение которой просматривается список файлов на сервере. Клиент запущен на хосте svr4 с флагом -d (отладка). При этом печатаются команды и отклики, которыми происходит обмен по управляющему соединению. Все строки, начинающиеся с --->, отправляются клиентом серверу, а строки, которые начинаются с 3-циферных чисел, это отклики от сервера. Клиенту выдается приглашение в виде ftp>.

svr4 % ftp -d bsdi                                 опция -d для отладочного вывода
Connected to bsdi.                                  клиент осуществляет активное открытие управляющего соединения
220 bsdi FTP server (Version 5.60) ready.           сервер отвечает, что он готов

Name (bsdi:rstevens):                               клиент спрашивает имя пользователя

---> USER rstevens                                  вводим RETURN, поэтому используется имя по умолчанию
331 Password required for rstevens.

Password:                                           вводим наш пароль; он не отображается эхом

---> PASS XXXXXXX                                   клиент передает пароль в виде открытого текста
230 User rstevens logged in.

ftp> dir hello.c                                   спрашиваем, находится ли в директории указанный файл

---> PORT 140,252,13,34,4,150                       см. рисунок 27.4
200 PORT command successful.

---> LIST hello.c
150 Opening ASCII mode data connection for /bin/ls.

-rw-r--r-- 1 rstevens staff 38 Jul 17 12:47 hello.c

226 Transfer complete.

remote: hello.c                                      вывод от клиента
56 bytes received in 0.03 seconds (1.8 Kbytes/s)

ftp> quit                                            мы закончили

---> QUIT
221 Goodbye.

Когда FTP клиент просит нас ввести имя пользователя, он выводит имя по умолчанию (наше имя на хосте клиента). Когда мы нажимаем клавишу RETURN, отправляется это имя по умолчанию.

Когда мы спрашиваем, присутствует ли указанный файл в директории, устанавливается соединение данных. Этот пример является продолжением процедуры, показанной на рисунках 27.4 и 27.5. Клиент спрашивает свой TCP модуль о динамически назначаемом номере порта для своего конца соединения данных и отправляет этот номер порта (1174) серверу в виде команды PORT. Мы также видим, что одна команда, введенная пользователем (dir) генерирует две FTP команды (PORT и LIST).

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

Рисунок 27.7 это временная диаграмма для соединения данных. Времена на этом рисунке начинаются с того же момента, как и на рисунке 27.6. Мы удалили все объявления окна, однако оставили поле типа сервиса, чтобы показать, что соединение данных использует отличный тип сервиса (максимальная пропускная способность), нежели управляющее соединение (минимальная задержка). (Значения TOS приведены на рисунке 3.2.)

На этой временной диаграмме FTP сервер осуществляет активное открытие соединения данных с порта 20 (который называется ftp-data) на номер порта из команды PORT (1174). Также в этом примере, где сервер выдает информацию в соединение данных, сервер осуществляет активное закрытие соединения данных, тем самым сообщая клиенту, когда список завершен.

Рисунок 27.6 Пример управляющего соединения FTP.

Рисунок 27.7 Пример соединения данных FTP.

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

Если клиент не посылает команду PORT на сервер, чтобы указать номер порта для клиентской стороны соединения данных, сервер использует тот же номер порта для соединения данных, который был использован для управляющего соединения. Это может вызвать проблемы для клиента, который использует потоковый режим (который Unix FTP клиенты и сервера используют всегда), как мы покажем ниже.

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

Давайте снова обратимся к предыдущему примеру (рисунок 27.6). Что произойдет, если мы попробуем узнать содержимое другой директории через несколько секунд после того, как узнали содержимое первой? Клиент попросит свое ядро выбрать еще один динамически назначаемый порт (может быть, с номером 1175), после чего будет открыто следующее соединение данных между svr4 порт 1175 и bsdi порт 20. Однако на рисунке 27.7 сервер осуществляет активное закрытие соединения данных, и мы видели на рисунке 18.6, что сервер не в состоянии назначить порт 20 для нового соединения данных, так как считается, что локальный порта используется предыдущим соединением, пока находится в состоянии ожидания 2MSL.

Сервер обходит эту проблему, указывая опцию SO_REUSEADDR, которую мы упоминали в разделе "Диаграмма состояний передачи TCP" главы 18. Это позволяет ему назначить порт 20 для нового соединения, которое будет иметь другой номер удаленного порта (1175). В состоянии ожидания 2MSL находится порт 1174.

Однако процедура открытия соединения изменяется, если клиент не пошлет команду PORT, указывая динамически назначаемый номер порта клиента. Мы можем смоделировать подобную ситуацию, исполнив пользовательскую команду sendport на FTP клиенте. Unix FTP клиенты используют эту команду, чтобы выключить отправку команд PORT на сервер перед каждым использованием соединения данных.

На рисунке 27.8 показана временная диаграмма для соединений данных при использовании двух команд LIST. Управляющее соединение установлено от порта 1176 на хосте svr4, так что в случае отсутствия команды PORT клиент и сервер используют тот же самый номер порта для соединения данных. (Мы удалили объявления окна и значения типа сервиса.)

Рисунок 27.8 Соединение данных для двух последовательных команд LIST.

Последовательность событий в данном случае следующая.

  1. Управляющее соединение устанавливается от порта клиента 1176 на порт сервера 21. (Здесь это не показано.)
  2. Когда клиент осуществляет пассивное открытие для соединения данных на порт 1176, он должен указать опцию SO_REUSEADDR, так как этот порт уже используется управляющим соединением клиента.
  3. Сервер осуществляет активное открытие соединения данных (сегмент 1) с порта 20 на порт 1176. Клиент принимает это (сегмент 2), даже если порт 1176 уже используется клиентом, потому что две пары сокетов

    <svr4, 1176, bsdi, 21>
    <svr4, 1176, bsdi, 20>

    различны (номер порта на bsdi отличается). TCP демультиплексирует входящие сегменты, просматривая IP адрес источника, номер порта источника, IP адрес назначения и номер порта назначения, поэтому пока один из четырех элементов отличается, все в порядке.

  4. Сервер осуществляет активное закрытие соединения данных (сегмент 5), при этом пара сокетов на сервере

    <svr4, 1176, bsdi, 20>

    помещается в состояние ожидания 2MSL.

  5. Клиент отправляет еще одну команду LIST по управляющему соединению. (Мы не показали этого.) Перед этим клиент осуществляет пассивное открытие порта 1176 на своем конце соединения данных. Клиент должен указать снова опцию SO_REUSEADDR, так как номер порта 1176 уже используется.
  6. Сервер осуществляет активное открытие соединения данных с порта 20 на порт 1176. Перед тем как сделать это, сервер должен указать SO_REUSEADDR, так как локальный порт (20) связан с соединением, которое находится в состоянии ожидания 2MSL, однако, как мы показали в разделе "Диаграмма состояний передачи TCP" главы 18, соединение открыть не удасться. Дело в том, что пара сокет для требуемого соединения эквивалентна паре сокет из шага 4, которая все еще находится в состоянии ожидания 2MSL. Правила TCP запрещают серверу отправить SYN. Для сервера не существует способа игнорировать состояние 2MSL для пары сокетов, если ему понадобилось повторно использовать эту же пару сокетов.

    В этом месте BSD серверы повторяют попытки установить соединение каждые 5 секунд, до 18 раз, что в целом составляет 90 секунд. Мы видим, что сегмент 9 успешно прошел примерно через одну минуту. (Мы говорили в главе 18, что SVR4 использует MSL равное 30 секундам, при этом получается, что ожидание 2MSL составляет 1 минуту.) Мы не увидим ни одного SYN в это время на временной диаграмме, потому что активное открытие не удалось, и TCP модуль сервера даже не пытается посылать SYN.

Причина, по которой требования к хостам Host Requirements RFC рекомендуют использовать команду PORT, заключается в том, что эта команда позволяет обойти состояние ожидания 2MSL между последовательными использованиями соединения данных. Так как порты последовательно меняются на одном конце, проблема, которую мы только что показали, исчезает сама собой.

Передача текстовых файлов: представление NVT ASCII или двоичное?

Давайте убедимся в том, что при передаче текстовых файлов по умолчанию используется формат NVT ASCII. В этот раз мы не будем использовать флаг -d, поэтому мы не увидим команды клиента, однако клиент все еще печатает отклики от сервера:

sun % ftp bsdi
Connected to bsdi.
220 bsdi FTP server (Version 5.60) ready.

Name (bsdi:rstevens):                                            вводим RETURN
331 Password required for rstevens.

Password: вводим пароль
230 User rstevens logged in.

ftp> get hello.c                                                получаем файл
200 PORT command successful.

150 Opening ASCII mode data connection for hello.c (38 bytes).
226 Transfer complete.                                           сервер сообщает, что файл содержит 38 байт

local: hello.c remote: hello.c                                   вывод от клиента
42 bytes received in 0.0037 seconds (11 Kbytes/s)                42 байта пришло по соединению данных

ftp> quit
221 Goodbye.

sun % ls -l hello.c
-rw-rw-r-- 1 rstevens 38 Jul 18 08:48 hello.c                    однако файл содержит 38 байт

sun % wc -l hello.c                                              подсчет строк в файле
4 hello.c

Сорок два байта было передано по соединению данных, потому что файл содержит четыре строки. Каждый Unix символ новой строки (\n) конвертируется в 2-байтную последовательность NVT ASCII конец строки (\r\n) сервером для передачи, а затем конвертируется обратно клиентом при записи на диск.

Более новые клиенты стараются определить, используется ли подобная система на сервере, и если да, передают файлы в двоичном виде (image тип файла) вместо ASCII. Это помогает в двух случаях.

  1. Отправитель и получатель не должны просматривать каждый байт (это большая экономия времени и ресурсов).
  2. Передается меньше байт, если операционная система хоста использует меньше байтов в качестве символа конца строки, нежели 2-байтовая последовательность NVT ASCII (это меньшая часть экономии).

Мы можем увидеть подобную оптимизацию с использованием BSD/386 клиента и сервера. Включен отладочный режим, что позволяет увидеть команды FTP клиента:

bsdi % ftp -d slip                                        указываем опцию -d, чтобы видеть команды клиента
Connected to slip.
220 slip FTP server (Version 5.60) ready.

Name (slip:rstevens):                                      вводим RETURN
---> USER rstevens
331 Password required for rstevens.

Password: вводим пароль
---> PASS XXXX
230 User rstevens logged in.

---> SYST                                                  это клиент посылает автоматически
215 UNIX Type: L8 Version: BSD-199103                      отклик сервера

Remote system type is UNIX.                                вывод информации клиентом
Using binary mode to transfer files.                       вывод информации клиентом

ftp> get hello.c                                          получить файл
---> TYPE I                                                это клиент посылает автоматически
200 Type set to I.

---> PORT 140,252,13,66,4,84                               номер порта = 4 х 256 + 84 = 1108
200 PORT command successful.

---> RETR hello.c
150 Opening BINARY mode data connection for hello.c (38 bytes).
226 Transfer complete.

38 bytes received in 0.035 seconds (1.1 Kbytes/s)          в этот раз только 38 байт

ftp> quit

---> QUIT
221 Goodbye.

После того как мы правильно ввели имя и пароль серверу, FTP клиент автоматически посылает команду SYST, в ответ на которую сервер сообщает свой тип системы. Если отклик начинается со строки "215 UNIX Type: L8", и если клиент работает под управлением Unix системы с 8-битными байтами, для передачи всех файлов используется двоичный (image) режим, если его не сменит пользователь.

Когда мы забираем файл hello.c, клиент автоматически посылает команду TYPE I, чтобы установить тип файла в двоичный. На этот раз по соединению данных было передано 38 байт.

Требования к хостам Host Requirements RFC говорят, что FTP сервер должен поддерживать команду SYST (это было необязательным условием в RFC 959). Из систем описанных в тексте (см. внутреннюю сторону обложки) эту команду поддерживают BSD/386 и AIX 3.2.2. SunOS 4.1.3 и Solaris 2.x выдают на эту команду отклик 500 (команда неизвестна). SVR4 ведет себя совсем по-дикому: отвечает 500 и закрывает управляющее соединение!

Прекращение передачи файла: сигнал синхронизации Telnet

Сейчас мы посмотрим, как FTP клиент прекращает передачу файла от сервера. Прекратить передачу файла от клиента к серверу достаточно просто - клиент прекращает посылку данных по соединению данных и посылает ABOR на сервер по управляющему соединению. Прекращение приема, однако, более сложная задача, потому что клиент хочет, чтобы сервер немедленно прекратил передачу данных. Ранее мы упоминали, что для этого используется сигнал синхронизации Telnet.

Мы начали прием, после чего ввели символ прерывания. Ниже приведена интерактивная сессия, процесс идентификации пользователя удален:

ftp> get a.out                                           получаем большой файл

---> TYPE I                                              клиент и сервер - Unix системы с 8-битными байтами

200 Type set to I.

---> PORT 140,252,13,66,4,99
200 PORT command successful.

---> RETR a.out
150 Opening BINARY mode data connection for a.out (28672 bytes).

^?                                                       вводим символ прерывания
receive aborted                                          вывод от клиента
waiting for remote to finish abort                       вывод от клиента

426 Transfer aborted. Data connection closed.
226 Abort successful

1536 bytes received in 1.7 seconds (0.89 Kbytes/s)

После того как введен символ прерывания, клиент немедленно сообщает, что он инициализировал прерывание передачи файла и ожидает, когда сервер его завершит. Сервер посылает два отклика: 426 и 226. Оба отклика посылаются Unix сервером, когда он принимает срочные данные от клиента с командой ABOR.

На рисунках 27.9 и 27.10 показаны временные диаграммы для этой сессии. Мы объединили вместе управляющее соединение (сплошные линии) и соединение данных (прерывистые линии), чтобы показать взаимосвязь между ними.

Рисунок 27.9 Прерывание передачи файла (первая половина).

Первые 12 сегментов на рисунке 27.9 как раз такие, как мы ожидали. Команды и отклики по управляющему соединению настраивают системы на передачу файла, открывается соединение данных и от сервера к клиенту посылается первый сегмент данных.

Рисунок 27.10 Прерывание передачи файла (вторая половина).

Сегмент 13 на рисунке 27.10 - это квитанция на шестой сегмент данных от сервера по соединению данных. Затем следует сегмент 14, который сгенерирован нашим вводом символа прерывания. Для того чтобы прервать передачу, клиент посылает десять байт:

<IAC, IP, IAC, DM, A, B, O, R, \r, \n>

Мы видим два сегмента (14 и 15), это связано с проблемой определения положения указателя срочности TCP (подробно описанной в разделе "Режим срочности (Urgent Mode)" главы 20). (На рисунке 26.17 мы видели, как Telnet решает эту проблему.) Требование к хостам Host Requirements RFC говорит, что указатель срочности должен указывать на последний байт срочных данных, тогда как большинство Berkeley реализаций указывают на один байт позади последнего байта срочных данных. FTP клиент специально пишет первые 3 байта как срочные данные, зная, что указатель срочности будет (некорректно) указывать на следующий байт, который будет записан (метка данных, DM, с номером последовательности 54). Эта первая запись из 3 байт срочных данных посылается немедленно, вместе с указателем срочности, за ними следуют следующие 7 байт. (BSD FTP сервер не имеет проблемы с интерпретацией указателя срочности, который используется клиентом. Когда сервер принимает срочные данные по управляющему соединению, он читает следующую FTP команду, в поиске ABOR или STAT, игнорируя любые вложенные команды Telnet.)

Несмотря на то, что сервер сообщил о прекращении передачи (сегмент 18, по управляющему соединению), клиент получил еще 14 сегментов данных (номера последовательности 1537 - 5120) по соединению данных. Эти сегменты, скорее всего, были поставлены в очередь в драйвере сетевого устройства на сервере, когда был принят сигнал о прекращении передачи. Однако клиент печатает "1536 байт принято", а это означает, что он проигнорировал эти сегменты данных (сегменты 17 и позже), которые были приняты после отправки прерывания передачи (сегменты 14 и 15).

В случае Telnet, когда пользователь вводит символ прерывания (рисунок 26.17), Unix клиент по умолчанию не посылает команду прерывания процесса в виде срочных данных. В этом нет ничего страшного, потому что существует очень маленькая вероятность того, что поток данных от клиента к серверу остановлен управлением потока данных. В случае FTP клиент также посылает команду прерывания процесса по управляющему соединению, и так как используется два соединения, существует очень небольшая вероятность того, что управляющее соединение будет остановлено управлением потока данных. Почему FTP посылает команду прерывания процесса в виде срочных данных, тогда как Telnet не делает этого? Дело в том, что FTP использует два соединения, тогда как Telnet использует одно, а для некоторых операционных систем довольно сложно обработать информацию приходящую по двум соединениям одновременно. FTP подразумевает, что эти операционные системы по крайней мере смогут понять, что по управляющему соединению срочные прибыли данные, и что в свою очередь позволит серверу переключиться от обработки соединения данных на обработку управляющего соединения.

Анонимный FTP

Существует невероятно популярная форма использования FTP. Она называется анонимный FTP (anonymous FTP). Если эта форма поддерживается сервером, она позволяет любому получить доступ к серверу и использовать FTP для передачи файлов. С помощью анонимного FTP можно получить доступ к огромному объему свободно распространяемой информации.

Тут возникает еще одна проблема, которая заключается в том, что очень сложно найти то, что нужно в этом море информации. Кратко это описано в разделе "Archie, WAIS, Gopher, Veronica и WWW" главы 30.

Воспользуемся анонимным FTP, чтобы получить файл опечаток для этой книги с хоста ftp.uu.. Чтобы использовать анонимный FTP, мы входим в систему с именем пользователя "anonymous" (чтобы выучить, как пишется это слово, попробуйте повторить его несколько раз). Когда появляется приглашение ввести пароль, мы вводим наш адрес электронной почты.

sun % ftp ftp.uu.net
Connected to ftp.uu.net.
220 ftp.UU.NET FTP server (Version 2.OWU(13) Fri Apr 9 20:44:32 EDT 1993) ready.

Name (ftp.uu.net:rstevens): anonymous
331 Guest login ok, send your complete e-mail address as password.

Password:                                                  мы вводим rstevens@noao.edu; это не отображается эхом

230-
230- Welcome to the UUNET archive.
230- A service of UUNET Technologies Inc, Falls Church, Virginia
230- For information about UUNET, call +1 703 204 8000, or see the files
230- in /uunet-info
                                                          здесь еще несколько приветственных строк

230 Guest login ok, access restrictions apply.

ftp> cd published/books                                    переходим в нужную директорию
250 CWD command successful.

ftp> binary                                                будем использовать двоичный формат файла
200 Type set to I.

ftp> get stevens.tcpipiv1.errata.Z                         получаем файл
200 PORT command successful.
150 Opening BINARY mode data connection for stevens.tcpipiv1.errata.Z (105 bytes).
226 Transfer complete. (у вас может быть другой размер файла)

local: stevens.tcpipiv1.errata.Z remote: stevens.tcpipiv1.errata.Z
105 bytes received in 4.1 seconds (0.83 Kbytes/s)

ftp> quit
221 Goodbye.

sun % uncompress stevens.tcpipiv1.errata.Z

sun % more stevens.tcpipiv1.errata

Программа uncompress используется потому, что большинство файлов, доступных через анонимный FTP, сжаты с использованием Unix программы compress(1), такие файлы имеют расширение .Z. Эти файлы должны быть переданы в виде двоичных файлов, а не ASCII файлов.

Анонимный FTP с неизвестного IP адреса

Мы можем объединить вместе некоторые характеристики маршрутизации и системы имен (Domain Name System) с использованием анонимного FTP. В разделе "Запросы указателя" главы 14 мы говорили о запросах указателя в DNS - которые воспринимают IP адреса и возвращают имя хоста. К сожалению, не все администраторы систем корректно конфигурируют свои DNS серверы таким образом, чтобы они отвечали на запросы указателей. Они часто добавляют новые хосты к файлам, необходимым для установления соответствия имя-адрес, однако забывают добавить их в файлы, устанавливающие соответствие адрес-имя. С этим можно столкнуться, работая с программой traceroute, когда она выдает IP адреса вместо имен хостов.

Некоторые анонимные FTP серверы требуют, чтобы клиент имел корректное имя домена. Это позволяет серверу зарегистрировать имя домена и хоста, с который осуществлен заход. Единственная информация, которую может узнать сервер о клиенте из IP датаграммы - это IP адрес клиента. Сервер осуществляет запрос указателя, чтобы узнать доменное имя клиента. Если DNS сервер, отвечающий за хост клиента, не сконфигурирован корректно, запрос указателя не сработает.

Давайте смоделируем подобную ошибку.

  1. Изменим IP адрес хоста slip (см. рисунок на внутренней стороне обложки) на 140.252.13.67. Это нормальный IP адрес для нашей подсети, однако его нет на DNS сервере домена noao.edu.
  2. Изменим IP адрес назначения SLIP канала на bsdi на 140.252.13.67.
  3. Добавим пункт в таблицу маршрутизации на sun, который будет направлять датаграммы для 140.252.13.67 на маршрутизатор bsdi. (Обратитесь к описанию этой таблицы маршрутизации, которое приведено в разделе "Принципы маршрутизации" главы 9.)

Хост slip доступен по Internet, потому что, как мы видели в разделе "RIP: протокол обмена информацией о маршрутизации" главы 10, маршрутизаторы gateway и netb все еще посылают датаграммы, которые предназначены в подсеть 140.252.13, на маршрутизатор sun. Наш маршрутизатор sun знает, что делать с этими датаграммами, в соответствии с пунктом маршрутизации, который мы сделали в шаге номер 3. Таким образом, мы создали хост, который полностью подключен к Internet, однако не имеющий корректного имени домена. Таким образом, запрос указателя для IP адреса 140.252.13.67 не будет работать. А сейчас воспользуемся анонимным FTP на сервер, который, как мы знаем, требует корректного имени домена.

slip % ftp ftp.uu.net
Connected to ftp.uu.net.

220 ftp.UU.NET FTP server (Version 2.OWU(13) Fri Apr 9 20:44:32 EDT 1993) ready.
Name (ftp.uu.net:rstevens): anonymous

530- Sorry, we're unable to map your IP address 140.252.13.67 to a hostname
530- in the DNS. This is probably because your nameserver does not have a
530- PTR record for your address in its tables, or because your reverse
530- nameservers are not registered. We refuse service to hosts whose
530- names we cannot resolve. If this is simply because your nameserver is
530- hard to reach or slow to respond then try again in a minute or so, and
530- perhaps our nameserver will have your hostname in its cache by then.
530- If not, try reaching us from a host that is in the DNS or have your
530- system administrator fix your servers.
530 User anonymous access denied..

Login failed.
Remote system type is UNIX.
Using binary mode to transfer files.

ftp> quit
221 Goodbye.

(К сожалению, мы не можем найти имя хоста, соответствующее вашему IP адресу. Возможно, это потому, что у вашего сервера имен нет в таблице PTR записи, соответствующей вашему адресу, или ваш сервер не зарегистрирован. Мы не работаем с хостами, для которых мы не можем определить имя домена. Если проблема в том, что ваш DNS сервер медленно отвечает или до него сложно достучаться, попробуйте еще раз через минуту. Может быть, в этот раз DNS сервер уже будет иметь ваше имя в своем кэше. Если дело не в этом, попробуйте зайти к нам с хоста, который корректно зарегистрирован в DNS, или попросите администратора починить ваш сервер.) Сообщение об ошибке говорит само за себя.

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

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

Управление соединением, которое осуществляется FTP, позволило нам увидеть, какие требования выдвигает TCP к управлению соединением. Мы видели состояние ожидания 2MSL на клиенте, который не выдает команды PORT.

FTP использует формат NVT ASCII для всех команд и откликов, которые передаются по управляющему соединению. Режим передачи данных по умолчанию это, как правило, также NVT ASCII. Мы видели, что более новые Unix клиенты автоматически посылают команду, чтобы убедиться в том, что сервер это Unix хост с 8-битными байтами, и если так, используют двоичный режим для передачи всех файлов, которые являются более эффективными.

Также мы показали пример анонимного FTP, популярного способа распространения программного обеспечения по Internet.

Упражнения

  1. Обратитесь к рисунку 27.8. Что изменится, если клиент осуществит активное открытие второго соединения данных вместо сервера?
  2. В примере для FTP клиента в этой главе мы сказали, что такие строки, как, например

    local: hello.c remote: hello.c
    42 bytes received in 0.0037 seconds (11 Kbytes/s)

    выдаются клиентом. Не заглядывая в исходные тексты, как можно определить, что они не от сервера?


SMTP: простой протокол передачи почты

Глава 28 SMTP: простой протокол передачи почты

Введение

Электронная почта (e-mail), несомненно, одно из самых популярных приложений. [Caceres et al. 1991] показывает, что примерно половина всех TCP соединений занята передачей почтовых сообщений с исполььзованием простого протокола передачи почты (SMTP - Simple Mail Transfer Protocol). (С точки зрения количества переданных байт, по FTP соединениям передается значительно больше данных.) [Paxson 1993] обнаружил, что среднее почтовое сообщение содержит примерно 1500 байт данных, однако некоторые сообщения содержат мегабайты данных, потому что электронная почта иногда используется для посылки файлов.

На рисунке 28.1 показан обмен почтой с использованием TCP/IP.

Рисунок 28.1 Доставка электронной почты в Internet.

Пользователи общаются с пользовательскими агентами (user agent). В настоящее время существует очень много реализаций пользовательских агентов. Популярные пользовательские агенты для Unix это MH, Berkeley Mail, Elm и Mush.

Обмен почтой с использованием TCP осуществляется посредством агентов передачи сообщений (MTA - message transfer agent). Наиболее распространенные MTA для Unix систем это Sendmail. Пользователи обычно не общаются с MTA. В задачу системного администратора входит установка локального MTA.

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

RFC 821 [Postel 1982] описывает протокол SMTP. А именно то, как два MTA общаются друг с другом по TCP соединению. RFC 822 [Crocker 1982] приводит формат сообщения электронной почты, которое передается между двумя MTA в соответствии с RFC 821.

Протокол SMTP

При общении между двумя MTA используется NVT ASCII. Команды посылаются клиентом серверу, а сервер отвечает с помощью цифровых кодов и опциональных текстовых строк (для чтения человеком). Это несколько напоминает сценарий, который мы видели для FTP в предыдущей главе.

Клиент может послать серверу небольшое количество команд: меньше дюжины. (Для сравнения, FTP имеет более сорока команд.) Вместо того чтобы описывать команды одну за другой, мы начнем с простого примера, для того чтобы показать что происходит, когда отправляется почта.

Простой пример

Пошлем простое сообщение размером в одну строку и посмотрим, как осуществляется SMTP соединение. Для этого воспользуемся нашим пользовательским агентом с флагом -v, который передается транспортному агенту почты (Sendmail в данном случае). Когда указан этот флаг, MTA отображает все, что посылается и принимается по SMTP соединению. Строки, начинающиеся с >>>, это команды, посылаемые SMTP клиентом, а строки, начинающиеся с 3-циферного кода отклика, приходят от SMTP сервера. Ниже приводится интерактивная сессия:

sun % mail -v rstevens@noao.edu                          запускаем пользовательского агента
To: rstevens@noao.edu                                     это вывод от пользовательского агента
Subject: testing                                         затем нас просят ввести тему сообщения
                             пользовательский агент добавляет одну пустую строку между заголовком и телом
1, 2, 3.                     это тело сообщения
.                            вводим точку в начале строки, сообщающую, что ввод сообщения завершен

Sending letter ... rstevens@noao.edu...                   отладочный вывод от пользовательского агента
                                                         следующий вывод от MTA (Sendmail)
Connecting to mailhost via ether...
Trying 140.252.1.54... connected.
220 noao.edu Sendmail 4.1/SAG-noao.G89 ready at Mon, 19 Jul 93 12:47:34 MST

>>> HELO sun.tuc.noao.edu.
250 noao.edu Hello sun.tuc.noao.edu., pleased to meet you

>>> MAIL From:<rstevens@sun.tuc.noao.edu>
250 <rstevens@sun.tuc.noao.edu>... Sender ok

>>> RCPT To:<rstevens@noao.edu>
250 <rstevens@noao.edu>... Recipient ok

>>> DATA
354 Enter mail, end with "." on a line by itself

>>> .
250 Mail accepted

>>> QUIT
221 noao.edu delivering mail

rstevens@noao.edu... Sent
sent.                                                     это вывод от пользовательского агента

Для отправки почты было использовано всего пять SMTP команд: HELO, MAIL, RCPT, DATA и QUIT.

Команда mail запускает пользовательского агента. Затем необходимо ввести тему сообщения, после чего можно печатать тело сообщения. Ввод точки в начале строки завершает сообщение, и пользовательский агент передает почту в MTA для доставки.

Клиент осуществляет активное открытие на TCP порт 25, после чего ожидает приветственного сообщения (отклик с кодом 220) от сервера. Ответ сервера должен начинаться с полного имени домена хоста сервера: noao.edu в данном примере. (Обычно вместе с цифровым кодом отклика возвращается необязательный текст. Здесь требуется имя домена.)

Затем клиент идентифицирует себя с использованием команды HELO. В качестве аргумента указывается полное имя домена хоста клиента: sun.tuc.noao.edu.

Команда MAIL идентифицирует автора сообщения (или отправителя). Следующая команда, RCPT, идентифицирует получателя. Если сообщение предназначено нескольким получателям, может быть исполнено несколько команд RCPT.

Клиент отправляет содержимое почтового сообщения с использованием команды DATA. Строка, содержащая только точку, указывает на конец сообщения. Последняя команда, QUIT, прекращает обмен почтой.

На рисунке 28.2 приведена временная диаграмма SMTP соединения между отправителем SMTP (клиент) и получателем SMTP (сервер). Мы удалили все связанное с установлением и разрывом соединения, а также объявления размера окна.

В этом примере пользовательский агент отправляет сообщение длиной в одну строку ("1, 2, 3."), однако в сегменте 12 передается 393 байта данных. Следующие 12 строк включают в себя 393 байта, которые были посланы клиентом:

Received: by sun.tuc.noao.edu. (4.1/SMI-4.1)
          id AA00502; Mon, 19 Jul 93 12:47:32 MST
Message-Id: <9307191947.AA00502@sun.tuc.noao.edu.>
From: rstevens@sun.tuc.noao.edu (Richard Stevens)
Date: Mon, 19 Jul 1993 12:47:31 -0700
Reply-To: rstevens@noao.edu
X-Phone: +1 602 676 1676
X-Mailer: Mail User's Shell (7.2.5 10/14/92)
To: rstevens@noao.edu
Subject: testing

1, 2, 3.

Рисунок 28.2 Принцип доставки почты SMTP.

MTA добавляет первые три строки, а также Received: и Message-Id:, а следующие девять строк генерируются пользовательским агентом.

SMTP команды

Минимальные SMTP реализации поддерживают восемь команд. Мы видели пять из них в предыдущем примере: HELO, MAIL, RCPT, DATA и QUIT.

Команда RSET прекращает текущую передачу почты и заставляет оба конца "сброситься". Любая сохраненная информация об отправителе, получателе или содержимое почты уничтожается.

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

Команда NOOP не делает ничего, однако заставляет сервер ответить, что все нормально, а именно откликом с кодом 200.

Существуют также дополнительные, необязательные команды. EXPN расширяет список почты и часто используется системными администраторами, так же как и VRFY. Более того, большинство версий Sendmail обрабатывают эти две команды одинаково.

Версия 8 Sendmail в 4.4BSD больше не обрабатывает одинаково эти две команды. VRFY не расширяет псевдонимы и не отслеживает файлы .forward.

Команда TURN позволяет клиенту и серверу поменяться ролями, чтобы послать почту в обратном направлении, не разрывая TCP соединение и не создавая новое. (Sendmail не поддерживает эту команду.) Существуют еще три команды (SEND, SOML и SAML), которые очень редко реализуются и призваны заменить собой команду MAIL. Эти три команды в своих комбинациях позволяют доставлять почту непосредственно на пользовательский терминал (если пользователь находится терминалом в системе) или складывать ее в почтовый ящик получателя.

Конверты, заголовки и тело

Электронная почта состоит из трех частей.

  1. Конверт используется MTA для доставки. В нашем примере конверт был указан двумя SMTP командами:

    MAIL From:<rstevens@sun.tuc.noao.edu>
    RCPT To:<rstevens@noao.edu>

    RFC 821 определяет содержимое и интерпретацию конверта, а также протокол, который используется для обмена почтой по TCP соединению.

  2. Заголовки используются пользовательскими агентами. В примере мы видели девять полей заголовков: Received, Message-Id, From, Date, Reply-To, X-Phone, X-Mailer, To и Subject. Каждое поле заголовка содержит имя, после которого следует двоеточие, а затем следует значение этого поля. RFC 822 определяет формат и интерпретацию полей заголовка. (Заголовки, начинающиеся с X-, это поля, определяемые пользователем. Другие определены в RFC 822.) Длинные поля заголовков, такие как Received в примере, могут быть разбиты на несколько строк, причем дополнительные строки начинаются с пробела.
  3. Тело это содержимое сообщения от отправителя к получателю. RFC 822 определяет тело сообщения как текстовые строки в формате NVT ASCII. Когда происходит передача с использованием команды DATA, заголовки передаются первыми, за ними следует пустая строка и затем следует тело сообщения. Каждая строка, передаваемая с использованием команды DATA, должна меньше чем 1000 байт.

Затем пользовательский агент берет то, что было указано в качестве тела сообщения, добавляет некоторые заголовки и передает результат в MTA. MTA добавляет свои заголовки, добавляет конверт и посылает результат другому MTA.

Термин содержимое часто используется, чтобы описать комбинацию заголовков и тела. Содержимое посылается клиентом с использованием команды DATA.

Транслирующие агенты

Первая информационная строка от локального MTA в примере была следующей: "Connecting to mailhost via ether".

Это объясняется тем, что система автора сконфигурирована таким образом, чтобы посылать всю нелокальную исходящую почту на транслирующую машину для дальнейшей доставки. Так сделано по двум причинам. Во-первых, упрощается конфигурацию всех MTA, кроме MTA транслирующей системы. (Конфигурация MTA не проста, каждый, кто когда-либо работал с Sendmail, может это подтвердить.) Во-вторых, одна система в организации может выступать в роли почтового ретранслятора, возможно пряча тем самым все остальные индивидуальные системы.

В нашем примере транслирующая система имеет имя хоста mailhost в локальном домене (.tuc.noao.edu), а все индивидуальные системы сконфигурированы таким образом, чтобы посылать свою почту на этот хост. Мы можем исполнить команду host, чтобы посмотреть, как это имя определено в DNS:

sun % host mailhost
mailhost.tuc.noao.edu CNAME noao.edu                     каноническое имя
noao.edu A 140.252.1.54                                  ее реальный IP адрес

Если хост, который используется в качестве ретранслирующего, в будущем изменится, необходимо только сменить его DNS имя - конфигурация почты всех индивидуальных систем не изменится.

Большинство организаций в настоящее время используют транслирующие системы. На рисунке 28.3 показана упрощенная схема почты в Internet (рисунок 28.2), причем сделано предположение, что большинство отправляющих хостов и окончательные получающие хосты используют транслирующие хосты.

Между отправителем и получателем присутствует четыре MTA. Локальный MTA на хосте отправителя просто доставляет почту на свой транслирующий MTA. (Этот транслирующий MTA должен иметь имя mailhost в домене организации.) Здесь SMTP использует локальные сети организации. Затем транслирующий MTA организации отправителя посылает почту по Internet на транслирующий MTA организации получателя. Другие транслирующие MTA доставляют почту на хост получателя после общения с локальными MTA хоста получателя. Все MTA в примере используют SMTP, хотя существует возможность того, что на этом пути могут встретиться и другие протоколы доставки.

NVT ASCII

Одна из характеристик SMTP заключается в том, что он использует NVT ASCII абсолютно везде: в конверте, заголовке и теле сообщения. Как мы сказали в разделе "Протокол Telnet" главы 26, NVT ASCII это 7-битная кодировка символов, символы при этом передаются как 8-битные байты, у которых старший бит установлен в 0.

Рисунок 28.3 Электронная почта по Internet с транслирующими системами на обоих концах.

В разделе "Будущее SMTP" этой главы мы обсудим некоторые более новые характеристики почты в Internet, расширенный SMTP и мультимедийную почту (MIME), которая позволяет отправлять и принимать звуковую и видео информацию. Мы увидим, что MIME использует NVT ASCII в качестве конверта, заголовка и тела, а изменения касаются только пользовательских агентов.

Интервалы между ретрансляциями

Когда пользовательский агент передает новое почтовое сообщение своему MTA, попытка доставить сообщение обычно осуществляется немедленно. Если доставить сообщение не удалась, MTA поставит сообщение в очередь и повторит попытку позже.

Требования к хостам Host Requirements RFC рекомендует устанавливать первоначальный тайм-аут по крайней мере в 30 минут. Отправитель должен повторять свои попытки по меньшей мере 4-5 дней. Более того, если сбои в доставке происходят часто (получатель вышел из строя или произошла временная потеря сетевого соединения), имеет смысл делать две попытки установить соединение в течение первого часа, когда сообщение находится в очереди.

Примеры SMTP

В предыдущем разделе показана обычная доставка почты; сейчас рассмотрим, как для доставки почты записи используются MX, а также проиллюстрируем работу команд VRFY и EXPN.

Записи MX: хост не подключен непосредственно к Internet

В разделе "Записи ресурсов" главы 14 мы сказали, что один из типов записи ресурса в DNS используется для обмена почты и называется записями MX. В следующем примере мы покажем, как используются записи MX, для отправки почты на хосты, не подключенные к Internet непосредственно. RFC 974 [Partridge 1986] описывает, как MTA обрабатывает записи MX.

Хост mlfarm.com не подключен к Internet, однако имеет MX запись, указывающую на перенаправителя почты, который, в свою очередь, находится в Internet:

sun % host -a -v -t mx mlfarm.com
The following answer is not authoritative:
mlfarm.com                    86388     IN       MX       10 mercury.hsi.com
mlfarm.com                    86388     IN       MX       15 hsi86.hsi.com
Additional information:
mercury.hsi.com               86388     IN        A       143.122.1.91
hsi86.hsi.com                172762     IN        A       143.122.1.6

Здесь показаны две записи MX, каждая с различной степенью предпочтительности. Мы ожидаем, что MTA начнет с меньшего из двух значений предпочтительности.

Следующий скрипт показывает, как почта будет послана этому хосту:

sun % mail -v ron@mlfarm.com                  флаг -v, чтобы посмотреть, что делает MTA
To: ron@mlfarm.com
Subject: MX test message
                                              здесь печатается тело сообщения (не показано)
.                                              точка в конце строки завершает сообщение
Sending letter ... ron@mlfarm.com...
Connecting to mlfarm.com via tcp...
mail exchanger is mercury.hsi.com              найдена запись MX
Trying 143.122.1.91... connected.              делается первая попытка с минимальной предпочтительностью
220 mercury.hsi.com ...
                                              здесь происходит обычная доставка почты SMTP

MTA определил, что хост назначения имеет MX запись и использует MX запись с минимальным значением предпочтительности.

Перед запуском этого примера на хосте sun была изменена конфигурация таким образом, чтобы при отправке почты не использовался обычный транслирующий хост, поэтому мы видим обмен почтой с хостом назначения. Также в конфигурации было сказано использовать DNS сервер на хосте noao.edu (к которому можно получить доступ через SLIP канал с дозвоном). Благодаря этому, с использованием tcpdump, мы можем посмотреть и передачу почты, и DNS траффик на SLIP канале. На рисунке 28.4 показана начальная часть вывода команды tcpdump.

1  0.0                          sun.1624 > noao.edu.53: 2+ MX? mlfarm.com. (28)
2  0.445572 (0.4456)            noao.edu.53 > sun.1624: 2* 2/0/2 MX
                               mercury.hsi.com. 10 (113)
3  0.505739 (0.0602)            sun.1143 > mercury.hsi.com.25: S 1617536000:1617536000(0)
                               win 4096
4  0.985428 (0.4797)            mercury.hsi.com.25 > sun.1143: S 1832064000:1832064000(0)
                               ack 1617536001 win 16384
5  0.986003 (0.0006)            sun.1143 > mercury.hsi.com.25: . ack 1 win 4096
6  1.735360 (0.7494)            mercury.hsi.com.25 > sun.1143: P 1:90(89) ack 1 win 16384

Рисунок 28.4 Посылка почты на хост, который использует MX записи.

В строке 1 MTA запрашивает свой DNS сервер о MX записи для mlfarm.com. Знак плюс, следующий за 2, означает, что установлен флаг "необходима рекурсия". Отклик в строке 2 имеет установленным бит полномочности (звездочка, следующая за 2) и содержит 2 записи ресурса (RR) ответа (два имени MX хостов), 0 записей ресурса RR прав доступа и две дополнительные RR (IP адреса двух хостов).

В строках 3-5 устанавливается TCP соединение с SMTP сервером на хосте mercury.hsi.com. Первоначальный отклик сервера (220) показан в строке 6.

Каким-либо образом хост mercury.hsi.com должен доставить это почтовое сообщение в пункт назначения - mlfarm.com. Протокол UUCP является распространенным способом обмена почтой с MX узлами для систем, которые не подключены к Internet.

В этом примере MTA запрашивает MX запись, получает ее и посылает почту. К сожалению, взаимодействие между MTA и DNS может отличаться в зависимости от реализации. RFC 974 указывает, что MTA должен сначала запросить MX записи, и если они не найдены, попробовать доставить почту на хост назначения (то есть запросить DNS на предмет записи A для хоста, его IP адрес). MTA должен также поинтересоваться записями CNAME в DNS (канонические имена).

Например, если мы пошлем почту хосту rstevens@mailhost.tuc.noao.edu с BSD/386 хоста, MTA (Sendmail) проделает следующее.

  1. Sendmail запрашивает DNS, существуют ли записи CNAME для хоста mailhost.tuc.noao.edu. Мы видим, что записи CNAME существуют:

    sun % host -t cname mailhost.tuc.noao.edu
    mailhost.tuc.noao.edu CNAME noao.edu

  2. Делается DNS запрос о существовании записи CNAME для noao.edu, в отклике сообщается, что записи не существует.
  3. Затем Sendmail запрашивает DNS о существовании MX записей для noao.edu и получает одну MX запись:

    sun % host -t mx noao.edu
    noao.edu MX noao.edu

  4. Sendmail запрашивает DNS о существовании записей A (IP адрес) для noao.edu и получает значение 140.252.1.54. (Возможно, эта A запись была возвращена DNS сервером для noao.edu как дополнительная запись ресурса (RR) с MX откликом в шаге 3.)
  5. Открывается SMTP соединение на адрес 140.252.1.54, и почта отправляется.

Запрос CNAME не осуществляется для данных, возвращенных в MX записи (noao.edu). Данные в MX записи не могут быть псевдонимами - они должны быть именем хоста, который имеет запись A.

Версия Sendmail, распространяемая с SunOS.4.1.3, обращается к DNS только с запросами о существовании MX записей и сразу прекращает все попытки, если MX запись не найдена.

MX записи: хосты, которые выключены

Другое использование MX записи заключается в том, чтобы найти альтернативного получателя почты, если хост назначения выключен. Если мы посмотрим DNS записи для нашего хоста sun, то увидим, что он имеет две MX записи:

sun % host -a -v -t mx sun.tuc.noao.edu
sun.tuc.noao.edu            86400     IN    MX        0 sun.tuc.noao.edu
sun.tuc.noao.edu            86400     IN    MX       10 noao.edu
Additional information:
sun.tuc.noao.edu            86400     IN     A        140.252.1.29
sun.tuc.noao.edu            86400     IN     A        140.252.13.33
noao.edu                     86400     IN     A        140.252.1.54

MX запись с минимальным уровнем предпочтительности указывает, что сначала надо попробовать доставить почту непосредственно на хост. Следующее значение предпочтительности указывает на необходимость осуществить доставку почты на хост noao.edu.

В следующем скрипте мы посылаем почту сами себе на хост sun.tuc.noao.edu, с хоста vangogh.cs.berkeley.edu, предварительно выключив SMTP сервер назначения. Когда на порт 25 прибывает запрос на соединение, TCP должен ответить сбросом (RST), так как не существует процесса, который может осуществить пассивное открытие для этого порта.

vangogh % mail -v rstevens@sun.tuc.noao.edu
A test to a host that's down.
.
EOT

rstevens@sun.tuc.noao.edu...Connecting to sun.tuc.noao.edu.(smtp)...
rstevens@sun.tuc.noao.edu...Connecting to noao.edu.(smtp)...

220 noao.edu ...
                                            дальше идет обычная передача почты SMTP

Мы видим, что MTA старается установить контакт с sun.tuc.noao.edu, затем прекращает попытки и устанавливает контакт с noao.edu.

На рисунке 28.5 показан вывод команды tcpdump, где показано, как TCP отвечает сбросом (RST) на входящие SYN.

1  0.0                    vangogh.3873 > 140.252.1.29.25: S 2358303745:2358303745(0) ...
2  0.000621 (0.0006)     140.252.1.29.25 > vangogh.3873: R 0:0(0) ack 2358303746 win 0

3  0.300203 (0.2996)     vangogh.3874 > 140.252.13.33.25: S 2358367745:2358367745(0) ...
4  0.300620 (0.0004)     140.252.13.33.25 > vangogh.3874: R 0:0(0) ack 2358367746 win 0

Рисунок 28.5 Попытки установить контакт с неработающим SMTP сервером.

В строке 1 vangogh отправляет SYN на порт 25 на первый IP адрес хоста sun: 140.252.1.29. В строке 2 мы видим отказ. SMTP клиент на vangogh пробует следующий IP адрес sun: 140.252.13.33 (строка 3), что также вызывает возврат RST (строка 4).

SMTP клиент не старается изменить свое поведение в результате ошибки, полученной на активное открытие, которое он осуществлял в строке 1, именно поэтому он старается обратиться к другому IP адресу в строке 2. Если ошибка была подобна "хост недоступен" (host unreachable) для первой попытки, возможно, что вторая попытка сработает.

Если причина, по которой активное открытие SMTP клиента не сработало, заключается в том, что хост выключен, мы увидим, что клиент будет повторно выдавать SYN на IP адрес 140.252.1.29 в течение 75 секунд (примерно так же, как на рисунке 18.6), затем отправит еще три SYN на IP адрес 140.252.13.33 в течение других 75 секунд. После 150 секунд клиент перейдет к следующей MX записи с более высоким значением предпочтительности.

Команды VRFY и EXPN

Команда VRFY проверяет, в порядке ли адрес получателя, не посылая при этом реальной почты. EXPN предназначена для расширения почтового псевдонима до почтового адреса. Большинство SMTP реализаций (такие как Sendmail) воспринимают эти команды абсолютно одинаково, однако мы уже отмечали, что более новые версии Sendmail проводят различие между ними.

В качестве простого теста мы можем подсоединиться к более новой версии Sendmail и посмотреть эти различия. (Мы удалили вывод клиента Telnet.)

sun % telnet vangogh.cs.berkeley.edu 25
220-vangogh.CS.Berkeley.EDU Sendmail 8.1C/6.32 ready at Tue, 3 Aug 1993 14:59:12 -0700
220 ESMTP spoken here

helo bsdi.tuc.noao.edu
250 vangogh.CS.Berkeley.EDU Hello sun.tuc.noao.edu [140.252.1.29], pleased to meet you

vrfy nosuchname
550 nosuchname... User unknown

vrfy rstevens
250 Richard Stevens <rstevens@vangogh.CS.Berkeley.EDU>

expn rstevens
250 Richard Stevens <rstevens@noao.edu>

Во-первых, обратите внимание на то, что мы специально напечатали неверное имя хоста для команды HELO: bsdi вместо sun. Большинство SMTP серверов берут IP адрес клиента и осуществляют запрос указателя DNS (глава 14, раздел "Запросы указателя"), после чего сравнивают имена хостов. Это позволяет серверу зарегистрировать соединение клиента, основываясь на его IP адресе, а не на имени, которое пользователь может указать неверно. Некоторые серверы отвечают юмористическими сообщениями, такими как "вы шарлатан" или "почему бы вам не позвонить самому себе...". В этом примере мы видели, что сервер просто напечатал реальное имя домена из запроса указателя вместе с IP адресом.

Затем мы вводим команду VRFY для несуществующего имени, на что сервер отвечает ошибкой 550. Затем мы печатаем существующее имя, а сервер отвечает именем пользователя на локальном хосте. Затем мы пробуем команду EXPN и получаем другой отклик. Команда EXPN определяет, что почта для этого пользователя будет перенаправлена и печатает адрес перенаправления.

На многих хостах команды VRFY и EXPN отключены, может быть с точки зрения секретности и иногда из-за того, что администраторы считают, что в этом находится какая-то дырка в секретности. Например, мы можем попробовать воспользоваться этими командами на SMTP сервере Белого Дома (США):

sun % telnet whitehouse.gov 25
220 whitehouse.gov SMTP/smap Ready.

helo sun.tuc.noao.edu
250 (sun.tuc.noao.edu) pleased to meet you.

vrfy clinton
500 Command unrecognized

expn clinton
500 Command unrecognized

Будущее SMTP

В настоящее время почта Internet заметно меняется. Вспомните, что почта Internet состоит из трех частей: конверт, заголовки и тело сообщения. Появляются новые SMTP команды, которые изменяют конверт, в заголовках могут быть использованы не-ASCII символы, а к телу добавляются структуры (MIME). В этом разделе мы по порядку рассмотрим расширение каждой из трех частей.

Изменения в конверте: расширенное SMTP

RFC 1425 [Klensin et al. 1993a] определяет основные расширения для SMTP. В результате получилось то, что называется расширенным SMTP (ESMTP - extended SMTP). Как и другие новые характеристики (уже описанные в тексте), эти изменения добавляются таким образом, что новые версии совместимы со старыми.

Клиент, которому необходимо использовать новые характеристики, открывает сессию с сервером с использованием команды EHLO вместо команды HELO. Совместимый сервер отвечает откликом с кодом 250. Этот отклик обычно содержит несколько строк, причем каждая строка содержит ключевое слово и дополнительный аргумент. Ключевые слова указывают на расширения SMTP, поддерживаемые сервером. (В случае многострочного отклика во всех строках кроме последней, между цифровым кодом и текстом стоит дефис. В последней строке между цифровым кодом и текстом стоит пробел.) Вновь появляющиеся расширения будут описаны в RFC и будут зарегистрированы в IANA.

Ниже показаны начальные стадии соединений с четырьмя SMTP серверами, три из которых поддерживают расширенный SMTP. Соединение осуществляется с помощью Telnet, однако весь вывод Telnet клиента удален.

sun % telnet vangogh.cs.berkeley.edu 25
220-vangogh.CS.Berkeley.EDU Sendmail 8.1C/6.32 ready at Mon, 2 Aug 1993 15:47:48 -0700
220 ESMTP spoken here

ehlo sun.tuc.noao.edu
250-vangogh.CS.Berkeley.EDU Hello sun.tuc.noao.edu [140.252.1.29], pleased to meet you
250-EXPN
250-SIZE
250 HELP

Этот сервер выдает многострочный отклик 220 в виде приветственного сообщения. Расширенные команды, приведенные в отклике 250, на команду EHLO это EXPN, SIZE и HELP. Первая и последняя из исходной спецификации RFC 821, однако они являются дополнительными командами (необязательными). Сервера ESMTP помимо дополнительных команд из RFC 821 поддерживают также и более новые команды.

Ключевое слово SIZE означает, что сервер поддерживает то, что описано в RFC 1427 [Klensin, Freed, and Moore 1993]. Подобное расширение позволяет клиенту указывать размер сообщения в байтах в командной строке MAIL FROM. Это позволяет серверу проверить, что он воспримет сообщение определенного размера, перед тем как клиент начнет его посылать. Эта команда была добавлена, так как в последнее время появляется все больше почтовых сообщений содержащих не-ASCII строки (например, бинарные или звуковые файлы).

Следующий хост также поддерживает ESMTP. Отклик 250 указывает, что поддерживаемое ключевое слово SIZE содержит дополнительный аргумент. Он указывает на то, что этот сервер может воспринимать сообщения размером до 461 мегабайта.

sun % telnet ymir.claremont.edu 25
220 ymir.claremont.edu -- Server SMTP (PMDF V4.2-13 #4220)

ehlo sun.tuc.noao.edu
250-ymir.claremont.edu
250-8BITMIME
250-EXPN
250-HELP
250-XADR
250 SIZE 461544960

Ключевое слово 8BITMIME определено в RFC 1426 [Klensin et al. 1993b]. Оно позволяет клиенту добавить ключевое слово BODY к команде MAIL FROM, указывая тем самым, содержит ли тело сообщения NVT ASCII символы (по умолчанию) или 8-битные данные. Если клиент не получил ключевое слово 8BITMIME от сервера в ответ на команду EHLO, клиенту запрещено посылать любые другие символы кроме NVT ASCII. (Когда мы будем говорить о MIME в этом разделе, то увидим, что для MIME не требуется 8-битный SMTP транспорт.)

Этот сервер также объявил ключевое слово XADR. Любое ключевое слово, которое начинается на X, обозначает местное расширение SMTP.

Следующий сервер также поддерживает ESMTP, объявляя ключевые слова HELP и SIZE, которые мы уже видели. Он также поддерживает три локальных расширения, которые начинаются на X.

sun % telnet dbc.mtview.ca.us 25
220 dbc.mtview.ca.us Sendmail 5.65/3.1.090690, it's Mon, 2 Aug 93 15:48:50 -0700

ehlo sun.tuc.noao.edu
250-Hello sun.tuc.noao.edu, pleased to meet you
250-HELP
250-SIZE
250-XONE
250-XVRB
250 XQUE

И в конце мы посмотрим, что произойдет, когда клиент пытается использовать ESMTP, выдав команду EHLO на сервер, который это не поддерживает.

sun % telnet relay1.uu.net 25
220 relay1.UU.NET Sendmail 5.61/UUNET-internet-primary ready at Mon, 2 Aug 93 18:50:27 -0400

ehlo sun.tuc.noao.edu
500 Command unrecognized

rset
250 Reset state

Вместо того чтобы получить отклик 250 на команду EHLO, клиент получил отклик 500. Затем клиент должен исполнить команду RSET, а уже затем команду HELO.

Изменения в заголовках: использование не-ASCII символов

RFC 1522 [Moore 1993] указывает способы передачи не-ASCII символов в заголовках сообщений, основанных на RFC 822. Основное назначение - разрешить использовать дополнительные символы в именах отправителя и получателя, а также в теме сообщения.

Поле заголовка может содержать закодированные слова. При этом формат следующий:

=? charset ? encoding ? encoded text ?=

charset это спецификация набора символов. Приемлемые значения это две строки us-ascii и iso-8859-X, где X это одна цифра, как, например, iso-8859-1.

encoding это один символ, который указывает метод кодирования. Поддерживаются два значения.

  1. Q означает quoted-printable и предназначен для латинского набора символов. Большинство символов передаются как NVT ASCII (старший бит установлен в 0, естественно). Любой отправляемый символ, у которого восьмой бит не равен 0, посылается как три символа: первый символ =, после чего следуют две шестнадцатиричные цифры. Например, символ й (бинарное 8-битное значение которого равно 0xe9) передается как три символа =E9. Пробелы всегда отправляются либо как подчеркивание, либо как три символа =20. Такое кодирование используется для текста, который в основном состоит из ASCII с небольшим количеством специальных символов.
  2. B означает кодирование по основанию 64. Три последовательных байта текста (24 бита) кодируются как четыре 6-битных значения. Для того, чтобы представить каждое из возможных 6-битных значений, используется, 64 символа NVT ASCII, как показано на рисунке 28.6.

6-битное значение | ASCII символ | 6-битное значение | ASCII символ | 6-битное значение | ASCII символ | 6-битное значение | ASCII символ
0 | A | 10 | Q | 20 | g | 30 | w
1 | B | 11 | R | 21 | h | 31 | x
2 | C | 12 | S | 22 | i | 32 | y
3 | D | 13 | T | 23 | j | 33 | z
4 | E | 14 | U | 24 | k | 34 | 0
5 | F | 15 | V | 25 | l | 35 | 1
6 | G | 16 | W | 26 | m | 36 | 2
7 | H | 17 | X | 27 | n | 37 | 3
8 | I | 18 | Y | 28 | o | 38 | 4
9 | J | 19 | Z | 29 | p | 39 | 5
a | K | 1a | a | 2a | q | 3a | 6
b | L | 1b | b | 2b | r | 3b | 7
c | M | 1c | c | 2c | s | 3c | 8
d | N | 1d | d | 2d | t | 3d | 9
E | O | 1e | e | 2e | u | 3e | +
f | P | 1f | f | 2f | v | 3f | /

Рисунок 28.6 Кодирование 6-битных значений (кодирование на основе 64).

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

Следующий пример этих двух типов кодирования взят из RFC 1522:

From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
CC: =?ISO-8859-1?Q?Andr=E9_?= Pirard <PIRARD@vm1.ulg.ac.be>
Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=

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

From: Keith Moore <moore@cs.utk.edu>
To: Keld Jorn Simonsen <keld@dkuug.dk>
CC: Andre Pirard <PIRARD@vm1.ulg.ac.be>
Subject: If you can read this you understand the example.

Для того чтобы посмотреть, как работает кодирование на основе 64, посмотрите на первые 4 закодированных символа в строке темы: SWYg. Напишем 6-битные значения для этих 4 символов в соответствии с рисунком 28.6 (S=0x12, W=0x16, Y=0x18 и g=0x20) в двоичном виде:

010010 010110 011000 100000

Затем перегруппируем эти 24 бита в три 8-битных байта:

01001001 01100110 00100000
=0x49    =0x66    =0x20

которые в виде ASCII представляют собой I, f и пробел.

Изменения в теле сообщения: Многофункциональные расширения почты Internet (MIME - Multipurpose Internet Mail Extensions)

Мы говорили, что RFC 822 описывает тело сообщения как строки NVT ASCII текста, без структуры. RFC 1521 [Borenstein and Freed 1993] определяет расширения, которые позволяют вводить структуру в тело сообщения. Это называется многофункциональным расширением почты Internet (MIME - Multipurpose Internet Mail Extensions).

MIME не требует тех расширений, что мы описали ранее в этом разделе (расширенный SMTP или не-ASCII заголовки). MIME просто добавляет некоторые новые заголовки (в соответствии с RFC 822), которые сообщают получателю о структуре тела сообщения. Тело сообщения может передаваться с использованием NVT ASCII, вне зависимости от содержимого почты. Несмотря на это некоторые из расширений, которые мы только что описали, могут быть вполне уместны при использовании вместе с MIME - команда SIZE расширенного SMTP, так как MIME сообщения могут быть довольно большими, и не-ASCII заголовки, эти расширения не требуются для MIME. Все что требуется для обмена MIME сообщениями с другой стороной - необходимо, чтобы на обоих концах присутствовали пользовательские агенты, которые понимают MIME. На промежуточных MTA не требуется никаких изменений.

MIME определяет пять новых полей заголовков:

Mime-Version:
Content-Type:
Content-Transfer-Encoding:
Content-ID:
Content-Description:

В качестве примера, приведены две строки заголовка, которые могут присутствовать в почтовом сообщении Internet:

Mime-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII

Текущая версия MIME - 1.0, в которой сообщения выглядят как простой ASCII текст, что является форматом по умолчанию для почты Internet. Слово PLAIN определяет подтип типа содержимого (TEXT), а строка charset=US-ASCII это параметр.

Text это всего лишь один из семи определенных в MIME типов содержимого. На рисунке 28.7 приводится краткое описание 16 различных типов содержимого и подтипов, определенных в RFC 1521. Для конкретных типов содержимого и подтипов может быть определено большое количество параметров.

Тип содержимого и кодирование при передаче, применяемое к телу сообщения, независимы друг от друга. Здесь указаны поле заголовка Content-Type (тип содержимого) и поле заголовка Content-Transfer-Encoding (кодирование содержимого при передаче). В RFC 1521 определено пять различных форматов кодирования.

  1. 7bit, что является NVT ASCII, по умолчанию.
  2. quoted-printable, как мы видели в примере, приведенном ранее, используется с не-ASCII заголовками. Подобный тип кодирования применим, только когда небольшое количество символов имеет установленный восьмой бит.
  3. base64, показан на рисунке 28.6.
  4. 8bit содержит строки символов, некоторые из которых не-ASCII и имеют установленный восьмой бит.
  5. binary кодирование, используется в случае 8-битных данных, которые не содержат строк.

Тип содержимого | Подтип | Описание
text | plain | Неформатированный текст.
 | richtext | Текст с простым форматированием, как, например, выделение жирным шрифтом, курсивом, подчеркивание и так далее.
 | enriched | Прояснение, упрощение и усовершенствование richtext.
multipart | mixed | Несколько частей тела сообщения обрабатываются последовательно.
 | parallel | Несколько частей тела сообщения могут быть обработаны параллельно.
 | digest | Краткое изложение почты.
 | alternative | Присутствует несколько частей тела сообщения, все с идентичным семантическим содержанием.
message | rfc822 | Содержимое это еще одно почтовое сообщение RFC 822.
 | partial | Содержимое это фрагмент почтового сообщения.
 | external-body | Содержимое это указатель на реальное сообщение.
application | octet-stream | Произвольные двоичные данные.
 | postscript | Программа PostScript.
image | jpeg | Формат ISO 10918.
 | gif | Формат CompuServe's Graphic Interchange.
audio | basic | Кодирование с использованием 8-битного ISDN формата m -law.
video | mpeg | Формат ISO 11172.

Рисунок 28.7 Типы и подтипы содержимого MIME.

Только первые три из приведенных приемлимы для MTA работающих по правилам RFC 821, так как в этих трех случаях генерируется содержимое тела сообщения в виде только NVT ASCII символов. При использовании расширенного SMTP с 8BITMIME, поддерживается 8bit кодирование.

Так как тип содержимого и кодирование независимы, RFC 1521 рекомендует quoted-printable для text с не-ASCII данными, и base64 для данных типа image, audio, video и octet-stream приложений. При этом гарантируется максимальная совместимость с MTA, работающими по правилам RFC 821. Типы содержимого multipart и message должны быть кодированы как 7bit.

На рисунке 28.8 показано почтовое сообщение, содержащее список распространяемых RFC, тип содержимого - multipart. Подтип - mixed, это означает, что каждая часть должна быть обработана последовательно, разделители между частями выглядят как строка NextPart, перед которой стоят два дефиса в начала строки.

За каждым разделителем может следовать строка, содержащая поля заголовка для следующей части. Часть сообщения перед первым разделителем игнорируется, так же как и часть следующая за последним разделителем.

Так как за первым разделителем следуют пустые строки и не указаны поля заголовка, между первым и вторым разделителями подразумевается тип содержимого данных text/plain с набором символов us-ascii. Это текстовое описание нового RFC.

За вторым разделителем, однако, следуют поля заголовков. Они определяют следующее сообщение как multipart, с разделителем OtherAccess. Подтип - alternative, причем присутствует два альтернативных варианта. Первый OtherAccess предназначен для получения RFC с использованием электронной почты, а второй для получения с использованием анонимного FTP. Пользовательский агент MIME предоставляет список из двух возможных вариантов, что позволяет автоматически получить копию RFC с использованием либо почты, либо анонимного FTP.

To: rfc-dist@nic.ddn.mil
Subject: RFC1479 on IDPR Protocol
Mime-Version: 1.0
Content-Type: Multipart/Mixed; Boundary="NextPart"
Date: Fri, 23 Jul 93 12:17:43 PDT
From: "Joyce K. Reynolds" <jkrey@isi.edu>

--NextPart                                             первый разделитель

A new Request for Comments is now available in online RFC libraries.

. . .                                                  (здесь приводится описание нового RFC)

Below is the data which will enable a MIME compliant Mail Reader
implementation to automatically retrieve the ASCII version
of the RFCs.

--NextPart                                             второй разделитель
Content-Type: Multipart/Alternative; Boundary="OtherAccess"
                                                      сообщение, состоящее из нескольких
                                                      частей с новым разделителем
--OtherAccess
Content-Type: Message/External-body;
access-type="mail-server";
server="mail-server@nisc.sri.com"

Content-Type: text/plain

SEND rfc1479.txt

--OtherAccess
Content-Type: Message/External-body;
name="rfc1479.txt";
site="ds.internic.net";
access-type="anon-ftp";
directory="rfc"

Content-Type: text/plain

--OtherAccess--
--NextPart--                                           последний разделитель

Рисунок 28.8 Пример MIME сообщения, состоящего из нескольких частей.

Этот раздел является кратким описанием MIME. Для получения более подробной информации и примеров MIME можно обратиться к RFC 1521 и [Rose 1993].

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

В работе электронной почты принимают участие пользовательские агенты на обоих концах (отправитель и получатель), а также два или несколько агентов передачи сообщения. Мы можем поделить почтовое сообщение на три части: конверт, заголовки и тело сообщения. Мы видели, как происходит обмен этими частями с использованием SMTP, который является стандартом в Internet. Все три состоят из символов NVT ASCII.

Также мы рассмотрели новые расширения этих трех частей: расширенный SMTP для конверта, не-ASCII заголовки и дополнительная структура для тела сообщения с использованием MIME. Структура и кодирование, используемые MIME, позволяют обмениваться двоичными данными с использованием существующих 7-битных MTA SMTP.

Упражнения

  1. Прочитайте RFC 822, чтобы найти объяснение того, что называется domain literal. Попробуйте послать почту самому себе.
  2. За исключением установления и прекращения соединения, какое минимальное количество промежутков времени равных времени возврата, необходимо, чтобы послать небольшое почтовое сообщение?
  3. TCP это полнодуплексный протокол, тогда как SMTP, использующий TCP, использует его в полудуплексном режиме. Клиент посылает команду, а затем останавливается и ждет отклика. Почему клиент не может послать несколько команд за раз, например, с помощью одной записи, содержащей команды HELO, MAIL, RCPT, DATA и QUIT (представим, что тело сообщения не очень велико)?
  4. Как это полудуплексное функционирование SMTP может поставить в тупик механизм медленного старта, когда сеть работает близко к пределу своих возможностей?
  5. Если существует несколько MX записей с одним и тем же значением предпочтительности, должны ли они всегда возвращаться сервером имен в одном и том же порядке?


NFS: сетевая файловая система

Глава 29 NFS: сетевая файловая система

Введение

В этой главе мы рассмотрим сетевую файловую систему (NFS - Network File System), популярное приложение, которое предоставляет приложениям клиентов прозрачный доступ к файлам. Краеугольным камнем NFS является Sun RPC: вызов удаленной процедуры (Remote Procedure Call), что мы и опишем в первую очередь.

Программе клиента не требуется специальных средств, чтобы воспользоваться NFS. Ядро определяет что файл находится на NFS сервере и автоматически генерирует RPC вызов, для того чтобы получить доступ к файлу.

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

Вызов удаленной процедуры компании Sun

В большинстве случаев задачи сетевого программирования решаются путем написания программ приложений, которые вызывают функции, предоставляемые системой, чтобы осуществить конкретные сетевые операции. Например, одна функция осуществляет активное открытие TCP, другая пассивное открытие TCP, третья посылает данные по TCP соединению, четвертая устанавливает конкретные опции протокола (включает TCP таймер "оставайся в живых") и так далее. В разделе "Интерфейсы прикладного программирования" главы 1 мы упоминали, что существует два популярных набора функций для сетевого программирования (прикладной программный интерфейс, API), это сокеты и TLI. Программный интерфейс, используемый клиентом, и программный интерфейс, используемый сервером, могут отличаться, так же как и операционные системы, которые функционируют у клиента и сервера. Именно коммуникационный и прикладной протоколы определяют, сможет ли конкретный клиент общаться с сервером. Unix клиент, написанный на C, использующий сокеты в качестве программного интерфейса, и TCP - в качестве коммуникационного протокола, может общаться с сервером на мейнфрейме, написанным на COBOLе с использованием других API и TCP, если оба хоста подключены к сети и оба имеют реализацию TCP/IP.

Обычно клиент посылает серверу команды, а сервер отправляет клиенту отклики. Все рассмотренные нами приложения, - Ping, Traceroute, демоны маршрутизации, клиенты и сервера DNS, TFTP, BOOTP, SNMP, Telnet, FTP, SMTP - все построены именно таким образом.

RPC, вызов удаленной процедуры, реализует иной подход к сетевому программированию. Программа клиента просто вызывает функции в программе сервера. Так это решено с точки зрения программиста, однако в действительности имеет место следующая последовательность действий.

  1. Когда клиент вызывает удаленную процедуру, вызывается функция на локальном хосте, которая сгенерирована пакетом RPC. Эта функция называется client stub. client stub упаковывает аргументы процедуры в сетевое сообщение и отправляет сообщение серверу.
  2. server stub на хосте сервера получает сетевое сообщение. Аргументы извлекаются из сетевого сообщения, и осуществляется вызов процедуры сервера, написанной прикладным программистом.
  3. Функция сервера возвращает управление server stubу, который, в свою очередь, принимает полученные значения, упаковывает их в сетевое сообщение и отправляет сообщение обратно к client stub.
  4. client stub возвращает приложению клиента значения из сетевого сообщения.

Сетевое программирование, использующее stubы и библиотечные RPC подпрограммы использует интерфейсы прикладного программирования API (сокеты или TLI), однако пользовательские приложения (программа клиента и процедуры сервера, вызываемые клиентом) никогда не обращаются к API. Приложению клиента достаточно вызывать процедуру сервера, при этом все детали реализации спрятаны пакетом RPC, client stubом и server stubом.

Пакеты RPC имеют следующие положительные стороны.

  • Программирование становится легче, так как не приходится решать задачи сетевого программирования (а если и приходится, то совсем немного). Прикладные программисты просто пишут программу клиента и процедуры сервера, которые вызывает клиент.
  • Если используется ненадежный протокол, такой как UDP, все детали, а именно тайм-ауты и повторные передачи обрабатываются пакетом RPC. Это, в свою очередь, упрощает пользовательское приложение.
  • Библиотека RPC обрабатывает необходимое преобразование аргументов и возвращаемых значений. Например, если аргументы состоят из целых чисел и чисел с плавающей точкой, пакет RPC обработает все различия между представлением целых чисел и чисел с плавающей точкой на клиенте и сервере. Благодаря этому упрощается реализация клиентов и серверов для функционирования в разнородных средах.

Программирование RPC подробно описано в главе 18 [Stevens 1990]. Два наиболее популярных RPC пакета это Sun RPC и RPC пакет в Open Software Foundation's (OSF) Distributed Computing Environment (DCE). Мы рассмотрим, как осуществляется вызов процедуры, как выглядит возвращаемое сообщение и как это соотносится с пакетом Sun RPC, так как именно этот пакет используется в сетевой файловой системе. Версия 2 Sun RPC описана в RFC 1057 [Sun Microsystems 1988a].

Sun RPC

Существует два вида Sun RPC. Одна версия построена с использованием API сокет и работает с TCP и UDP. Другая называется TI-RPC (независимо от транспорта - transport independent), построена с использованием TLI API и работает с любыми транспортными уровнями, предоставляемыми ядром. С нашей точки зрения между ними нет никакой разницы, так как в этой главе мы рассматриваем только TCP и UDP.

На рисунке 29.1 показан формат сообщения вызова процедуры RPC, с использованием UDP.

Рисунок 29.1 Сообщения вызова процедуры RPC в формате UDP датаграммы.

Стандартные IP и UDP заголовки показаны раньше (рисунок 3.1 и рисунок 11.2). Все, что следует после UDP заголовка, определяется пакетом RPC.

Идентификатор транзакции (XID - transaction ID) устанавливается клиентом и возвращается сервером. Когда клиент получает отклик, он сравнивает XID, возвращенный сервером, с XID отправленного запроса. Если они не совпадают, клиент отбрасывает сообщение и ожидает прихода следующего. Каждый раз, когда клиент выдает новый RPC, он меняет XID. Однако если клиент передает RPC повторно (если отклик не был получен), XID не меняется.

Переменная call равна 0 для вызова и 1 для отклика. Текущая версия RPC (RPC version) равна 2. Три следующие переменные, номер программы (program number), номер версии (version number) и номер процедуры (procedure number), идентифицируют конкретную процедуру, которая должна быть вызвана на сервере.

Полномочия (credentials) идентифицируют клиента. В некоторых примерах это поле остается незаполненным, а в других здесь можно встретить цифровой идентификатор пользователя и идентификатор группы к который он принадлежит. Сервер может заглянуть в полномочия и решить, обработать ли запрос или нет. Проверка (verifier) используется для защищенного RPC (Secure RPC), которое использует DES шифрование. Несмотря на то, что поля полномочий и проверки это поля с переменной длиной, их длина передается как часть поля.

Дальше следуют параметры процедуры. Их формат зависит от того, как приложение определяет удаленную процедуру. Как получатель (server stub) узнает размер параметров? Так как используется UDP, размер параметров можно рассчитать как размер UDP датаграммы минус длина всех полей вплоть до поля проверки. Когда вместо UDP используется TCP, понятия фиксированной длины не существует, так как TCP это поток байтов без разделителей записей. В подобном случае, между TCP заголовком и XID появляется 4-байтовое поле длины, из которого приемник узнает длину RPC вызова в байтах. Это позволяет, если необходимо, послать сообщение вызова RPC в нескольких TCP сегментах. (DNS использует подобную технику; упражнение 4 главы 14.)

На рисунке 29.2 показан формат RPC отклика. Он отправляется от server stub к client stub, когда удаленная процедура завершает свою работу.

Рисунок 29.2 Формат сообщения отклика процедуры RPC как UDP датаграмма.

XID вызова просто копируется в XID отклика. В поле reply находится 1, по этому полю проводится различие между вызовом и откликом. Поле статуса (status) содержит нулевое значение, если сообщение вызова было принято. (Сообщение может быть отброшено, если номер версии RPC не равен 2 или если сервер не может аутентифицировать клиента.) Поле проверки (verifier) используется в случае защищенного RPC, чтобы указать сервер.

В поле статуса приема (accept status) находится нулевое значение, если все нормально. Ненулевое значение может указывать, например, на неверный номер версии или неверный номер процедуры. Если вместо UDP используется TCP, то, как и в случае сообщения вызова RPC, между TCP заголовком и XID посылается 4-байтовое поле длины.

XDR: представление внешних данных

Представление внешних данных (XDR - External Data Representation) это стандарт, используемый для кодирования значений в RPC вызове и отклике сообщениях - полей заголовка RPC (XID, номер программы, статус приема и так далее), параметров процедуры и результатов процедуры. Стандартный способ кодирования данных позволяет клиенту вызвать процедуру в системе с отличной архитектурой. XDR определен в RFC 1014 [Sun Microsystems 1987].

XDR определяет определенное количество типов данных и точный способ того, как они передаются в RPC сообщении (порядок битов, порядок байтов и так далее). Отправитель должен построить RPC сообщение в XDR формате, тогда получатель конвертирует XDR формат в исходное представление. (В тот формат, который принят для его системы.) Мы видим, например, на рисунках 29.1 и 29.2, что все целые значения, которые мы показали (XID, вызов, номер программы и так далее), это 4-байтовые целые числа. И действительно, все целые в XDR занимают 4 байта. XDR поддерживает и другие типы данных, включая целые без знака, логические, числа с плавающей точкой, массивы фиксированной длины, массивы переменной длины и структуры.

Соответствие портов

Программы RPC сервера, содержащие удаленные процедуры, используют динамически назначаемые порты, а не заранее известные порты. Это требует "регистрации" в какой-либо форме, для того чтобы постоянно иметь информацию, какая динамически назначаемый порт использует та или иная RPC программа. В Sun RPC этот регистратор называется преобразователь портов (port mapper). (Port mapper - это сервер, который конвертирует номера RPC программ в номера портов протоколов DARPA. Этот сервер обязательно должен быть запущен, чтобы можно было исполнить RPC вызов.)

Термин "порт" (port) в названии происходит от номеров портов TCP и UDP, характеристики семейства протоколов Internet. Так как TI-RPC работает поверх любых транспортных уровней, а не только поверх TCP и UDP, название port mapper в системах, использующих TI-RPC (SVR4 и Solaris 2.2, например), было преобразовано в rpcbind. Однако мы будем продолжать использовать более привычное - port mapper.

В действительности, сам преобразователь портов должен иметь заранее известный порт: UDP порт 111 и TCP порт 111. Преобразователь портов - это всего лишь программа RPC сервера. Он имеет номер программы (100000), номер версии (2), TCP порт 111 и UDP порт 111. Серверы регистрируют друг друга в преобразователе портов, используя RPC вызовы, а клиенты запрашивают преобразователь портов, используя RPC вызовы. Преобразователь портов предоставляет четыре процедуры сервера:

  1. PMAPPROC_SET. Вызывается RPC сервером при старте, чтобы зарегистрировать номер программы, номер версии и протокол в преобразователе портов.
  2. PMAPPROC_UNSET. Вызывается сервером, чтобы удалить ранее зарегистрированное преобразование.
  3. PMAPPROC_GETPORT. Вызывается RPC клиентом при старте, чтобы получить номер порта для заданного номера программы, номера версии и протокола.
  4. PMAPPROC_DUMP. Возвращает все пункты (номер программы, номер версии, протокол и номер порта) в базу данных преобразователя портов.

Когда стартует программа сервер RPC и позже, когда она вызывается программой клиента RPC, осуществляются следующие шаги.

  1. Преобразователь портов должен стартовать первым, обычно при загрузке системы. При этом создается конечная точка TCP и осуществляется пассивное открытие TCP порта 111. Также создается конечная точка UDP, которая находится в ожидании, когда на UDP порт 111 прибудет UDP датаграмма.
  2. При старте программа сервера RPC создает конечную точку TCP и конечную точку UDP для каждой поддерживаемой версии программы. (Программа RPC может поддерживать несколько версий. Клиент указывает требуемую версию при вызове процедуры сервера.) Динамически назначаемый номер порта закрепляется за каждой конечной точкой. (Нет никакой разницы, одинаковые ли номера портов TCP и UDP или разные.) Сервер регистрирует каждую программу, версию, протокол и номер порта, осуществляя удаленной вызов процедуры преобразователя портов PMAPPROC_SET.
  3. Когда стартует программа клиента RPC, она вызывает процедуру преобразователя портов PMAPPROC_GETPORT, чтобы получить динамически назначаемый номер порта для заданной программы, версии и протокола.
  4. Клиент отправляет сообщение вызова RPC на номер порта, полученный в пункте 3. Если используется UDP, клиент просто посылает UDP датаграмму, содержащую сообщение вызова RPC (рисунок 29.1), на номер UDP порта сервера. В ответ сервер отправляет UDP датаграмму, содержащую сообщение RPC отклика (рисунок 29.2). Если используется TCP, клиент осуществляет активное открытие на номер TCP порта сервера и затем посылает сообщение вызова RPC по соединению. Сервер отвечает сообщением отклика RPC по соединению.

Программа rpcinfo(8) печатает все текущие настройки преобразователя портов. (Здесь происходит вызов процедуры преобразователя портов PMAPPROC_DUMP.) Ниже показан обычный вывод:

sun % /usr/etc/rpcinfo -p
program    vers    proto    port
  100005       1      tcp      702   mountd            демон монтирования NFS
  100005       1      udp      699   mountd
  100005       2      tcp      702   mountd
  100005       2      udp      699   mountd

  100003       2      udp     2049   nfs               сам NFS

  100021       1      tcp      709   nlockmgr          менеджер блокирования NFS
  100021       1      udp     1036   nlockmgr
  100021       2      tcp      721   nlockmgr
  100021       2      udp     1039   nlockmgr
  100021       3      tcp      713   nlockmgr
  100021       3      udp     1037   nlockmgr

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

Доступ к обеим версиям монтирующего демона можно получить через один и тот же номер TCP порта (702) и один и тот же номер UDP порта (699), однако каждая версия блокирующего менеджера имеет свой собственный номер порта.

Протокол NFS

NFS предоставляет клиентам прозрачный доступ к файлам и файловой системе сервера. Это отличается от FTP (глава 27), который обеспечивает передачу файлов. С помощью FTP осуществляется полное копирование файла. NFS осуществляет доступ только к тем частям файла, к которым обратился процесс, и основное достоинство NFS в том, что он делает этот доступ прозрачным. Это означает, что любое приложение клиента, которое может работать с локальным файлом, с таким же успехом может работать и с NFS файлом, без каких либо модификаций самой программы.

NFS это приложение клиент-сервер, построенное с использованием Sun RPC. NFS клиенты получают доступ к файлам на NFS сервере путем отправки RPC запросов на сервер. Это может быть реализовано с использованием обычных пользовательских процессов - а именно, NFS клиент может быть пользовательским процессом, который осуществляет конкретные RPC вызовы на сервер, который так же может быть пользовательским процессом. Однако, NFS обычно реализуется иначе, это делается по двум причинам. Во-первых, доступ к NFS файлам должен быть прозрачным для клиента. Поэтому, вызовы NFS клиента осуществляются операционной системой клиента от имени пользовательского процесса клиента. Во-вторых, NFS сервера реализованы внутри операционной системы для повышения эффективности работы сервера. Если бы NFS сервер являлся пользовательским процессом, каждый запрос клиента и отклик сервера (включая данные, которые будут считаны или записаны) должен пройти через разделитель между ядром и пользовательским процессом, что вообще довольно дорогое удовольствие.

В этом разделе мы рассмотрим версию 2 NFS, как она документирована в RFC 1094 [Sun Microsystems 1988b]. Лучшее описание Sun RPC, XDR и NFS дано в [X/Open 1991]. Подробности использования и администрирования NFS приведены в [Stern 1991]. Спецификации версии 3 протокола NFS были реализованы в 1993 году, о чем мы поговорим в разделе "NFS версия 3" этой главы.

На рисунке 29.3 показаны типичные настройки NFS клиента и NFS сервера. На этом рисунке необходимо обратить внимание на следующее.

  1. Клиенту безразлично, получает ли он доступ к локальному файлу или к NFS файлу. Ядро определяет это, когда файл открыт. После того как файл открыт, ядро передает все обращения к локальным файлам в квадратик, помеченный как "доступ к локальным файлам", а все ссылки на NFS файлы передаются в квадратик "NFS клиент".
  2. NFS клиент отправляет RPC запросы NFS серверу через модуль TCP/IP. NFS обычно использует UDP, однако более новые реализации могут использовать TCP.
  3. NFS сервер получает запросы от клиента в виде UDP датаграмм на порт 2049. Несмотря на то, что NFS может работать с преобразователем портов, что позволяет серверу использовать динамически назначаемые порты, UDP порт 2049 жестко закреплен за NFS в большинстве реализаций.

    Рисунок 29.3 Типичные настройки NFS клиента и NFS сервера.

  4. Когда NFS сервер получает запрос от клиента, он передаются локальной подпрограмме доступа к файлу, которая обеспечивает доступ к локальному диску на сервере.
  5. Серверу может потребоваться время, для того чтобы обработать запросы клиента. Даже доступ к локальной файловой системе может занять некоторое время. В течение этого времени сервер не хочет блокировать запросы от других клиентов, которые также должны быть обслужены. Чтобы справиться с подобной ситуацией, большинство NFS серверов запускаются несколько раз, то есть внутри ядра существует несколько NFS серверов. Конкретные методы решения зависят от операционной системы. В большинстве ядер Unix систем не "живет" несколько NFS серверов, вместо этого запускается несколько пользовательских процессов (которые обычно называются nfsd), которые осуществляют один системный вызов и остаются внутри ядра в качестве процесса ядра.
  6. Точно так же, NFS клиенту требуется время, чтобы обработать запрос от пользовательского процесса на хосте клиента. RPC выдается на хост сервера, после чего ожидается отклик. Для того, чтобы пользовательские процессы на хосте клиента могли в любой момент воспользоваться NFS, существует несколько NFS клиентов, запущенных внутри ядра клиента. Конкретная реализация также зависит от операционной системы. Unix система обычно использует технику, напоминающую NFS сервер: пользовательский процесс, называемый biod, осуществляет один единственный системный вызов и остается внутри ядра как процесс ядра.

Большинство Unix хостов может функционировать как NFS клиент и как NFS сервер, или как и то и другое одновременно. Большинство PC реализаций (MS-DOS) имеют только реализации NFS клиента. Большинство IBM мейнфреймов предоставляет только функции NFS сервера.

NFS в действительности - это нечто большее, чем просто NFS протокол. На рисунке 29.4 показаны различные программы RPC, которые используются с NFS.

Приложение | Номер программы | Номер версии | Количество процедур
преобразователь портов | 100000 | 2 | 4
NFS | 100003 | 2 | 15
программа mount | 100005 | 1 | 5
менеджер блокирования | 100021 | 1,2,3 | 19
монитор статуса | 100024 | 1 | 6

Рисунок 29.4 Различные RPC программы, используемые в NFS.

Версии, которые мы показали на этом рисунке в виде единиц, найдены в таких системах как SunOS 4.1.3. Новые реализации предоставляют более новые версии некоторых программ. Solaris 2.2, например, также поддерживает версии 3 и 4 преобразователя портов и версию 2 демона mount. SVR4 также поддерживает версию 3 преобразователя портов.

Демон монтирования вызывается на хосте NFS клиента, перед тем как клиент может получить доступ к файловой системе сервера. Мы опишем этот процесс ниже.

Менеджер блокирования и монитор статуса позволяют клиенту заблокировать часть файлов, которые находятся на NFS сервере. Эти две программы не зависимы от протокола NFS, потому что блокирование требует идентификации клиента и на хосте клиента, и на сервере, а NFS сам по себе "безразличен". (Ниже мы скажем о безразличности NFS более подробно.) Главы 9, 10 и 11 [X/Open 1991] документируют процедуры, которые используются менеджером блокирования и монитором статуса для блокирования в NFS.

Описатели файлов

Одна из основ NFS реализуется описателями файлов. Для обращения к файлу или директории на сервере объекта используется opaque. Термин opaque обозначает, что сервер создает описатель файла, передает его обратно клиенту, который клиент затем использует при обращении к файлу. Клиент никогда не просматривает содержимое описателя файла - его содержимое представляет интерес только для сервера.

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

Обычно пользовательский процесс не работает с описателями файлов. Обмен описателями файлов осуществляют NFS клиент и NFS сервер. В версии 2 NFS описатель файла занимает 32 байта, а в версии 3 он вырос до 64 байт.

Unix серверы обычно хранят в описателе файла следующую информацию: идентификатор файловой системы (major и minor номера устройства файловой системы), номер инода (i-node) (уникальный номер внутри файловой системы), номер поколения инода (номер, который изменяется каждый раз, когда инод повторно используется для другого файла).

Протокол монтирования

Клиент использует NFS протокол монтирования, чтобы смонтировать файловую систему сервера, перед тем как получить доступ к NFS файлам. Обычно это происходит при загрузке клиента. В результате клиент получает описатель файла файловой системы сервера.

На рисунке 29.5 описана последовательность действий Unix клиента при исполнении команды mount(8).

Рисунок 29.5 Протокол монтирования, используемый Unix командой mount.

При этом осуществляются следующие шаги.

  1. При загрузке сервера на нем стартует преобразователь портов.
  2. После преобразователя портов на сервере стартует демон монтирования (mountd). Он создает конечную точку TCP и конечную точку UDP, а также назначает каждой из них динамически назначаемый номер порта. Затем он регистрирует эти номера у преобразователя портов.
  3. Клиент исполняется команду mount, которая выдает RPC вызов на преобразователь портов сервера, чтобы получить номер порта от демона монтирования на сервере. Для обмена между клиентом и преобразователем портов могут быть использованы и TCP и UDP, однако обычно используется UDP.
  4. Преобразователь портов сообщает номер порта.
  5. Команда mount выдает RPC вызов демону монтирования, чтобы смонтировать файловую систему сервера. И снова может быть использован как TCP, так и UDP, однако обычно используется UDP. Теперь сервер может проверить "годность" клиента основываясь на его IP адресе и номере порта, чтобы убедиться, можно ли этому клиенту смонтировать указанную файловую систему.
  6. Демон монтирования откликается описателем файла указанной файловой системы.
  7. Команда mount клиента выдает системный вызов mount, чтобы связать описатель файла, полученный в шаге 5, с локальной точкой монтирования на хосте клиента. Описатель файла хранится в коде NFS клиента, и с этого момента любое обращение пользовательских процессов к файлам на файловой системе сервера будет использовать описатель файла как стартовую точку.

Подобная реализация отдает весь процесс монтирования, кроме системного вызова mount на клиенте, пользовательским процессам, а не ядру. Три программы, которые мы показали - команда mount, преобразователь портов и демон монтирования - пользовательские процессы.

В этом примере на хосте sun (NFS клиент) была исполнена команда

sun # mount -t nfs bsdi:/usr /nfs/bsdi/usr

Эта команда монтирует директорию /usr на хосте bsdi (NFS сервер) как локальную файловую систему /nfs/bsdi/usr. На рисунке 29.6 показан результат.

Рисунок 29.6 Монтирование директории bsdi:/usr как /nfs/bsdi/usr на хосте sun.

После чего при обращении к файлу /nfs/bsdi/usr/rstevens/hello.c на клиенте sun, происходит обращение к файлу /usr/rstevens/hello.c на сервере bsdi.

Процедуры NFS

NFS сервер предоставляет 15 процедур, которые мы сейчас опишем. (Числа, которые использованные при описании, не совпадают с номерами NFS процедур, так как мы сгруппировали их по функциональному признаку.) Несмотря на то что NFS разрабатывалась таким образом, чтобы работать между различными операционными системами, а не только между Unix системами, некоторые из процедур основаны именно на Unix функционировании, что, в свою очередь, может не поддерживаться другими операционными системами (например, жесткие линки, символические линки, групповое пользование, права доступа на исполнение и так далее). Глава 4 [Stevens 1992] содержит дополнительную информацию о характеристиках файловых систем, некоторыми из которых пользуется NFS.

  1. GETATTR. Возвращает атрибуты файлов: тип файла (обычный файл, директория и так далее), права доступа, размер файла, владельца файла, время последнего обращения и так далее.
  2. SETATTR. Устанавливает атрибуты файла. Установлен может быть только определенный набор атрибутов: права доступа, владелец, групповое владение, размер, время последнего обращения и время последней модификации.
  3. STATFS. Возвращает статус файловой системы: размер свободного пространства, оптимальный размер для передачи и так далее. Используется, например, Unix командой df.
  4. LOOKUP. "Оценивает" файл. Эта процедура вызывается клиентом каждый раз, когда пользовательский процесс открывает файл, который находится на NFS сервере. Возвращается описатель файла, вместе с атрибутами файла.
  5. READ. Читает из файла. Клиент указывает описатель файла, начальное смещение в байтах и максимальное количество байтов, которое необходимо считать (до 8192).
  6. WRITE. Записывает в файл. Клиент указывает описатель файла, начальное смещение в байтах, количество байт, которое необходимо записать, и данные, которые необходимо записать.

    Требуется, чтобы NFS записи были синхронными (с ожиданием). Сервер не может ответить OK до тех пор, пока данные не были успешно записаны (и любая другая информация о файле, которая должна быть обновлена) на диск.

  7. CREATE. Создает файл.
  8. REMOVE. Удаляет файл.
  9. RENAME. Переименовывает файл.
  10. LINK. Делает жесткий линк на файл. Жесткий линк это Unix концепция, которая определяет, что конкретный файл на диске может иметь любое количество точек входа (имен, которые также называются жесткими линками), которые указывают на этот файл.
  11. SYMLINK. Создает символический линк на файл. Символический линк это файл, который содержит имя другого файла. Большинство операций, которые осуществляются над символическим линком (например, открытие), в действительности совершаются с тем файлом, на котороый указывает символический линк.
  12. READLINK. Чтение символического линка возвращает имя файла, на который указывает символический линк.
  13. MKDIR. Создает директорию.
  14. RMDIR. Удаляет директорию.
  15. READDIR. Читает директорию. Используется, например, Unix командой ls.

В действительности, приведенные имена процедур начинаются с префикса NFSPROC_, который мы опустили.

UDP или TCP?

NFS был исходно написан, чтобы использовать UDP, и эту возможность предоставляют все производители. Однако, более новые реализации, также поддерживают TCP. Поддержка TCP используется для работы в глобальных сетях, которые становится все быстрее. Поэтому использование NFS в настоящее время уже не ограничено локальными сетями.

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

NFS поверх TCP

Реализация NFS Berkeley Net/2 поддерживает как UDP, так и TCP. [Macklem 1991] описывает эту реализацию. Давайте рассмотрим, чем отличается использование NFS при работе поверх TCP.

  1. Когда сервер загружается, он запускает NFS сервер, который осуществляет активное открытие на TCP порт 2049, ожидая прихода запроса на соединение от клиента. Это обычно делается в дополнение к обычному NFS UDP, который ожидает входящие датаграммы на UDP порте 2049.
  2. Когда клиент монтирует файловую систему сервера с использованием TCP, он осуществляет активное открытие на TCP порт 2049 на сервере. При этом устанавливается TCP соединение между клиентом и сервером для этой файловой системы. Если тот же самый клиент монтирует еще одну файловую систему на том же самом сервере, создается еще одно TCP соединение.
  3. И клиент, и сервер устанавливают TCP опцию "оставайся в живых" на своих концах соединения (глава 23). Это позволяет определить момент выхода из строя или перезагрузки того или иного участника обмена.
  4. Все приложения на клиенте, которые используют файловую систему сервера, делят одно и то же TCP соединение для этой файловой системы. Например, если была на рисунке 29.6, бы еще одна директория на bsdi, с именем smith, ниже директории /usr, обращения к файлам в /nfs/bsdi/usr/rstevens и /nfs/bsdi/usr/smith делили бы одно и то же TCP соединение.
  5. Если клиент определяет, что сервер вышел из строя или перезагрузился (после получения TCP ошибки "соединение закрыто по тайм-ауту" или "соединение закрыто хостом"), он старается повторно подсоединиться к серверу. Клиент осуществляет еще одно активное открытие, чтобы повторно установить TCP соединение для этой файловой системы. Любой запрос от клиента, для которого отработан тайм-аут на предыдущем соединении, повторно выдается на новое соединение.
  6. Если клиент вышел из строя, то же происходит и с приложениями, которые работали до выхода из строя. Когда клиент перезагружается, он, возможно, повторно смонтирует файловую систему сервера с использованием TCP, причем будет использовано другое TCP соединение с сервером. Предыдущее соединение между клиентом и сервером для этой файловой системы находится в полуоткрытом состоянии (сервер думает, что оно все еще открыто), однако так как сервер установил опцию "оставайся в живых", это полуоткрытое соединение будет закрыто, когда TCP сервер пошлет следующую пробу "оставайся в живых".

Со временем и другие производители планируют начать поддержку NFS поверх TCP.

Примеры NFS

Давайте воспользуемся tcpdump, чтобы посмотреть, какие NFS процедуры привлекаются клиентом для обычных операций с файлом. Когда tcpdump определяет, что UDP датаграмма содержит RPC вызов (call равен 0 на рисунке 29.1) с портом назначения 2049, он декодирует датаграмму как NFS запрос. Точно так же, если UDP датаграмма содержит RPC отклик (reply равен 1 на рисунке 29.2) с портом источника равным 2049, он декодирует датаграмму как NFS отклик.

Простой пример: чтение файла

В первом примере мы скопируем файл, находиться на NFS сервере, на терминал с использованием команды cat(1):

sun % cat /nfs/bsdi/usr/rstevens/hello.c         копирование файла на терминал
main()
{
               printf ("hello, world\n");
}

Файловая система /nfs/bsdi/usr на хосте sun (NFS клиент) в действительности является файловой системой /usr на хосте bsdi (NFS сервер), как показано на рисунке 29.6. Ядро sun определяет это, когда cat открывает файл и использует NFS для доступа к файлу. На рисунке 29.7 показан вывод команды tcpdump.

1  0.0                   sun.7aa6 > bsdi.nfs: 104 getattr
2  0.003587 (0.0036)    bsdi.nfs > sun.7aa6: reply ok 96

3  0.005390 (0.0018)    sun.7aa7 > bsdi.nfs: 116 lookup "rstevens"
4  0.009570 (0.0042)    bsdi.nfs > sun.7aa7: reply ok 128

5  0.011413 (0.0018)    sun.7aa8 > bsdi.nfs: 116 lookup "hello.c"
6  0.015512 (0.0041)    bsdi.nfs > sun.7aa8: reply ok 128

7  0.018843 (0.0033)    sun.7aa9 > bsdi.nfs: 104 getattr
8  0.022377 (0.0035)    bsdi.nfs > sun.7aa9: reply ok 96

9  0.027621 (0.0052)    sun.7aaa > bsdi.nfs: 116 read 1024 bytes @ 0
10  0.032170 (0.0045)    bsdi.nfs > sun.7aaa: reply ok 140

Рисунок 29.7 Функционирование NFS при чтении файла.

Команда tcpdump декодирует NFS запрос или отклик, также она печатает поле XID для клиента, вместо номера порта. Поле XID в строках 1 и 2 равно 0x7aa6.

Имя файла /nfs/bsdi/usr/rstevens/hello.c обрабатывается функцией открытия в ядре клиента по одному элементу имени за раз. Когда функция открытия достигает /nfs/bsdi/usr, она определяет, что это точка монтирования файловой системы NFS.

В строке 1 клиент вызывает процедуру GETATTR, чтобы получить атрибуты директории сервера, которую смонтировал клиент (/usr). Этот RPC запрос содержит 104 байта данных, помимо IP и UDP заголовков. Отклик в строке 2 возвращает OK и содержит 96 байт данных, помимо IP и UDP заголовков. Мы видим на этом рисунке, что минимальное NFS сообщение содержит примерно 100 байт данных.

В строке 3 клиент вызывает процедуру LOOKUP для файла rstevens и получает отклик OK в строке 4. LOOKUP указывает имя файла rstevens и описатель файла, который был сохранен ядром, когда монтировалась удаленная файловая система. Отклик содержит новый описатель файла, который используется в следующем шаге.

В строке 5 клиент осуществляет LOOKUP файла hello.c с использованием описателя файла из строки 4. Он получает другой описатель файла в строке 6. Этот новый описатель файла как раз то, что клиент использует в строках 7 и 9, чтобы обратиться к файлу /nfs/bsdi/usr/rstevens/hello.c. Мы видим, что клиент осуществляет LOOKUP для каждого компонента имени в пути к открываемому файлу.

В строке 7 клиент еще раз исполняет GETATTR, затем следует READ в строке 9. Клиент запрашивает 1024 байта, начиная со смещения равного 0, однако получает данных меньше чем 1024 байта. (После вычитания размеров RPC полей и других значений, возвращенных процедурой READ, в строке 10 возвращаются 38 байт данных. Это как раз размер файла hello.c.)

В этом примере пользовательский процесс ничего не знает об этих NFS запросах и откликах, которые осуществляются ядром. Приложение всего лишь вызывает функцию открытия ядра, которая вызывает обмен 3 запросами и 3 откликами (строки 1-6), а затем вызывает функцию чтение ядра, которая вызывает 2 запроса и 2 отклика (строки 7-10). Для приложения клиента, файл, находящийся на NFS сервере, прозрачен.

Простой пример: создание директории

В качестве еще одного примера сменим рабочую директорию на директорию, которая находится на NFS сервере, а затем создадим новую директорию:

sun % cd /nfs/bsdi/usr/rstevens             меняем рабочую директорию
sun % mkdir Mail                            создаем директорию

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

1   0.0                    sun.7ad2 > bsdi.nfs: 104 getattr
2   0.004912 ( 0.0049)    bsdi.nfs > sun.7ad2: reply ok 96

3   0.007266 ( 0.0024)    sun.7ad3 > bsdi.nfs: 104 getattr
4   0.010846 ( 0.0036)    bsdi.nfs > sun.7ad3: reply ok 96

5  35.769875 (35.7590)    sun.7ad4 > bsdi.nfs: 104 getattr
6  35.773432 ( 0.0036)    bsdi.nfs > sun.7ad4: reply ok 96

7  35.775236 ( 0.0018)    sun.7ad5 > bsdi.nfs: 112 lookup "Mail"
8  35.780914 ( 0.0057)    bsdi.nfs > sun.7ad5: reply ok 28

9  35.782339 ( 0.0014)    sun.7ad6 > bsdi.nfs: 144 mkdir "Mail"
10  35.992354 ( 0.2100)    bsdi.nfs > sun.7ad6: reply ok 128

Рисунок 29.8 Функционирование NFS при смене директории (cd) на NFS директорию, а затем создание директории (mkdir).

При смене директории клиент вызывает процедуру GETATTR дважды (строки 1-4). Когда мы создаем новую директорию, клиент вызывает процедуру GETATTR (строки 5 и 6), затем LOOKUP (строки 7 и 8, чтобы проверить, что такой директории не существует), затем MKDIR, чтобы создать директорию (строки 9 и 10). Отклик OK в строке 8 не означает, что директория существует. Он просто означает, что процедура вернула какое-то значение. tcpdump не интерпретирует значение, возвращаемое NFS процедурами. Команда просто печатает OK и количество байт данных в отклике.

Безразличность

Одна из характеристик NFS (критики NFS называют это бородавкой, а не характеристикой) заключается в том, что NFS сервер безразличен. Сервер не заботится о том, какие клиенты получают доступ и к каким файлам. Заметьте, что в списке NFS процедур, показанных ранее, нет процедуры открытия или закрытия. Процедура LOOKUP напоминает открытие, однако сервер никогда не знает, осуществил ли клиент обращение к файлу, после того как был сделан LOOKUP.

Причина такого "безразличного поведения" заключается в том, чтобы упростить восстановление после выхода из строя сервера, после того как он сломался и перезагрузился.

Пример: выход сервера из строя

В следующем примере мы читаем файл с NFS сервера, когда сервер выходит из строя и перезагружается. Это покажет как "безразличность" сервера позволяет, клиенту "не знать" о том, что сервер вышел из строя. Все то время, пока сервер сломался и перезагружается, клиент не знает о проблеме, и приложение клиента работает так же, как и раньше.

На клиенте sun мы стартовали cat с очень большим файлом в качестве аргумента (/usr/share/lib/termcap на NFS сервере svr4), отсоединили Ethernet кабель в процессе передачи, выключили и перезагрузили сервер и затем снова подсоединили кабель. Клиент был сконфигурирован таким образом, чтобы читать 1024 байта за одно NFS чтение. На рисунке 29.9 показан вывод tcpdump.

Строки 1-10 соответствуют открытию файла клиентом. Эта операция напоминает ту, что показана на рисунке 29.7. В строке 11 мы видим первое чтение (READ) из файла 1024-х байт данных; отклик возвратился в строке 12. Это продолжается до строки 129 (чтение READ по 1024 байта и затем отклик OK).

В строках 130 и 131 мы видим два запроса, которые отработаны по тайм-ауту и повторно переданы в строках 132 и 133. Первый вопрос: мы видим два запроса на чтение, один начинается со смещения 65536, а другой начинается со смещения 73728, почему? Ядро клиента определило, что приложение клиента осуществляет последовательное считывание, и постаралось получить блоки данных заранее. (Большинство Unix ядер осуществляют это чтение вперед (read-ahead).) Ядро клиента также запустило несколько NFS демонов блочного ввода-вывода (I/O) (biod процессы), которые стараются сгенерировать несколько RPC запросов от имени клиента. Один демон считывает 8192 байта, начиная с 65536 (в 1024-байтных цепочках), а другие осуществляют чтение вперед по 8192 байта, начиная с 73728.

Повторные передачи клиента появляются в строках 130-168. В строке 169 мы видим, что сервер перезагрузился, и послал ARP запрос перед тем, как откликнуться на NFS запрос клиента из строки 168. Отклик на строку 168 посылается в строке 171. Запросы клиента на чтение (READ) продолжаются.

  1    0.0                    sun.7ade > svr4.nfs: 104 getattr
  2    0.007653 ( 0.0077)    svr4.nfs > sun.7ade: reply ok 96

  3    0.009041 ( 0.0014)    sun.7adf > svr4.nfs: 116 lookup "share"
  4    0.017237 ( 0.0082)    svr4.nfs > sun.7adf: reply ok 128

  5    0.018518 ( 0.0013)    sun.7ae0 > svr4.nfs: 112 lookup "lib"
  6    0.026802 ( 0.0083)    svr4.nfs > sun.7ae0: reply ok 128

  7    0.028096 ( 0.0013)    sun.7ae1 > svr4.nfs: 116 lookup "termcap"
  8    0.036434 ( 0.0083)    svr4.nfs > sun.7ae1: reply ok 128

  9    0.038060 ( 0.0016)    sun.7ae2 > svr4.nfs: 104 getattr
10    0.045821 ( 0.0078)    svr4.nfs > sun.7ae2: reply ok 96

11    0.050984 ( 0.0052)    sun.7ae3 > svr4.nfs: 116 read 1024 bytes @ 0
12    0.084995 ( 0.0340)    svr4.nfs > sun.7ae3: reply ok 1124

                             считывание

128    3.430313 ( 0.0013)    sun.7b22 > svr4.nfs: 116 read 1024 bytes @ 64512
129    3.441828 ( 0.0115)    svr4.nfs > sun.7b22: reply ok 1124

130    4.125031 ( 0.6832)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
131    4.868593 ( 0.7436)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

132    4.993021 ( 0.1244)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
133    5.732217 ( 0.7392)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

134    6.732084 ( 0.9999)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
135    7.472098 ( 0.7400)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

136   10.211964 ( 2.7399)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
137   10.951960 ( 0.7400)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

138   17.171767 ( 6.2198)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
139   17.911762 ( 0.7400)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

140   31.092136 (13.1804)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
141   31.831432 ( 0.7393)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

142   51.090854 (19.2594)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
143   51.830939 ( 0.7401)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

144   71.090305 (19.2594)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536
145   71.830155 ( 0.7398)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728

                             повторные передачи

167  291.824285 ( 0.7400)    sun.7b24 > svr4.nfs: 116 read 1024 bytes @ 73728
168  311.083676 (19.2594)    sun.7b23 > svr4.nfs: 116 read 1024 bytes @ 65536

                             сервер перезагрузился

169  311.149476 ( 0.0658)    arp who-has sun tell svr4
170  311.150004 ( 0.0005)    arp reply sun is-at 8:0:20:3:f6:42

171  311.154852 ( 0.0048)    svr4.nfs > sun.7b23: reply ok 1124

172  311.156671 ( 0.0018)    sun.7b25 > svr4.nfs: 116 read 1024 bytes @ 66560
173  311.168926 ( 0.0123)    svr4.nfs > sun.7b25: reply ok 1124
                             считывание

Рисунок 29.9 Считывание файла клиентом, когда NFS сервер вышел из строя и перезагрузился.

Приложение клиента никогда не узнает, что сервер выходил из строя и перезагружался, за исключением того, что между строками 129 и 171 была 5-минутная пауза, таким образом, выход из строя сервера прозрачен для клиента.

Чтобы оценить продолжительность тайм-аутов при повторных передачах в этом примере, представьте, что существуют два демона клиента, каждый со своими собственными тайм-аутами. Интервалы для первого демона (читающего со смещения 65536) примерно следующие (округлено до двух знаков после запятой): 0,68; 0,87; 1,74; 3,48; 6,96; 13,92; 20,0; 20,0; 20,0 и так далее. Интервалы для второго демона (читающего со смещения 73728) точно такие же. Это означает, что эти NFS клиенты используют тайм-ауты, которые кратны 0,875 секунды с верхним пределом равным 20 секундам. После каждого тайм-аута интервал повторной передачи удваивается: 0,875; 1,75; 3,5; 7,0 и 14,0.

Сколько времени клиент будет осуществлять повторные передачи? Клиент имеет две опции, которые могут повлиять на это. Во-первых, если файловая система сервера смонтирована жестко (hard) , клиент будет повторно передавать вечно, однако если файловая система сервера смонтирована мягко (soft) , клиент прекратит свои попытки после фиксированного количества повторных передач. Также, в случае жесткого монтирования клиент имеет опцию, позволяющую пользователю прервать неудачные повторные передачи или не прерывать. Если при монтировании файловой системы сервера, хост клиента указывает что прервать можно, и если мы не хотим ждать 5 минут, пока сервер перезагрузится после выхода из строя, мы можем ввести символ прерывания, чтобы прекратить работу приложения клиента.

Несколько одинаковых процедур

RPC процедуры могут быть исполнены сервером несколько раз, но при этом все равно возвращают тот же самый результат. Например, процедура чтения NFS. Как мы видели на рисунке 29.9, клиент просто повторно выдает вызов READ до тех пор, пока он получает отклик. В нашем примере причина повторной передачи была в том, что сервер вышел из строя. Если сервер не вышел из строя, а сообщения, содержащие RPC отклики, были потеряны (так как UDP ненадежный протокол), клиент просто повторно передает, и сервер снова осуществляет то же самое чтение (READ). Та же самая часть того же самого файла считывается снова и посылается клиенту.

Это работает, потому что каждый запрос на чтение READ содержит начальное смещение. Если бы NFS процедура попросила сервер считать следующие N байт файла, это бы не сработало. Если бы сервер не был безразличным (это значение наоборот к безразличности), и отклик потерян, а клиент повторно выдает READ для следующих N байт, результат будет отличаться. Именно поэтому процедуры NFS READ и WRITE имеют начальное смещение. Именно клиент поддерживает состояние (текущее смещение для каждого файла), а не сервер.

К несчастью, не все операции с файловыми системами можно исполнить несколько раз. Например, представьте себе следующие шаги: клиент NFS выдает запрос REMOVE, чтобы удалить файл; NFS сервер удаляет файл и отвечает OK; отклик сервера потерян; NFS клиент отрабатывает тайм-аут и повторно передает запрос; NFS сервер не может найти файл и возвращает ошибку; приложение клиента получает ошибку, сообщающую о том, что файл не существует. Эта ошибка возвращается приложению клиента, и эта ошибка несет неверную информацию - файл не существовал и был удален.

Ниже приведен список NFS процедур, которые можно исполнить несколько раз: GETATTR, STATFS, LOOKUP, READ, WRITE, READLINK и READDIR. Процедуры, которые нельзя исполнить несколько раз: CREATE, REMOVE, RENAME, LINK, SYMLINK, MKDIR и RMDIR. SETATTR обычно исполняется несколько раз, если только она не была использована для того, чтобы обрезать файл.

Так как в случае использования UDP всегда могут появиться потерянные отклики, NFS сервера должны иметь способ обработать операции, которые нельзя исполнять несколько раз. Большинство серверов имеют кэш последних откликов, в котором они хранят последние принятые отклики для подобных операций. Каждый раз, когда сервер получает запрос, он, во-первых, просматривает свой кэш, и если найдено совпадение, возвращает предыдущий отклик, вместо того чтобы вызывать NFS процедуру снова. [Juszczak 1989] описывает детали этих типов кэша.

Подобный подход к процедурам на серверах применяется ко всем приложениям, основанным на UDP, а не только NFS. DNS, например, предоставляет сервис, безболезненно используемый несколько раз. DNS сервер может осуществить запрос разборщика любое количество раз, что не приведет к отрицательным результатам (может быть, кроме того, что будут заняты сетевые ресурсы).

NFS версии 3

В течение 1994 года были выпущены спецификации для версии 3 протокола NFS [Sun Microsystems 1993]. Реализации, как ожидается, станут доступными в течение 1994 года.

Здесь вкратце описаны основные различия между версиями 2 и 3. Мы будем называть их V2 и V3.

  1. Описатели файлов в V2 это массив фиксированного размера - 32 байта. В V3 это массив переменного размера с размером до 64 байт. Массив переменной длины в XDR определяется 4-байтным счетчиком, за которым следуют реальные байты. Это уменьшает размер описателя файла в таких реализациях, как, например, Unix, где требуется всего около 12 байт, однако позволяет не-Unix реализациям обмениваться дополнительной информацией.
  2. V2 ограничивает количество байт на процедуры READ или WRITE RPC размером 8192 байта. Это ограничение не действует в V3, что, в свою очередь, означает, что с использованием UDP ограничение будет только в размере IP датаграммы (65535 байт). Это позволяет использовать большие пакеты при чтении и записи в быстрых сетях.
  3. Размеры файлов и начальное смещение байтов для процедур READ и WRITE расширены с 32 до 64 бит, что позволяет работать с файлами большего размера.
  4. Атрибуты файла возвращаются в каждом вызове, который может повлиять на атрибуты. Это уменьшает количество вызовов GETATTR, требуемых клиентом.
  5. Записи (WRITE) могут быть асинхронными, тогда как в V2 они должны были быть синхронными. Это может улучшить производительность процедуры WRITE.
  6. Одна процедура была удалена (STATFS) и семь были добавлены: ACCESS (проверка прав доступа к файлу), MKNOD (создание специального файла Unix), READDIRPLUS (возвращает имена файлов в директории вместе с их атрибутами), FSINFO (возвращает статистическую информацию о файловой системе), FSSTAT (возвращает динамическую информацию о файловой системе), PATHCONF (возвращает POSIX.1 информацию о файле) и COMMIT (передает ранее сделанные асинхронные записи на постоянное хранение).

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

RPC это способ построить приложение клиент-сервер таким образом, что клиент просто вызывает процедуры на сервере. Все сетевые детали спрятаны в stubах клиента и сервера, которые генерируются для приложений пакетом RPC и в подпрограммах библиотеки RPC. Мы показали формат RPC сообщений вызова и отклика и упомянули, что XDR используется, чтобы кодировать значения, что позволяет RPC клиентам и серверам работать на машинах с различной архитектурой.

Одно из наиболее широко используемых приложений RPC это Sun NFS, протокол доступа к разнородным файлам, который широко используется на хостах практически всех размеров. Мы рассмотрели NFS и то, как он использует UDP или TCP. В протоколе NFS версии 2 (NFS Version 2) определено 15 процедур.

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

NFS старается сделать все свои процедуры независимыми от количества исполнений таким образом, чтобы клиент мог просто повторно выдать запрос, если отклик был потерян. Мы видели примеры этого: в случае, когда клиент читал файл, пока сервер вышел из строя и перезагружался.

Упражнения

  1. На рисунке 29.7 мы видели, что tcpdump интерпретирует пакеты как NFS запросы и отклики, и при этом печатает XID. Может ли tcpdump сделать это для любых RPC запросов или откликов?
  2. Как Вы думаете, почему в Unix системах программа RPC сервера использует динамически назначаемые порты, а не заранее известные?
  3. RPC клиент вызвал две процедуры сервера. Первая процедура потребовалось на исполнение 5 секунд, а второй - 1 секунда. Клиент имеет тайм-аут равный 4 секундам. Нарисуйте временную диаграмму того, чем обмениваются клиент и сервер. (Представьте, что на прохождение сообщения от клиента к серверу и наоборот время не тратится.)
  4. Что произойдет в примере на рисунке 29.9, если пока NFS сервер был выключен, его Ethernet плата была удалена?
  5. Когда сервер перезагрузился на рисунке 29.9, он обрабатывал запрос, начинающийся на смещении 65536 (строки 168 и 171), а затем обрабатывал следующий запрос, начинающийся со смещения 66560 (строки 172 и 173). Что произойдет с запросом, начинающимся со смещением 73728 (строка 167)?
  6. Когда мы описывали независимые от количества исполнений NFS процедуры, то показали пример отклика REMOVE, который потерялся в сети. Что произойдет в этом случае, если используется TCP вместо UDP?
  7. Если NFS сервер использует динамически назначаемый порт вместо порта 2049, что произойдет с NFS клиентом, когда сервер выйдет из строя и перезагрузится?
  8. Номеров зарезервированных портов (глава 1, раздел "Номера портов") очень-очень мало, их максимум 1023 на хост. Если NFS сервер требует, чтобы его клиенты имели зарезервированные порты (что обычно так и есть), и NFS клиент, использующий TCP, монтирует N файловых систем на N различных серверах, необходимо ли клиенту иметь различные зарезервированные номера портов для каждого соединения?


Другие приложения TCP/IP

Глава 30 Другие приложения TCP/IP

Введение

В этой главе мы опишем приложения TCP/IP, которые присутствуют почти во всех реализациях. Некоторые из них просты и их можно описать целиком (Finger и Whois), тогда как другие достаточно сложны (X Window System). Сложные приложения мы рассмотрим, не вдаваясь в подробности, фокусируя свое внимание на то, как они используют протоколы TCP/IP.

В дополнение, мы приведем обзор некоторых средств Internet, которые позволяют определить ресурсы. Это средства, которые помогают нам ориентироваться в Internet, искать отдельные пункты, точное положение которых и точные имена которых не известны.

Протокол Finger

Протокол Finger возвращает информацию об одном или нескольких пользователях на указанном хосте. Это приложение обычно используется, для того чтобы посмотреть, находится ли конкретный пользователь в настоящее время в системе, или чтобы получить имя какого-либо пользователя, чтобы послать ему почту. RFC 1288 [Zimmerman 1991] описывает этот протокол.

Многие узлы не запускают Finger сервер по двум причинам. Во-первых, ошибки в программировании в ранних версиях сервера были одной из точек входа "червяка" в Internet в 1988 году. (RFC 1135 [Reynolds 1989] и [Curry 1992] описывают этого "червяка" более подробно.) Во-вторых, протокол Finger может предоставить подробную информацию о пользователях (login имя, телефонные номера, время последнего логирования и так далее), а эту информацию большинство администраторов считают частной. Раздел 3 RFC 1288 детально описывает аспекты секретности, соответствующие сервису Finger.

Сервер Finger использует заранее известный порт 79. Клиент осуществляет активное открытие на этот порт и отправляет запрос длиной в 1 строку. Сервер обрабатывает запрос, посылает назад вывод и закрывает соединение. Запрос и отклик в формате NVT ASCII, почти так же как мы видели в случае FTP и SMTP.

Обычно большинство пользователей Unix получают доступ к серверу Finger с использованием клиента finger(1), однако мы воспользуемся Telnet клиентом, подсоединимся непосредственно к серверу и рассмотрим команды, которые выдаются клиентом (команды длиной в 1 строку). Если запрос клиента состоит из пустой строки (которая в NVT ASCII передается как CR, за которой следует LF), это воспринимается как запрос на информацию о всех текущих пользователях.

sun % telnet slip finger                       первые три строки - вывод Telnet клиента
Trying 140.252.13.65 ...
Connected to slip.
Escape character is '^]'.
                                               здесь вводим RETURN в качестве команды клиента Finger
Login       Name                Tty     Idle    Login Time      Office       Office Phone
rstevens    Richard Stevens    *c0    45       Jul 31 09:13
rstevens    Richard Stevens    *c2    45       Aug 5 09:41
Connection closed by foreign host.              вывод Telnet клиента

Пустые поля с названием офиса и рабочего телефона взяты из необязательных полей в файле пользовательских паролей (которые в данном случае отсутствуют).

Сервер на своей стороне осуществляет активное закрытие, при этом сервер возвращает переменное количество информации, а получение клиентом метки конца файла оповещает клиента о том, что вывод завершен.

Когда в запросе клиента содержится имя пользователя, сервер выдает информацию только об этом пользователе. Здесь приведен еще один пример, вывод Telnet клиента удален:

sun % telnet vangogh.cs.berkeley.edu finger
rstevens                                      это клиент, о котором мы хотим узнать
Login: rstevens                                Name: Richard Stevens
Directory: /a/guest/rstevens                   Shell: /bin/csh
Last login Thu Aug 5 09:55 (PDT) on ttyq2 from sun.tuc.noao.edu
Mail forwarded to: rstevens@noao.edu
No Plan.

Когда Finger сервис отключен, клиент, в ответ на активное открытие, получает от сервера RST, так как никакой из процессов не осуществил пассивное открытие на порт 79:

sun % finger @svr4
[svr4.tuc.noao.edu] connect: Connection refused

Некоторые узлы предоставляют определенный сервис на порт 79, однако они просто сообщают информацию, не обращая внимание на клиентские запросы:

sun % finger @att.com
[att.com]                      это строка вывода от Finger клиента; все остальное от сервера
------------------------------------------------------------
There are no user accounts on the AT&T Internet gateway.
To send email to an AT&T employee, send email to their name
separated by periods at att.com. If the employee has an email
address registered in the employee database, they will receive
email - otherwise, you'll receive a non-delivery notice.
For example: John.Q.Public@att.com1

sun % finger clinton@whitehouse.gov
[whitehouse.gov]

Finger service for arbitrary addresses on whitehouse.gov is not
supported. If you wish to send electronic mail, valid addresses are
"PRESIDENT@WHITEHOUSE.GOV", and "VICE-PRESIDENT@WHITEHOUSE.GOV"2.

  1. На Internet маршрутизаторе фирмы AT&T нет пользовательских бюджетов. Чтобы послать электронную почту работникам AT&T, отправьте ее на имя работника, отделенного точкой от имени att.com. Если работник имеет адрес электронной почты - он ее получит, иначе Вы получите сообщение о недоставке почты. Например, John.Q.Public@att.com.
  2. Сервис Finger не поддерживается для адреса whitehouse.gov. Если Вам необходимо послать электронную почту, самые подходящие адреса это "PRESIDENT@WHITEHOUSE.GOV" и "VICE-PRESIDENT@WHITEHOUSE.GOV".

Существует возможность организовать брандмауэр (firewall gateway). Маршрутизатор между организацией и Internet, который фильтрует конкретные IP датаграммы. ([Cheswick and Bellovin 1994] описывает брандмауэры более подробно.) Брандмауэр может быть сконфигурирован таким образом, чтобы отбрасывать входящие датаграммы, которые содержат TCP сегменты на порт 79. В этом случае Finger клиент прекратит работу по тайм-ауту примерно через 75 секунд.

У Finger сервера и Unix клиента finger существуют набор опций. Обратитесь к RFC 1288 и к страницам помощи по finger(1) за более подробной информацией.

RFC 1288 заявляет, что машины с TCP/IP соединениями, которые предоставляют сервер Finger, должны отвечать на запрос клиента содержащий пустую строку, списком всех пунктов, доступных в настоящий момент. Они должны отвечать на запрос клиента содержащий имя, счетчиком или списком доступных пунктов для этого продукта.

Протокол Whois

Протокол Whois это еще один информационный сервис. Несмотря на то, что любой узел может предоставить Whois сервис, наиболее широко используется InterNIC, rs.internic.net. Этот сервер содержит информацию о всех зарегистрированных DNS доменах и о большинстве системных администраторов, которые ответственны за системы, подключенные к Internet. (Еще один подобный сервер nic.ddn.mil содержит информацию о сети MILNET.) К сожалению, не всегда предоставляется полная информация. RFC 954 [Harrenstein, Stahl, and Feinler 1985] документирует сервис Whois.

С точки зрения протокола, сервер Whois работает с заранее известным портом TCP 43. Он принимает от клиента запрос на соединение, после чего клиент отправляет на сервер запрос длиной в 1 строку. Сервер выдает информацию и закрывает соединение. Запросы и отклики передаются в формате NVT ASCII. Он практически идентичен серверу Finger, за исключением того, что запросы и отклики содержат разную информацию.

Широко используемый Unix клиент - программа whois(1) , однако можно использовать Telnet и ввести команды самостоятельно. Сначала отправляется запрос, содержащий знак вопроса, на что возвращается более подробная информация о поддерживаемых запросах клиента.

Когда NIC было переименовано в InterNIC в 1993 году, узел для сервера Whois переместился с nic.ddn.mil на rs.internic.net. Многие производители все еще поставляют версии клиента whois с именем nic.ddn.mil, встроенном вовнутрь. Вам может потребоваться указать аргумент командной строки -h rs.internic.net, чтобы подсоединиться к нужному серверу.

Другой способ - использовать Telnet, чтобы подсоединиться к rs.internic.net с именем whois.

Воспользуемся Whois сервером, чтобы получить информацию об авторе. (Весь вывод Telnet клиента удален.) Первый запрос - поиск всех имен, совпадающих с шаблоном "stevens."

sun % telnet rs.internic.net whois
stevens                                 это командная строка, которую мы ввели клиенту

                                        информация о 25 других "стивенсах", которую мы игнорировали
Stevens, W. Richard (WRS28) stevens@kohala.com          +1 602 297 9416

The InterNIC Registration Services Host ONLY contains Internet
Information (Networks, ASN's, Domains, and POC's).
Please use the whois server at nic.ddn.mil for MILNET Information.

Три заглавные буквы, которые стоят перед цифрами в скобках после имени, (WRS28), это персональный описатель NIC. Следующий запрос содержит восклицательный знак и описатель NIC, что позволяет получить более подробную информацию об этом человеке.

sun % telnet rs.internic.net whois
!wrs28                             запрос клиента
Stevens, W. Richard (WRS28)        stevens@kohala.com
         Kohala Software
         1202 E. Paseo del Zorro
         Tuscon, AZ 85718
         +1 602 297 9416

         Record last updated on 11-Jan-91.

Также можно получить дополнительную информацию о переменных Internet. Например, запрос net 140.252 возвращает информацию о сети класса B 140.252.

Белые страницы

С использованием команды VRFY в SMTP, вместе с протоколом Finger и протоколом Whois можно определить положение пользователей в Internet. Это напоминает использование белых страниц в телефонной книге, когда необходимо найти чей-нибудь телефонный номер. В настоящее время существуют широко доступные средства, однако проводятся исследования, призванные улучшить этот тип сервиса.

[Schwartz and Tsirigotis 1991] приводит дополнительную информацию о различных сервисах белых страниц, которые появляются в Internet. Один из них Netfind; к нему можно получить доступ по Telnet на хосте bruno.cs.colorado.edu или ds.internic.net (имя пользователя netfind).

RFC 1309 [Weider, Reynolds, and Heker 1992] предоставляет краткое описание службы директорий OSI, которая называется X.500, где приводится сравнение отличие с текущими технологиями Internet (Finger и Whois).

Archie, WAIS, Gopher, Veronica и WWW

Средства, которые мы описали в двух предыдущих разделах, - Finger, Whois и сервис белых страниц предоставляют информацию о местонахождении персон. Существуют другие средства, которые позволяют находить файлы и документы. В следующем разделе дается краткое описание этих средств. Мы приводим только краткое описание, потому что рассмотрение в деталях каждого из них может занять целую книгу. Новые программы разрабатываются постоянно. [Obraczka, Danzig, and Li 1993] описывает сервисы поиска ресурсов в Internet.

Archie

Многое из того, что показано этой книге, было получено с использованием анонимного FTP. Проблема заключается в том, чтобы найти FTP узел, на котором находится необходимая программа. Иногда мы даже не знаем точное имя файла, однако знаем какое-либо ключевое слово, которое может появиться в имени файла.

Archie предоставляет информацию о тысячах FTP серверов на бескрайних просторах Internet. Мы можем получить доступ к этой информации, зайдя на сервер Archie и осуществив поиск файла, имя которого содержит указанное регулярное выражение. В ответ появится список серверов, для которых было найдено совпадение с именами файлов. Затем мы используем анонимный FTP, чтобы получить файл.

Существует много Archie серверов по всему миру. Стартовая точка - Telnet заход на ds.internic.net, под именем archie и исполнение команды servers. При этом будет возвращен список всех Archie серверов и их положений.

WAIS: информационные сервисы глобальных сетей

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

WAIS знает о сотнях баз данных, которые содержат информацию по темам, связанным с компьютерами, и другим общим темам. Чтобы использовать WAIS, мы выбираем базы данных для поиска и указываем ключевые слова. Чтобы попробовать WAIS, попробуйте подсоединиться Telnetом к quake.think.com с именем пользователя wais.

Gopher

Gopher это меню-ориентированное приложение для работы с разнообразными сервисами Internet, таких как Archie, WAIS и анонимный FTP. Gopher это одно из наиболее легко используемых средств, так как его пользовательский интерфейс не зависит от того, какой сервис используется.

Чтобы воспользоваться Gopher, подсоединитесь Telnetом к is.internic.net с именем пользователя gopher.

Veronica: Very Easy Rodent-Oriented Netwide Index to Computerized Archives

Тогда как Archie это индекс анонимных FTP серверов, Veronica это индекс заголовков пунктов Gopher. Veronica обычно осуществляет поиск по сотням Gopher серверов.

Чтобы получить доступ к Veronica, необходимо воспользоваться клиентом Gopher. Выберите пункт меню в Gopher "Beyond InterNIC: Virtual Treasures of the Internet", а затем выберите Veronica из следующего меню.

WWW: World Wide Web

World Wide Web позволяет нам просмотреть в мировом масштабе большой набор сервисов и документов с использованием средства, которое называется гипертекст (hypertext). По мере того как информация отображается, определенные ключевые слова подсвечиваются, и мы можем получить более подробную информацию с использованием этих ключевых слов.

Чтобы получить доступ к WWW, подсоединитесь Telnetом к info.cern.ch.

X Window System

X Window System, или просто X, это приложение клиент-сервер, которое позволяет нескольким клиентам (приложениям) использовать графический дисплей, управляемый сервером. Сервер это программное обеспечение, которое управляет дисплеем, клавиатурой и мышкой. Клиент это программа приложения, которая запущена либо на том же самом хосте, что и сервер, либо на другом хосте. В последнем случае обычная форма связи между клиентом и сервером это TCP, однако могут быть использованы и другие протоколы, такие как DECNET. В некоторых случаях сервер это часть аппаратного обеспечения (X терминал), которая общается с клиентом на другом хосте. В другом случае отдельно стоящая рабочая станция, клиент и сервер находятся на одном и том же хосте и общаются с помощью коммуникаций между процессами на этом хосте, без использования сетевого обеспечения. Однако наиболее стандартная ситуация, когда клиенты запущены на том же хосте, что сервер, или когда клиенты запущены на других хостах.

X требует надежный, двунаправленный потоковый протокол, такой как TCP. (X не разработана для работы с ненадежными протоколами, такими как UDP.) Общение между клиентом и сервером осуществляется посредством обмена 8-битовыми байтами по этому соединению. [Nye 1992] приводит формат более чем 150 сообщений, которыми обмениваются клиент и сервер по TCP соединению.

В Unix системах, когда X клиент и X сервер находятся на одном и том же хосте, вместо TCP обычно используются протоколы Unix domain, потому что в этом случае осуществляется меньше обработки протоколов, нежели при использовании с TCP. Протоколы Unix domain это форма общения между процессами, которая может быть использована между клиентами и серверами на одном и том же хосте. Обратитесь к рисунку 2.4, на котором видно, что когда TCP используется для общения между двумя процессами на одном и том же хосте, эти данные передаются по петле ниже IP уровня, при этом осуществляется обработка данных и TCP, и UDP. На рисунке 30.1 показан возможный вариант общения трех клиентов, использующих один дисплей. Один клиент на том же самом хосте, что и сервер, использует Unix domain протоколы. Остальные два клиента находятся на других хостах и используют TCP. Один клиент это обычно оконный менеджер (window manager), который имеет право располагать окна на дисплее. Оконный менеджер позволяет нам передвигать окна по экрану или, например, менять их размер.

На первый взгляд термины клиент и сервер не совсем уместны в данном случае. С такими приложениями как Telnet и FTP мы считаем, что клиент это интерактивный пользователь, который сидит за клавиатурой и дисплеем. Однако в случае X клавиатура и дисплей принадлежат самому серверу. Поэтому в данном случае сервер это то, что предоставляет сервис. Сервис, предоставляемый X, это доступ к окну, клавиатуре и мышке. В случае Telnet сервис это терминальный заход на удаленный хост. В случае FTP сервис это файловая система сервера.

X сервер обычно стартует, когда X терминал или рабочая станция загружаются. Сервер создает конечную точку TCP и осуществляет пассивное открытие на порт 6000 + n, где n это номер дисплея (обычно 0).

Рисунок 30.1 Три X клиента, использующие один дисплей.

Большинство Unix серверов также создают Unix domain сокет с именем /tmp/.X11-unix/Xn, где n это тот же номер дисплея.

Когда клиент стартует на другом хосте, он создает TCP конечную точку и осуществляет активное открытие на порт 6000 + n сервера. Каждый клиент имеет свое собственное TCP соединение с сервером. Уже от сервера будет зависеть, как он поделит ресурсы между всеми клиентами. Клиент отправляет по TCP соединению запросы на сервер (создает окно), сервер посылает обратно отклики, а также сервер посылает события клиенту (нажатие кнопки мыши, нажатие клавиш на клавиатуре, перемещение окна, изменение размера окна и так далее).

На рисунке 30.2 повторен рисунок 30.1, где показаны фазы общения клиента с процессом X сервера, которые, в свою очередь, обслуживают окна на дисплее. Здесь не показано, как X сервер обслуживает клавиатуру и мышь.

Рисунок 30.2 Три клиента, использующие один дисплей.

Подобная форма, когда один сервер обрабатывает несколько клиентов, отличается от обычного TCP конкурентного сервера, который мы описали в разделе "Реализация TCP сервера" главы 18. FTP и Telnet серверы, например, запускают новый процесс каждый раз, когда приходит запрос на новое TCP соединение, поэтому каждый клиент общается со своим процессом сервера. В случае X, все клиенты, запущенные на этом же хосте или на другом хосте, общаются с одним сервером.

По TCP соединению между X клиентом и его сервером осуществляется обмен большим количеством данных. Количество данных зависит от конкретной реализации приложения. Например, если мы запустим клиента Xclock, который отображает текущее время и дату в окне клиента на сервере и укажем ему обновляться один раз в секунду, X сообщения посылаются по TCP соединению от клиента к серверу один раз в секунду. Если мы запустим эмулятор X терминала, Xterm, каждое нажатие клавиши будет генерировать 32-байтное X сообщение (72 байта вместе со стандартными IP и TCP заголовками), и еще больше X сообщений в обратном направлении с символами, отраженными эхом. [Droms and Dyksen 1990] рассчитывает TCP траффик между различными X клиентами и одним конкретным сервером.

Программа Xscope

Удобная программа, с помощью которой можно определить, чем обмениваются X клиент и его сервер, это Xscope. Она поставляется с большинством реализаций X Window. Программа находится между клиентом и сервером, пропуская все данные в обоих направлениях, при этом отлавливая все запросы клиента и отклики сервера. На рисунке 30.3 показано, как это происходит.

Рисунок 30.3 Использование xscope для мониторинга X соединения.

Во-первых, мы стартовали процесс xscope на том же самом хосте, где находится сервер, при этом xscope слушает запросы на TCP соединение приходящие на порт 6001, а не 6000. Затем мы стартовали клиента на другом хосте и указали дисплей номер 1, а не 0, таким образом, клиент подключается к xscope, а не непосредственно к серверу. Когда запрос на соединение прибывает от клиента, xscope создает TCP соединение на реальный порт сервера 6000 и копирует все между клиентом и сервером, а также предоставляет читаемое описание запросов и откликов. Мы запустим xscope на хосте sun, а клиента xclock на хосте svr4.

svr4 % DISPLAY=sun:1 xclock -digital -update 5

При этом отображаются время и дата в цифровом формате

Thu Sep 9 10:32:55 1993

в окне на хосте sun. Мы указали обновлять время один раз каждые 5 секунд.

Также указана опция -q команды xscope, чтобы получать минимальный вывод. Доступны различные уровни отладки, что позволяет просмотреть все поля в каждом сообщении. Следующий вывод показывает первые три запроса и отклика.

sun % xscope -q
0.00: Client --> 12 bytes
0.02:                             152 bytes <-- X11 Server
0.03: Client --> 48 bytes
       ............REQUEST: CreateGC
       ............REQUEST: GetProperty
0.20:              396 bytes <-- X11 Server
                   ..............REPLY: GetProperty
0.30: Client --> 8 bytes
0.38: Client --> 20 bytes
       ............REQUEST: InternAtom
0.43:                32 bytes <-- X11 Server
                   ..............REPLY: InternAtom

Первое сообщение от клиента в момент времени 0,00 и отклик сервера в момент времени 0,02 это стандартное установление соединения между клиентом и сервером. Клиент указывает порядок следования байтов и версию сервера, которую он ожидает. Сервер сообщает о себе различную информацию.

Следующее сообщение в момент времени 0,03 содержит два запроса от клиента. Первый запрос создает на сервере графическое окружение, в котором клиент будет рисовать. Второй получает свойства от сервера (характеристика RESOURCE_MANAGER). Свойства обеспечивают общение между клиентами, обычно между приложением и оконным менеджером. Отклик от сервера размером 396 байт в момент времени 0,20 содержит это свойство.

Следующие два сообщения от клиента в моменты времени 0,30 и 0,38 имеют форму одного запроса, который требует вернуть атом. (Каждое свойство имеет уникальный целый идентификатор, который называется атом.) В момент времени 0.43 сервер отправляет отклик, содержащий атом.

Рассматривать этот пример дальше невозможно без подробного описания того, как работает система X window system, что не является целью нашего повествования. В этом примере мы видим, что перед тем как на дисплее появилось окно, клиент отправил 12 сегментов, содержащих 1668 байт, а сервер отправил 10 сегментов, содержащих 1120 байт. Весь процесс занял 3,17 секунды. С этого момента, каждые 5 секунд, клиент посылает маленький запрос в среднем по 44 байта, которые обновляют окно. Это продолжается до тех пор, пока клиент не будет выключен.

LBX: X с низкой шириной полосы

Кодирование, используемое протоколом X, оптимально для работы в локальных сетях, где время, которое тратится на кодирование и декодирование данных, более важно, нежели минимизация количества передаваемых данных. При работе с Ethernet все нормально, однако в случае медленных последовательных каналов, таких как SLIP и PPP (глава 2, разделы "SLIP: IP по последовательной линии" и "PPP: протокол точка-точка") возникают проблемы.

Поэтому был разработан стандарт Low Bandwidth X (LBX), который использует следующую технику для уменьшения сетевого траффика: кэширование, отправку изменений от предыдущих пакетов и сжатие. Спецификации появились в 1993 году с реализацией X window system Release 6.

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

Первые два приложения, которые мы рассмотрели, Finger и Whois, предназначены для получения информации о пользователях. Клиент Finger запрашивает сервер, чаще всего для того, чтобы найти какое-либо имя (например, чтобы отправить пользователю почту) или для того чтобы посмотреть, зашел ли кто-либо в систему терминалом в настоящий момент. Клиент Whois обычно общается с сервером, запущенным от InterNIC, в поисках информации о человеке, организации, домене или номере сети.

Другие сервисы, позволяющие более эффективно работать с ресурсами Internet, это Archie, WAIS, Gopher, Veronica и WWW. Они помогают найти в Internet файлы и документы. В настоящее время разрабатываются и другие средства определения ресурсов.

Эта глава закончена кратким рассмотрением системы X Window System, еще одного очень "серьезного" приложения, работающего с TCP/IP. Мы видели, что X сервер обслуживает несколько окон на дисплее и обеспечивает общение клиента с его окном. С помощью программы Xscope мы видели, как существует возможность поместить еще одну программу между клиентом и сервером, чтобы получить информацию о том, с помощью каких сообщений осуществляется обмен.

Упражнения

  1. Используйте Whois, чтобы найти владельца сети класса A с идентификатором сети 88.
  2. Используйте Whois, чтобы найти DNS сервер в домене whitehouse.gov. Совпадет ли отклик с ответом, который можно получить от системы DNS?
  3. Как Вы думаете, должен ли быть процесс xscope, показанный на рисунке 30.3, запущен на том же самом хосте, что и X сервер?


Программа tcpdump

Приложение A Программа tcpdump

Программа tcpdump была написана Van Jacobson, Craig Leres и Steven McCanne, во время их работы в лаборатории Lawrence Berkeley, Калифорнийского университета, Беркли. В тексте этой книги используется версия 2.2.1 (июнь 1992 года).

Программа tcpdump разработана таким образом, чтобы переводить сетевую плату в смешанный режим (promiscuous mode), при этом, каждый пакет, проходящий по кабелю, фиксируется. Обычно сетевые платы для сред передачи, таких как Ethernet, захватывают только фреймы канального уровня, адресованные конкретному интерфейсу или отправленные на широковещательный адрес (глава 2, раздел "Ethernet и IEEE 802 инкапсуляция").

Операционная система должна позволить поместить интерфейс в смешанный режим и позволить пользовательскому процессу захватывать фреймы. Реализации tcpdump существуют для следующих Unix систем: 4.4BSD, BSD/386, SunOS, Ultrix и HP-UX. Просмотрите файл README, который поставляется вместе с дистрибутивом tcpdump, где подробно описано, как в данном случае функционирует операционная система и какие версии поддерживаются.

Существуют альтернативы для tcpdump. На рисунке 10.8 мы использовали программу snoop, поставляемую в составе Solaris 2.2, чтобы просмотреть некоторые пакеты. В AIX 3.2.2 есть программа iptrace, которая предоставляет подобные возможности.

Пакетный фильтр BSD

В современных реализациях ядра BSD существует пакетный фильтр BSD (BPF - BSD Packet Filter), именно его использует tcpdump, чтобы отлавливать и фильтровать пакеты из сетевой платы (которая помещена в смешанный режим). BPF также работает с каналами точка-точка, такими как SLIP (глава 2, раздел "SLIP: IP по последовательной линии") (при этом не требуется специальных настроек), и с интерфейсом loopback (глава 2, раздел "Интерфейс loopback").

BPF имеет долгую историю. Пакетный фильтр Enet был создан в 1980 году Mike Accetta и Rick Rashid в университете Carnegie Mellon. Jeffrey Mogul из Stanford перенес этот код в BSD и продолжил его разработку с 1983 года. С тех пор туда же был включен Ultrix Packet Filter на DEC, STREAMS NIT модуль на SunOS 4.1, и BPF. Steven McCanne из лаборатории Lawrence Berkeley Laboratory начал работать с BPF летом 1990 года. Однако большая часть разработки принадлежит Van Jacobson. Подробности о последней версии и сравнения с Sun's NIT даны в [McCanne and Jacobson 1993].

На рисунке А.1 показаны характеристики BPF, когда он используется в случае Ethernet.

Рисунок А.1 Пакетный фильтр BSD.

BPF помещает драйвер Ethernet устройства в смешанный режим и затем получает от драйвера копию каждого полученного и отправленного пакета. Эти пакеты проходят через фильтр, указанный пользователем, таким образом, только те пакеты, которые интересуют пользователя, попадают в обработку.

Несколько процессов могут наблюдать за указанным интерфейсом, и каждый процесс использует свой собственный фильтр. На рисунке А.1 показаны пример работы tcpdump и демона RARP (глава 5, раздел "Реализация RARP сервера"), оба наблюдают за одним и тем же Ethernetом. При каждом появление tcpdump используется собственный фильтр. Фильтр для tcpdump может быть указан пользователем в командной строке, тогда как rarpd всегда использует один и тот же фильтр для отлова только RARP запросов.

Помимо указанного фильтра, каждый пользователь BPF также указывает значение тайм-аута. Так как скорость данных в сети может легко переполнить мощности процессора, и так как не очень эффективно осуществлять маленькие чтения из ядра, BPF старается упаковать несколько фреймов в один буфер чтения и возвратить только тогда, когда буфер полон или истек тайм-аут, установленный пользователем. tcpdump устанавливает тайм-аут в 1 секунду, так как он обычно получает от BPF много данных, тогда как демон RARP получает немного фреймов, поэтому rarpd устанавливает тайм-аут в 0 (фрейм возвращается при получении).

Фильтр, указанный пользователем, сообщает BPF, какие фреймы необходимо обрабатывать. Эти инструкции интерпретируются фильтром BPF в ядре. Фильтрация в ядре, а не в пользовательском процессе, уменьшает количество данных, которые должны быть переданы от ядра пользовательскому процессу. Демон RARP всегда использует одну и ту же программу фильтрации, которая встроена в программу. tcpdump, с другой стороны, позволяет пользователю указать выражение фильтрации в командной строке при каждом запуске. tcpdump конвертирует выражение, указанное пользователем, в соответствующую последовательность инструкций для BPF. В качестве примера tcpdump можно привести выражение:

% tcpdump tcp port 25
% tcpdump 'icmp[0] != 8 and icmp[0] != 0'

В первом случае печатаются только TCP сегменты с портом источника или назначения равным 25. Во втором случае печатаются только ICMP сообщения, которые не являются эхо запросами или эхо откликами (не ping пакеты). Эти выражения указывают, что первый байт ICMP сообщения (поле type на рисунке 6.2) не должен быть равен 8 или 0, что соответствует эхо запросам или эхо откликам на рисунке 6.3. Как Вы можете видеть, грамотная фильтрация требует знаний структуры пакета. Выражение во втором примере, заключено в одиночные кавычки, что предотвращает от интерпретации командным интерпретатором Unix (shell) специальных символов.

Обратитесь к страницам помощи tcpdump(1), где приведены детали о выражениях, которые могут быть указаны пользователем. В страницах помощи bpf(4) приведены инструкции виртуальной машины, используемой BPF. [McCanne and Jacobson 1993] сравнивает реализацию и производительность этой машины с другими подходами.

Краник в сетевом интерфейсе SunOS

SunOS 4.1.x предоставляет потоковый (STREAMS) драйвер псевдоустройства, который называется краник в сетевом интерфейсе (NIT - Network Interface Tap) . ([Rago 1993] содержит дополнительные детали о драйверах потоковых устройств. Мы будем называть эту характеристику "потоками" (streams).) NIT напоминает пакетный фильтр BSD, однако он не такой мощный и эффективный. На рисунке А.2 показаны потоковые модули, которые используются в NIT. Одно отличие между этим рисунком и рисунком А.1 заключается в том, что BPF может захватывать пакеты, принятые и переданные с сетевого интерфейса, тогда как NIT захватывает только пакеты, полученные с этого интерфейса. С использованием tcpdump с NIT мы можем видеть только пакеты, отправленные другими хостами в сеть - мы никогда не увидим пакеты, отправленные нашим собственным хостом. (Несмотря на то, что BPF работает с SunOS 4.1.x, он требует изменения исходного кода для Ethernet драйвера устройства, что невозможно для большинства пользователей, которые не имеют доступа к исходным кодам.)

Когда устройство /dev/nit открыто, потоковый драйвер nit_if открыт. Так как NIT построен с использованием потоков, обрабатывающие модули могут быть помещены поверх драйвера nit_if. tcpdump помещает модуль nit_buf в STREAM. Этот модуль собирает вместе несколько фреймов из сети в один буфер чтения, а пользовательский процесс устанавливает значение тайм-аута. Это напоминает то, что мы описали в случае BPF. Демон RARP не помещает этот модуль в свой поток, так как он работает с небольшим количеством пакетов.

Рисунок А.2 Краник в сетевом интерфейсе SunOS.

Фильтрация, указанная пользователем, осуществляется потоковым модулем nit_pf. На рисунке А.2 этот модуль используется демоном RARP, однако не используется tcpdump. Вместо этого под SunOS tcpdump осуществляет свою собственную фильтрацию в пользовательском процессе. Причина этого в том, что инструкции виртуальной машины, используемые nit_pf, отличаются (а также они не такие мощные) от тех, которые поддерживаются BPF. Это означает, что когда пользователь указывает выражение фильтрации tcpdump, больше данных пересекают границу между ядром к пользователю с использованием NIT, чем при использовании BPF.

Поставщик интерфейса канального уровня в SVR4

SVR4 поддерживает поставщика интерфейса канального уровня (DLPI - Data Link Provider Interface), который является потоковой реализацией OSI Data Link Service Definition. Большинство версий SVR4 все еще поддерживают версию 1 DLPI, SVR4.2 поддерживает обе версии 1 и 2, а Sun's Solaris 2.x поддерживают версию 2 с дополнительными расширениями.

Программы мониторинга сети, такие как tcpdump, должны использовать DLPI для символьного доступа к драйверам устройств канального уровня. В Solaris 2.x модуль потокового пакетного фильтра был переименован в pfmod, а модуль буфера переименован в bufmod.

Несмотря на то, что Solaris 2.x это достаточно новая система, вскоре должна появиться реализация tcpdump. В Sun есть программа, которая называется snoop, осуществляющая функции, напоминающие функции tcpdump. (snoop заменяет программу, работающую в SunOS 4.x, которая называется etherfind.)

Вывод tcpdump

Вывод команды tcpdump - "символьный" (raw). Мы будем модифицировать и дополнять его, чтобы сделать вывод более читаемым.

Во-первых, tcpdump всегда вводит имя сетевого интерфейса, который "слушает". Мы удалим эту строку.

Затем вывод времени, осуществляемый tcpdump, осуществляется в формате 09:11:22.642008 для систем с точностью до микросекунд, или в формате 09:11:22.64 для систем с точностью часов до 10 миллисекунд. (В приложении В мы расскажем более подробно о точности компьютерных часов.) В любом случае формат HH:MM:SS это не то, что мы хотим. В данном случае хочется видеть относительное время для каждого пакета с момента начала мониторинга сети (dump), а также разницу во времени между последовательно приходящими пакетами. Мы модифицировали вывод, чтобы показать эти временные показатели. Первый показатель мы показываем в виде шести цифр справа от десятичной точки, когда можно получить точность разрешения времени в микросекундах (в случае 10-миллисекундной точности показано две цифры), а второй показатель мы показываем в виде четырех или двух цифр справа от десятичной точки (в зависимости от точности часов).

В этом тексте большинство вывода команды tcpdump было получено на хосте sun, который имеет точность часов до одной микросекунды. Некоторый вывод был получен на хосте bsdi, на котором работает операционная система BSD/386 Version 0.9.4, и который предоставляет 10-миллисекундную точность часов (рисунок 5.1). Некоторый вывод был также получен на хосте bsdi, который работает под управлением операционной системы BSD/386 Version 1.0, которая предоставляет микросекундную точность.

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

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

И в завершение, вывод tcpdump может не помещаться в страницу. Мы обрезаем длинные строки, для того чтобы их удобней было читать.

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

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

sun % tcpdump -e

tcpdump: listening on le0

09:11:22.642008 0:0:c0:6f:2d:40 ff:ff:ff:ff:ff:ff arp 60: arp who-has svr4 tell bsdi

09:11:22.644182 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

09:11:22.644839 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]

09:11:22.649842 0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 ip 60: svr4.discard > bsdi.1030:

 S 3562228225:3562228225 (0) ack 596459522 win 4096 <mss 1024>

09:11:22.651623 0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip 60: bsdi.1030 > svr4.discard:

. ack 1 win 4096 [tos 0x10]

^?

9 packets received by filter

0 packets dropped by kernel

Рисунок А.3 Вывод команды tcpdump для рисунка 4.4.

Соглашения о безопасности

Необходимо обратить внимание на то, что просмотр сетевого траффика позволяет нам увидеть многие вещи, которые мы видеть вообще-то не должны. Например, пароли, печатаемые пользователями для приложений, таких как Telnet или FTP, передаются по сети именно так, как их ввел пользователь. (Это называется представлением пароля в виде открытого текста (cleartext), в отличие от зашифрованного представления (encrypted). Именно в зашифрованном виде пароли хранятся в файле паролей Unix, обычно /etc/passwd или /etc/shadow.) Тем не менее, существует множество моментов, когда администратору сети необходимо использовать средство, подобное tcpdump, чтобы диагностировать проблемы в сети.

Мы используем tcpdump как обучающее средство, чтобы посмотреть, что в действительности передается по сети. Доступ к tcpdump и подобным ей утилитам зависит от системы (и может быть от воли системного администратора). В случае SunOS, например, доступ к NIT устройству разрешен только суперпользователю. Пакетный фильтр BSD использует другую технику: доступ контролируется доступом к устройству /dev/bpfXX. Обычно эти устройства может читать и писать только владелец (который должен быть суперпользователем), а также они доступны для чтения членам группы (группа системного администратора). Это означает, что обычный пользователь не может запускать программы, подобные tcpdump, без санкции на то системного администратора (например, с помощью setUID).

Опция отладки сокета

Еще один способ посмотреть, что проходит в TCP соединении, это включить опцию отладки сокета, естественно на системах, которые поддерживают эту характеристику. Эта характеристика работает только с TCP (она не работает с другими протоколами) и требует поддержки приложения (чтобы включить опцию сокета, когда оно стартует).

Большинство реализаций Berkeley поддерживают это, включая SunOS, 4.4BSD и SVR4.

Программа включает опцию сокета, а ядро затем осуществляет записи того, что происходит для этого соединения. Затем эта информация может быть получена путем запуска программы trpt(8). Чтобы включить опцию отладки сокета, не требуется специальных прав доступа, однако необходимо иметь специальные привилегии, чтобы запустить trpt, так как она осуществляет доступ к памяти ядра.

Наша программа sock (приложение С) поддерживает эту характеристику с опцией -D, однако информацию, которую можно получить в выводе, сложнее описать и понять, нежели соответствующий вывод команды tcpdump. Тем не менее, мы использовали эту программу в разделе "Пример RTT" главы 21, чтобы посмотреть переменные ядра в TCP соединении, к которому tcpdump не имел доступа.


Компьютерные часы

Приложение B Компьютерные часы

Так как в большинстве примеров в этом тексте рассчитываются какие-либо временные интервалы, нам необходимо более подробно описать способы поддержки времени в современных Unix системах. Все сказанное ниже имеет отношение к системам, на которых использовались примеры в этой книге. Более подробно о поддержке времени можно прочитать в разделах 3.4 и 3.5 публикации [Leffler et al. 1989].

С определенной частотой генерируются аппаратные прерывания от часов. В случае Sun SPARCs и Intel 80386s эти прерывания возникают каждые 10 миллисекунд.

Необходимо отметить, что в большинстве компьютеров используется нескомпенсированный кварцевый генератор для генерации прерываний. Как можно заметить из таблицы 7 RFC 1305 [Mills 1992], не стоит беспокоиться о том, на сколько отстает в день этот генератор. Очень немногие компьютеры могут поддерживать точное время (другими словами, прерывания не возникают точно каждые 10 миллисекунд). Приближение 0,01% дает ошибку примерно в 8,64 секунды в день. Чтобы поддерживать более точное время, (1) нужно использовать лучший генератор (кварц), (2) можно использовать внешний источник времени с повышенной точностью (например, источник времени, предоставляемый Global Positioning Satellites) или (3) получить доступ по Internet к системам с более точными часами. Последнее решается с помощью протокола Network Time Protocol, как описано в RFC 1305, но это выходит за рамки обсуждения нашей книги.

Еще один хорошо известный источник ошибок во времени в Unix системах заключается в том, что прерывания, которые появляются каждые 10 миллисекунд, всего лишь заставляют ядро увеличивать переменную, которая отслеживает время. Если ядро потеряло прерывание (например, оно было слишком занято в период между двумя соседними 10-миллисекундными прерываниями), часы отстанут на 10 миллисекунд. Подобная потеря прерываний часто приводит к отставанию часов в Unix системах.

Даже если прерывания часов происходят примерно каждые 10 миллисекунд, более новые системы, такие как SPARCs, предоставляют более высокую точность часов. При работе с NIT драйвером (описанным в приложении А), tcpdump имеет доступ к этому таймеру с повышенным разрешением. В SPARC этот таймер предоставляет микросекундное разрешение. Пользовательские процессы могут получить доступ к этому таймеру с повышенным разрешением через функцию gettimeofday(2).

Автор провел следующий эксперимент. Была запущена программа, которая вызывает функцию gettimeofday циклически 10000 раз, при этом каждый раз возвращенное значение сохранялось в массиве. В конце цикла были напечатаны 9999 отрезков времени. Для SPARC ELC величины отрезков времени показаны на рисунке В.1.

Микросекунды | Счетчик
36 | 4914
37 | 4831
38 | 167
39 | 8
другие | 79

Рисунок В.1 Распределение времени, необходимое для вызова gettimeofday 10000 раз в SPARC ELC.

Полное время, необходимое для запуска программы, составило 0,38 секунды, при практически свободной системе. Отсюда можно сделать вывод, что процессу потребовалось для вызова gettimeofday примерно 37 микросекунд. Так как ELC имеет скорость примерно 21 MIPS (миллион инструкций в секунду), 37 микросекунд соответствуют примерно 800 инструкциям. Ядро принимает системный вызов от пользовательского процесса, исполняет системный вызов, копирует назад 8 байт в качестве результата и возвращает управление пользовательскому процессу. (Скорость MIPS достаточно спорная величина, поэтому на современных системах сложно оценить время исполнения инструкций. Все что мы попробуем сделать, это получить примерное представление и увидеть, могут ли полученные значения иметь какой-то определенный смысл.)

Из этого простого эксперимента мы можем сказать, что значения, возвращенные gettimeofday, содержат микросекундную точность.

Если мы запустим подобный тест в SVR4/386, результаты будут отличны от приведенных выше. Это объясняется тем, что большинство 386 Unix систем, таких как SVR4, имеют только 10-миллисекундные прерывания часов и даже не стараются предоставить более высокое разрешение часов. На рисунке В.2 показано распределение 9999 отрезков времени для компьютера 25Мгц 80386, работающего под управлением операционной системы SVR4.

Микросекунды | Счетчик
0 | 9871
10000 | 128

Рисунок В.2 Время, необходимое для вызова gettimeofday 10000 раз в случае SVR4/386.

Эти значения менее точны, разница во времени обычно меньше чем 10 миллисекунд, что воспринимается как 0. Практически все, что мы можем сделать на подобных системах, это рассчитать время часов на свободной машине и поделить его на количество циклов. При этом мы получим верхний предел, так как сюда входит время, необходимое, чтобы 9999 раз вызвать printf и записать результаты в файл. (В случае SPARC, рисунок В.1, промежутки времени не включают в себя время, необходимое для работы функции printf, так как все 10000 значений были сначала получены, а затем был напечатан результат.) Под управлением SVR4 время часов составило 3,15 секунды, что дает примерно 315 микросекунд на один системный вызов. Время системного вызова в 8,5 раз медленнее, чем в SPARC, что выглядит похожим на правду.

BSD/386 Version 1.0 предоставляет микросекундную точность, такую же как SPARC. Она читает регистр часов 8253 и рассчитывает количество микросекунд, прошедшее с последнего тика часов. Это разрешение доступно для процессов вызывающих gettimeofday и для модулей ядра, таких как пакетный фильтр BSD.

В случае tcpdump эти цифры означают, что мы можем доверять миллисекундным значениям и значениям, равным частям миллисекунд, которые напечатаны в системах SPARC и BSD/386, однако значения, напечатанные tcpdump под управлением SVR4/386, всегда будут кратны 10 миллисекундам. Для других программ, которые печатают время возврата, таких как ping (глава 7) и traceroute (глава 8), в случае SPARC и BSD/386 систем мы можем верить миллисекундным значениям вывода, однако значения, напечатанные под управлением SVR4/386, будут всегда кратны 10. Чтобы оценить время возврата ping в локальной сети, что мы показывали в главе 7, равное примерно 3 миллисекундам, требуется запустить ping на SPARC или BSD/386.

В некоторых примерах запущенных под BSD/386 Version 0.9.4 получена 10-миллисекундная точность часов (как под SVR4). Когда мы показывали вывод команды tcpdump с этих систем, то приводили только две цифры справа от десятичной точки, в соответствии с предоставляемой точностью часов.


Программа sock

Приложение C Программа sock

Для генерации TCP и UDP данных использовалась простая тестовая программа, называемая sock. Она выполняла функции как клиента, так и сервера. С помощью этой тестовой программы, которую можно запустить из приглашения shellа, мы избежали необходимости писать новые программы клиента и сервера на языке программирования C для каждой специальной характеристики, которую хотели протестировать. Так как цель этой книги - понимание сетевых протоколов, а не сетевого программирования, в этом приложении мы ограничимся описанием программы и ее опций.

Существует определенное количество программ, функционально похожих на sock. Juergen Nickelsen написал программу, которая называется socket, а Dave Yost программу sockio. Обе реализуют много похожих функций. Части программы sock также были получены из свободно распространяемой программы ttcp, написанной Mike Muuss и Terry Slattery.

Программа sock может работать в одном из четырех режимов:

  1. Интерактивный клиент: по умолчанию. Программа подсоединяется к серверу и затем копирует стандартный ввод на сервер и копирует все, что получено от сервера, на стандартный вывод. Это показано на рисунке С.1.

    Рисунок С.1 Функционирование программы sock как интерактивного клиента.

    Мы должны указать имя хоста сервера и имя сервиса, к которому необходимо подсоединиться. Хост может быть указан в виде IP адреса, а сервис может быть указан в виде целого номера порта. Подсоединение к стандартному эхо серверу (глава 1, раздел "Стандартные простые сервисы"), от sun к bsdi, отражает эхом все, что введено:

sun % sock bsdi echo

a test line                    вводим строку

a test line                    эхо сервер вернул копию

^D                             вводим символ конца файла, чтобы прекратить работу программы
  2. Интерактивный сервер: указана опция -s. Необходимо указать имя сервиса (или номер порта):

sun % sock s 5555            работает как сервер, слушает порт 5555

    Программа ожидает соединения от клиента и затем копирует стандартный ввод клиенту и копирует все, что получено от клиента, на стандартный вывод. В командной строке перед номером порта может стоять Internet адрес, чтобы указать, с какого локального интерфейса будет принято соединение:

sun % sock -s 140.252.13.33 5555        принимает соединения только по Ethernet

    По умолчанию запрос на соединение принимается с любого локального интерфейса.

  3. Клиент источник: указана опция -i. По умолчанию, буфер размером 1024 байта записывается в сеть 1024 раза. С помощью опций -n и -w можно изменить эти значения. Например, команда
    sun % sock -i -n12 -w4096 bsdi discard

    запишет 12 буферов, каждый из которых по 4096 байт, на discard сервис хоста bsdi.

  4. Сервер приемник: указаны опции -i и -s. Данные читаются из сети и отбрасываются.

Несмотря на то, что в этих примерах использовался TCP (по умолчанию), с помощью опции -u можно использовать UDP.

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

-b n

Воспринимает n как номер локального порта клиента. (По умолчанию используется динамический номер порта, назначенный системой клиента.)

-c

Конвертирует символы новой строки, которые прочитаны из стандартного ввода, в символы возврата каретки и пропуска строки. Когда осуществляется чтение из сети, конвертирует последовательность <возврат каретки, пропуск строки> в единственный символ новой строки. Большинство Internet приложений работают с NVT ASCII (глава 26, раздел "Протокол Telnet"), где возврат каретки и пропуск строки используются для завершения каждой строки.

-f a.b.c.d.p

Указывает удаленный IP адрес (a.b.c.d) и удаленный номер порта (p) для конечной точки UDP (глава 11, раздел "Сервер UDP").

-h

Переводит TCP соединение в наполовину закрытое состояние (глава 18, раздел "Наполовину закрытый TCP"). То есть, соединение не разрывается при появлении в стандартном вводе символа конца файла. Вместо этого, соединение наполовину закрывается, при этом чтение из сети продолжается до тех пор, пока сам хост не закроет соединение.

-i

Клиент источник или сервер приемник. Либо пишет данные в сеть (по умолчанию), либо, если используется с опцией -s, читает данные из сети. Опция -n указывает количество буферов, которые необходимо записать (или считать), опция -w указывает размер каждой записи, а опция -r указывает размер каждого чтения.

-n n

Когда используется с опцией -i, n указывает количество буферов, которые необходимо считать или записать. По умолчанию n равно 1024.

-p n

Указывает количество секунд, в течение которых необходимо выдержать паузу, перед каждым чтением или записью. Это может быть использовано в случае клиента источника (-i) или сервера приемника (-is), чтобы вставить задержку между каждым чтением или записью в сеть. Также существует опция -P, которая позволяет вставить паузу перед первым чтением или записью.

-q n

Указывает размер очереди ожидания соединения для TCP сервера: разрешенное количество соединений, которое TCP может поставить в очередь для приложения (рисунок 18.23). По умолчанию равно 5.

-r n

Когда используется с опциями -is, n определяет размер каждого чтения из сети. По умолчанию 1024 байта за одно чтение.

-s

Функционирует как сервер, а не как клиент.

-u

Использует UDP вместо TCP.

-v

Отладочный режим. Печатает дополнительные подробности (как, например, динамически назначаемые номера портов клиента и сервера) в стандартный вывод ошибок.

-w n

Когда используется с опцией -i, определяет размер каждой записи в сеть. По умолчанию 1024 байта за запись.

-A

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

-B

Включает опцию сокета SO_BROADCAST, что позволяет UDP датаграммам быть отправленным на широковещательный IP адрес.

-D

Включает опцию сокета SO_DEBUG. При этом выводится дополнительная отладочная информация, которую содержит ядро для этого TCP соединения (приложение A, раздел "Опция отладки сокета"). Эта информация может быть получена позже, с помощью запуска программы trpt(8).

-E

Включает опцию сокета IP_RECVDSTADDR, если она поддерживается реализацией (глава 11, раздел "Сервер UDP"). Это предназначено для UDP серверов, чтобы печатать IP адреса назначения полученных UDP датаграмм.

-F

Будет использоваться конкурентный TCP сервер. В этом случае сервер создает новый процесс с использованием программы fork для каждого соединения клиента.

-K

Включает опцию TCP сокета SO_KEEPALIVE (глава 23).

-L n

Устанавливает время задержки (опция сокета SO_LINGER) для конечной точки TCP в значение n. Время задержки равное 0 означает, что когда соединение закрывается, любые данные, оставшиеся в очереди для отправки, отбрасываются, а удаленному хосту посылается сброс (глава 18, раздел "Сегменты сброса (Reset)"). Положительное время задержки - это время (в сотых долях секунды), в течение которого соединение не закрывается, пока все данные не будут отправлены и подтверждены. Если, после закрытия сетевого соединения, не все данные были отправлены и подтверждены в момент истечения этого таймера, закрытие вернет ошибочный код возврата.

-N

Устанавливает опцию сокета TCP_NODELAY, чтобы выключить алгоритм Нагла (глава 19, раздел "Алгоритм Нагла").

-O n

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

-P n

Указывает количество секунд, в течение которых необходимо выдержать паузу, перед тем как будет осуществлено первое чтение или запись в сеть. Это может быть использовано с сервером приемником (-is), чтобы осуществить задержку после приема запроса на соединение от клиента, однако перед тем, как осуществить первое чтение из сети. При использовании с клиентом источником или сервером приемником (-i), эта величина определяет промежуток времени между установлением соединения и перед первой записью в сеть. Опция -p определяет паузу между каждыми соседними чтениями или записями.

-Q n

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

-R n

Устанавливает приемный буфер сокета (опция сокета SO_RCVBUF) в значение n. Это может непосредственно повлиять на размер приемного окна, объявляемого TCP. В случае UDP определяет максимальный размер UDP датаграммы, которая может быть принята.

-S n

Устанавливает отправляющий буфер сокета (опция сокета SO_SNDBUF) в значение n. В случае UDP определяет максимальный размер UDP датаграммы, которая может быть отправлена.

-U n

Переводит TCP в режим срочности после осуществления записи номер n в сеть. Для входа в режим срочности записывается один байт данных (глава 20, раздел "Режим срочности (Urgent Mode)").


Решения и ответы на упражнения

Приложение D Решения и ответы на упражнения

Глава 1

  1. Значение равно 27 - 2 (126) плюс 214 - 2 (16382) плюс 221 - 2 (2097150), что составит 2113658. Мы вычитаем 2 при каждом расчете, так как идентификатор сети, состоящий из всех нулевых битов или всех единичных битов, не может существовать.
  2. На рисунке D.1 точками показаны значения до августа 1993 года.

    Рисунок D.1 Количество сетей в сети NSFNET.

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

  3. "Будь либеральным к тому, что принимаешь, и требовательным к тому, что отправляешь."

Глава 3

  1. Нет, любой адрес класса сети с идентификатором, начинающимся со 127, имеет право существовать, однако большинство систем использует 127.0.0.1.
  2. kpno имеет пять интерфейсов: три канала точка-точка и два Ethernetа. R10 имеет четыре интерфейса Ethernet. gateway имеет три интерфейса: два канала точка-точка и один Ethernet. И netb имеет один интерфейс Ethernet и два канала точка-точка.
  3. Нет никакой разницы: оба имеют маски подсети 255.255.255.0, как адреса класса С, которые не поделены на подсети.
  4. Можно. Это называется непересекающиеся сетевые маски, так как 16 бит, выделенные под маску подсети, не пересекаются. RFC, однако, рекомендует не использовать непересекающиеся сетевые маски.
  5. Так сложилось исторически. Значение получилось как 1024 + 512 при этом напечатанные значения MTU включают все требуемые заголовки. Solaris 2.2 устанавливает MTU для loopback интерфейса равный 8232 (8192 + 40), что вмещает в себя 8192 байта пользовательских данных вместе с обычным 20-байтовым IP заголовком и 20-байтовым TCP заголовком.
  6. Во-первых, с датаграммами значительно легче работать маршрутизаторам. Во-вторых, на основе датаграмм могут быть основаны и ненадежные (UDP) и надежные (TCP) транспортные уровни. В-третьих, датаграммы представляют собой минимальное представление сетевого уровня, что позволяет использовать разнообразные канальные уровни.

Глава 4

  1. Исполнение команды rsh устанавливает TCP соединение с удаленным хостом. При этом начинается обмен IP датаграммами между двумя хостами. При этом требуется, чтобы на удаленном хосте была запись в ARP кэше. Даже если ARP кэш был пуст перед исполнением команды rsh, можно гарантировать, что запись для нашего хоста в ARP кэше появится, перед тем как сервер rsh исполнит команду arp.
  2. Убедитесь, что ваш хост не имеет записи в своем ARP кэше для какого-либо другого хоста в той же Ethernet сети, скажем foo. Убедитесь, что foo посылает "беспричинный" ARP запрос при загрузке. Это можно сделать, запустив tcpdump на другом хосте, когда загружается foo. Затем погасите хост foo и внесите неверный пункт в ARP кэш на вашей системе для foo. Используйте команду arp и не забудьте указать опцию temp. Загрузите foo, и когда он загрузился, посмотрите ваш ARP кэш на предмет того, была ли исправлена неверная запись.
  3. Прочитайте раздел 2.3.2.2 в требованиях к хостам Host Requirements RFC и раздел "Взаимодействие между UDP и ARP" главы 11 этой книги.
  4. Допустим, у клиента существовала полная ARP запись для сервера, когда сервер был выключен. Если мы будем продолжать попытки подсоединиться к серверу (выключенному), тайм-аут ARP будет продлен еще на 20 минут. Когда сервер окончательно перезагрузится с новым аппаратным адресом, и если он не разошлет "беспричинный" ARP, старая неверная ARP запись будет все еще останется у клиента. У нас не будет возможности подсоединиться к серверу на его новый аппаратный адрес до тех пор, пока мы вручную не удалим запись в ARP кэше или прекратим на 20 минут попытки достучаться до сервера.

Глава 5

  1. Разные типы фреймов это не абсолютное требование, так как поле op на рисунке 4.3 имеет различные значения для всех четырех операций (ARP запрос, ARP отклик, RARP запрос и RARP отклик). Однако отличить реализацию RARP сервера от ARP сервера, находящегося в ядре, значительно легче с разными полями в поле типа фрейма.
  2. Каждый RARP сервер может осуществить маленькую задержку на случайный момент времени, перед тем как отправить ответ. В качестве усовершенствования, один RARP сервер может быть назначен как первичный, а все остальные как вторичные. Первичный сервер может отвечать без задержки, а вторичные со случайными задержками. Еще одно усовершенствование. В случае использования первичных и вторичных серверов, вторичные сервера могут быть запрограммированы таким образом, чтобы отвечать только на повторные запросы, принятые за короткие промежутки времени. При этом подразумевается, что причина появления повторных запросов заключается в том, что первичный сервер выключен.

Глава 6

  1. Если бы на одном локальном кабеле находилась сотня хостов, каждый попробовал бы послать ICMP ошибку о недоступности порта примерно в одно и то же время. Это привело бы к коллизиям (если используется Ethernet), при этом сеть станет практически бесполезной в течение секунды или двух.
  2. В этом случае "следует".
  3. ICMP ошибка всегда отправляется с TOS равным 0, как показано на рисунке 3.2. ICMP запрос может быть отправлен с любым TOS, но соответствующий отклик должен быть отправлен с тем же самым TOS.
  4. Команда netstat -s обычно используется для того, чтобы посмотреть статистику по протоколам. На хосте SunOS 4.1.1 (gemini), который получил 48 миллионов IP датаграмм, ICMP статистика следующая:

Output histogram:

       echo reply: 1757

       destination unreachable: 700

       time stamp reply: 1

Input histogram:

       echo reply: 211

       destination unreachable: 3071

       source quench: 249

       routing redirect: 2789

       echo: 1757

       #10: 21

       time exceeded: 56

       time stamp: 1

    21 входное сообщение с типом 10 - это требования к маршрутизатору, которые не поддерживаются SunOS 4.1.1.

    При использовании SNMP (рисунок 25.26) некоторые системы, как, например, Solaris 2.2, генерируют вывод netstat -s, который использует имена переменных SNMP.

Глава 7

  1. 86 байт поделенные на 960 байт/сек, умноженные на 2 дают 179,2 миллисекунды. Когда ping запущен с этой скоростью, печатаются значения равные 180 миллисекундам.
  2. (86 + 48) байт поделенные на 960 байт/сек, умноженные на 2 дают 279,2 миллисекунды. Появление дополнительных 48 байт объясняются тем, что последние 48 байт из 56 байт в части данных должны быть экранированы: 0xc0 это символ END в протоколе SLIP.
  3. CSLIP сжимает только TCP и IP заголовки в TCP сегментах. Он не влияет на ICMP сообщения, которые используются программой ping.
  4. На SPARCstation ELC ping на loopback адрес дает RTT равное 1,310 миллисекунды, тогда как ping на Ethernet адрес хоста дает RTT равное 1,460 милисекунды. Эта разница объясняется дополнительной обработкой, осуществляемой Ethernet драйвером, который определяет, что датаграмма действительно предназначена локальному хосту. Вам потребуется версия ping, которая дает микросекундное разрешение, чтобы оценить подобную разницу.

Глава 8

  1. Если входящая датаграмма имеет TTL равное 0, вычитание единицы и затем проверка могут установить TTL в значение 255 и продлить существование датаграммы. Несмотря на то, что маршрутизатор никогда не должен получать датаграммы с TTL равным 0, это может произойти.
  2. Мы заметили, что traceroute сохраняет 12 байт данных из раздела данных UDP датаграммы, которые содержат время отправки датаграммы. Однако, из рисунка 6.9 видно, что ICMP возвращает только первые 8 байт IP датаграммы, которая вызвала ошибку, там находилось 8 байт UDP заголовка. То есть, значение времени, сохраненное traceroute, не возвратилось в ICMP сообщение об ошибке. traceroute сохранила время, при отправке пакета, а когда был получен ICMP отклик, определила текущее время и, использовав эти два значения, получила RTT. Из главы 7 мы видим, что ping сохраняет время в исходящем ICMP эхо запросе, а сервер отражает эти данные. Это позволяет ping напечатать корректный RTT, даже если пакеты вернулись беспорядочно.
  3. Первая строка вывода корректна и указывает на R1. Следующая проба стартует с TTL равным 2, оно уменьшается на единицу в R1. Когда R2 получает датаграмму с уменьшенным на единицу TTL (от 1 до 0), то некорректно перенаправляет ее в R3. R3 видит, что входящий TTL равен 0 и отправляет назад ошибку об истечении времени. Это означает, что вторая строка вывода (для TTL равного 2) указывает на R3, а не на R2. Третья строка вывода корректно указывает на R3. Ошибка присутствует в двух последовательных строках вывода, которые указывают на один и тот же маршрутизатор.
  4. В данном случае TTL равное 1 указывает на R1, TTL равное 2 указывает на R2, а TTL равное 3 указывает на R3; однако, когда TTL равно 4, UDP датаграммы достигает пункта назначения со входящим TTL равным 1. Генерируется ICMP ошибка о недоступности порта, однако TTL сообщение об ошибке равно 1 (некорректно скопировано из входящего TTL). ICMP сообщение идет на R3, где TTL уменьшается на единицу, и сообщение отбрасывается. ICMP ошибка об истечении времени не генерируется, так как датаграмма, которая была отброшена, - это ICMP сообщение об ошибке (порт недоступен). Тоже происходит и с пробой, TTL которой равен 5, однако на этот раз исходящая ошибка о недоступности порта стартует с TTL равным 2 (скопировано из входящего TTL), и доходит до R2, где отбрасывается. Ошибка о недоступности порта, соответствующая пробе с TTL равным 6, доходит до R1, где тоже отбрасывается. И, в конце концов, ошибка о недоступности порта для пробы с TTL равным 7 проходит полный путь и возвращается назад с входящим TTL равным 1. (traceroute считает, что прибывающее ICMP сообщение с TTL равным 0 или 1 это одно и то же, поэтому она печатает восклицательный знак после RTT.) И в завершение, строки с TTL равными 1, 2 и 3 корректно идентифицируют R1, R2 и R3, затем следуют три строки, каждая из которых содержит три тайм-аута, за которым следует строка для TTL равного 7, которая идентифицирует пункт назначения.
  5. Все эти маршрутизаторы установили исходящий TTL ICMP сообщения в значение 255. Это общепринятая практика. Как мы и ожидали, входящее значение от netb равно 255, однако значение равное 253 от butch означает, что возможно где-то есть "невидимый" маршрутизатор. Иначе в этой точке появился бы входящий TTL равный 254. Точно так же, от enss142.UT.westnet.net мы ожидаем значение равное 252, а не 249. Это означает, что и здесь есть "невидимые" маршрутизаторы, которые некорректно обработали UDP датаграмму, однако они корректно уменьшили TTL и вернули ICMP сообщение.

    Необходимо достаточно внимательно просматривать исходящий TTL, так как иногда значение отличается от ожидаемого. Это может объясняться тем, что ICMP сообщение возвращается по другому пути, нежели ушла исходящая UDP датаграмма. В этом примере, однако, существуют именно "невидимые" маршрутизаторы, которые traceroute не нашла при использовании опции свободной маршрутизации от источника.

  6. Клиент ping устанавливает в поле идентификатора в ICMP эхо запросе (рисунок 7.1) свой идентификатор процесса. ICMP эхо отклик содержит это поле идентификатора. Каждый клиент просматривает возвращенное поле идентификатора и обрабатывает только те, которые он послал.

    Клиент traceroute устанавливает номер порта источника UDP в логическое ИЛИ со своим идентификатором процесса и 32768. Так как возвращенное ICMP сообщение всегда содержит первые 8 байт IP датаграммы, на которую сгенерирована ошибка (рисунок 6.9), что включает целиком UDP заголовок, этот номер порта источника возвращается в ICMP ошибке.

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

    Клиент traceroute не может поступить подобным образом, потому что все, что возвращается в ICMP ошибке, - это UDP заголовок (рисунок 6.9), а не UDP данные. Таким образом, traceroute должна помнить, когда она посылает запрос, дождаться отклика и рассчитать разницу во времени.

    Это иллюстрирует еще одно различие между Ping и Traceroute: Ping отправляет один пакет в секунду, вне зависимости от того, получены ли какие-либо отклики, тогда как Traceroute отправляет запрос и затем ожидает, придет ли отклик или будет отработан тайм-аут перед отправкой следующего запроса.

  8. Так как Solaris 2.2 стартует на динамически назначаемом UDP порте с номером равным 32768 по умолчанию, существует значительно большая вероятность того, что будет занят порт назначения на хосте назначения.

Глава 9

  1. Когда впервые был разработан стандарт ICMP, RFC 792 [Postel 1981b], разделение на подсети не использовалось. Также, использование перенаправления в одну сеть вместо перенаправления на N хостов (для всех N хостов, находящихся в сети назначения) сохраняет некоторое пространство в таблице маршрутизации.
  2. Эта запись не требуется, однако если она удалена, все IP датаграммы, предназначенные slip, посылаются на маршрутизатор по умолчанию (sun), который затем перенаправляет их на маршрутизатор bsdi. Так как sun перенаправляет датаграммы по тому же интерфейсу, по которому они были получены, он отправляет ICMP перенаправление хосту svr4. При этом создается та же самая запись в таблице маршрутизации хоста svr4, которая была удалена, однако в этот раз она создается путем перенаправления, вместо того чтобы создаваться в момент загрузки машины.
  3. Когда хост 4.2BSD получает датаграмму, предназначаемую для 140.1.255.255, он определяет, что у него есть маршрут к сети (140.1), поэтому он старается направить датаграмму. Чтобы сделать это, он посылает широковещательный запрос ARP в поисках 140.1.255.255. Так как на запрос не приходит отклик, датаграмма отбрасывается. Если на кабеле существует несколько хостов 4.2BSD, каждый отправляет этот ARP широковещательный запрос в одно и то же время, при этом сеть временно становится практически недоступной.
  4. В этом случае отклик приходит на каждый ARP запрос, где сообщается, что каждому хосту 4.2BSD необходимо послать датаграмму на указанный аппаратный адрес (широковещательный запрос Ethernet). Если существует k хостов 4.2BSD на этом кабеле, все получают свой собственный ARP отклик, в результате чего, каждый из них генерирует еще один широковещательный запрос. Каждый хост получает каждую широковещательную IP датаграмму, направляемую на 140.1.255.255, а так как каждый хост сейчас имеет запись в ARP кэше, датаграмма снова направляется на широковещательный адрес. Это продолжается и генерирует так называемое половодье Ethernet (meltdown). [Manber 1990] описывает другие формы цепных реакций в сетях.

Глава 10

  1. Тринадцать из этих маршрутов пришли от kpno: все, за исключением 140.252.101.0 и 140.252.104.0 - это сети, к которым gateway подключен непосредственно.
  2. Перед тем как 25 маршрутов, объявленных в потерянной датаграмме, будут обновлены пройдет шестьдесят секунд. В этом нет никакой проблемы, потому что RIP объявляет маршрут умершим только в том случае, если в течение 3-х минут не было обновлений.
  3. RIP работает поверх UDP, а UDP предоставляет необязательную контрольную сумму для данных, находящихся в UDP датаграмме (глава 11, раздел "Контрольная сумма UDP"). OSPF, однако, работает поверх IP. Контрольная сумма IP охватывает только IP заголовок, таким образом, OSPF должен добавлять свое собственное поле контрольной суммы.
  4. Балансировка загруженности уменьшает вероятность того, что пакеты будут доставлены в беспорядке, что может исказить времена возврата, рассчитанные транспортным уровнем.
  5. Это называется простым "расщепленным горизонтом" (технология расщепленных горизонтов основывается на том факте, что бессмысленно отправлять информацию о маршрутах в том направлении, откуда она пришла).
  6. На рисунке 12.1 мы показали, что каждый из ста хостов обрабатывает широковещательную UDP датаграмму через драйвер устройства, IP уровень и UDP уровень, где она будет окончательно отброшена, когда будет обнаружено, что UDP порт 520 не обслуживается.

Глава 11

  1. Так как при использовании инкапсуляции IEEE 802 в заголовке находится 8 дополнительных байт, 1465 байт пользовательских данных - это наименьший размер, который вызовет фрагментацию.
  2. Для IP посылается 8200 байт, 8192 байта пользовательских данных и 8 байт UDP заголовка. Используя форму записи tcpdump, первый фрагмент это 1480@+ (1480 байт данных, смещение 0 и установлен бит "дальше следуют еще фрагменты"). Второй это 1480@1480+, третий 1480@2960+, четвертый 1480@4440+, пятый 1480@5920+ и шестой 800@7400. 1480 x 5 + 800 = 8200 это количество байт, которые необходимо отправить.
  3. Каждый фрагмент размером 1480 байт поделен на три части: два фрагмента по 528 байт и один фрагмент 424 байта. Максимальное число кратное 8, меньшее чем 532 (552 - 20), это 528. Фрагмент размером 800 байт делится на две части: фрагмент размером 528 байт и фрагмент размером 272 байта. Таким образом, из исходной датаграммы размером 8192 байта получается 17 фреймов, которые передаются по SLIP каналу.
  4. Нет. Проблема заключается в том, что когда приложение осуществляет тайм-аут и повторную передачу, IP датаграмма, сгенерированная при повторной передаче, имеет новое поле идентификации. Промежуточная сборка осуществляется только для фрагментов, которые имеют одинаковое поле идентификации.
  5. Поле идентификации в IP заголовке одно и то же (47942).
  6. Во-первых, из рисунка 11.4 мы видим, что у gemini выключен расчет исходящей контрольной суммы UDP. Вполне возможно, что операционная система на этом хосте (SunOS 4.1.1) это одна из тех, что никогда не проверяют входящую контрольную сумму UDP, если не включен расчет исходящей контрольной суммы UDP. Во-вторых, вполне возможно, что большинство UDP траффика это локальный траффик, а не траффик по глобальным сетям. Поэтому нет необходимости исполнять все прихоти свойственные глобальным сетям.
  7. Опции свободной и жесткой маршрутизации от источника копируются в каждый фрагмент. Опция временной марки и опция записи маршрута не копируются в каждый фрагмент - они присутствуют только в первом фрагменте.
  8. Нет. Мы видели в разделе "Сервер UDP" главы 11, что большинство реализаций могут фильтровать входящие датаграммы, направляемые на заданный номер порта UDP, на основании IP адреса назначения, IP адреса источника и номера порта источника.

Глава 12

  1. Широковещательные запросы сами по себе не увеличивают сетевой траффик, однако они требуют дополнительной обработки на хостах. Широковещательные запросы могут вызвать увеличение сетевого траффика, если получающие хосты некорректно отвечают с помощью ошибок, таких как ICMP ошибки о недоступности порта. Маршрутизаторы обычно не перенаправляют широковещательные пакеты, однако их перенаправляют мосты, таким образом, широковещательные запросы в сетях, построенных на основе мостов, могут уйти значительно дальше, чем в сетях, построенных на маршрутизаторах.
  2. Каждый хост получает копию каждого широковещательного запроса. Интерфейсный уровень получает фрейм и передает его в драйвер устройства. Если поле типа принадлежит какому-либо другому протоколу, драйвер устройства отбрасывает фрейм.
  3. Сначала исполним netstat -r, чтобы посмотреть таблицу маршрутизации. Она покажет имена всех интерфейсов. Затем запустим ifconfig (глава 3, раздел "Команда ifconfig") для каждого интерфейса: флаги расскажут нам, поддерживает ли интерфейс широковещательные запросы, и если да, то в выводе также будут показаны широковещательные адреса.
  4. Berkeley реализации не позволяют фрагментировать широковещательные датаграммы. Когда мы указываем длину равную 1472 байта, результирующая IP датаграмма будет длиной ровно 1500 байт, что равно MTU Ethernet. Запрет на фрагментацию широковещательных датаграмм это политическое решение. Технической причины не существует (а именно, никакой другой причины, нежели просто уменьшить количество широковещательных пакетов).
  5. В зависимости от того, поддерживается ли групповая рассылка в различных сетевых платах Ethernet на 100 хостах, групповая датаграмма может быть игнорирована сетевой платой или отброшена драйвером устройства.

Глава 13

  1. Используйте некоторые уникальные значения для хоста при генерации случайных значений. IP адрес и адрес канального уровня это два значения, которые должны отличаться на каждом хосте. Время дня это плохой выбор, особенно если все хосты используют, например, протокол NTP для синхронизации своих часов.
  2. Они добавляют заголовок протокола приложения, который включает номер последовательности и временную марку.

Глава 14

  1. Разборщик - это всегда клиент, однако сервер имен это и клиент, и сервер.
  2. Возвращенный вопрос, он рассчитан для первых 44 байт. Единственный ответ занимает оставшийся 31 байт: 2-байтный указатель на имя домена (указатель на имя домена в вопросе), 10 байт на поля фиксированного размера (тип, класс, TTL и длина ресурса) и 19 байт для данных ресурса (имя домена). Обратите внимание, что имя домена в данных ресурса (svr4.tuc.noao.edu.) не делит суффикс с именем домена в вопросе (34.13.252.140.in-addr.arpa.), таким образом, указатель не может быть использован.
  3. Использование обратного порядка означает использование, во-первых, DNS, и затем, если это не сработает, попытку конвертировать аргумент в номер, состоящий из десятичных чисел, разделенных точками (форма записи IP адреса). Это означает, что каждый раз, когда используется адрес в виде десятичных цифр, разделенных точками, используется DNS, при этом привлекается DNS сервер. На это тратятся дополнительные ресурсы.
  4. Раздел 4.2.2 RFC 1035 указывает, что перед реальным DNS сообщением должна стоять 2-байтовая длина.
  5. Когда сервер имен стартует, он обычно читает (вполне возможно, устаревший) список корневых серверов из дискового файла. Затем он старается установить контакт с одним из этих корневых серверов, запрашивая записи сервера имен (тип запроса NS) для корневого домена. При этом возвращается текущий список корневых серверов. Минимум, что необходимо для этого, это чтобы хотя бы одна запись в стартовом файле на диске соответствовала текущему положению.
  6. Регистрационный сервер InterNIC обновляет корневые сервера три раза в неделю.
  7. Разборщик функционирует как приложение; если система сконфигурирована таким образом, чтобы использовать несколько DNS серверов, разборщик не может отслеживать времена возврата до различных DNS серверов. Это может привести к возникновению тайм-аутов на слишком короткие запросы разборщика, при этом будут происходить ненужные повторные передачи.
  8. Сортировка записей А должна осуществляться разборщиком, а не сервером имен, так как разборщик обычно знает больше, чем сервер, о топологии сети клиента. (Новые релизы BIND предоставляют разборщику возможность сортировать А записи.)

Глава 15

  1. TFTP запросы, посылаемые на широковещательный адрес, должны быть игнорированы. Как указывается в требованиях к хостам Host Requirements RFC, ответы на широковещательные запросы могут создать значительные проблемы в безопасности. Проблема, однако, заключается в том, что не все реализации и API предоставляют адрес назначения для UDP датаграмм для процессов, которые получают датаграммы (глава 11, раздел "Сервер UDP"). По этой причине многие TFTP серверы не следуют этому ограничению.
  2. К сожалению, RFC ничего не говорит о подобном переходе номера блоков. Реализации должны иметь возможность передавать файлы размером до 33553920 байт (65535 x 512). Большинство реализаций выдают ошибку, когда размер файла превышает 16776704 (32767 x 512), так как они некорректно определяют количество блоков в виде 16-битного целого со знаком, вместо того чтобы использовать целое без знака.
  3. Это упрощает реализацию TFTP клиента, что позволяет поместить его в ПЗУ, потому что сервер является отправителем загрузочных файлов, таким образом, сервер должен осуществлять тайм-ауты и повторные передачи.
  4. Так как TFTP - протокол с остановкой и ожиданием отклика, он может передавать максимум 512 байт за одну посылку от клиента серверу. Максимальная пропускная способность TFTP составляет в таком случае 512 байт, поделенных на время возврата между клиентом и сервером. В случае Ethernet (представим время возврата равное 3 миллисекундам), максимальная пропускная способность будет составлять примерно 170000 байт в секунду.

Глава 16

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

    BOOTP не имеет этих проблем с откликами, так как адрес отклика это обычный IP адрес, причем маршрутизатор всегда знает, как перенаправить информацию на этот адрес. Проблема заключается в том, что RARP использует адреса только канального уровня, а маршрутизаторы обычно не знают этих значений для хостов, находящихся в других, не подключенных непосредственно, сетях.

  2. Он должен использовать свой собственный аппаратный адрес, который должен быть уникальным и который устанавливается в запросе и возвращается в отклике.

Глава 17

  1. Все обязательные, за исключением UDP контрольной суммы. Контрольная сумма IP охватывает только IP заголовок, тогда как остальные захватывают все непосредственно после IP заголовка.
  2. IP адрес источника, номер порта источника или поле протокола должны быть повреждены.
  3. Большинство приложений Internet используют возврат каретки и пропуск строки, чтобы указать на конец каждой записи от приложения. Это кодирование NVT ASCII (глава 26, раздел "Протокол Telnet"). Альтернативным способом является установка префикса перед каждой записью, содержащего счетчик байтов, который используется в DNS (упражнение 4 главы 14) и Sun RPC (глава 29, раздел "Вызов удаленной процедуры Sun").
  4. Как мы видели в разделе "ICMP ошибка недоступности порта" главы 6, ICMP ошибка должна вернуть по крайней мере первые 8 байт, находящиеся позади IP заголовка в IP датаграмме, которая вызвала ошибку. Когда TCP получает ICMP ошибку, он должен проверить два номера порта, чтобы определить какому соединению соответствует ошибка, поэтому номер порта должен находиться в первых 8 байтах TCP заголовка.
  5. Существуют опции, находящиеся в конце TCP заголовка, однако в UDP заголовке не существует опций.

Глава 18

  1. ISN это 32-битный счетчик, который перескакивает со значения примерно 4294912000 на 8704, приблизительно через 9,5 часов после того, как система была загружена. По прошествию еще примерно 9,5 часов он перескакивает на 17408, затем на 26112 еще после 9,5 часов, и так далее. Так как ISN стартует с 1, когда система загружается, и так как минимальный порядок циклов цифр через 4, 8, 2, 6 и 0, ISN всегда должен быть нечетным числом.
  2. В первом случае мы использовали нашу программу sock, а она по умолчанию передает Unix символ новой строки так, как он есть - один ASCII символ 012 (восьмеричный). Во втором случае мы использовали Telnet клиента, а он конвертирует Unix символ новой строки в два ASCII символа - возврат каретки (восьмеричный 015), за которым следует пропуск строки (восьмеричный 012).
  3. В случае полузакрытого соединения один конец отправляет FIN и ожидает данные или FIN с удаленного конца. Полуоткрытое соединение это когда один конец вышел из строя, но это неизвестно удаленному концу.
  4. Соединение может войти в состояние ожидания 2MSL только в том случае, если оно уже было в состоянии ESTABLISHED.
  5. Во-первых, сервер дневного времени осуществляет активное закрытие TCP соединения после того, как он выдал дату и время клиенту. Это ясно из сообщения, напечатанного нашей программой sock: "connection closed by peer" (соединение закрыто удаленным концом). Клиент в этом соединении прошел через состояние пассивного закрытия. Это помещает пару сокетов в состояние TIME_WAIT на сервере, но не на клиенте. Затем, как показано на рисунке 18.6, большинство Berkeley реализаций позволяют прибыть новому запросу на соединение на пару сокетов, находящуюся в состоянии TIME_WAIT, это как раз то, что произошло в данном случае.
  6. Сброс посылается в ответ на FIN, потому что FIN прибывает для соединения, которое было в состоянии CLOSED.
  7. Сторона, которая набирает номер, осуществляет активное открытие. Сторона, на которой звонит телефон, осуществляет пассивное открытие. Одновременное открытие не разрешено, однако разрешено одновременное закрытие.
  8. Мы бы увидели только ARP запросы, а не TCP сегменты SYN, однако ARP запросы имеют то же временное расписание, как и на рисунке.
  9. Клиент это хост solaris, а сервер это хост bsdi. Подтверждение клиента (ACK) на SYN сервера комбинируется с первым сегментом данных от клиента (строка 3). Это полностью допустимо по правилам TCP, однако большинство реализаций так не поступают. Затем клиент посылает свой FIN (строка 4) не дождавшись ACK на свои данные. Это позволяет серверу подтвердить и данные, и FIN в строке 5. Этот обмен (отправка одного сегмента данных от клиента к серверу) требует 7 сегментов. Обычное установление и прекращение соединения (рисунок 18.13), вместе с одним сегментом данных и его подтверждением, требуют 9 сегментов.
  10. Во-первых, подтверждение сервера (ACK) на FIN клиента обычно не задерживается (мы обсуждаем задержанные ACK в разделе "Задержанные подтверждения" главы 19), а отправляется сразу же, по прибытию FIN. В этом случае, когда приложение получает EOF, оно сообщает своему TCP о необходимости закрыть свой конец соединения. Во-вторых, сервер, который получил FIN, не должен закрывать свой конец соединения по получении FIN от клиента. Как мы видели в разделе "Наполовину закрытый TCP" главы 18, данные все еще могут быть отправлены.
  11. Если прибывающий сегмент, на который сгенерирован RST, имеет поле ACK, номер последовательности RST берется из поле ACK прибывшего сегмента. Значение ACK равное 1 в строке 6 соответствует ISN равному 26368001 в строке 2.
  12. См. [Crowcroft et al. 1992] для получения комментариев о создании уровней.
  13. Выдается пять запросов. Представьте, что три пакета используются для установления соединения, один для запроса, один для подтверждения (ACK) запроса, один для отклика, один для ACK на отклик и четыре для того, чтобы закрыть соединение. Это означает, что используется 11 пакетов на один запрос, то есть всего 55 пакетов. При использовании UDP это значение уменьшается до 10 пакетов.

    Оно может быть уменьшено до 10 пакетов на запрос, если ACK на запрос комбинируется с откликом (глава 19, раздел "Задержанные подтверждения").

  14. Предел составляет примерно 268 соединений в секунду: максимальное количество номеров TCP порта (65536 - 1024 = 64512, игнорируя заранее известные порты) поделенное на продолжительность состояния TIME_WAIT 2MSL.
  15. Дублированный FIN подтверждается, и перестартовывается таймер 2MSL.
  16. Получение RST, пока соединение находится в состоянии TIME_WAIT, приводит к тому, что это состояние преждевременно разрывается. Это называется убийство TIME_WAIT. RFC 1337 [Braden 1992a] обсуждает это более подробно и показывает потенциальные проблемы. Простое решение этой проблемы, предложенное в RFC, заключается в том, чтобы игнорировать сегменты RST, пока соединение находится в состоянии TIME_WAIT.
  17. Это когда реализация не поддерживает полузакрытое состояние. Если приложение требует, чтобы был отправлен FIN, приложение уже не может больше читать из соединения.
  18. Нет. Входящие сегменты данных демультиплексируются с использованием IP адреса источника, номера порта источника, IP адреса назначения и номера порта назначения. Для входящих запросов на соединение, мы видели в разделе "Реализация TCP сервера" главы 18, что TCP сервер обычно может не принять соединение, основываясь на IP адресе назначения.

Глава 19

  1. Две записи приложения, за которыми следует чтение, вызывают задержку, потому что, скорее всего, используется алгоритм Нагла. Первый сегмент (с 8 байтами данных) отправляется, на него ожидается ACK перед отправкой 12 байт данных. Если сервер поддерживает задержанные ACK, он может осуществить задержку до 200 миллисекунд (плюс RTT), перед тем как этот ACK будет получен.
  2. В случае 5-байтовых CSLIP заголовков (IP и TCP) и 2-х байт данных, RTT по SLIP каналу для этих сегментов составит примерно 14,5 миллисекунды. Это RTT по Ethernet (обычно 5-10 миллисекунд), к которому необходимо добавить время маршрутизации на sun и bsdi. Таким образом, приведенное время выглядит вполне правдоподобно.
  3. На рисунке 19.6 разница во времени между сегментами 6 и 9 составляет примерно 533 миллисекунды. На рисунке 19.8 разница во времени между сегментами 8 и 12 составляет 272 миллисекунды. (Мы рассчитали время для клавиши F2, а не для клавиши F1, так как первое эхо для клавиши F1 на втором рисунке было потеряно.)

Глава 20

  1. Байт с номером 0 это SYN, а байт с номером 8193 это FIN. SYN и FIN занимают по одному байту в пространстве номеров последовательности.
  2. Первая запись, которую осуществляет приложение, вызывает отправку первого сегмента с флагом PUSH. Так как BSD/386 всегда использует медленный старт, она ожидает первый ACK перед отправкой остальных данных. В течение этого времени приложение осуществляет следующие три записи, в это же время в TCP буферы записываются данные, которые необходимо отправить. Следующие три сегмента не содержат флага PUSH, так как в буфере данных достаточно для отправки. Здесь к записям приложения начинается применяться медленный старт, при этом каждая запись приложения вызывает отправку сегмента, а так как этот сегмент является последним в буфере, устанавливается флаг PUSH.
  3. Необходимо определить емкость, для чего надо решить уравнения ширины пропускания в зависимости от задержки. Результаты будут следующими: 1920 байт в первом случае и 2062 для спутникового канала. Похоже, принимающий TCP объявляет окно равное всего лишь 2048 байт.

    Насытить спутниковый канал можно при использовании окна большего, чем 16000 байт.

  4. Нет, потому что TCP может пересобрать пакеты данных после тайм-аута, как мы увидим в разделе "Пересборка пакетов" главы 21.
  5. Сегмент 15 это обновление окна, которое посылается TCP модулем автоматически, так как приложение считало данные, поэтому окно было открыто. Это напоминает сегмент 9 на этом же рисунке. Появление сегмента 16 вызвано тем, что приложение закрыло свой конец соединения.
  6. При этом отправитель может направить пакеты в сеть с большей скоростью, чем сеть может в действительности обработать. Это называется компрессией ACK или пустым ACK [Mogul 1993, Sec. 15.8.13]. Эта ссылка означает, что в Internet использовалась компрессия ACK, хотя это редко приводит к переполнению.

Глава 21

  1. Следующий тайм-аут устанавливается в 48 секунд: 0 + 4 x 12. Коэффициент равный 4 это следующий множитель при экспотенциальном наращивании.
  2. Похоже, что SVR4 все еще использует коэффициент 2D вместо 4D при расчете RTO.
  3. Протокол с остановкой и ожиданием подтверждения, который используется в TFTP, ограничен отправкой 512 байт данных за время возврата. 32768/512 x 1,5 это 96 секунд.
  4. Показано четыре сегмента, пронумерованные как 1, 2, 3 и 4. Представьте, что они приняты в следующем порядке: 1, 3, 2 и 4. Подтверждения, сгенерированные получателем, будут ACK 1 (нормальный ACK), ACK 1 (дублированный ACK, когда принят сегмент 3, который пришел не в свое время), ACK 3, когда принят сегмент 2 (подтверждающий оба сегмента 2 и 3), и затем ACK 4. В этом случае сгенерирован один дублированный ACK. Если порядок приема будет 1, 3, 4, 2, то будет сгенерировано два дублированных ACK.
  5. Нет, потому что наклон все еще вправо-вверх, а не вниз.
  6. См. рисунок E.1.
  7. На рисунке 21.2 сегменты содержат 256 байт данных, передача по по CSLIP каналу со скоростью 9600 бит/сек между slip и bsdi, занимает примерно 250 миллисекунд. Представим, что сегменты данных не были поставлены в очередь где-нибудь между bsdi и vangogh, они прибыли на vangogh примерно через 250 миллисекунд. Так как это больше чем 200 миллисекунд (величина таймера задержанного ACK), каждый сегмент подтверждается, когда истекает следующий таймер задержанного ACK.

Глава 22

  1. Подтверждения (ACK), возможно, все задержаны на хосте bsdi, потому что нет необходимости посылать их немедленно. Именно поэтому соответственные времена имеют 0,170 и 0,370 в дробной части. Также, похоже на то, что 200-миллисекундный таймер на bsdi запущен примерно на 18 миллисекунд позже, чем тот же самый таймер на sun.
  2. Флаг FIN, так же как флаг SYN, занимает 1 байт в пространстве номеров последовательности. Объявленное окно будет на 1 байт меньше, потому что TCP предоставляет место для 1 байта номера последовательности занятого флагом FIN.

Глава 23

  1. Обычно легче активизировать опцию "оставайся в живых", нежели пробы из приложения; пробы "оставайся в живых" требуют меньшей ширины пропускания сети, нежели пробы приложения (так как пробы "оставайся в живых" и ответы не содержат данных); пробы не посылаются, если соединение используется для передачи.
  2. Опция "оставайся в живых" может привести к тому, что абсолютно нормальное соединение будет разорвано, потому что сеть временно вышла из строя; интервал между пробами (2 часа) обычно не конфигурируется с точки зрения приложений;

Глава 24

  1. Это означает, что отправляющий TCP поддерживает опцию масштабирования окна, однако не нуждается в том, чтобы масштабировать свое окно для этого соединения. Удаленная сторона (которая получила этот SYN) может затем установить коэффициент масштабирования окна (который может быть равен 0 или нет).
  2. 64000: размер принимающего буфера (128000) сдвигается вправо на 1 бит. 55000: размер принимающего буфера (220000) сдвигается вправо на 2 бита.
  3. Нет. Проблема заключается в том, что подтверждения доставляются ненадежно (за исключением тех случаев, когда они двигаются вместе с данными), поэтому изменение шкалы, находящееся в ACK, может быть потеряно.
  4. 232 x 8 / 120 равно 286 Мбит/сек, что в 2,86 раза больше чем скорость данных FDDI.
  5. Каждый TCP должен помнить последнюю временную марку, полученную по любому соединению от каждого хоста. Прочитайте приложение B.2 в RFC 1323 для получения более подробной информации.
  6. Приложение должно установить размер принимающего буфера перед установлением соединения с удаленным концом, так как опция масштабирования окна посылается в исходном SYN сегменте.
  7. Если получатель подтверждает каждый второй сегмент данных, пропускная способность составляет 1118881 байт в секунду. При использовании окна размером 62 сегмента, с подтверждением каждого 31 сегмента, значение составляет 1158675.
  8. Когда опция временной марки отражается эхом в ACK, она всегда берется из сегмента, который подтверждается. В данном случае нет никакой двусмысленности по поводу того, какому повторно переданному сегменту принадлежит ACK, однако другая часть алгоритма Karnа, имеющая отношение к экспотенциальному наращиванию передачи, все еще требуется.
  9. Получающий TCP ставит данные в очередь, однако они не могут быть переданы приложению до тех пор, пока не завершится трехразовое рукопожатие: когда получающий TCP переходит в состояние ESTABLISHED.
  10. Осуществляется обмен пятью сегментами:
    1. От клиента к серверу: SYN, данные (запрос) и FIN. Сервер должен поставить в очередь данные, как описано в предыдущем упражнении.
    2. От сервера к клиенту: SYN и ACK на SYN клиента.
    3. От клиента к серверу: ACK на SYN сервера и FIN клиента (снова). Это приводит к тому, что сервер переходит в состояние ESTABLISHED, а данные, поставленные в очередь с сегмента 1, передаются приложению сервера.
    4. От сервера к клиенту: ACK на FIN клиента (который также подтверждает данные клиента), данные (отклик сервера) и FIN сервера. Здесь подразумевается, что SPT достаточно короток, чтобы позволить этот задержанный ACK. Когда TCP клиент получает этот сегмент, отклик передается приложению клиента, однако полное время будет равно удвоенному RTT плюс SPT.
    5. От клиента к серверу: ACK на FIN сервера.
  11. 16128 транзакций в секунду (64512 поделено на 4).
  12. Время транзакций, используемое T/TCP, не может быть меньше, чем время, необходимое для обмена UDP датаграммами между двумя хостами. T/TCP всегда работает дольше, так как он еще осуществляет обработку состояний, чего не делает UDP.

Глава 25

  1. Если система запускает и менеджера, и агента, это скорее всего разные процессы. Менеджер слушает UDP порт 162 на предмет прихода ловушек, а агент слушает UDP порт 161 для запросов. Если один и тот же порт используется и для ловушек, и для запросов, отделить менеджера от агента будет достаточно сложно.
  2. Обратитесь к разделу "Таблица доступа" в разделе "Простые примеры" главы 25.

Глава 26

  1. Мы ожидаем, что сегменты 2, 4 и 9 от сервера будут задержаны. Разница во времени между сегментами 2 и 4 составляет 190,7 миллисекунды, а разница во времени между сегментами 2 и 9 составляет 400,7 миллисекунды.

    Все ACK от клиента к серверу будут задержаны: сегменты 6, 11, 13, 15, 17 и 19. Разница во времени последних пяти от сегмента 6 составляет 400,0; 600,0; 800,0; 1000,0 и 2600 миллисекунд.

  2. Если один конец соединения находится в режиме срочности TCP, то каждый раз, когда сегмент принимается, один отправляется. Этот сегмент не сообщает получателю ничего нового (он не подтверждает новые данные, например) и не содержит данных, а всего лишь подтверждает, что соединение находится в срочном режиме.
  3. Наличие 512 зарезервированных портов (512-1023) ограничивают количество Rlogin клиентов. Их может быть не больше 512. В реальной жизни значение меньше чем 512, так как некоторые из портов в этом диапазоне используются в качестве заранее известных портов различными сервисами, такими, например, как Rlogin сервер.

    Ограничение TCP заключается в том, что пара сокетов, выделенная для соединения, должна быть уникальной. Так как Rlogin сервер всегда использует один и тот же заранее известный порт (513), несколько Rlogin клиентов на данном хосте могут использовать один и тот же зарезервированный порт, только если они подсоединены к различным серверам. Клиенты Rlogin, однако, не используют эту технику повторного использования зарезервированных портов. Если эта техника используется, теоретический лимит максимального количества соединений составляет 512 Rlogin клиентов, в одно и то же время подсоединенных к одному и тому же хосту сервера.

Глава 27

  1. Теоретически соединение не может быть установлено, пока пара сокетов находится в состоянии ожидания 2MSL на каком-либо из концов. В действительности, однако, мы видели в разделе "Диаграмма состояний передачи TCP" главы 18, что большинство Berkeley реализаций принимают все-таки новый SYN для соединения, находящегося в состоянии TIME_WAIT.
  2. Эти строки не являются частью отклика сервера, который всегда начинается с 3-циферного кода отклика.

Глава 28

  1. domain literal это IP адрес в виде десятичных цифр, разделенных точками, внутри квадратных скобок. Например: mail rstevens@ [140.252.1.54].
  2. Шесть: команда HELO, MAIL, RCPT, DATA, тело сообщения и QUIT.
  3. Это вполне законно и называется pipelining [Rose 1993, Sec. 4.4.4]. К сожалению, существуют "сумасшедшие" реализации SMTP, которые очищают свой входной буфер после обработки каждой команды, из-за чего подобная техника не работает. Если эта техника используется, клиент не может отбросить сообщение до тех пор, пока все отклики не будут проверены на предмет того, что сообщение было принято сервером.
  4. Рассмотрим первые пять сетевых обменов из упражнения 28.2. Каждый является маленькой командой (возможно, одним сегментом), который совсем немножко загружает сеть. Если все пять пройдут на сервер без повторных передач, окно переполнения должно быть равно шести сегментам, когда отправляется тело. Если тело большое, клиент может послать первые шесть сегментов за раз, что сеть, может быть, не сможет обработать.
  5. Новые реализации BIND смешивают MX записи с тем же самым значением, что и форма балансировки загрузки.

Глава 29

  1. Нет, потому что tcpdump не может отличить RPC запрос или отклик от любой другой UDP датаграммы. Единственное случай, когда интерпретируется содержание UDP датаграммы как NFS пакет, это когда номер порта источника или назначения равен 2049. Случайные RPC запросы и отклики могут использовать номер динамически назначаемого порта на каждом конце.
  2. Из раздела "Номера портов" главы 1 известно, что процесс должен иметь привилегии суперпользователя, чтобы назначить самому себе номер порта меньше чем 1024 (заранее известный порт). Несмотря на то, что это нормально для серверов, запускаемых системой, таких как Telnet сервер, FTP сервер и Port Mapper сервер, нет необходимости использовать это ограничение для всех RPC серверов.
  3. Две концепции, заложенные в этом примере, заключаются в том, что клиент игнорирует любые отклики от сервера, которые не имеют XID, ожидаемые клиентом, а UDP ставит в очередь полученные датаграммы (до определенного предела) до тех пор, пока приложение считывает эти датаграммы. XID не изменяются при тайм-аутах и повторных передачах, они изменяются только когда вызывается еще одна процедура сервера.

    События, осуществляемые stubом клиента, следующие: момент времени 0: отправка запроса 1; момент времени 4: тайм-аут и повторная передача запроса 1; момент времени 5: получение отклика 1 от сервера, возвращение отклика приложению; момент времени 5: отправка запроса 2; момент времени 9: тайм-аут и повторная передача запроса 2; момент времени 10: получение отклика 1 от сервера, однако он игнорируется, так как мы ожидаем отклик 2; момент времени 11: получение отклика 2 от сервера, возвращение отклика приложению.

    События на сервере следующие: момент времени 0: получение запроса 1, начало операции; момент времени 5: отправка отклика 1; момент времени 5: получение запроса 1 (от повторной передачи клиента в момент времени 4), начало операции; момент времени 10: отправка отклика 1; момент времени 10: получение запроса 2 (от передачи клиента в момент времени 5), начало операции; момент времени 11: отправка отклика 2; момент времени 11: получение запроса 2 (от повторной передачи клиента в момент времени 9), начало операции; момент времени 12: отправка отклика 2. Этот последний отклик сервера просто поставлен клиентским UDP в очередь для следующего приема, осуществляемого клиентом. Когда клиент считывает его, XID будет неверен, и клиент его проигнорирует.

  4. Замена сетевой платы Ethernet меняет физический адрес сервера. Даже несмотря на то, что мы видели в разделе "Беспричинный ARP" главы 4, что SVR4 не посылает "беспричинный" ARP при загрузке, он все равно должен отправить ARP запрос, чтобы узнать физический адрес sun, перед тем как сможет отправить отклик на его NFS запросы. Так как sun уже имеет ARP запись для svr4, он обновит эту запись новым аппаратным адресом сервера из ARP запроса.
  5. Второй блок демона ввода-вывода клиента (считывание со смещением 73728) не синхронизирован с первым примерно на 0,74 секунды. Таким образом, второй демон отработает тайм-аут размером 0,74 секунды после первого в строках 131-145. Сервер никогда видел запрос в строке 167, однако увидел запрос в строке 168. Второй блок демона ввода-вывода не будет передан до тех пор, пока не пройдет 0,74 секунды после строки 168, и в это определенное время первый блок демона ввода-вывода продолжает выдавать запросы.
  6. Если используется TCP, и TCP сегмент, содержащий отклик от сервера, потерян в сети, TCP модуль сервера осуществит тайм-аут и повторную передачу отклика, когда он не получит ACK от TCP модуля клиента. Возможно, сегмент прибудет в клиентскую часть TCP. Отличие здесь в том, что два TCP модуля осуществляют тайм-аут и повторную передачу, а не NFS клиент и сервер. (Когда используется UDP, NFS клиент осуществляет тайм-аут и повторную передачу.) Тем не менее, NFS клиент никогда не знает, что отклик был потерян и должен быть повторно передан.
  7. NFS сервера может получить другой номер порта после перезагрузки. Это может запутать клиента, потому что он должен знать, что сервер вышел из строя, установить контакт с преобразователем портов сервера после перезагрузки, чтобы найти новый номер порта NFS сервера.

    Подобное, когда сервер выходит из строя и перезагружается, а приложение RPC сервера получает новый динамически назначаемый порт, может возникнуть с любым RPC приложением, которое не использует заранее известный порт.

  8. Нет. NFS клиент может повторно использовать тот же самый локальный зарезервированный номер порта для различных серверов. TCP требует, чтобы локальный IP адрес, локальный порт, удаленный IP адрес и удаленный порт были уникальными, а удаленный IP адрес отличаться от каждого хоста сервера.

Глава 30

  1. Напечатайте whois "net 88". Идентификаторы сети класса А 64-95 зарезервированы.
  2. Напечатайте whois whitehouse-dom. И команда host, и nslookup могут запросить DNS.
  3. Нет, xscope можно запустить на другом хосте (не на сервере). Если хосты разные, xscope может использовать TCP порт 6000 для входящих соединений.


Конфигурируемые опции

Приложение E Конфигурируемые опции

В тексте встречаются характеристики TCP/IP, которые нам пришлось описывать следующим образом "это зависит от конфигурации". Например, рассчитывается или нет контрольная сумма UDP (глава 11, раздел "Контрольная сумма UDP"), является IP адрес назначения с одним и тем же идентификатором сети, но с различными идентификаторами подсети, локальным или нелокальным (глава 18, раздел "Максимальный размер сегмента"), и будет ли перенаправляться широковещательный запрос или нет (глава 12, раздел "Примеры широковещательных запросов"). И действительно, множество характеристик в реализациях TCP/IP могут быть модифицированы системным администратором. В этом приложении приводится список некоторых конфигурируемых опций для различных реализаций TCP/IP, которые использовались в тексте. Как Вы могли ожидать, продукт каждого поставщика программного обеспечения отличается от других. В этом приложении дается представление о том, какие типы параметров в различных реализациях могут быть модифицированы. Небольшое количество опций, которые принадлежат совсем уж конкретным реализациям, такие как, например, метка низкой воды для буфера памяти, не рассматриваются.

Эти переменные описываются только для информационных целей. Их имена, значения по умолчанию или интерпретации могут изменяться от одного релиза к другому. Обратитесь к документации, которая поставляется с программным обеспечением, для того чтобы составить для себя представление об этих переменных.

Здесь не приводится описания процесса установки опций, которая происходит каждый раз при загрузке системы: инициализация каждого сетевого интерфейса с использованием ifconfig (установка IP адреса, маски подсети), ввод статических маршрутов в таблицу маршрутизации и так далее. Вместо этого в приложении сфокусировано внимание на опциях конфигурации, которые влияют на функционирование TCP/IP.

BSD/386 Version 1.0

Эта система является примером "классической" BSD конфигурации, которая используется, начиная с 4.2BSD. Так как вместе с системой распространяются исходные тексты, администратор может указать опции конфигурации, после чего необходимо пересобрать ядро. Существует два типа опций: константы, которые определяются в конфигурационном файле ядра (см. страницы помощи config(8)), и переменные, которые инициализируются в различных файлах С текста. Смелые и квалифицированные администраторы могут изменить значение этих С переменных в работающем ядре или в дисковом имидже ядра с использованием отладчика, чтобы не пересобирать ядро. Ниже приведены константы, которые могут быть изменены в конфигурационном файле ядра.

IPFORWARDING

Значение этой константы устанавливается в переменной ядра ipforwarding . Если равно 0 (по умолчанию), IP датаграммы не перенаправляются. Если равно 1, перенаправление включено.

GATEWAY

Если эта константа определена, IPFORWARDING должна быть установлена в 1. Помимо того, определение этой константы вызывает увеличение определенных системных таблиц (ARP кэша и таблицы маршрутизации).

SUBNETSARELOCAL

Значение этой константы устанавливает переменную ядра subnetsarelocal. Если равно 1 (по умолчанию), IP адрес назначения с тем же самым идентификатором сети, как и у посылающего хоста, но с другим идентификатором подсети, считается локальным. Если равно 0, только IP адреса назначения непосредственно подключенной подсети считаются локальными. Кратко это описано на рисунке Е.1.

Идентификатор

сети

Идентификатор

подсети

subnetsarelocal

Комментарий

1

0

одинаковый

одинаковый

локальный

локальный

всегда локальный

одинаковый

разный

локальный

нелокальный

зависит от конфигурации

разный

нелокальный

нелокальный

всегда нелокальный

Рисунок Е.1 Интерпретация переменной ядра subnetsarelocal.

Это влияет на MSS, который выбирается TCP. Когда происходит отправка на локальный пункт назначения, TCP выбирает MSS, основываясь на MTU исходящего интерфейса. Когда происходит отправка на нелокальный пункт назначения, TCP использует значение переменной tcp_mssdflt как MSS.

IPSENDREDIRECTS

Значение этой константы инициализирует переменную ядра ipsendredirects. Если равно 1 (по умолчанию), хост будет отправлять ICMP перенаправления при перенаправлении IP датаграмм. Если установлено в 0, ICMP перенаправления не отправляются.

DIRECTED_BROADCAST

Если равно 1 (по умолчанию), полученные датаграммы, адреса назначения которых это широковещательные адреса подключенных интерфейсов, перенаправляются как широковещательные запросы канального уровня. Если равно 0, эти датаграммы молча отбрасываются.

Следующие переменные также могут быть модифицированы. Эти переменные рассеяны в различных файлах в директории /usr/src/sys/netinet.

tcprexmtthresh

Количество последовательно принятых подтверждений, которое включает алгоритм быстрой повторной передачи и быстрого восстановления. По умолчанию равно 3.

tcp_ttl

Это значение устанавливается по умолчанию в поле TTL в TCP сегментах. Значение по умолчанию 60.

tcp_mssdflt

Значение MSS TCP по умолчанию для нелокальных пунктов назначения. Значение по умолчанию 512.

tcp_keepidle

Количество 500-миллисекундных тиков часов перед отправкой пробы "оставайся в живых". Значение по умолчанию 14400 (2 часа).

tcp_keepintvl

Количество 500-миллисекундных тиков часов между последовательными пробами "оставайся в живых", когда не получен ответ. Значение по умолчанию 150 (75 секунд).

tcp_sendspace

Размер по умолчанию отправляющего буфера TCP. Значение по умолчанию 4096.

tcp_recvspace

Размер по умолчанию приемного буфера TCP. Он оказывает влияние на предлагаемый размер окна. Значение по умолчанию 4096.

udpcksum

Если не равно 0, рассчитывается контрольная сумма UDP для исходящих UDP датаграмм, и если входящие UDP датаграммы содержат ненулевую контрольную сумму, эти контрольные суммы проверяются. Если равно 0, исходящие UDP датаграммы не содержат контрольную сумму, и не осуществляется проверка контрольных сумм для входящих UDP датаграмм, даже если отправитель рассчитал контрольную сумму. По умолчанию равно 1.

udp_ttl

Значение по умолчанию для поля TTL в UDP датаграммах. По умолчанию равно 30.

udp_sendspace

Размер по умолчанию для отправляющего буфера UDP. Определяет максимальный размер UDP датаграммы, которая может быть отправлена. По умолчанию 9216.

udp_recvspace

Размер по умолчанию приемного буфера UDP. По умолчанию равно 41600, что означает 40 датаграмм размером 1024 байта.

SunOS 4.1.3

Метод, используемый с SunOS 4.1.3, напоминает тот, что мы видели в случае BSD/386. Так как большинство кодов ядра не распространяются, все установки С переменных содержатся в одном файле С кода, который поставляется в системе. Из файла конфигурации ядра администратора (см. страницы помощи config(8)) можно определить следующие переменные. После модификации конфигурационного файла должно быть создано новое ядро, и система должна быть перезагружена.

IPFORWARDING

Значение этой константы инициализирует переменную ядра ip_forwarding. Если равно -1, IP датаграммы никогда не перенаправляются. Более того, переменная не может быть изменена. Если равно 0 (по умолчанию), IP датаграммы не перенаправляются, однако значение переменной изменяется в 1, если "поднято" несколько интерфейсов. Если равно 1, перенаправление всегда включено.

SUBNETSARELOCAL

Эта константа инициализирует переменную ядра ip_subnetsarelocal. Если равно 1 (по умолчанию), IP адрес назначения с тем же самым идентификатором сети, как и у отправляющего хоста, однако с другим идентификатором подсети, считается локальным. Если равно 0, только IP адреса назначения, подключенные непосредственно к этой подсети, считаются локальными. Подобный подход кратко описан на рисунке Е.1. Когда осуществляется отправка на локальные пункты назначения, TCP выбирает MSS на основе MTU исходящего интерфейса. Когда происходит отправка на нелокальный пункт назначения, TCP использует для этой цели переменную tcp_default_mss.

IPSENDREDIRECTS

Значение этой константы инициализирует переменную ядра ip_sendredirects. Если равно 1 (по умолчанию), хост будет отправлять ICMP перенаправления при перенаправлении IP датаграмм. Если равно 0, ICMP перенаправления не отправляются.

DIRECTED_BROADCAST

Значение этой константы инициализирует переменную ядра ip_dirbroadcast . Если равно 1 (по умолчанию), принятые датаграммы, адрес назначения которых это широковещательный адрес непосредственно подключенного интерфейса, перенаправляются как широковещательные запросы канального уровня. Если равно 0, эти датаграммы молча отбрасываются.

Файл /usr/kvm/sys/netinet/in_proto.c определяет следующие переменные, которые могут быть изменены. Если переменные изменены, должно быть собрано новое ядро и система перезагружена.

tcp_default_mss

Значение по умолчанию MSS TCP для нелокальных пунктов назначения. Значение по умолчанию 512.

tcp_sendspace

Размер по умолчанию отправляющего буфера TCP. Значение по умолчанию 4096.

tcp_recvspace

Размер по умолчанию приемного буфера TCP. Оказывает влияние на предлагаемый размер окна. Значение по умолчанию 4096.

tcp_keeplen

Проба "оставайся в живых" на хост 4.2BSD должна содержать один байт данных, для того чтобы получить отклик. Установка переменной в 1 осуществляет совместимость с этими более старыми реализациями. Значение по умолчанию 1.

tcp_ttl

Значение по умолчанию для поля TTL TCP сегментов. По умолчанию 60.

tcp_nodelack

Если не равно 0, подтверждения не задерживаются. Значение по умолчанию 0.

tcp_keepidle

Количество 500-миллисекундных тиков часов перед отправкой пробы "оставайся в живых". Значение по умолчанию 14400 (2 часа).

tcp_keepintvl

Количество 500-миллисекундных тиков часов между последовательными пробами "оставайся в живых", если не получен отклик. Значение по умолчанию 150 (75 секунд).

udp_cksum

Если не равно 0, UDP контрольные суммы рассчитываются для исходящих UDP датаграмм, а для входящих UDP датаграмм, содержащих ненулевую контрольную сумму, контрольная сумма проверяется. Если равно 0, исходящие UDP датаграммы не содержат контрольную сумму, и не осуществляется проверка контрольной суммы для входящих UDP датаграмм, даже если отправитель рассчитал контрольную сумму. По умолчанию 0.

udp_ttl

Значение по умолчанию для поля TTL в UDP датаграммах. По умолчанию 60.

udp_sendspace

Размер по умолчанию для отправляющего буфера UDP. Определяет максимальный размер UDP датаграммы, которая может быть отправлена. По умолчанию 9000.

udp_recvspace

Размер по умолчанию приемного буфера UDP. Значение по умолчанию 18000, что означает две датаграммы размером 9000 байт.

System V Release 4

Конфигурация TCP/IP в SVR4 напоминают две предыдущие системы, однако доступно значительно меньше опций. В файле /etc/conf/pack.d/ip/space.c определены две константы, которые могут быть изменены, после чего ядро должно быть перестроено, а система перезагружена.

IPFORWARDING

Значение этой константы инициализирует переменную ядра ipforwarding . Если равно 0 (по умолчанию), IP датаграммы не перенаправляются. Если равно 1, перенаправление всегда включено.

IPSENDREDIRECTS

Значение этой константы инициализирует переменную ядра ipsendredirects. Если равно 1 (по умолчанию), хост будет отправлять ICMP перенаправления при перенаправлении IP датаграмм. Если равно 0, ICMP перенаправления не отправляются.

Большинство из переменных, которые мы описали в двух предыдущих разделах, определены в ядре, однако необходимо иметь patch (patch (англ.) - заплата; исправления, поставляемые производителями к программному обеспечению), чтобы модифицировать их. В качестве примера можно привести переменную tcp_keepidle со значением 14400.

Solaris 2.2

Solaris 2.2 это типичная Unix система нового поколения. Она предоставляет программу, которая позволяет администратору изменить опции конфигурации для системы TCP/IP. Это позволяет осуществлять переконфигурацию без модификации файлов с исходными текстами и перестройки ядра. Конфигурационная программа называется ndd(1) . Мы можем запустить программу, чтобы посмотреть, какие параметры могут быть просмотрены или модифицированы в UDP модуле:

solaris % ndd /dev/udp \?

udp_wroff_extra                    (read and write-чтение и запись)

udp_def_ttl                        (read and write-чтение и запись)

udp_first_anon_port                (read and write-чтение и запись)

udp_trust_optlen                   (read and write-чтение и запись)

udp_do_checksum                    (read and write-чтение и запись)

udp_status                         (read only-только чтение)

Мы можем указать пять модулей: /dev/ip, /dev/icmp, /dev/arp, /dev/udp и /dev/tcp. Знак вопроса в качестве аргумента (перед которым необходимо поставить обратный слэш, чтобы предотвратить интерпретацию вопросительного знака интерпретатором команд) сообщает программе о необходимости показать список всех параметров для этого модуля. В качестве примера, который запрашивает значение переменной, можно привести следующее:

solaris % ndd /dev/tcp tcp_mss_def
536

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

solaris # ndd -set /dev/ip ip_forwarding 0

Эти переменные могут быть поделены на три категории:

  1. Переменные конфигурации, которые могут быть изменены системным администратором (например, ip_forwarding).
  2. Переменные статуса, которые могут быть только просмотрены (например, ARP кэш). Обычно, эта информация предоставляется в формате, пригодном для чтения, командами ifconfig, netstat и arp.
  3. Отладочные переменные, предназначенные для исходного кода ядра. Включение некоторых из них генерирует отладочный вывод ядра, что может значительно снизить производительность.

Сейчас мы опишем эти параметры для каждого модуля. Все параметры могут быть считаны и записаны, если они не помечены "только для чтения" (Read only). Эти параметры только для чтения являются переменными статуса (см. выше, пункт 2). Переменные, упомянутые в пункте 3, помечены как "отладочные" (Debug). Все временные характеристики приведены в миллисекундах, что отличается от других систем, которые обычно указывают время в некотором количестве 500-миллисекундных тиков часов.

/dev/ip

ip_cksum_choice

(Отладочная) Осуществляет выбор между двумя независимыми реализациями алгоритма расчета контрольной суммы IP.

ip_debug

(Отладочная) Включает печать отладочного вывода от ядра, если больше чем 0. Чем больше значение, тем больше генерируется вывода. По умолчанию 0.

ip_def_ttl

Значение TTL по умолчанию для исходящих IP датаграмм (если не указано на транспортном уровне). По умолчанию 255.

ip_forward_directed_broadcasts

Если равно 1 (по умолчанию), принятые датаграммы, адрес назначения которых является широковещательным адресом непосредственно подключенного интерфейса, перенаправляются как широковещательные запросы канального уровня. Если равно 0, эти датаграммы молча отбрасываются.

ip_forward_src_routed

Если равно 1 (по умолчанию), принятые датаграммы, содержащие опцию маршрутизации от источника, перенаправляются. Если равно 0, эти датаграммы отбрасываются.

ip_forwarding

Указывает, будет ли система перенаправлять входящие IP датаграммы: 0 означает нет перенаправления, 1 обозначает перенаправляет всегда и 2 (по умолчанию) означает, что перенаправление осуществляется только в том случае, если функционирует два или более интерфейсов.

ip_icmp_return_data_bytes

Количество байт данных после IP заголовка, которые возвращаются в ICMP ошибке. По умолчанию 64.

ip_ignore_delete_time

(Отладочная) Минимальное время жизни записи в таблице маршрутизации IP (IRE). По умолчанию 30 секунд. (Этот параметр приводится в секундах, а не в миллисекундах).

ip_ill_status

(Только для чтения) Отображает статус для структуры данных уровня, находящегося ниже IP. Существует одна структура нижнего уровня для каждого интерфейса.

ip_ipif_status

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

ip_ire_cleanup_interval

(Отладочная) Интервал, с которым просматриваются записи в таблице маршрутизации IP для возможного удаления. По умолчанию 30000 миллисекунд (30 секунд).

ip_ire_flush_interval

Интервал, через который информация ARP безусловно удаляется из таблицы маршрутизации IP. По умолчанию 1200000 миллисекунд (20 минут).

ip_ire_pathmtu_interval

Интервал, через который алгоритм определения транспортного MTU пытается увеличить MTU. По умолчанию 30000 миллисекунд (30 секунд).

ip_ire_redirect_interval

Интервал, через который записи в таблице маршрутизации IP, которые получены с помощью ICMP перенаправления, удаляются. По умолчанию 60000 миллисекунд (60 секунд).

ip_ire_status

(Только для чтения) Отображает все записи в таблице маршрутизации IP.

ip_local_cksum

Если равно 0 (по умолчанию), IP не рассчитывает контрольную сумму IP или контрольную сумму верхних протоколов (TCP, UDP, ICMP или IGMP) для датаграмм, которые отправляются или принимаются через loopback интерфейс. Если равно 1, контрольные суммы рассчитываются.

ip_mrtdebug

(Отладочная) Включает печать отладочного вывода, связанного с групповой маршрутизацией ядра, если равно 1. По умолчанию равно 0.

ip_path_mtu_discovery

Если равно 1 (по умолчанию), определение транспортного MTU осуществляется уровнем IP. Если равно 0, IP никогда не установит бит "не фрагментировать" для исходящих датаграмм.

ip_respond_to_address_mask

Если равно 0 (по умолчанию), хост не отвечает на ICMP запросы маски адреса. Если равно 1 - отвечает.

ip_respond_to_echo_broadcast

Если равно 1 (по умолчанию), хост отвечает на ICMP эхо запросы, которые отправлены на широковещательный адрес. Если равно 0 - не отвечает.

ip_respond_to_timestamp

Если равно 0 (по умолчанию), хост не отвечает на ICMP запросы временной марки. Если равно 1 - отвечает.

ip_respond_to_timestamp_broadcast

Если равно 0 (по умолчанию), хост не отвечает на ICMP запросы временной марки, которые отправлены на широковещательный адрес. Если равно 1 - отвечает.

ip_rput_pullups

(Отладочная) Счетчик количества буферов в драйвере сетевого интерфейса, который необходимо опросить, чтобы получить полный IP заголовок. Устанавливается в 0 во время загрузки и может быть сброшен в 0.

ip_send_redirects

Если равно 1 (по умолчанию), хост отправляет ICMP перенаправления, когда функционирует как маршрутизатор. Если равно 0 - не отправляет.

ip_send_source_quench

Если равно 1 (по умолчанию), хост генерирует ICMP ошибки подавления источника, когда входящие датаграммы отбрасываются. Если равно 0 - не генерирует.

ip_wroff_extra

(Отладочная) Количество байт дополнительного пространства, необходимого в буфере, чтобы расположить IP заголовки. По умолчанию 32.

/dev/icmp

icmp_bsd_compat

(Отладочная) Если равно 1 (по умолчанию), поле длины в IP заголовке принятой датаграммы настраивается так, чтобы исключить длину IP заголовка. Это совместимо с Berkeley реализациями и необходимо для приложений, которые читают символьные (raw) IP или символьные ICMP пакеты. Если равно 0, поле длины не изменяется.

icmp_def_ttl

Значение TTL по умолчанию для исходящих ICMP сообщений. По умолчанию 255.

icmp_wroff_extra

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

/dev/arp

arp_cache_report

(Только для чтения) ARP кэш.

arp_cleanup_interval

Интервал, после которого запись удаляется из ARP кэша. По умолчанию 300000 миллисекунд (5 минут). (IP поддерживает свой собственный кэш полной ARP трансляции; см. ip_ire_flush_interval.)

arp_debug

(Отладочная) Если равно 1, включает печать отладочного вывода ARP драйвера. По умолчанию 0.

/dev/udp

udp_def_ttl

TTL по умолчанию для исходящих UDP датаграмм. По умолчанию 255.

udp_do_checksum

Если равно 1 (по умолчанию), рассчитывается контрольная сумма UDP для исходящих UDP датаграмм. Если равно 0, исходящие UDP датаграммы не содержат контрольной суммы. (В отличие от большинства других реализаций, этот флаг контрольной суммы UDP не влияет на входящие датаграммы. Если получена датаграмма, которая имеет ненулевую контрольную сумму, контрольная сумма всегда будет проверена.)

udp_largest_anon_port

Самый большой номер порта, который может быть назначен как динамически назначаемый UDP порт. По умолчанию 65535.

udp_smallest_anon_port

Начальный номер порта, с которого начинаются динамически назначаемые UDP порты. По умолчанию 32768.

udp_smallest_nonpriv_port

Процессу требуются привилегии суперпользователя, чтобы назначить самому себе номер порта меньше чем этот. По умолчанию 1024.

udp_status

(Только для чтения) Статус всех локальных конечных точек UDP: локальный IP адрес и порт, удаленный IP адрес и порт.

udp_trust_optlen

(Отладочная) В настоящее время не используется.

udp_wroff_extra

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

/dev/tcp

tcp_close_wait_interval

Значение 2MSL: время, которое необходимо провести в состоянии TIME_WAIT. По умолчанию 240000 миллисекунд (4 минуты).

tcp_conn_grace_period

(Отладочная) Время, добавляемое к значению таймера при отправке SYN. По умолчанию 500 миллисекунд.

tcp_conn_req_max

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

tcp_cwnd_max

Максимальное значение окна переполнения. По умолчанию 32768.

tcp_debug

(Отладочная) Если равно 1, включается печать отладочного вывода TCP. По умолчанию 0.

tcp_deferred_ack_interval

Время, которое необходимо подождать перед отправкой задержанного ACK. По умолчанию 50 миллисекунд.

tcp_dupack_fast_retransmit

Количество последовательно пришедших дублированных ACK, которые включают быструю повторную передачу и алгоритм быстрого восстановления. По умолчанию 3.

tcp_eager_listeners

(Отладочная) Если равно 1 (по умолчанию), TCP завершает трехразовое рукопожатие перед передачей нового соединения приложению с ожидающим пассивным открытием. Это способ, которым пользуется большинство реализаций TCP. Если равно 0, TCP передает пришедший запрос на соединение (принятый SYN) приложению и не завершает трехразовое рукопожатие до тех пор, пока приложение не примет соединение. (Установка этого значения в 0 может сломать многие существующие приложения.)

tcp_ignore_path_mtu

(Отладочная) Если равно 1, определение транспортного MTU игнорирует принятые ICMP сообщения о необходимости фрагментации. Если равно 0 (по умолчанию), для TCP включается алгоритм определения транспортного MTU.

tcp_ip_abort_cinterval

Полное время тайм-аута повторной передачи, когда TCP осуществляет активное открытие. По умолчанию 240000 миллисекунд (4 минуты).

tcp_ip_abort_interval

Полное время тайм-аута повторной передачи для TCP соединения, после того как оно установлено. По умолчанию 120000 миллисекунд (2 минуты).

tcp_ip_notify_cinterval

Величина тайм-аута, когда TCP осуществляет активное открытие, после чего TCP уведомляет IP о необходимости найти новый маршрут. По умолчанию 10000 миллисекунд (10 секунд).

tcp_ip_notify_interval

Величина тайм-аута для установленного соединения, после истечения которого TCP уведомляет IP о необходимости найти новый маршрут. По умолчанию 10000 миллисекунд (10 секунд).

tcp_ip_ttl

TTL, используемый для исходящих TCP сегментов. По умолчанию 255.

tcp_keepalive_interval

Время, в течение которого соединение не используется, по истечению которого будет отправлена проба "оставайся в живых". По умолчанию 7200000 миллисекунд (2 часа).

tcp_largest_anon_port

Самый большой номер порта, который может быть использован как динамически назначаемый порт TCP. По умолчанию 65535.

tcp_maxpsz_multiplier

(Отладочная) Указывает множитель MSS, на основании которого данные, которые выдает приложение, разбиваются на пакеты. По умолчанию 1.

tcp_mss_def

Значение MSS по умолчанию для нелокальных пунктов назначения. По умолчанию 536.

tcp_mss_max

Максимальный MSS. По умолчанию 65495.

tcp_mss_min

Минимальный MSS. По умолчанию 1.

tcp_naglim_def

(Отладочная) Максимальное значение порога алгоритма Нагла для каждого соединения. По умолчанию равно 65535. Значение для соединения начинается с минимального MSS или этого значения. Значение для соединения устанавливается в 1 опцией сокета TCP_NODELAY, которая выключает алгоритм Нагла.

tcp_old_urp_interpretation

(Отладочная) Если равно 1 (по умолчанию), используется старая (но наиболее распространенная) BSD интерпретации указателя срочности: он указывает на 1 байт позади последнего байта срочных данных. Если равно 0, используется интерпретация, приведенная в требованиях к хостам RFC: указатель срочности указывает на последний байт срочных данных.

tcp_rcv_push_wait

(Отладочная) Максимальное количество байт, полученных без установленного флага PUSH, перед тем как данные передаются приложению. По умолчанию 16384.

tcp_rexmit_interval_initial

(Отладочная) Первоначальная величина тайм-аута повторной передачи. По умолчанию 500 миллисекунд.

tcp_rexmit_interval_max

(Отладочная) Максимальная величина тайм-аута повторной передачи. По умолчанию 60000 миллисекунд (60 секунд).

tcp_rexmit_interval_min

(Отладочная) Минимальная величина тайм-аута повторной передачи. По умолчанию 200 миллисекунд.

tcp_rwin_credit_pct

(Отладочная) Часть приемного окна (в процентах), которая должна быть буферизирована, перед тем как управление потоком проверяет каждый принятый сегмент. По умолчанию 50%.

tcp_smallest_anon_port

Начальный номер порта, с которого начинаются динамические назначаемые порты TCP. По умолчанию 32768.

tcp_smallest_nonpriv_port

Процессу требуются привилегии суперпользователя, чтобы назначить самому себе номер порта меньше чем этот. По умолчанию 1024.

tcp_snd_lowat_fraction

(Отладочная) Если не равно 0, метка "низкой воды" отправляющего буфера равна размеру отправляющего буфера поделенного на это значение. По умолчанию 0 (выключено).

tcp_status

(Только для чтения) Информация о всех TCP соединениях.

tcp_sth_rcv_hiwat

(Отладочная) Если не равно 0, значение устанавливается в метку "высокой воды" для потока. По умолчанию равно 0.

tcp_sth_rcv_lowat

(Отладочная) Если не равно 0, значение устанавливается в метку "низкой воды" для потока. По умолчанию 0.

tcp_wroff_xtra

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

AIX 3.2.2

AIX 3.2.2 позволяет установить сетевые опции с использованием команды no. Она может отобразить значения опций, установить значения опций или установить значения по умолчанию. Например, посмотреть опцию можно следующим образом:

aix % no -o udp_ttl
udp_ttl = 30

Модифицированы могут быть следующие опции.

arpt_killc

Время (в минутах) перед тем, как неиспользуемая запись ARP полностью удаляется. По умолчанию 20.

ipforwarding

Если равно 1 (по умолчанию), IP датаграммы всегда перенаправляются. Если равно 0, перенаправление выключено.

ipfragttl

Время жизни (в секундах) для IP фрагментов, которые ожидают повторной сборки. По умолчанию 60.

ipsendredirects

Если равно 1 (по умолчанию), хост будет отправлять ICMP перенаправления, когда перенаправляет IP датаграммы. Если равно 0, ICMP перенаправления не отправляются.

loop_check_sum

Если равно 1 (по умолчанию), контрольная сумма IP рассчитывается для датаграмм, которые посылаются по loopback интерфейсу. Если равно 0, эта контрольная сумма не рассчитывается.

nonlocsrcroute

Если равно 1 (по умолчанию), полученные датаграммы, содержащие опцию маршрутизации от источника, перенаправляются. Если равно 0, эти датаграммы отбрасываются.

subnetsarelocal

Если равно 1 (по умолчанию), IP адрес назначения с тем же самым идентификатором сети, как и у отправляющего хоста, однако с другим идентификатором подсети, считается локальным. Если равно 0, только IP адреса назначения принадлежащие непосредственно подключенной подсети считаются локальными. Кратко это описано на рисунке Е.1. Когда происходит отправка на локальный пункт назначения, TCP выбирает MSS, основываясь на MTU исходящего интерфейса. Когда происходит отправка на нелокальные пункты назначения, TCP использует значение по умолчанию (536) в качестве MSS.

tcp_keepidle

Количество 500-миллисекундных тиков часов перед отправкой пробы "оставайся в живых". Значение по умолчанию 14400 (2 часа).

tcp_keepintvl

Количество 500-миллисекундных тиков часов перед отправкой последовательных проб "оставайся в живых", если не получен отклик. Значение по умолчанию 150 (75 секунд).

tcp_recvspace

Размер по умолчанию приемного буфера TCP. Это оказывает влияние на предлагаемый размер окна. Значение по умолчанию 16384.

tcp_sendspace

Размер по умолчанию отправляющего буфера TCP. Значение по умолчанию 16384.

tcp_ttl

Значение TTL по умолчанию для TCP сегментов. Значение по умолчанию 60.

udp_recvspace

Размер по умолчанию приемного буфера UDP. Значение по умолчанию 41600, что позволяет принять 40 датаграмм размером 1024 байта.

udp_sendspace

Размер по умолчанию отправляющего буфера UDP. Определяет максимальный размер UDP датаграммы, которая может быть отправлена. По умолчанию 9216.

udp_ttl

Значение TTL по умолчанию в UDP датаграммах. По умолчанию 30.

4.4BSD

4.4BSD это первый релиз Berkeley, который предоставлял динамическую конфигурацию большого количества параметров ядра. При этом использовалась команда sysctl(8) . Имена параметров были выбраны так, чтобы напоминать имена MIB из SNMP. Просмотреть параметры можно следующим образом:

vangogh % sysctl net.inet.ip.forwarding
net.inet.ip.forwarding = 1

Чтобы изменить параметр (обязательно с привилегиями суперпользователя), можно сделать следующее:

vangogh # sysctl -w net.inet.ip.ttl=128

Могут быть изменены следующие параметры.

net.inet.ip.forwarding

Если равно 0 (по умолчанию), IP датаграммы не перенаправляются. Если равно 1, перенаправление включено.

net.inet.ip.redirect

Если равно 1 (по умолчанию), хост будет отправлять ICMP перенаправления при перенаправлении IP датаграмм. Если равно 0, ICMP перенаправления не отправляются.

net.inet.ip.ttl

Значение TTL по умолчанию для TCP и UDP. По умолчанию 64.

net.inet.icmp.maskrepl

Если равно 0 (по умолчанию), хост не отвечает на ICMP запросы маски адреса. Если равно 1 - отвечает.

net.inet.udp.checksum

Если равно 1 (по умолчанию), рассчитывается контрольная сумма UDP для исходящих UDP датаграмм, а для входящих UDP датаграмм, если контрольная сумма не равна нулю, она проверяется. Если равно 0, исходящие UDP датаграммы не содержат контрольной суммы, и не осуществляется проверка контрольной суммы для входящих UDP датаграмм, даже если отправитель рассчитал контрольную сумму.

Помимо этого, большое количество переменных, которые мы описали раньше в этом приложении, находятся в различных файлах исходных текстов (tcp_keepidle, subnetsarelocal и так далее) и могут быть модифицированы.


Где можно взять исходные тексты

Приложение F Где можно взять исходные тексты

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

Техника, которая используется для получения подобного программного обеспечения, называется анонимный FTP (anonymous FTP), где FTP это стандартный протокол передачи файлов по Internet (File Transfer Protocol) (глава 27). В разделе "Примеры FTP" главы 27 приведен пример использования анонимного FTP. Для того чтобы изучить основные ресурсы Internet и особенности работы с анонимным FTP, обратитесь к любой популярной книге по Internet, например [LaQuey 1993] или [Krol 1992].

Хосты, упоминаемые в приложении, это основные хосты, содержащие эти пакеты программ, однако может существовать еще множество хостов, где могут быть эти программы. Сервис Internet Archie поможет определить месторасположения программ. Однако, в тексте используются только те версии программ, которые описаны ниже.

Пока Вы читаете эту книгу, могут появиться более новые версии этих программ.

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

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

RFC (глава 1, раздел "RFC")

В разделе "RFC" главы 1 приводятся адреса электронной почты, на которые необходимо отправлять запросы. В ответе будет приведено определенное количество узлов, с которых можно получить RFC, используя либо электронную почту (e-mail), либо анонимный FTP.

Запомните, что начинать надо с того, что получить текущий индекс и найти RFC, который Вас интересует в этом индексе. Это позволит Вам получить самый полный и самый новый RFC.

BSD Net/2 Source Code (глава 1, раздел "Реализации")

Исходный код BSD Net/2, который включает реализации ядра протоколов TCP/IP вместе со стандартными утилитами (клиент и сервер Telnet, клиент и сервер FTP, и так далее), можно получить с хоста ftp.uu.net в дереве директорий, начинающемся с systems/unix/bsd-sources.

SLIP (глава 2, раздел "SLIP: IP по последовательной линии")

Версия SLIP, используемая в тексте, находится на ftp.ee.lbl.gov. Имя файла начинается с cslip, так как он поддерживает сжатый SLIP (глава 2, раздел "SLIP с компрессией (CSLIP)").

Программа icmpaddrmask (глава 6, раздел "ICMP запрос и отклик маски адреса")

Обратитесь к последнему пункту этого раздела.

Программа icmptime (глава 6, раздел "ICMP запрос и отклик временной марки")

Обратитесь к последнему пункту этого раздела.

Программа ping (глава 7)

BSD версия программы ping обычно имеет больше опций и характеристик, чем версии, поставляемые другими производителями. Хост ftp.uu.net содержит последнюю BSD версию в файле systems/unix/bsd-sources/sbin/ping.

Программа traceroute (глава 8)

Программа traceroute доступна с ftp.ee.lbl.gov. Обратитесь к последнему пункту в этом разделе, где рассказывается, какая версия используется в разделе "Опция IP маршрутизации от источника" главы 8, что позволяет рассмотреть жесткую и свободную маршрутизации от источника.

Демон обнаружения маршрутизаторов (Router Discovery Daemon) (глава 9, раздел "ICMP сообщения поиска маршрутизатора")

Существует программа, которая предоставляет поддержку хоста и маршрутизатора для сообщений поиска маршрутизатора. Хост ftp.gregorio.stanford.edu, файл gw-discovery/nordmark-rdisc.tar. Программа была написана в Sun Microsystems и теперь распространяется свободно.

Демон gated (глава 10, раздел "Демоны маршрутизации в Unix")

Демон маршрутизации gated, о котором мы упоминали в разделе "Демоны маршрутизации в Unix" главы 10, находится на хосте ftp.gated.cornell.edu.

Программа traceroute.pmtu (глава 11, раздел "Определение транспортного MTU с использованием Traceroute")

Обратитесь к последнему пункту этого раздела.

Программное обеспечение групповой рассылки IP (глава 13)

Модификации, которые необходимо сделать для поддержки групповой рассылки IP для SunOS 4.x и Ultrix, можно получить с хоста ftp.gregorio.stanford.edu в директории vmtp-ip. В этой директории также содержатся исходные коды модификаций, необходимых для реализации групповой рассылки IP в системах Berkeley Unix.

Сервер имен BIND (глава 14)

Сервер имен BIND, демон named , доступен с хоста ftp.uu.net в файле networking/ip/dns/bind/bind.4.8.3.tar.Z. Более новая версия, 4.9, доступна с хоста ftp.gatekeeper.dec.com в директории pub/BSD/bind/4.9.

Программа host (глава 14)

Программа host находится на хосте ftp.nikhefh.nikhef.nl в файле host.tar.Z.

Программы dig и doc (глава 14)

Программы dig и doc, которые мы упомянули в главе 14, можно получить с хоста ftp.isi.edu в файлах dig.2.0.tar.Z и doc.2.0.tar.Z.

Сервер BOOTP (глава 16)

Различные версии широкоиспользуемого BOOTP сервера для Unix находятся на хосте ftp.lancaster.andrew.cmu.edu в директории pub.

Высокоскоростные расширения TCP (глава 24)

Общедоступные исходные тексты реализаций TCP опции масштабирования окна, опции временной марки и алгоритма PAWS доступны как patch набор для релиза BSD Net/2 с хоста ftp.uxc.cso.uiuc.edu в файле pub/tcplw.shar.Z.

ISODE SNMP менеджер и агент (глава 25)

SNMP менеджер и агент, описанные в разделе "Простые примеры" главы 25, являются частью пакета ISODE 8.0. Их можно получить со многих FTP узлов, таких как ftp.uu.net в директории networking/osi/isode.

Программы и примеры MIME (глава 28, раздел "Будущее SMTP")

Программа под названием MetaMail, которая предоставляет возможности MIME для большинства различных пользовательских агентов, есть на хосте ftp.thumper.bellcore.com в директории pub/nsb. Также в этой директории находится дополнительная информация о MIME.

Sun RPC (глава 29, раздел "Вызов удаленной процедуры Sun")

Версия исходных текстов RPC 4.0 (которая использует сокеты API) находится на хосте ftp.uu.net в директории systems/sun/sextape/rpc4.0. Версию исходных текстов TI-RPC (которая использует TLI API) можно получить с хоста ftp.uu.net в директории networking/rpc.

Sun NFS (глава 29)

Свободно распространяемые реализации NFS клиента и сервера являются частью BSD Net/2 Source Code, который описан раньше в этом приложении.

Программа tcpdump (приложение A)

Версия tcpdump, которая используется в этом тексте, взята с хоста ftp.ee.lbl.gov из файла tcpdump-2.2.1.tar.Z.

Пакетный фильтр BSD (приложение A, раздел "Пакетный фильтр BSD")

Пакетный фильтр BSD является частью пакета tcpdump.

Программа sock (приложение C)

Обратитесь к последнему пункту этого раздела.

Программа ttcp

(Программа ttcp не использовалась в тексте, однако это очень полезное средство, с помощью которого читатели могут многого добиться.) ttcp это средство для определения TCP и UDP производительности между двумя системами. Она была создана в исследовательской лаборатории баллистики американской армии (BRL) и сейчас общедоступна. Копии находятся на большом количестве анонимных FTP серверов, однако расширенная версия находится на хосте ftp.sgi.com в директории sgi/src/ttcp.

Программы, написанные автором

Программы, написанные автором, которые используются в этой книге, можно получить с хоста ftp.uu.net в директории published/books/stevens.tcpipiv1.tar.Z.


Библиография

Приложение G Библиография

Все RFC можно получить абсолютно бесплатно, либо с использованием электронной почты, либо через анонимное FTP. Как это сделать - описано в разделе "RFC" главы 1.

Albitz, P., and Liu, C. 1992. DNS and BIND. O'Reilly & Associates, Sebastopol, Calif.

Подробно описаны задачи администрирования и обслуживания DNS сервера.

Alexander, S., and Droms, R. 1993. "DHCP Options and BOOTP Vendor Extensions", RFC 1533, 30 pages (Oct.).

Almquist, P. 1992. "Type of Service in the Internet Protocol Suite", RFC 1349, 28 pages (July).

Описывается, как использовать поле типа сервиса в IP заголовке.

Almquist, P., ed. 1993. "Requirements for IP Routers", Internet Draft (Mar.).

Временно заменяет RFC 1009 [Braden and Postel 1987]. Новый RFC скорее всего появится в четырех томах. Том 1: Архитектура Internet, терминология и общие соглашения. Том 2: канальный уровень, сетевой уровень, транспортный уровень и прикладной уровень. Том 3: перенаправления и протоколы маршрутизации. Том 4: работа в сети, поддержание работоспособности и управление сетью.

Можно получить через анонимный FTP с хоста ftp.jessica.stanford.edu в директории rred. После того как выйдет RFC, необходимость в этой публикации отпадет.

Bellovin, S. M. 1993. Private Communication.

Bhide, A., Elnozahy, E. N., and Morgan, S. P. 1991. "A Highly Available Network File Server", Proceedings of the 1991 Winter USENIX Conference, pp. 199-205, Dallas, Tex.

Описывает использование "беспричинного" ARP (глава 4, раздел "Беспричинный ARP").

Borenstein, N., and Freed, N. 1993. "MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies", RFC 1521, 81 pages (Sept.).

Этот RFC заменяет ранний RFC 1341. В приложение H этого RFC приводится список отличий от RFC 1341.

Borman, D. A., ed. 1990. "Telnet Linemode Option", RFC 1184, 23 pages (Oct.).

Borman, D. A. 1991. "IP Bandwidth Limits", Message-ID <91011437.AA17276@berserkly.cray.com>, Usenet, comp.protocols.tcp-ip Newsgroup (Jan.).

Описывает три практических ограничения производительности TCP, которые мы описали в конце раздела "Производительность TCP" главы 24.

Borman, D. A. 1992. "TCP/IP Performance at Cray Research", Proceedings of the Twenty-third Internet Engineering Task Force, pp. 492-493 (Mar.), San Diego Supercomputer Center, San Diego, Calif.

Borman, D. A., ed. 1993a. "Telnet Environment Option", RFC 1408, 7 pages (Jan.).

Опция Telnet для передачи переменных окружения от клиента серверу.

Borman, D. A. 1993b. "A Practical Perspective on Host Networking", in Internet System Handbook, eds. D. C. Lynch and M. T. Rose, pp. 309-367. Addison-Wesley, Reading, Mass.

Практический взгляд на требования к хостам RFC (1122 и 1123).

Braden, R. T., ed. 1989a. "Requirements for Internet Hosts - Communication Layers", RFC 1122, 116 pages (Oct.).

Первая половина требований к хостам RFC. Эта половина охватывает канальный уровень, IP, TCP и UDP.

Braden, R. T., ed. 1989b. "Requirements for Internet Hosts - Application and Support", RFC 1123, 98 pages (Oct.).

Вторая половина требованний к хостам RFC. Эта половина описывает Telnet, FTP, TFTP, SMTP и DNS.

Braden, R. T. 1989c. "Perspective on the Host Requirements RFCs", RFC 1127, 20 pages (Oct.).

Краткое неофициальное описание обсуждений и сделанных выводов в рабочей группе IETF, которая разрабатывает требования к хостам RFC.

Braden, R. T. 1992a. "TIME-WAIT Assassination Hazards in TCP", RFC 1337, 11 pages (May).

Рассказ о том, к каким проблемам может привести получение RST в состоянии TIME_WAIT.

Braden, R. T. 1992b. "Extending TCP for Transactions - Concepts", RFC 1379, 38 pages (Nov.).

Исторические факты и концепции, положенные в основу T/TCP.

Braden, R. T. 1992c. "Extending TCP for Transactions - Functional Specification", Internet Draft, 32 pages (Dec.).

Спецификации и обсуждения реализации T/TCP.

Braden, R. T., Borman, D. A., and Partridge, C. 1988. "Computing the Internet Checksum", RFC 1071, 24 pages (Sept.).

Алгоритмы расчета контрольной суммы, используемые IP, ICMP, IGMP, UDP и TCP.

Braden, R. T., and Postel J.B. 1987. "Requirements for Internet Gateways", RFC 1009, 55 pages (June).

Эквивалент требований к хостам RFC для маршрутизаторов. RFC заменен; обратитесь к [Almquist 1993].

Callon, R. 1992. "TCP and UDP with Bigger Addresses (TUBA), A Simple Proposal for Internet Addressing and Routing", RFC 1347, 9 pages (June).

Caceres, R., Danzig, P. B., Jamin, S., and Mitzel, D.J. 1991. "Characteristics of Wide-Area TCP/IP Conversations", Computer Communication Review, vol. 21, no. 4, pp. 101-112 (Sept.).

Case, J. D., Fedor, M. S., Schoffstall, M. L., and Davin, C. 1990. "Simple Network Management (SNMP)", RFC 1157, 36 pages (May).

Спецификация протокола SNMP.

Case, J. D., McCloghrie, K., Rose, M. T., and Waldbusser, S. 1993. "An Introduction to Version 2 of the Internet-Standard Network Management Framework", RFC 1441, 13 pages (Apr.).

Введение в SNMPv2 со ссылками на остальные 11 RFC, определяющие SNMPv2.

Case, J. D., and Partridge, C. 1989. "Case Diagrams: A First Step to Diagrammed Management Information Bases", Computer Communication Review, vol. 19, no. 1, pp. 13-16 (Jan.).

Описывает диаграммы, используемые для повышения наглядности при изучении взаимосвязи между переменными SNMP в заданном модуле.

Casner, S., and Deering, S. E. 1992. "First IETF Internet Audiocast", Computer Communication Review, vol. 22, no. 3, pp. 92-97 (July).

Описание того, как живой звук с совещания IETF передавался по Internet с помощью групповой рассылки. PostScript копию этой публикации можно получить через анонимный FTP с хоста ftp.venera.isi.edu в файле pub/ietf-audiocaast-article.ps. Файл mbone/faq.txt на этом же хосте содержит самые распространенные вопросы о групповой магистрали Internet.

Cheriton, D. P. 1988. "VMTP: Versatile Message Transaction Protocol", RFC 1045, 123 pages (Feb.).

Cheswick, W. R., and Bellovin, S. M. 1994. Firewalls and Internet Security: Repelling the Wily Hacker. Addison-Wesley, Reading, Mass.

Описывает, как установить и администрировать брэндмауэр на основе технологии firewall, а также приводит рассуждения о безопасности сетей.

Clark, D. D. 1982. "Window and Acknowledgment Strategy in TCP", RFC 813, 22 pages (July).

Оригинальный RFC, описывающий синдром глупого окна и то, как его можно избежать.

Clark, D. D. 1988. "The Design Philosophy of the DARPA Internet Protocols", Computer Communication Review, vol. 18, no. 4, pp. 106-114 (Aug.).

Описывает, как в самом начале строились протоколы Internet.

Comer, D. E., and Stevens, D. L. 1993. Internetworking with TCP/IP: Vol. III: Client-Server Programming and Applications, BSD Socket Version. Prentice-Hall, Englewood Cliffs, N.J.

Cooper, A. W., and Postel, J. B. 1993. "The US Domain", RFC 1480, 47 pages (June).

Описывает домен .us в DNS.

Crocker, D. H. 1982. "Standard for the Format of ARPA Internet Text Messages", RFC 822, 47 pages (Aug.).

Определяет формат почтовых сообщений, передаваемых с помощью SMTP.

Crocker, D. H. 1993. "Evolving the System", in Internet System Handbook, eds. D. C. Lynch and M. T. Rose, pp. 41-76. Addison-Wesley, Reading, Mass.

История разработки стандартов ARPANET, а также подробное описание современной структуры сообщества Internet. Описывает текущие стандарты Internet.

Croft, W., and Gilmore, J. 1985. "Bootstrap Protocol (BOOTP)", RFC 951, 12 pages (Sept.).

Crowcroft, J., Wakeman, I., Wang, Z., and Sirovica, D. 1992. "Is Layering Harmful?", IEEE Network, vol. 6, no. 1, pp. 20-24 (Jan.).

Семь отсутствующих рисунков в этой публикации приводятся в следующем выпуске, том 6, номер 2 (Март).

Curry, D. A. 1992. UNIX System Security: A Guide for Users and System Administrators. Addison-Wesley, Reading, Mass.

Книга, посвященная безопасности Unix. Главы 4 и 5 посвящены сетевой безопасности.

Dalton, C., Watson, J., Banks, D., Calamvokis, C., Edwards, A., and Lumley, J. 1993. "Afterburner", IEEE Network, vol. 7, no. 4, pp. 36-43 (July).

Рассказывает, как увеличить скорость работы TCP путем уменьшения количества осуществляемых копирований данных и с помощью использования специализированной сетевой платы.

Danzig, P. B., Obraczka, K., and Kumar, A. 1992. "An Analysis of Wide-Area Name Server Traffic", Computer Communication Review, vol. 22, no.4, pp. 281-292 (Oct.).

Анализ траффика на одном из корневых DNS серверов за 24 часа. Показано, как траффик от неправильно разработанных и сконфигурированных систем может занять полосу пропускания в 20 раз шире, чем необходимо.

Deering, S. E. 1989. "Host Extensions for IP Multicasting", RFC 1112, 17 pages (Aug.).

Спецификация групповой рассылки IP и IGMP.

Deering, S. E., ed. 1991. "ICMP Router Discovery Messages", RFC 1256, 19 pages (Sept.).

Deering, S. E., and Cheriton, D. P. 1990. "Multicast Routing in Datagram Internetworks and Extended LANs", ACM Transactions on Computer Systems, vol. 8, no. 2, pp. 85-110 (May).

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

Dixon, T. 1993. "Comparison of Proposals for Next Version of IP", RFC 1454, 15 pages (May).

Сравнение и краткое описание SIP, PIP и TUBA.

Droms, R. 1993. "Dynamic Host Configuration Protocol", RFC 1531, 39 pages (Oct.).

Droms, R., and Dyksen, W. R. 1990. "Performance Measurements of the X Window system Communication Protocol", Software Practice & Experience, vol. 20, pp. 119-136 (Oct.).

Расчеты использования средств передачи TCP при использовании различных Х клиентов.

Fedor, M. S. 1988. "GATED: A Multi-routing Protocol Daemon for UNIX", Proceedings of the 1988 Summer USENIX Conference, pp. 365-376, San Francisco, Calif.

Finlayson, R. 1984. "Bootstrap Loading using TFTP", RFC 906, 4 pages (June).

Finlayson, R., Mann, T., Mogul, J. C., and Theimer, M. 1984. "A Reverse Address Resolution Protocol", RFC 903, 4 pages (June).

Ford, P. S., Rekhter, Y., and Braun, H-W. 1993. "Improving the Routing and Addressing of IP", IEEE Network, vol. 7, no. 3, pp. 10-15 (May).

Описание CIDR (бесклассовая маршрутизация между доменами).

Fuller, V., Li, T., Yu, J. Y., and Varadhan, K. 1993. "Classless Inter-Domain Routing (CIDR): An Address Assignment and Aggregation Strategy", RFC 1519, 24 pages (Sept.).

Спецификация CIDR.

Gerich, E. 1993. "Guidelines for Management of IP Address Space", RFC 1466, 10 pages (May).

Рассказывает, как в будущем будут распределяться IP адреса (использование вместо адресов класса В блоков адресов класса С).

Gurwitz, R., and Hinden, R. 1982. "IP-Local Area Network Addressing Issues", IEN 212, 11 pages (Sept.).

Одно из самых ранних упоминаний о широковещательной рассылке IP.

Harrenstein, K., Stahl, M. K., and Feinler, E. J. 1985. "NICNAME/WHOIS", RFC 954, 4 pages (Oct.).

Hedrick, C. L. 1988a. "Routing Information Protocol", RFC 1058, 33 pages (June).

Hedrick, C. L. 1988b. "Telnet Terminal Speed Option", RFC 1079, 3 pages (Dec.).

Hedrick, C. L., and Borman, D. A. 1992. "Telnet Remote Flow Control Option", RFC 1372, 6 pages (Oct.).

Hornig, C. 1984. "Standard for the Transmission of IP Datagrams over Ethernet Networks", RFC 894, 3 pages (Apr.).

Huitema, C. 1993. "IAB Recommendation for an Intermediate Strategy to Address the Issue of Scaling", RFC 1481, 2 pages (July).

Рекомендации IAB по реализации CIDR.

Jacobson, V. 1988. "Congestion Avoidance and Control", Computer Communication Review, vol. 18, no. 4, pp. 314-329 (Aug.).

Классическое описание TCP алгоритмов медленного старта и предотвращения переполнения. PostScript копию этой публикации можно получить через анонимный FTP с хоста ftp.ee.lbl.gov в файле congavoid.ps.Z.

Jacobson, V. 1990a. "Compressing TCP/IP Headers for Low-Speed Serial Links", RFC 1144, 43 pages (Feb.).

Описание CSLIP, версии SLIP, где происходит сжатие IP и TCP заголовков.

Jacobson, V. 1990b. "Modified TCP Congestion Avoidance Algorithm", April 30, 1990, end2end-interest mailing list (Apr.).

Описывает алгоритмы быстрой повторной передачи и быстрого восстановления.

Jacobson, V. 1990c. "Berkeley TCP Evolution from 4.3-Tahoe to 4.3-Reno", Proceedings of the Eighteenth Internet Engineering Task Force, p. 365 (Sept.), University of British Columbia, Vancouver, B.C.

Jacobson, V., and Braden, R. T. 1988. "TCP Extensions for Long-Delay Paths", RFC 1072, 16 pages (Oct.).

Описывает опцию селективных подтверждений TCP, описание которой было удалено из более поздних RFC.

Jacobson, V., Braden, R. T., and Borman, D. A. 1992. "TCP Extensions for High Performance", RFC 1323, 37 pages (May).

Описывает опцию масштабирования окна, опцию временной марки и алгоритм PAWS. Здесь же доказывается необходимость использования этих модификаций.

Jacobson, V., Braden, R. T., and Zhang, L. 1990. "TCP Extensions for High-Speed Paths", RFC 1185, 21 pages (Oct.).

Несмотря на то, что этот RFC был заменен на RFC 1323, стоит прочитать приложение, посвященное защите от старых дублированных сегментов в TCP.

Juszczak, C. 1989. "Improving the Performance and Correctness of an NFS Server", Proceedings of the 1989 Winter USENIX Conference, pp. 53-63, San Diego, Calif.

Рассматривает подробности реализации кэша NFS сервера.

Kantor, B. 1991. "BSD Rlogin", RFC 1282, 5 pages (Dec.).

Спецификация протокола Rlogin.

Karn, P., and Partridge, C. 1987. "Improving Round-Trip Time Estimates in Reliable Transport Protocols", Computer Communication Review, vol. 17, no. 5, pp. 2-7 (Aug.).

Детали алгоритма Карна, посвященные обработке тайм-аута повторной передачи для сегментов, которые передаются повторно.

Katz, D. 1990. "Proposed Standard for the Transmission of IP Datagrams Over FDDI Networks", RFC 1188, 11 pages (Oct.).

Рассказывает об инкапсуляции IP датаграмм и ARP запросов, описывает сети FDDI, включая проблемы групповой рассылки.

Kent, C. A., and Mogul, J. C. 1987. "Fragmentation Considered Harmful", Computer Communication Review, vol. 17, no. 5, pp. 390-401 (Aug.).

Kent, S. T. 1991. "U.S. Department of Defense Security Options for the Internet Protocol", RFC 1108, 17 pages (Nov.).

Kleinrock, L. 1992. "The Latency/Bandwidth Tradeoff in Gigabit Networks", IEEE Communications Magazine, vol. 30, no. 4, pp. 36-40 (Apr.).

Klensin, J., Freed, N., and Moore, K. 1993. "SMTP Service Extension for Message Size Declaration", RFC 1427, 8 pages (Feb.).

Klensin, J., Freed, N., Rose, M. T., Stefferud, E. A., and Crocker, D. 1993a. "SMTP Service Extensions", RFC 1425, 10 pages (Feb.).

Klensin, J., Freed, N., Rose, M. T., Stefferud, E. A., and Crocker, D. 1993b. "SMTP Service Extension for 8bit-MIME Transport", RFC 1426, 6 pages (Feb.).

Krol, E. 1992. The Whole Internet. O'Reilly & Associates, Sebastopol, Calif.

Введение в Internet для новичков.

LaQuey, T. 1993. The Internet Companion: A Beginner's Guide to Global Networking. Addison-Wesley, Reading, Mass.

Leffler, S. J., and Karels, M. J. 1984. "Trailer Encapsulations", RFC 893, 3 pages (Apr.).

Leffler, S. J., McKusick, M. K., Karels, M. J., and Quarterman, J. S. 1989. The Design and Implementation of the 4.3BSD UNIX Operating System. Addison-Wesley, Reading, Mass.

Полное руководство по Unix системе 4.3BSD. Эта книга описывает Tahoe релиз 4.3BSD.

Lougheed, K., and Rekhter, Y. 1991. "A Border Gateway Protocol 3 (BGP-3)", RFC 1267, 35 pages (Oct.).

Lynch, D. C. 1993. "Historical Perspective", in Internet System Handbook, eds. D. C. Lynch and M. T. Rose, pp. 3-14. Addison-Wesley, Reading, Mass.

Ранние дни Internet: ARPANET.

Macklem, R. 1991. "Lessons Learned Tuning the 4.3BSD Reno Implementation of the NFS Protocol", Proceedings of the 1991 Winter USENIX Conference, pp. 53-64, Dallas, Tex.

Описывает реализацию NFS, который использует и UDP, и TCP.

Malkin, G. S. 1993a. "RIP Version 2: Carrying Additional Information", RFC 1388, 7 pages (Jan.).

Malkin, G. S. 1993b. "Traceroute Using an IP Option", RFC 1393, 7 pages (Jan.).

Предлагаемые модификации для ICMP, предназначенные для новой версии traceroute.

Mallory, T., and Kullberg, A. 1990. "Incremental Updating of the Internet Checksum", RFC 1141, 2 pages (Jan.).

Описывает технику расчета контрольной суммы.

Manber, U. 1990. "Chain Reactions in Networks", IEEE Computer, vol. 23, no. 10, pp. 57-63 (Oct.).

Описывает типы "широковещательного шторма" (неконтролируемый рост числа широковещательных сообщений) и "паводков" в сети, это примерно то, что мы показали в упражнениях 9.3 и 9.4.

McCanne, S., and Jacobson, V. 1993. "The BSD Packet Filter: A New Architecture for User-Level Packet Capture", Proceedings of the 1993 Winter USENIX Conference, pp. 259-269, San Diego, Calif.

Подробное описание пакетного фильтра BSD и сравнение с "краником" в сетевом интерфейсе от Sun (NIT).

McCloghrie, K., and Rose, M. T. 1991. "Management Information Base for Network Management of TCP/IP-based Internets: MIB-II", RFC 1213 (Mar.).

McGregor, G. 1992. "PPP Internet Protocol Control Protocol (IPCP)", RFC 1332, 12 pages (May).

Описание NCP для PPP, как это решено в TCP/IP.

Mills, D. L. 1992. "Network Time Protocol (Version 3): Specification, Implementation, and Analysis", RFC 1305, 113 pages (Mar.).

Mockapetris, P. V. 1987a. "Domain Names: Concepts and Facilities", RFC 1034, 55 pages (Nov.).

Введение в DNS.

Mockapetris, P. V. 1987b. "Domain Names: Implementation and Specification", RFC 1035, 55 pages (Nov.).

Спецификация DNS.

Mogul, J. C. 1990. "Efficient Use of Workstations for Passive Monitoring of Local Area Networks", Computer Communications Review, vol. 20, no. 4, pp. 253-263 (Sept.).

Описывает использование рабочих станций для мониторинга локальных сетей, вместо дорогостоящих специализированных аппаратных анализаторов сети.

Mogul, J. C. 1992. "Holy Turbocharger Batman, (evil cheating), NFS async writes", Message-ID <1992Mar2.191711.9935@PA.dec.com>, Usenet, comp.protocols.nfs Newsgroup (Mar.).

Интересная статистика по ошибкам в контрольных суммах, собранная на загруженном DNS сервере за 40 дней.

Mogul, J. C. 1993. "IP Network Performance", in Internet System Handbook, eds. D. C. Lynch and M. T. Rose, pp. 575-675. Addison-Wesley, Reading, Mass.

Описывает множество аспектов в протоколах TCP/IP, которые могут быть настроены для повышения производительности.

Mogul, J. C., and Deering, S. E. 1990. "Path MTU Discovery", RFC 1191, 19 pages (Apr.).

Mogul, J. C., and Postel, J. B. 1985. "Internet Standard Subnetting Procedure", RFC 950, 18 pages (Aug.).

Moore, K. 1993. "MIME (Multipurpose Internet Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text", RFC 1522, 10 pages (Sept.).

Описывает способы отправки не-ASCII символов в заголовках почтовых сообщений по RFC 822, с использованием 7-битного ASCII.

Moy, J. 1991. "OSPF Version 2", RFC 1247, 189 pages (July).

Nagle, J. 1984. "Congestion Control in IP/TCP Internetworks", RFC 896, 9 pages (Jan.).

Описание алгоритма Нагла.

Nye, A., ed. 1992. The X Window System, Volume 0: X Protocol Reference Manual, Third Edition. O'Reilly & Associates, Sebastopol, Calif.

Obraczka, K., Danzig, P. B., and Li, S. 1993. "Internet Resource Discovery Services", IEEE Computer, vol. 26, no. 9, pp. 8-22 (Sept.).

Представляет обзор современных средств поиска ресурсов Internet: Alex, Archie, Gopher, Indie, Knowbot Information Service, Netfind, Prospero, WAIS, WWW и X.500. PostScript копию можно получить через анонимный FTP с хоста ftp.caldera.usc.edu, имя файла /pub/kobraczk/ieeecomputer.ps.Z.

Papadopoulos, C., and Parulkar, G. M. 1993. "Experimental Evaluation of SunOS IPC and TCP/IP Protocol Implementation", IEEE/ACM Transactions on Networking, vol. 1, no. 2 (Apr.).

Оценивает дополнения, которые дополняются разными уровнями и передаются и принимаются как данные.

Partridge, C. 1986. "Mail Routing and the Domain System", RFC 974, 7 pages (Jan.).

Как используются DNS MX записи для маршрутизации почты.

Partridge, C. 1994. Gigabit Networking. Addison-Wesley, Reading, Mass.

Описывает, что случается, когда скорости достигают гигабитного предела.

Partridge, C., and Pink, S. 1993. "A Faster UDP", IEEE/ACM Transactions on Networking, vol. 1, no. 4 (Aug.).

Описывает улучшение кодов Berkeley, которые позволяют повысить производительность работы UDP примерно на 30%.

Paxson, V. 1993. "Empirically-Derived Analytic Models of Wide-Area TCP Connections: Extended Report", LBL-34086, Lawrence Berkeley Laboratory and EECS Division, University of California, Berkeley (June).

Приводится анализ 2,5 миллионов TCP соединений открытых по 14-ти маршрутам в глобальных сетях. PostScript копию этого отчета можно получить через анонимный FTP с хоста ftp.ee.lbl.gov, файлы WAN-TCP-models.1.ps.Z и WAN-TCP-models.2.ps.Z.

Perlman, R. 1992. Interconnections: Bridges and Routers. Addison-Wesley, Reading, Mass.

Подробное описание методов связывания сетей (мосты и маршрутизаторы) и различных алгоритмов маршрутизации.

Plummer, D. C. 1982. "An Ethernet Address Resolution Protocol", RFC 826, 10 pages (Nov.).

Postel, J. B. 1980. "User Datagram Protocol", RFC 768, 3 pages (Aug.).

Postel, J. B., ed. 1981a. "Internet Protocol", RFC 791, 45 pages (Sept.).

Postel, J. B. 1981b. "Internet Control Message Protocol", RFC 792, 21 pages (Sept.).

Postel, J. B., ed. 1981c. "Transmission Control Protocol", RFC 793, 85 pages (Sept.).

Postel, J. B. 1982. "Simple Mail Transfer Protocol", RFC 821, 68 pages (Aug.).

Postel, J. B. 1987. "TCP and IP Bake Off", RFC 1025, 6 pages (Sept.).

Описывает некоторые процедуры и осуществляет оценку различных реализаций TCP/IP в ранней стадии развития, с точки зрения совместимости.

Postel, J. B., ed. 1993. "Internet Official Protocol Standards", RFC 1500, 36 pages (Aug.).

Статус всех протоколов Internet. Этот RFC регулярно обновляется - убедитесь, что Вы читаете последний по индексу.

Postel, J. B., and Reynolds, J. K. 1983a. "Telnet Protocol Specification", RFC 854, 15 pages (May).

Основная спецификация протокола Telnet. Более поздние RFC описывают отдельные опции Telnet.

Postel, J. B., and Reynolds, J. K. 1983b. "Telnet Binary Transmission", RFC 856, 4 pages (May).

Postel, J. B., and Reynolds, J. K. 1983c. "Telnet Echo Option", RFC 857, 5 pages (May).

Postel, J. B., and Reynolds, J. K. 1983d. "Telnet Suppress Go Ahead Option", RFC 858, 3 pages (May).

Postel, J. B., and Reynolds, J. K. 1983e. "Telnet Status Option", RFC 859, 3 pages (May).

Postel, J. B., and Reynolds, J. K. 1983f. "Telnet Timing Mark Option", RFC 860, 4 pages (May).

Postel, J. B., and Reynolds, J. K. 1985. "File Transfer Protocol (FTP) ", RFC 959, 69 pages (Oct.).

Postel, J. B., and Reynolds, J. K. 1988. "Standard for the Transmission of IP Datagrams over IEEE 802 Networks", RFC 1042, 15 pages (Apr.).

Спецификация на инкапсуляцию IP датаграмм и ARP запросов и рассмотрение сетей IEEE 802.

Pusateri, T. 1993. "IP Multicast Over Token-Ring Local Area Networks", RFC 1469, 4 pages (June).

Rago, S. A. 1993. UNIX System V Network Programming. Addison-Wesley, Reading, Mass.

Книга про TLI и потоковую подсистему.

Rekhter, Y., and Gross, P. 1991. "Application of the Border Gateway Protocol in the Internet", RFC 1268, 13 pages (Oct.).

Rekhter, Y., and Li, T. 1993. "An Architecture for IP Address Allocation with CIDR", RFC 1518, 27 pages (Sept.).

Reynolds, J. K. 1989. "The Helminthiasis of the Internet", RFC 1135, 33 pages (Dec.).

Подробное описание червя в Internet в 1988 году.

Reynolds, J. K., and Postel, J. B. 1992. "Assigned Numbers", RFC 1340, 138 pages (July).

Все магические числа семейства протоколов Internet. Этот RFC регулярно обновляется. Проверьте по индексу, какая версия последняя.

Romkey, J. L. 1988. "A Nonstandard for Transmission of IP Datagrams Over Serial Lines: SLIP", RFC 1055, 6 pages (June).

Rose, M. T. 1990. The Open Book: A Practical Perspective on OSI. Prentice-Hall, Englewood Cliffs, N.J.

Книга про OSI протоколы. В главе 8 приводится описание ASN.1 и BER.

Rose, M. T. 1993. The Internet Message: Closing the Book with Electronic Mail. Prentice-Hall, Englewood Cliffs, N.J.

Книга о почте Internet с подробным описанием MIME.

Rose, M. T. 1994. The Simple Book: An Introduction to Internet Management, Second Edition. Prentice-Hall, Englewood Cliffs, N.J.

Книга о SNMPv2. Первая редакция описывает SNMPv1.

Rose, M. T., and McCloghrie, K. 1990. "Structure and Identification of Management Information for TCP/IP-based Internets", RFC 1155, 22 pages (May).

Определяет SMI для SNMPv1.

Rosenberg, W., Kenney, D., and Fisher, G. 1992. Understanding DCE. O'Reilly & Associates, Sebastopol, Calif.

Обзор Distributed Computing Environment OSF.

Routhier, S. A. 1993. "Implementation Experience for SNMPv2", The Simple Times, vol. 2, no. 4, pp. 1-4 (July-Aug.).

Описывает необходимые модификации для SNMPv1, обеспечивающие поддержку SNMPv2. Этот журнал распространяется электронным образом, абсолютно бесплатно. Пошлите почту с указанием help в поле subject на st-subscriptions@simple-times.org, чтобы получить информацию о подписке.

Schryver, V. J. 1993. "Info on High Speed Transport Protocols Requested", Message-ID <i0imr8g@rhyolite.wpd.sgi.com>, Usenet, comp.protocols.tcp-ip Newsgroup (May).

Некоторые подробности о производительности TCP по FDDI.

Schwartz, M. F., and Tsirigotis, P. G. 1991. "Experience with a Semantically Cognizant Internet White Pages Directory Tool", Journal of Internetworking Research and Experience, vol. 2, no. 1, pp. 23-50 (Mar.).

Также доступна по анонимному FTP с хоста ftp.cs.colorado.edu в файле pub/cs/techreports/schwartz/PostScript/White.Pages.ps.Z.

Simpson, W. A. 1992. "Point-to-Point Protocol (PPP) for the Transmission of Multi-Protocol Datagrams Over Point-to-Point Links.", RFC 1331, 66 pages (May).

Описывает PPP и его управляющий протокол.

Sollins, K. R. 1992. "The TFTP Protocol (Revision 2)", RFC 1350, 11 pages (July).

Stallings, W. 1987. Handbook of Computer-Communications Standards, Volume 2: Local Network Standards. Macmillan, New York.

Содержит подробное описание стандартов IEEE 802.

Stallings, W. 1993. SNMP, SNMPv2, and CMIP: The Practical Guide to Network-Management Standards. Addison-Wesley, Reading, Mass.

Описывает разницу между SNMPv1 и SNMPv2.

Stern, H. 1991. Managing NFS and NIS. O'Reilly & Associates, Sebastopol, Calif.

Подробное описание процедуры инсталляции, использование и администрирования NFS.

Stevens, W. R. 1990. UNIX Network Programming. Prentice-Hall, Englewood Cliffs, N.J.

Аспекты сетевого программирования в среде Unix.

Stevens, W. R. 1992. Advanced Programming in the UNIX Environment. Addison-Wesley, Reading, Mass.

Программирование в Unix.

Sun Microsystems. 1987. "XDR: External Data Representation Standard", RFC 1014, 20 pages (June).

Sun Microsystems. 1988a. "RPC: Remote Procedure Call, Protocol Specification, Version 2", RFC 1057, 25 pages (June).

Sun Microsystems. 1988b. "NFS: Network File System Protocol Specification", RFC 1094, 27 pages (Mar.).

Спецификация второй версии Sun NFS.

Sun Microsystems. 1993. NFS: Network File System Version 3 Protocol Specification. Sun Microsystems, Mountain View, Calif.

PostScript копию этого документа можно получить через анонимный FTP с хоста ftp.uu.net в файле networking/ip/nfs/NFS3.spec.ps.z.

Tanenbaum, A. S. 1989. Computer Networks, Second Edition. Prentice-Hall, Englewood Cliffs, N.J.

Общее описание компьютерных сетей.

Topolcic, C. 1993. "Status of CIDR Deployment in the Internet", RFC 1467, 9 pages (Aug.).

Tsuchiya, P. F. 1991. "On the Assignment of Subnet Numbers", RFC 1219, 13 pages (Apr.).

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

Ullmann, R. 1993. "TP/IX: The Next Internet", RFC 1475, 35 pages (June).

Еще одно предложение о будущем развитии протоколов Internet.

VanBokkelen, J. 1989. "Telnet Terminal-Type Option", RFC 1091, 7 pages (Feb.).

Waitzman, D. 1988. "Telnet Window Size Option", RFC 1073, 4 pages (Oct.).

Waitzman, D., Partridge, C., and Deering, S. E. 1988. "Distance Vector Multicast Routing Protocol", RFC 1075, 24 pages (Nov.).

Warnock, R. P. 1991. "Need Help Selecting Ethernet Cards for Very High Performance Throughput Rates", Message-ID <lbhal10@sgi.sgi.com>, Usenet, comp.protocols.tcp-ip Newsgroup (Sept.).

Производительность TCP, которую мы вычислили на рисунке 24.9.

Weider, C., Reynolds, J. K., and Heker, S. 1992. "Technical Overview of Directory Services Using the X.500 Protocol", RFC 1309, 16 pages (Mar.).

Wimer, W. 1993. "Clarifications and Extensions for the Bootstrap Protocol", RFC 1532, 22 pages (Oct.).

X/Open. 1991. Protocols for X/Open Internetworking: XNFS. X/Open, Reading, Berkshire, U.K.

Лучшее описание Sun RPC, XDR и NFS. Также содержит описание менеджера блокировки NFS и протоколов монитора статуса, а также приложения, описывающие различия между доступом к NFS файлам и локальным файлам. Номер X/Open документа XO/CAE/91/030.

Zimmerman, D. P. 1991. "Finger User Information Protocol", RFC 1288, 12 pages (Dec.).