глава 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 маршрутизацией. на рисунке 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 никогда не генерируется в ответ на:
рисунок 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
первое, на что стоит обратить внимание в выводе программы, это то, что значение, возвращенное от svr4, неверно. это произошло из-за того, что svr4 возвращает общую маску подсети класса в, подразумевающую, что деление на подсети не осуществлено, даже несмотря на то что интерфейс svr4 сконфигурирован с правильной маской подсети:
svr4 % ifconfig emd0
это одна из известных ошибок svr4 возникающая при обработке icmp запросов маски адреса. мы рассмотрим обмен пакетами для хоста bsdi, используя tcpdump. вывод показан на рисунке 6.5. была использована опция -e, которая позволяет посмотреть аппаратные адреса.
1 0.0
8:0:20:3:f6:42 ff:ff:ff:ff:ff:ff ip 60:
рисунок 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 geminiorig = 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 gatewayorig = 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 миллисекунды. другие возможные варианты существуют другие способы получить время и дату.
sun % telnet bsdi daytime сервер времени возвращает 32-битное двоичное значение, содержащее количество секунд после полуночи 1 января 1900 года, utc. (команда rdate, которую мы упоминали ранее, использует сервис времени tcp.)
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
команда 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
рисунок 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, соответствующее создавшемуся условию. некоторые из ошибок мы рассмотрим в следующих главах.
рисунок 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, эту команду мы будем довольно часто использовать в следующих главах. упражнения
|