Так случилось, что Мефодий мало что знал о компьютерных сетях до знакомства с Linux. Если пользоваться только www-броузером и почтовой программой, сведений, вроде «у каждого компьютера Internet есть имя, на компьютерах бывает почта и WWW», бывает вполне достаточно. Строго говоря, если сеть настроена, почтовые клиенты или броузеры Linux не требуют большего объёма знаний. Однако Linux хорош именно тем, что позволяет проследить работу сети от процедур самого низкого уровня, вроде поведения сетевых карт, до приложений высокого уровня и их протоколов.
В разговоре о сетях передачи данных понятие «уровень» возникает неспроста. Дело в том, что передача данных между компьютерами — сложный процесс, в котором решается сразу несколько разноплановых задач. Если представить себе весь процесс организации сети «на пустом месте», как если бы никаких сетевых разработок доныне не было, все эти задачи встают одна за другой, по мере решения предыдущей.
Итак, если бы Мефодий получил задание «придумать Internet» на пару с Гуревичем, какие бы вопросы перед ними встали?
Ответы на эти вопросы в формализованном виде носят название протоколов: в них пунктуально описывается, как именно предлагается решать ту или иную задачу, какая для этого потребна дополнительная информация, какова должна быть логика поведения передающей и принимающей стороны и т. п.
В приведённом делении на этапы (уровни) примечательна их относительная независимость: если группа задач, связанная с некторым уровнем, решена, на следующем уровне можно забыть, как именно решались эти задачи. Так, устройство передачи данных типа «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
— относится к уровню интерфесному.
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» имеет отношение к алгоритму, с помощью которого решается задача очерёдности передачи. Алгоритм базируется на трёх принципах:
Приведённый алгоритм обладает двумя недостатками. Во-первых, уже на интерфейсном уровне время передачи одного пакета может быть любым, так как неопределённое промедление с передачей предусмотрено протоколом. Во-вторых, сеть 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-таблица отражает соответствие между 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.
Есть такие протоколы уровня 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 соответстует именно этой службе.
Самый простой способ проверить, предоставляет ли некий сервер услуги по некоему 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
в указанный сокет или из него.
Как уже говорилось, интерпретацией прикладных протоколов занимаются разнообразные программы. Прикладной протокол можно представить как обмен сообщениями, часто текстовыми, между клиентом и сервером. Было бы естественно оформлять такие программы в виде фильтров, чтобы пользоваться простейшими функциями ввода-вывода. Однако механизм сокетов предусматривает асинхронную передачу данных, для чего используются другие функции. Программа, желающая обслуживать сетевые соединения по определённому порту, должна удовлетворять четырём требованиям:
Нетрудно заметить, что первые три свойства — общие для большинства сервисов. В 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-адресам.
Все программы, работающие с доменными именами, оказываются клиентами какого-нибудь сервера доменных имён (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
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
1Ethernet с коаксиальным кабелем имеет топологию «общая шина»: все абоненты подключаются к единому кабелю, «врезая» в него Т-образный отводок; ethernet с витой парой имеет топологию «звезда»: от каждого абонента идёт собственный кабель к центральному устройству-концентратору.
2Организация TCP/IP с помощью почтовых голубей описана в RFC1149.
3Поэтому, если в книге написано, что TPC/IP имеет четыре уровня, это тоже будет правдой — с учётом двойственности самого нижнего.
4Время ожидания не удваивается а выбирается, опять-таки случайно, но из другого временного диапазона.
5Применяется побитовая операция «И».
6Значение «MTU: 1492» наводит на мысль о том, что в качестве среды передачи данных был использован именно ethernet (с MTU 1500), так как ещё восемь байтов отводится для служебной информации самого PPP.
7Обратная ситуация, когда клиент хочет передать что-то серверу, сути дела не меняет: сервер предоставляет услугу клиенту, на этот раз — по приёму данных.
8Так поступают, например, при создании «чёрных списков» абонентов сети, от которых почтовый сервер не принимает писем.