Сетевые протоколы. Семейство протоколов TCP/IP

Так случилось, что Мефодий мало что знал о компьютерных сетях до знакомства с Linux. Если пользоваться только www-броузером и почтовой программой, сведений, вроде "у каждого компьютера Internet есть имя, на компьютерах бывает почта и WWW", бывает вполне достаточно. Строго говоря, если сеть настроена, почтовые клиенты или броузеры Linux не требуют большего объёма знаний. Однако Linux хорош именно тем, что позволяет проследить работу сети от процедур самого низкого уровня, вроде поведения сетевых карт, до приложений высокого уровня и их протоколов.

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

Итак, если бы Мефодий получил задание "придумать Internet" на пару с Гуревичем, какие бы вопросы перед ними встали?

  1. Среда передачи данных. Посредством чего передавать данные? Как именно представляется передаваемая информация?
  2. Устройство передачи данных. (Раз уж известно, как передаются данные). Как подключаться к среде? Как отличить данные от не-данных (т. е. определить идёт ли передача)? Как определить очерёдность работы нескольких устройств, подключённых к одной среде передачи данных? Как определить, кому предназначаются даннее, передаваемые в общей среде?

  3. Топология неоднородной сети. (Раз уж известно, как подключить компьютер к одной или нескольким средам передачи данных). Если в сеть объединены несколько сред передачи данных, как определить адресата (и отправителя тоже)? Как обеспечить пересылку данных из одной среды в другую? Как выстроить непрерывный маршрут пересылок от отправителя к адресату.

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

  5. Интерпретация данных. (Раз уж возможна надёжная и без искажений доставка). Что делать с полученными данными? Какие части операционной системы отвечают за их обработку, и откуда про это знает абонент с другой стороны соединения?

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

В приведённом делении на этапы (уровни) примечательна их относительная независимость: если группа задач, связанная с некторым уровнем, решена, на следующем уровне можно забыть, как именно решались эти задачи. Так, устройство передачи данных типа "Ethernet" с точки зрения компьютера всегда одно и то же, какой бы носитель при этом не использовался: коаксиальный кабель или кабель типа "витая пара", хотя с физической и даже топологической точки зрения эти среды сильно различаются(1). Точно так же обстоят дела при переходе со второго уровня на третий: во время получения данных уже совершенно неважно, какие среды передачи были при этом задействованы (ethernet, три провода, голубиная почта(2)...). Переход с третьего уровня на четвёртый и с четвёртого на пятый тоже обладает этим свойством.

По всей видимости, именно с этими задачами сталкивались и разработчики из института ARPA (Advanced Research Projects Agency, "Агенство Перспективных Исследовательских Проектов"; в процессе работы оно было переименовано в DARPA, где "D" означало Defence). По крайней мере, предложенное ими в середине семидесятых семейство протоколов TCP/IP также подразделялось на пять уровней: аппаратный, интерфейсный, сетевой, транспортный и прикладной. Впоследствии аппаратный уровень стали смешивать с интерфейсным, так как с точки зрения операционной системы они неразличимы(3). Именно разделение на независимые друг от друга уровни позволило со временем объединить большинство разнородных локальных сетей в единое сетевое пространство -- глбальную сеть Internet.

В TCP/IP вопрос о том, как обеспечить нескольким абонентам сети возможность передавать данные, не мешая друг другу, решён с помощью разделения пакетов данных. Разделение пакетов предполагает, что данные передаются не единым блоком, а по частям, пакетами. Алгоритмы, определяющие, когда абоненту разрешено посылать следующий пакет, могут быть разными, но результат всегда один: в сети передаются попеременно фрагменты всех сеансов передачи данных. В сильно загруженном состоянии такая сеть может просто не принять очередной пакет от абонента-отправителя, и тому придётся ждать удобного случая, чтобы всё-таки "пропихнуть" его в переполненную другими пакетами среду. Таким образом, обеспечить гарантированное время передачи одного пакета в сетях с разделением пакетов бывает довольно сложно, хотя есть алгоритмы, позволяющие это сделать.

Протвоположность метода разделения пакетов -- метод разделения каналов, который предполагает, что в сети имеется определённое число каналов передачи данных, которые абоненты сети арендуют на всё время передачи. По такому принципу построены, например, телефонные линии: дозвонившись, мы арендуем канал связи между двумя телефонными аппаратами, и до тех пор, пока этот канал занят, невозможно ни воспользоваться им кому-то другому, ни огранизовать параллельно передачу данных откуда-нибудь ещё. Главное достоинство сетей с разделением каналов -- постоянная (за вычетом помех на линии) скорость передачи данных. Главный недостаток -- ограниченное количество каналов передачи. Проектировать среду передачи так, чтобы каждый абонент был связан с каждым отдельным каналом, имеет смысл только когда абонентов очень мало: количество каналов будет пропорционально квадрату количества абонентов. Количество каналов в большой сети будет существенно меньшим, и ровно столько сеансов передачи данных можно будет в этой сети установить. Попытка соединиться с абонентом, когда все каналы уже заняты, окончится неудачей. Мефодий припомнил своего знакомого, дозвониться которому тяжело, хотя телефон тот занимает нечасто и не подолгу. По всей видимости, каналов между какими-то двумя АТС, через которые Мефодий связывается с приятелем, хронически недостаёт (так бывает, когда отдалённый район быстро застраивается и наполняется телефонами).

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

Таким образом, процесс передачи данных выглядит так: порция данных прикладного уровня нарезается на части, соответствующие размеру пакета транспортного уровня (фрагментируется), к каждому фрагменту приписывается транспортная служебная информация, получаются пакеты транспортного уровня. Каждый пакет транспортного уровня может быть опять-таки фрагментирован для передачи по сети, к каждому получившемуся фрагменту добавляется служебная информация сетевого уровня, что даёт последовательность сетевых пакетов. Каждый сетевой пакет тоже может быть фрагментирован до размера, "пролезающего" через конкретное сетевае устройство, из него формируются пакеты интерфейсного уровня (фреймы). Наконец, к каждому фрейму само устройство (по крайне мере, так это сделано в Ethernet) приписывает некоторый ключ, по которому принимающее устройство распознаёт начало фрейма. В таком виде данные передадутся по проводам. Процесс "заворачивания" пакетов более высокого уровня в пакеты более низкого уровня называется инкапсуляцией.

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

сетевой пакет

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

Аппаратный и интерфейсный уровни

Итак, на аппаратном уровне возможна какая угодно среда передачи данных, с точки зрения Linux сеть начинается в месте подключения к этой среде, то есть на сетевом интерфейсе. Список сетевых интерфейсов и их настрокек в системе можно посмотреть с помощью команды ifconfig (от interface configuration):

methody@localhost:~ $ ifconfig
 -bash: ifconfig: command not found
methody@localhost:~ $ /sbin/ifconfig 
 Warning: cannot open /proc/net/dev (Permission denied). Limited output.
 Warning: cannot open /proc/net/dev (Permission denied). Limited output.
 eth0     Link encap:Ethernet  HWaddr 00:0C:29:56:C1:36  
          inet addr:192.168.102.125  Bcast:192.168.102.255  Mask:255.255.255.0
          UP BROADCAST NOTRAILERS RUNNING MULTICAST  MTU:1500  Metric:1

 Warning: cannot open /proc/net/dev (Permission denied). Limited output.
 lo       Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1

Запуск ifconfig

Утилитой ifconfig пользуется, в основном, сама система или администратор; некоторые данные ifconfig получает, обращаясь с системным вызовом ioctl() к открытому сетевому сокету, а некторые считывает из /proc. Название сетевого интерфейса состоит из его типа и порядкового номера (каким по счёту его распознало ядро). Все сетевые интерфейсы Ethernet в Linux называются ethномер, начиная с eth0. Параметр MTU (Maximum Transfer Unit) определяет наибольший размер фрейма. Большинство других параметров относятся к сетевому уровню, но как минимум ещё один -- HWaddr -- относится к уровню интерфесному.

сетевой интерфейс

Точка взаимодействия утилит Linux с реализацией TCP/IP в ядре системы. Как правило, имеет уникальный сетевой адрес. Интерфейсу может соответствовать некоторое сетевое оборудование (например, карта ethernet), в этом случае определён также и интерфейсный его адрес.

HWaddr (от HardWare address, аппаратный адрес) -- это уникальный внутри среды передачи данных идентификатор сетевого устройства. В Ethernet аппаратный адрес называется MAC-address (от Media Access Control, управление доступом к среде), и состоит из шести байтов, которые принято записывать в шестнадцатеричной системе счисления и разделять двоеточиями. Каждая ethernet-карта имеет собственный уникальный MAC-address (в примере -- 00:0C:29:56:C1:36), поэтому его легко использовать для определения отправителя и получателя в рамках одной ethernet-среды. Если идентификатор получателя неизвестен, используется аппаратный широковещательный адрес, FF:FF:FF:FF:FF:FF. Сетевая карта, получив широковещательный фрейм или фрейм, MAC-адрес получателя в котором совпадает с её MAC-адресом, обязана отправить его на обработку системе.

Термин "Media Access Control" имеет отношение к алгоритму, с помощью которого решается задача очерёдности передачи. Алгоритм базируется на трёх принципах:

  1. Прослушивание среды. Каждое устройство умеет определять, идёт ли в данное время передача данных по среде. Если среда свободна, значит устройство имеет право само передавать данные.
  2. Обнаружение коллизий. Если решение о начале передаче данных одновременно приняли несколько устройств, в среде возникнет коллизия, и распознать, где чьи были данные, становится невозможно. Зато устройства всегда замечают произошедшую коллизию, и передают данные повторно.

  3. Случайное время ожидания перед повтором. Если бы после коллизии все устройства начали одновременно повторять передачу данных, случилась бы новая коллизия. Поэтому каждое устройство выжидает некторое случайное время, и только после этого повторяет передачу. Если повторная коллизия всё-таки возникает, устройство ждёт вдвое дольше(4), и т. д. до тех пор, пока не превысится допустимое время ожидания, после чего системе сообщается об ошибке.

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

Сетевой уровень

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

Адресация

Адрес, определяемый протоколом IP (Internetwork Protocol), состоит из четырёх байтов, записываемых традиционно в десятичной системе счисления и разделяемых точкой. Адрес сетевого интерфейса eth0 из примера -- 192.168.102.125. Второй сетевой интерфейс из примера, lo, -- так называемая заглушка (loopback), которая используется для огранизации сетевых взаимодействий компьютера с самим собой: любой посланный в заглушку пакет немедленно обрабатывается как принятый оттуда. Заглушка обычно имеет адрес 127.0.0.1.

Отдельная среда передачи данных (локальная сеть) также имеет собственный адрес. Если предстивить IP-адрес в виде линейки из 32 битов, она строго разделяется на две части: столько-то битов слева отводится под адрес сети, а оставшиеся -- под адрес абонента в этой сети. Для того, чтобы определить размер адреса сети, используется сетевая маска -- линейка из 32 битов, в которой на месте адреса сети стоят единицы, а на месте адреса компьютера -- нули. При наложении маски на IP-адрес все единицы в нём, которым соответствуют нули в маске, превращаются в нули(5). Таким образом вычисляется IP-адрес сети. В примере сетевая маска интерфейса eth0 равна 255.255.255.0, т. е. 24 единицы и 8 нулей. Тогда IP-адрес сети будет равен 192.168.102.0. Мефодий заметил, что если сетевая маска выровнена по границе байта, производить двоичные операции вообще не надо: так, в примере можно было просто сказать, что адрес сети занимает три байта, а адрес абонента -- оставшийся один.

Заметим, что адрес сети может содержать значащие нули: например, в адресе 10.0.0.1 при сетевой маске 255.255.0.0 адрес сети занимает два байта. из которых второй -- полностью нулевой. Для того, чтобы не гадать, какие нули -- значащие, а какие -- отрезаны маской, к адресу сети принято приписывать уточнение вида /количество_единиц_в_маске. В приведённом случае адрес сети выглядел бы так: 10.0.0.0/16, а в предыдущем -- 192.168.102.0/24.

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

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

MAC-адреса компьютеров локальной сети хранятся в специальной таблице ядра, называемой "таблица ARP". Просмотрть содержимое этой таблицы можно с помощью команды arp -a:

[root@localhost root]# arp -a
 fuji.nipponman.ru (192.168.102.1) at 00:50:56:C0:00:01 [ether] on eth0
 edoh.nipponman.ru (192.168.102.7) at 00:50:56:C3:11:a2 [ether] on eth0
[root@localhost root]# sleep 60
[root@localhost root]# arp -a
[root@localhost root]# ping -c1 192.168.102.1
 PING 192.168.102.1 (192.168.102.1) 56(84) bytes of data.
 64 bytes from 192.168.102.1: icmp_seq=1 ttl=64 time=0.217 ms

 --- 192.168.102.1 ping statistics ---
 1 packets transmitted, 1 received, 0% packet loss, time 0ms
 rtt min/avg/max/mdev = 0.217/0.217/0.217/0.000 ms
[root@localhost root]# arp -a
 fuji.nipponman.ru (192.168.102.1) at 00:50:56:C0:00:01 [ether] on eth0

Просмотр таблицы ARP

Более точно, ARP-таблица отражает соответствие между IP-и MAC-адресами. Таблица эта динамическая: устаревшие соответствия из неё удаляются, так как компьютеру может быть назначен другой IP-адрес, интерфейс можно отключить от сети, заменить и т. д. Если вновь понадобится связаться с компьютером, чей MAC-адрес устрел, соответствие IP и MAC придётся устанавливать по новой. В примере была использована команда ping, посылающая на указанный IP-адрес пакеты служебного протокола ICMP, на который адресат обязан ответить. Если ответа нет, значит, связь по каким-то прчинам невозможна.

Устанавливать соответствие между адресами сетевого и интерфейсного уровня -- дело протоколя ARP (Address Resolution Protocol, "протокол преобразования адресов"). В случае преобразования IP в MAC он работает так: отправляется широковещательный ethernet-фрейм типа "ARP-запрос", внутри которого -- IP-адрес, что означает "Эй! У кого такой IP?". Каждый работающий компьютер обрабатывает этот фрейм и тот, чей IP-адрес совпадает с запрошенным, возвращает отправителю пустой фрейм типа "ARP-ответ", в поле "отправитель" которого указан искомый MAC-адрес. Это означает "У меня. А что?". Тут ARP-таблица заполняется и первый компьютер готов к инкапсуляции IP-пакета.

Маршрутизация

Более сложный вопрос встаёт, если IP-адрес компьютера-адресата не входит в локальную сеть компьютера-отправителя. Ведь и в этом случае пакет необходимо отослать какому-то абоненту локальной сети, с тем, чтобы тот перенаправил его дальше. Этот абонент, маршрутизатор, подключён к нескольким сетям, и ему вменяется в обязанность пересылать пакеты между ними по определённым правилам. В самом простом случае таких сетей две: "внутренняя", к которой подключены компьютеры, и "внешняя", соединяющая маршрутизатор со всей глобалной сетью. Таблицу, управляющую маршрутизацией пакетов, можно просмотреть с помощью команды netstat -r или route (обе команды имеют ключ "-n", заставляющий их использовать в выдаче IP-адреса, а не имена компьютеров):

[root@localhost root]# route -n
 Kernel IP routing table
 Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
 192.168.102.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
 127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
 0.0.0.0         192.168.102.1   0.0.0.0         UG    0      0        0 eth0

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

На машине Мефодия в таблице маршрутизации всего три записи: одна -- про сеть 192.168.102.0/24, доступную по интерфейсу eth0, другая -- про сеть 127.0.0.0/8, доступную через заглушку, и последняя -- про сеть 0.0.0.0/0, доступную через маршрутизатор (gateway) с адресом 192.168.102.1. Сеть 0.0.0.0/0 -- это и есть "весь интернет", потому что ей принадлежат любые IP-адреса (ни одного бита на сетевую маску), такая запись в таблице назывется маршрут по умолчанию. Если маршрут по умолчанию не задан, попытка связаться с удалённым компьютером может окончиться ошибкой "No route to host": система не сможет определить, кому пересылать пакет.

На маршрутизаторе таблица выглядит посложнее:

[root@fuji root]# route -n
 Kernel IP routing table
 Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
 83.237.29.1     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
 192.168.102.0   0.0.0.0         255.255.255.0   U     0      0        0 eth1
 10.13.0.0       0.0.0.0         255.255.0.0     U     0      0        0 eth0
 127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
 0.0.0.0         83.237.29.1     0.0.0.0         UG    0      0        0 ppp0
[root@fuji root]# ifconfig ppp0
 ppp0     Link encap:Point-to-Point Protocol  
          inet addr:83.237.29.51  P-t-P:83.237.29.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1492  Metric:1
          RX packets:17104 errors:0 dropped:0 overruns:0 frame:0
          TX packets:23839 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3 
          RX bytes:5879278 (5.6 Mb)  TX bytes:1750644 (1.6 Mb)

Сложная таблица маршрутизации

Начать с того, что вдобавок к сетевым интерфейсам eth0 и eth1 тут наличествует интерфейс типа "точка-точка", ppp0. Это виртуальный интерфейс: он не соответствует никакому сетевому устройству, а организуется по инициативе демона pppd, работающего в соответствии с протоколом PPP (Point to Point Protocol). PPP-соединение позволяет организовать "сеть", состоящую всего из двух абонентов, связанных любой средой передачи данных: двумя модемами и телефоном, тремя проводами, ethernet и т. п(6).

Получив IP-пакет, система начинает "примерять" его поочерёдно ко всем записям таблицы маршрутизации, отсортированным в порядке убывания размера сетевой маски (в том же порядке выдаёт их команда route). Если сеть адресата совпадает с сетью из таблицы, пакет нужно пересылать по адресу, указанному в поле "Gateway". Этот адрес используется вместо поля адресата и поиск возобновляется с начала таблицы. Если поле "Gateway" -- нулевое, значит, речь идёт об абоненте локальной сети, и пакет надо передать на уровень ниже (eth при этом может обновить ARP-таблицу, ppp -- действовать как-то ещё). Если ни одна сеть не подходит, выдаётся сообщение об ошибке. В примере все пакеты, не предназначенные сетям 192.168.102.0/24, 10.13.0.0/15 и 127.0.0.0/8, отправляются на маршрутизатор по умолчанию с адресом 83.237.29.1. Первая же запись рассказывает, как добраться до этого маршрутизатора (точнее, до сети 83.237.29.1/32, что эквивалентно единственному абоненту 83.237.29.1).

Относительно IP-адресов на маршрутизаторе Гуревич как-то заметил, что только один из них -- 83.237.29.1 -- "настоящий". Он имел в виду стандарт RFC1918, описывающий, какие диапазоны IP-адресов можно использовать в любой внутренней сети. Задача системного администратора -- сделать так, чтобы при работе с сетью Internet ни в одном пакете не стояло такого внутреннего адреса отправителя: например, подменять внутренние адреса на единственный внешний ("настоящий"). Задача эта решается с помощью межсетевого экрана (firewall), который в Linux называется iptables, но когда Мефодий попросил Гуревича рассказать поподробнее, тот только рукой махнул: надо хорошо знать TCP/IP.

Служебный протокол ICMP

Есть такие протоколы уровня IP, действие которых этим уровнем и ограничиваются. Например, служебный протокол ICMP (Internet Control Message Protocol), предназначенный для передачи служебных сообщений. С однм примером применения ICMP Мефодий уже знаком: это утилита ping. Другое применение ICMP -- сообщать отправителю, почему его пакет невозможно доставить адресату, или передавать информацию об изменении маршрута, о возможности фрагментации и т. п. Протоколом ICMP пользуется утилита traceroute, позволяющая приблизительно определять маршрут следования пакета (ключ "-n", как и в команде route, означает, что преобразовывать IP-одреса в доменные имена не надо):

[root@localhost root]# traceroute www.ru -n   
 traceroute to www.ru (194.87.0.50), 30 hops max, 38 byte packets
  1  192.168.102.1  0.223 ms  0.089 ms  0.105 ms
  2  83.237.29.1  25.599 ms  21.390 ms  21.812 ms
  3  195.34.53.53  24.111 ms  21.213 ms  25.778 ms
  4  195.34.53.53  23.614 ms  33.172 ms  22.238 ms
  5  195.34.53.10  43.552 ms  48.731 ms  44.402 ms
  6  195.34.53.81  26.805 ms  21.307 ms  22.138 ms
  7  213.248.67.93  41.737 ms  41.565 ms  42.265 ms
  8  213.248.66.9  50.239 ms  47.081 ms  64.781 ms
  9  213.248.65.42  99.002 ms  81.968 ms  62.771 ms
 10  213.248.78.170  62.768 ms  63.751 ms  78.959 ms
 11  194.87.0.66  101.865 ms  88.289 ms  66.340 ms
 12  194.87.0.50  70.881 ms  67.340 ms  63.791 ms

Определения маршрута пакета

Утилита traceroute показывает список абонентов, через которых проходит пакет по пути к адресату, и потраченное на это время. Однако список этот приблизительный. Дело в том, что первому пакету (точнее, первым трём, так как по умолчанию traceroute шлёт пакеты по три) в специальное поле TTL (Time To Live, время жизни) выставляется значение "1". Каждый маршрутизатор должен уменьшать это значение на 1, и если оно обнулилось, передавать отправителю ICMP-пакет о том, что время жизни закончилось, а адресат так и не найден. Так что на первую серию пакетов отреагирует первый же маршрутизатор, и traceroute выдаст первую строку маршрута. Второй пакет посылается с TTL=2, и, если за две пересылки адресат не достигнут, об этом рапортует второй маршрутизатор. Процесс продолжается до тех пор, пока очередной пакет не "доживёт" до места назначения. Строго говоря, неизвестно, каким маршрутом шла очередная группа пакетов, потому что с тех пор, как посылалась предыдущая группа, какой-нибудь из промежуточных маршрутизаторов мог передумать и послать новые пакеты другим путём.

Транспортный уровень

Транспортных протоколов в TCP/IP два -- это TCP (Transmission Control Protocol, протокол управления соединением) и UDP (User Datagram Protocol). UDP устроен просто. Пользовательские данные помещаются в единственный транспортный пакет-датаграмму, которой приписываются обычные для транспортного уровня данные: адреса и порты отправителя и получателя, после чего пакет уходит в сеть искать адресата. Проверять, был ли адресат способен этот пакет принять, дошёл ли пакет до него и не испортился ли по дороге, предоставляется следующему -- прикладному -- уровню.

Иное дело -- TCP. Этот протокол очень заботится о том, чтоюы передаваемые данные дошли до адресата в целости и сохранности. Для этого предпринимаются следующие действия:

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

Обрабатываются подтверждения

Отслеживаются состояния абонентов

Кажется, что TCP -- протокол по всем статьям удобнее UDP. Однако в случаях, когда пользовательские данные всегда помещаются в один пакет, зато самих пакетов идёт очень много, посылать всего одну датаграмму намного выгоднее, чем всякий раз устанавливать соединение, пересылать данные и закрывать соединение (что как минимум требует по три пакета в каждую сторону). Очень трудно использовать TCP для широковещательных передач, когда число абонентов-адресатов весьма велико или вовсе неизвестно. Посмотреть параметры всех передаваемых через сетевой интерфейс пакетов можно с помощью команды tcpdump -pi интерфейс, хотя Мефодию не хватило поверхностного знания TCP/IP для того, чтобы понять выдачу этой команды.

Прикладной уровень

Как бы не был надёжен протокол TCP, он не имеет никакого понятия о том, что же, собственно, за данные с его помощью передаются. Да и не должен: принцип разделения уровней не позволяет заглядывать "внутрь" передаваемого пакета, и способов наверняка распознать используемый в нём прикладной протокол нет. Прикладной уровень, в отличие от транспортного, предусматривает сколько угодно протоколов передачи данных. Интерпретация данных, в конце концов, дело уже не ядра, а какой-нибудь программы ("приложения", как правило, демона). Для того, чтобы можно было предположить, какой протокол используется при передаче данных и чтобы система могла передать эти данные соответствующей программе, ещё на транспортном уровне было введено понятие порт.

Клиент-серверная модель

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

Служба, запускаясь на сервере, создаёт сетевой сокет и прикрепляет его к определённому порту сервера с помощью системного вызова bind(). Затем она регистрируется в качестве обработчика запросов (listener), приходящих на этот порт. Служба ждёт запросов, и когда они поступают, предпринимает какие-нибудь действия, например, считывает пришедшие данные и анализирует их в соответствии со своим протоколом, отсылает какие-то данные абоненту, пославшему запрос и т. п.

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

Как приложение узнаёт, к какому именно порту необходимо подключиться? За большинством прикладных протоколов закреплён постоянный номер порта. Постоянные номера портов и названия соответствущих протоколов хранятся в файле /etc/services:

[root@localhost root]# wc /etc/services                                                
  553  2794 19869 /etc/services
[root@localhost root]# egrep "^(ftp|http|smtp|ssh).*tcp" /etc/services 
 ftp             21/tcp                      # File Transfer [Control]
 ssh             22/tcp                      # SSH Remote Login Protocol
 smtp            25/tcp      mail            # Simple Mail Transfer Protocol
 http            80/tcp      www www-http    # World Wide Web HTTP

Постоянные номера портов для некоторых протоколов

Этот файл -- не догма, а руководство к действию: каждый может организовать, допустим, сервис HTTP по 25-му порту. Только как об этом узнают другие клиенты, и что подумают почтовые программы, ожидая по этому порту встретить сервис SMTP (пересылка почты)? Вывести список установленных соединений, а также служб-обработчиков можно командой netstat:

[root@localhost root]# netstat -anA inet
 Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address        Foreign Address         State      
 tcp        0      0 0.0.0.0:111          0.0.0.0:*               LISTEN      
 tcp        0      0 0.0.0.0:22           0.0.0.0:*               LISTEN      
 tcp        0      0 192.168.102.125:22   192.168.102.1:33208     ESTABLISHED
 udp        0      0 0.0.0.0:111          0.0.0.0:*

Просмотр установленных соединений и служб

Здесь видно, что на компьютере зарегистрировано два TCP-обработчика (на портах 111 и 22), один UDP-обработчик по 11-му порту (понятие Listener, то есть обработчик соединения для UDP не имеет смысла), а также установлено одно соединение с компьютера 192. 168. 102. 1, исходящий порт 33208, к 22-му порту (это порт службы Secure Shell, предоставляющей удалённый терминальный доступ... видимо, Гуревич работает?). В более сложных случаях, когда номер порта заранее неизвестен, а известно только название и версия сервиса, используется служба portmap, которая раздаёт незанятые порты службам и сообщает приложениям, к какому из них надо обратиться. Порт 111 соответстует именно этой службе.

Обслуживание прикладного уровня в Linux

Самый простой способ проверить, предоставляет ли некий сервер услуги по некоему TCP-порту -- это подключиться к нему. Если под рукой нет приложения, работающего по требуемому протоколу, не беда: подойдёт утилита telnet. В качестве первого параметра следует указать адрес компьютера, к которому нужно подключиться, а в качестве второго (необязательного) -- номер порта. Когда-то эта утилита использовалась в качестве клиента к терминальной службе, однако от неё пришлось отказаться: пароль пользователя передавался по сети нешифрованным. Но в качестве клиента других служб, многие из которых используют текстовые протоколы, telnet используется и поныне. Если даже протокол и не текстовый -- не беда: можно выйти в командный режим telnet, нажав "^[", и подать команду close, которая закроет соединение:

[root@localhost root]# telnet 192.168.102.1 112
 Trying 192.168.102.1...
 telnet: connect to address 192.168.102.1: Connection refused
[root@localhost root]# telnet 192.168.102.1 111
 Trying 192.168.102.1...
 Connected to 192.168.102.1.
 Escape character is '^]'.
 ^]
 telnet> close
 Connection closed.

Использование telnet

В сцераниях вместо интерактивной утилиты telnet стоит использовать netcat, которая работает как cat в указанный сокет или из него.

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

  1. Быть демоном, то есть постоянно находиться в памяти;

  2. Создавать сокет, прикреплять его к порту;
  3. Регистрироваться как обработчик по этому сокету и принимать соединения (возможно, придётся обрабатывать несклько соединений одновременно);
  4. Анализировать прикладной протокол и действовать по результатам анализа.

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

Мефодий минут за пять написал службу, которая в ответ на подключение передаёт календарь на текущий месяц. В его системе используется модернизированная версия inetd -- xinted, обученная чтению конфигурационных файлов по схеме ". d":

[root@localhost root]# grep quake /etc/services 
 quake           26000/tcp
 quake           26000/udp
[root@localhost root]# cat /etc/xinetd.d/calendar 
 service quake
 {
        socket_type     = stream
        protocol        = tcp
        wait            = no
        user            = nobody
        server          = /usr/bin/cal
        disable         = no
 }

Настройка cal в качестве сетевой службы

Вместо номера порта можно использовать название протокола из /etc/services. Мефодий воспользовался портом 26000 (чем мог создать некоторые трудности поклонникам одной компьютерной игры). Осталось только перезагрузить xinetd, чтобы он нашёл новый конфигурационный файл, и подключиться к 26000 порту:

[root@localhost root]# service xinetd restart
 Stopping xinetd service: [ DONE ]
 Starting xinetd service: [ DONE ]
[root@localhost root]# telnet localhost quake
 Trying 127.0.0.1...
 Connected to localhost.
 Escape character is '^]'.
    December 2004
 Su Mo Tu We Th Fr Sa 
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30 31

Подключение к самодельной службе "календарь"

Служба доменных имён

В предыдущих примерах Мефодий использовал ключ "-n" многих сетевых утилит, чтобы избежать путаницы между IP-адресами и доменными именами компьютеров. С другой стороны, доменные имена -- несколько слов (часто осмысленных)--запоминать гораздо удобнее, чем адреса (четыре каких-то числа).

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

Проще всего было структурировать сами имена компьютеров. Вся сеть была поделена на домены -- зоны ответственности отдельных государств ("us", "uk", "ru", "it" и т. п.) или независимые зоны ответственности ("com", "org", "net", "edu" и т. п.). Для каждого из таких доменов первого уровня должно присутствовать подразделение, выдающее всем желающим абонентам имена, заканчивающиеся на ".домен". Подразделение обязано огранизовать и поддерживать службу, заменяющую файл hosts: любой желающий имеет право узнать, какой IP-адрес соответствует имени компьютера в этом домене или какому доменному имени соответствует определённый IP-адрес.

Такая служба называется DNS (Domain Name Service, служба доменных имён). Она имеет иерархическую структуру. Если за какую-то группу абонентов домена отвечают не хозяева домена, а кто-то другой, ему выделяется поддомен (или домен второго уровня), и он сам распоряжается именами вида "имя_компьютера.поддомен.домен". Например, за компьютеры в домене ". ru", принадлещие корпорации "Dry Bugs" отвечаут сотрудники соответствующего подразделения этой корпорации. Корпорация владеет поддоменом dbugs.ru. В домене ru не обязаны знать, какие именно адреса есть в поддомене, но обязаны предоставить информацию о том, как обратиться к серверу имён поддомена dbugs.ru. Сами сотрудники "Dry Bugs" тоже отвечают только за несколько собственных компьютеров, а всю ответственность за компьютеры в подразделениях перекладывают на сетевых администраторов этих подразделений, выделив им поддомены третьего уровня pr.dbugs.ru, cook.dbugs.ru и warehouse.dbugs.ru. Таким образом получается нечто вроде распределённой сетевой базы данных, хранящей короткие записи о соответствии доменных имён IP-адресам.

доменное имя

Имя абонента Internet, имеющее текстовый формат и используемое вместо IP-адреса. Состоит из собственного имени абонента в домене и имени домена, определяющего административную принадлежность абонента. В отличие от IP-адреса, доменное имя не задаётся самим абонентом сети, а устанавливается службой доменных имён.

Все программы, работающие с доменными именами, оказываются клиентами какого-нибудь сервера доменных имён (DNS-сервера). Для этого они обращаются к функциям из библиотеки libresolv (или им подобным), а те уже определяют, как превратить доменное имя в адрес. Функции используют файл /etc/host.conf, описывающий, какими способами они должны выполнять преобразование доменных имён в адреса и обратно, а также формат выдачи данных в различных случаях. Обычно сначала проверяется файл /etc/hosts, а если там соответствий не найдено -- /etc/resosv.conf. В resolv.conf указан домен по умолчанию (он приписывается в конец имени, если другим способом имя никак не удаётся преобразовать в адрес) и один-два адреса DNS-серверов, к которым и обращаются функции. Такими клиентами выступили утилиты ping и traceroute в предыдущих примерах, преобразуя имя www.ru в адрес 194.87.0.50. Если утилите traceroute не указывать ключ "-n", она подаст несколько DNS-запросов, по одному на каждый маршрутизатор, на обратное преобразование -- из IP-адреса в доменное имя.

[root@localhost root]# cat /etc/host.conf 
 order hosts,bind
 multi on
[root@localhost root]# cat /etc/resolv.conf
 domain nipponman.ru
 nameserver 192.168.102.1
[root@localhost root]# traceroute -q1 www.ru
 traceroute to www.ru (194.87.0.50), 30 hops max, 38 byte packets
  1  fuji.nipponman.ru (192.168.102.1)  1.378 ms
  2  ppp83-237-29-1.pppoe.mtu-net.ru (83.237.29.1)  41.155 ms
  3  195.34.53.53 (195.34.53.53)  48.503 ms
  4  195.34.53.53 (195.34.53.53)  24.033 ms
  5  M9-cr01-A197-cr01.core.mtu.ru (195.34.53.10)  33.414 ms
  6  M9-gw2-M9-cr01.core.mtu.ru (195.34.53.81)  26.259 ms
  7  s-b3-pos0-0.telia.net (213.248.67.93)  59.791 ms
  8  s-bb1-pos5-0-0.telia.net (213.248.66.1)  67.011 ms
  9  mow-b1-pos1-0.telia.net (213.248.101.10)  76.138 ms
 10  demos-101566-mow-okt-i1.c.telia.net (213.248.78.170)  78.591 ms
 11  m9-3-GE4-0-0-vl10.Demos.net (194.87.0.66)  69.813 ms
 12  www.ru (194.87.0.50)  70.583 ms

Работа DNS-клиента, встроенного в traceroute

Как видно из примера, обратное преобразование в современной сети работает не всегда. Отсутствие обратной зоны не поощряется сообществом, но и не считается преступлением. Мефодий заметил, что компьютер, не имеющий обратного преобразования адреса, вообще какой-то странный: один раз он передал пакет сам себе (здесь Мефодий не совсем прав: на самом деле этот маршрутизатор отчего-то уменьшает TTL пакета на 2, поэтому-то и на третьем, и на четвёртом шаге именно он возвращает ICMP-сообщение). Кстати сказать, именно по причине того, что DNS-запрос невелик, зато даже один абонент сети может выдать их множество, основным транспортным протоколом для DNS выбран UDP, а не TCP (это не касается протоколов обмена целыми зонами между DNS-серверами, там, коначно, господствует TCP).

Если вся задача пользователя -- это послать DNS-запрос, то лучше воспользоваться утилитой host, специально для этого предназначенной:

methody@localhost:~ $ host www.ru      
 www.ru has address 194.87.0.50
methody@localhost:~ $ host 194.87.0.51
 51.0.87.194.in-addr.arpa domain name pointer www.demos-internet.ru.
methody@localhost:~ $ host -t ns www.ru
 www.ru name server ns.demos.su.
 www.ru name server ns1.demos.net.
methody@localhost:~ $ host -t mx www.ru
 www.ru mail is handled by 5 hq.demos.ru.

Утилита host

Довольно необычен формат, в котором хранятся таблицы обратного преобразования адресов. Оказывается, IP-адрес представлен в такой таблице как имя в домене in-addr.arpa, причём это имя совпадает с адресом, записанным задом наперёд. Такой формат идёт от иерархической структуры DNS. Если некоторая организация получает во владение сеть, допустим, 194.0.0.0/8, она должна обслуживать DNS-запросы к домену 194.in-addr.arpa. Если при этом сеть 194.87.0.0/16 передана другой организации, ей же передаётся обязанность обслуживать DNS-запросы к поддомену этого домена -- 87.194.in-addr.arpa, и так вплоть до собственно IP-адресов. Вместо host можно использовать утилиту dig, которая выводит больше информации о том, как проходил сам запрос.

Помимо записей типа "адрес" (A, прямое преобразование) и "указатель ни имя" (PTR, обратное преобразование) в системе DNS может храниться и другая информация. Таблица (зона) некоторого дамена должна содержать адреса доменных серверов всех его поддоменов (записи типа NS). Кроме того в домене должно быть определено имя почтового пересыльщика (запись типа MX). Если почтовый пересыльщик домена не указан, электронная почта направляется почтовому пересыльщику родительского домена.

В зоне DNS есть даже запись типа "просто текст", TXT: при желании можно самому определить интерпретацию полей этой записи и организвать поверх DNS предназначенную для каких угодно целей базу данных по IP-адресам или доменным именам(8). В отличие от dig, которой для запроса к обратной зоне требуется ключ "-x", утилита host различает запросы к прямой и обратной зонам по тому, задан ли в качестве параметра адрес или доменное имя. Для указания конкретного типа искомой записи обеим утилитам требуется этот тип передать явно. Тип any означает поиск всех записей с указанным именем:

methody@localhost:~ $ dig www.us any

 ; <<>> DiG 9.2.4rc5 <<>> www.us any
 ;; global options:  printcmd
 ;; Got answer:
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6451
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 0, ADDITIONAL: 4

 ;; QUESTION SECTION:
 ;www.us.                                IN      ANY

 ;; ANSWER SECTION:
 www.us.                 1766    IN      A       209.173.57.26
 www.us.                 1766    IN      A       209.173.53.26
 www.us.                 1767    IN      NS      pine.neustar.com.
 www.us.                 1767    IN      NS      willow.neustar.com.
 www.us.                 1767    IN      NS      cypress.neustar.com.
 www.us.                 1767    IN      NS      oak.neustar.com.
 www.us.                 1771    IN      MX      20 pine.neustar.com.
 www.us.                 1771    IN      MX      5 oak.neustar.com.
 www.us.                 1771    IN      MX      5 willow.neustar.com.
 www.us.                 1771    IN      MX      10 cypress.neustar.com.

 ;; ADDITIONAL SECTION:
 pine.neustar.com.       135024  IN      A       209.173.57.70
 willow.neustar.com.     135024  IN      A       209.173.53.84
 cypress.neustar.com.    135024  IN      A       209.173.57.84
 oak.neustar.com.        135024  IN      A       209.173.53.70

 ;; Query time: 932 msec
 ;; SERVER: 192.168.102.1#53(192.168.102.1)
 ;; WHEN: Wed Dec 22 22:01:24 2004
 ;; MSG SIZE  rcvd: 281

Утилита dig


(1) Ethernet с коаксиальным кабелем имеет топологию "общая шина": все абоненты подключаются к единому кабелю, "врезая" в него Т-образный отводок; ethernet с витой парой имеет топологию "звезда": от каждого абонента идёт собственный кабель к центральному устройству-концентратору.

(2) Организация TCP/IP с помощью почтовых голубей описана в RFC1149.

(3) Поэтому, если в книге написано, что TPC/IP имеет четыре уровня, это тоже будет правдой -- с учётом двойственности самого нижнего.

(4) Время ожидания не удваивается а выбирается, опять-таки случайно, но из другого временного диапазона.

(5) Применяется побитовая операция "И".

(6) Значение "MTU: 1492" наводит на мысль о том, что в качестве среды передачи данных был использован именно ethernet (с MTU 1500), так как ещё восемь байтов отводится для служебной информации самого PPP.

(7) Обратная ситуация, когда клиент хочет передать что-то серверу, сути дела не меняет: сервер предоставляет услугу клиенту, на этот раз -- по приёму данных.

(8) Так поступают, например, при создании "чёрных списков" абонентов сети, от которых почтовый сервер не принимает писем.