Приложение A Программа tcpdump
Программа tcpdump была написана Van Jacobson, Craig Leres и Steven McCanne, во время их работы в лаборатории Lawrence Berkeley, Калифорнийского университета, Беркли. В тексте этой книги используется версия 2.2.1 (июнь 1992 года).
Программа tcpdump разработана таким образом, чтобы переводить сетевую плату в смешанный режим (promiscuous mode), при этом, каждый пакет, проходящий по кабелю, фиксируется. Обычно сетевые платы для сред передачи, таких как Ethernet, захватывают только фреймы канального уровня, адресованные конкретному интерфейсу или отправленные на широковещательный адрес (глава 2, раздел "Ethernet и IEEE 802 инкапсуляция").
Операционная система должна позволить поместить интерфейс в смешанный режим и позволить пользовательскому процессу захватывать фреймы. Реализации tcpdump существуют для следующих Unix систем: 4.4BSD, BSD/386, SunOS, Ultrix и HP-UX. Просмотрите файл README, который поставляется вместе с дистрибутивом tcpdump, где подробно описано, как в данном случае функционирует операционная система и какие версии поддерживаются.
Существуют альтернативы для tcpdump. На рисунке 10.8 мы использовали программу snoop, поставляемую в составе Solaris 2.2, чтобы просмотреть некоторые пакеты. В AIX 3.2.2 есть программа iptrace, которая предоставляет подобные возможности.
В современных реализациях ядра BSD существует пакетный фильтр BSD (BPF - BSD Packet Filter), именно его использует tcpdump, чтобы отлавливать и фильтровать пакеты из сетевой платы (которая помещена в смешанный режим). BPF также работает с каналами точка-точка, такими как SLIP (глава 2, раздел "SLIP: IP по последовательной линии") (при этом не требуется специальных настроек), и с интерфейсом loopback (глава 2, раздел "Интерфейс loopback").
BPF имеет долгую историю. Пакетный фильтр Enet был создан в 1980 году Mike Accetta и Rick Rashid в университете Carnegie Mellon. Jeffrey Mogul из Stanford перенес этот код в BSD и продолжил его разработку с 1983 года. С тех пор туда же был включен Ultrix Packet Filter на DEC, STREAMS NIT модуль на SunOS 4.1, и BPF. Steven McCanne из лаборатории Lawrence Berkeley Laboratory начал работать с BPF летом 1990 года. Однако большая часть разработки принадлежит Van Jacobson. Подробности о последней версии и сравнения с Sun's NIT даны в [McCanne and Jacobson 1993].
На рисунке А.1 показаны характеристики BPF, когда он используется в случае Ethernet.
Рисунок А.1 Пакетный фильтр BSD.
BPF помещает драйвер Ethernet устройства в смешанный режим и затем получает от драйвера копию каждого полученного и отправленного пакета. Эти пакеты проходят через фильтр, указанный пользователем, таким образом, только те пакеты, которые интересуют пользователя, попадают в обработку.
Несколько процессов могут наблюдать за указанным интерфейсом, и каждый процесс использует свой собственный фильтр. На рисунке А.1 показаны пример работы tcpdump и демона RARP (глава 5, раздел "Реализация RARP сервера"), оба наблюдают за одним и тем же Ethernetом. При каждом появление tcpdump используется собственный фильтр. Фильтр для tcpdump может быть указан пользователем в командной строке, тогда как rarpd всегда использует один и тот же фильтр для отлова только RARP запросов.
Помимо указанного фильтра, каждый пользователь BPF также указывает значение тайм-аута. Так как скорость данных в сети может легко переполнить мощности процессора, и так как не очень эффективно осуществлять маленькие чтения из ядра, BPF старается упаковать несколько фреймов в один буфер чтения и возвратить только тогда, когда буфер полон или истек тайм-аут, установленный пользователем. tcpdump устанавливает тайм-аут в 1 секунду, так как он обычно получает от BPF много данных, тогда как демон RARP получает немного фреймов, поэтому rarpd устанавливает тайм-аут в 0 (фрейм возвращается при получении).
Фильтр, указанный пользователем, сообщает BPF, какие фреймы необходимо обрабатывать. Эти инструкции интерпретируются фильтром BPF в ядре. Фильтрация в ядре, а не в пользовательском процессе, уменьшает количество данных, которые должны быть переданы от ядра пользовательскому процессу. Демон RARP всегда использует одну и ту же программу фильтрации, которая встроена в программу. tcpdump, с другой стороны, позволяет пользователю указать выражение фильтрации в командной строке при каждом запуске. tcpdump конвертирует выражение, указанное пользователем, в соответствующую последовательность инструкций для BPF. В качестве примера tcpdump можно привести выражение:
% tcpdump tcp port 25
В первом случае печатаются только TCP сегменты с портом источника или назначения равным 25. Во втором случае печатаются только ICMP сообщения, которые не являются эхо запросами или эхо откликами (не ping пакеты). Эти выражения указывают, что первый байт ICMP сообщения (поле type на рисунке 6.2) не должен быть равен 8 или 0, что соответствует эхо запросам или эхо откликам на рисунке 6.3. Как Вы можете видеть, грамотная фильтрация требует знаний структуры пакета. Выражение во втором примере, заключено в одиночные кавычки, что предотвращает от интерпретации командным интерпретатором Unix (shell) специальных символов.
Обратитесь к страницам помощи tcpdump(1), где приведены детали о выражениях, которые могут быть указаны пользователем. В страницах помощи bpf(4) приведены инструкции виртуальной машины, используемой BPF. [McCanne and Jacobson 1993] сравнивает реализацию и производительность этой машины с другими подходами.
Краник в сетевом интерфейсе SunOS
SunOS 4.1.x предоставляет потоковый (STREAMS) драйвер псевдоустройства, который называется краник в сетевом интерфейсе (NIT - Network Interface Tap) . ([Rago 1993] содержит дополнительные детали о драйверах потоковых устройств. Мы будем называть эту характеристику "потоками" (streams).) NIT напоминает пакетный фильтр BSD, однако он не такой мощный и эффективный. На рисунке А.2 показаны потоковые модули, которые используются в NIT. Одно отличие между этим рисунком и рисунком А.1 заключается в том, что BPF может захватывать пакеты, принятые и переданные с сетевого интерфейса, тогда как NIT захватывает только пакеты, полученные с этого интерфейса. С использованием tcpdump с NIT мы можем видеть только пакеты, отправленные другими хостами в сеть - мы никогда не увидим пакеты, отправленные нашим собственным хостом. (Несмотря на то, что BPF работает с SunOS 4.1.x, он требует изменения исходного кода для Ethernet драйвера устройства, что невозможно для большинства пользователей, которые не имеют доступа к исходным кодам.)
Когда устройство /dev/nit открыто, потоковый драйвер nit_if открыт. Так как NIT построен с использованием потоков, обрабатывающие модули могут быть помещены поверх драйвера nit_if. tcpdump помещает модуль nit_buf в STREAM. Этот модуль собирает вместе несколько фреймов из сети в один буфер чтения, а пользовательский процесс устанавливает значение тайм-аута. Это напоминает то, что мы описали в случае BPF. Демон RARP не помещает этот модуль в свой поток, так как он работает с небольшим количеством пакетов.
Рисунок А.2 Краник в сетевом интерфейсе SunOS.
Фильтрация, указанная пользователем, осуществляется потоковым модулем nit_pf. На рисунке А.2 этот модуль используется демоном RARP, однако не используется tcpdump. Вместо этого под SunOS tcpdump осуществляет свою собственную фильтрацию в пользовательском процессе. Причина этого в том, что инструкции виртуальной машины, используемые nit_pf, отличаются (а также они не такие мощные) от тех, которые поддерживаются BPF. Это означает, что когда пользователь указывает выражение фильтрации tcpdump, больше данных пересекают границу между ядром к пользователю с использованием NIT, чем при использовании BPF.
Поставщик интерфейса канального уровня в SVR4
SVR4 поддерживает поставщика интерфейса канального уровня (DLPI - Data Link Provider Interface), который является потоковой реализацией OSI Data Link Service Definition. Большинство версий SVR4 все еще поддерживают версию 1 DLPI, SVR4.2 поддерживает обе версии 1 и 2, а Sun's Solaris 2.x поддерживают версию 2 с дополнительными расширениями.
Программы мониторинга сети, такие как tcpdump, должны использовать DLPI для символьного доступа к драйверам устройств канального уровня. В Solaris 2.x модуль потокового пакетного фильтра был переименован в pfmod, а модуль буфера переименован в bufmod.
Несмотря на то, что Solaris 2.x это достаточно новая система, вскоре должна появиться реализация tcpdump. В Sun есть программа, которая называется snoop, осуществляющая функции, напоминающие функции tcpdump. (snoop заменяет программу, работающую в SunOS 4.x, которая называется etherfind.)
Вывод команды tcpdump - "символьный" (raw). Мы будем модифицировать и дополнять его, чтобы сделать вывод более читаемым.
Во-первых, tcpdump всегда вводит имя сетевого интерфейса, который "слушает". Мы удалим эту строку.
Затем вывод времени, осуществляемый tcpdump, осуществляется в формате 09:11:22.642008 для систем с точностью до микросекунд, или в формате 09:11:22.64 для систем с точностью часов до 10 миллисекунд. (В приложении В мы расскажем более подробно о точности компьютерных часов.) В любом случае формат HH:MM:SS это не то, что мы хотим. В данном случае хочется видеть относительное время для каждого пакета с момента начала мониторинга сети (dump), а также разницу во времени между последовательно приходящими пакетами. Мы модифицировали вывод, чтобы показать эти временные показатели. Первый показатель мы показываем в виде шести цифр справа от десятичной точки, когда можно получить точность разрешения времени в микросекундах (в случае 10-миллисекундной точности показано две цифры), а второй показатель мы показываем в виде четырех или двух цифр справа от десятичной точки (в зависимости от точности часов).
В этом тексте большинство вывода команды tcpdump было получено на хосте sun, который имеет точность часов до одной микросекунды. Некоторый вывод был получен на хосте bsdi, на котором работает операционная система BSD/386 Version 0.9.4, и который предоставляет 10-миллисекундную точность часов (рисунок 5.1). Некоторый вывод был также получен на хосте bsdi, который работает под управлением операционной системы BSD/386 Version 1.0, которая предоставляет микросекундную точность.
tcpdump всегда печатает имя отправляющего хоста, затем знак "больше", затем имя хоста назначения. Из-за этого довольно сложно отслеживать поток пакетов между двумя хостами. Так как в нашем случае вывод tcpdump часто выглядит подобным образом, мы строим на его основе временную диаграмму. (Впервые это было сделано в тексте на рисунке 6.11.) В наших временных диаграммах один хост находится слева, а другой справа. При этом значительно легче увидеть, какая сторона посылает, а какая получает каждый пакет.
Мы добавляем номера строк в вывод команды tcpdump, что позволяет делать сноски на конкретные строки в тексте. Мы также добавляем дополнительные пробелы между строками, чтобы отделить один пакет от другого.
И в завершение, вывод tcpdump может не помещаться в страницу. Мы обрезаем длинные строки, для того чтобы их удобней было читать.
В качестве примера можно показать вывод, осуществленный командой tcpdump, соответствующий рисунку 4.4, который показан на рисунке А.3, при этом мы считаем, что используется окно терминала с 80 колонками.
Мы не показываем ввод символа прерывания (который прекращает работу tcpdump) и не показываем количество пакетов, полученных, но отброшенных. (Отброшенные пакеты это те, которые прибывают быстрее, чем их может обработать tcpdump. Так как примеры в тексте обычно запускаются в довольно свободных сетях, эта величина всегда равна 0.)
sun % tcpdump -e tcpdump: listening on le0 09:11:22.642008 0:0:c0:6f:2d:40 ff:ff:ff:ff:ff:ff arp 60: arp who-has svr4 tell bsdi 09:11:22.644182 0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 arp 60: arp reply svr4 is-at 0:0:c0:c2:9b:26 09:11:22.644839 0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip 60: bsdi.1030 > svr4.discard: S 596459521:596459521 (0) win 4096 <mss 1024> [tos 0x10] 09:11:22.649842 0:0:c0:c2:9b:26 0:0:c0:6f:2d:40 ip 60: svr4.discard > bsdi.1030: S 3562228225:3562228225 (0) ack 596459522 win 4096 <mss 1024> 09:11:22.651623 0:0:c0:6f:2d:40 0:0:c0:c2:9b:26 ip 60: bsdi.1030 > svr4.discard: . ack 1 win 4096 [tos 0x10] ^? 9 packets received by filter 0 packets dropped by kernel
Рисунок А.3 Вывод команды tcpdump для рисунка 4.4.
Необходимо обратить внимание на то, что просмотр сетевого траффика позволяет нам увидеть многие вещи, которые мы видеть вообще-то не должны. Например, пароли, печатаемые пользователями для приложений, таких как Telnet или FTP, передаются по сети именно так, как их ввел пользователь. (Это называется представлением пароля в виде открытого текста (cleartext), в отличие от зашифрованного представления (encrypted). Именно в зашифрованном виде пароли хранятся в файле паролей Unix, обычно /etc/passwd или /etc/shadow.) Тем не менее, существует множество моментов, когда администратору сети необходимо использовать средство, подобное tcpdump, чтобы диагностировать проблемы в сети.
Мы используем tcpdump как обучающее средство, чтобы посмотреть, что в действительности передается по сети. Доступ к tcpdump и подобным ей утилитам зависит от системы (и может быть от воли системного администратора). В случае SunOS, например, доступ к NIT устройству разрешен только суперпользователю. Пакетный фильтр BSD использует другую технику: доступ контролируется доступом к устройству /dev/bpfXX. Обычно эти устройства может читать и писать только владелец (который должен быть суперпользователем), а также они доступны для чтения членам группы (группа системного администратора). Это означает, что обычный пользователь не может запускать программы, подобные tcpdump, без санкции на то системного администратора (например, с помощью setUID).
Еще один способ посмотреть, что проходит в TCP соединении, это включить опцию отладки сокета, естественно на системах, которые поддерживают эту характеристику. Эта характеристика работает только с TCP (она не работает с другими протоколами) и требует поддержки приложения (чтобы включить опцию сокета, когда оно стартует).
Большинство реализаций Berkeley поддерживают это, включая SunOS, 4.4BSD и SVR4.
Программа включает опцию сокета, а ядро затем осуществляет записи того, что происходит для этого соединения. Затем эта информация может быть получена путем запуска программы trpt(8). Чтобы включить опцию отладки сокета, не требуется специальных прав доступа, однако необходимо иметь специальные привилегии, чтобы запустить trpt, так как она осуществляет доступ к памяти ядра.
Наша программа sock (приложение С) поддерживает эту характеристику с опцией -D, однако информацию, которую можно получить в выводе, сложнее описать и понять, нежели соответствующий вывод команды tcpdump. Тем не менее, мы использовали эту программу в разделе "Пример RTT" главы 21, чтобы посмотреть переменные ядра в TCP соединении, к которому tcpdump не имел доступа.