Аннотация |
В этом документе содежится информация по установке и настройке в Linux 2.2.12 перехвата IP-соединений, при помощи системы divert-сокетов, входящей в комплект FreeBSD. |
Copyright 1999(c) by Ilia Baldine. This document may be distributed only subject to the terms and conditions set forth in the LDP License at, except that this document must not be distributed in modified form without the author's consent.
Авторские права на русский перевод этого текста принадлежат ╘ 2000 ASPLinux Все права зарезервированы.
Этот документ является частью проекта Linux HOWTO.
Авторские права на документы Linux HOWTO принадлежат их авторам, если явно не указано иное. Документы Linux HOWTO, а также их переводы, могут быть воспроизведены и распространены полностью или частично на любом носителе, физическом или электронном, при условии сохранения этой заметки об авторских правах на всех копиях. Коммерческое распространение разрешается и поощряется; но, так или, иначе автор текста и автор перевода желали бы знать о таких дистрибутивах.
Все переводы и производные работы, выполненные по документам Linux HOWTO должны сопровождаться этой заметкой об авторских правах. Это делается в целях предотвращения случаев наложения дополнительных ограничений на распространение документов HOWTO. Исключения могут составить случаи получения специального разрешения у координатора Linux HOWTO, с которым можно связаться по адресу приведенному ниже.
Мы бы хотели распространить эту информацию по всем возможным каналам. Но при этом сохранить авторские права и быть уведомленными о всех планах распространения HOWTO. Если у вас возникли вопросы, пожалуйста, обратитесь к координатору проекта Linux HOWTO по электронной почте: <linux-howto@metalab.unc.edu> или к координатору русского перевода Linux HOWTO компании ASPLinux по адресу <linux-howto@asplinux.ru>
Этот документ был разработан в рамках проекта по обеспечению безопасности компьютерных сетей, финансируемого агентством DARPA. Ни я (Ilia Baldine), ни мой работодатель (MCNC), ни DARPA не несут ответственности за нанесенный или потенциальный ущерб, который может вызвать применение программы и/или использование процедур, описанных в документе. Как и множество других сетевых приложений, divert-сокеты могут использоваться как в законной плоскости, так и незаконной, а выбор этой плоскости остается за вами!
Here's an easy game to play, Here's an easy thing to say: If a packet hits a pocket on a socket on a port And the bus is interrupted as a very last resort, And the address of the memory makes your floppy disk abort, Then the socket packet pocket has an error to report!! If your cursor finds a menu item followed by a dash, And the double clicking icon puts your window in the trash, And your data is corrupted 'cause the index doesn't hash, Then the situation's hopeless, and your system's gonna crash! YOU CAN'T SAY THIS? WHAT A SHAME SIR! WE'LL FIND ANOTHER GAME SIR If the label on the cable on the table at your house, Says the network is connected to the button on your mouse, But your packets want to tunnel on another protocol, That's repeatedly rejected by the printer down the hall, And your screen is all distorted by the side effects of gauss So your icons in the window are as wavy as a souse, Then you may as well reboot and go out with a bang, 'Cause as sure as I'm a poet, the sucker's gonna hang! When the copy of your floppy's getting sloppy on the disk And the microcode instructions cause unnecessary risc, Then you have to flash your memory and you'll want to RAM your ROM Quickly turn off your computer and be sure to tell your mom! -- Anonymous |
Вам, наверное. не раз хотелось перехватить пакеты, проходящие туда-сюда через вашу машину? Нет, я не говорю о простом просмотре пакетов в натуральном виде (как в raw-сокетах или libpcap (tcpdump)). Я говорю именно о том, чтобы перехватить пакет и, затем, возможно, после некоторой модификации, переслать его дальше. Ну что ж, время, когда это было простой мечтой, закончилось - теперь у нас есть divert-сокеты для Linux!
Divert-сокеты именно это и делают - они отфильтровывают пакеты, в соответствии с настройками firewall, и передают их вашей программе. Затем вы можете: просто отправить пакет дальше, модифицировать и отправить, или не пересылать его вообще.
Как вы наверно уже догадались по названию пакета, этот механизм включает в себя использование специальных RAW-сокетов, называемых divert (IPPROTO_DIVERT), позволяющих вам производить передачу и прием данных на них таким же образом, как и на обычных сокетах. Единственное различие состоит в том, что этот сокет привязан к порту - на него можно пересылать конкретные пакеты, попадающие в firewall. Любые данные, проходящие через firewall, могут быть посланы в этот сокет.
Эта система изначально входила в состав FreeBSD. Divert-сокеты для Linux - это адаптированная версия исходного пакета, претендующая на совместимость с ним (по крайней мере на уровне исходных текстов программ, использующих этот механизм).
Для использования divert-сокетов в Linux, вам понадобятся две вещи - модифицированные патчем исходные тексты ядра и исходные тексты пакета ipchains версии 1.3.9, также модифицированные соответствующим патчем.
Оба патча можно взять с веб-сайта divert-сокетов http://www.anr.mcnc.org/~divert Для ядра существуют два варианта - патч к неизмененным исходным текстам ядра версии 2.2.12, или готовые модифицированные исходные тексты ядра 2.2.12 (значительно большие по размеру, чем патч). ipchains находится там же, в виде уже модифицированного пакета с исходными текстами.
Собрать ipchains достаточно просто - дайте команду
make |
Для включения поддержки divert-сокетов, вам надо пересобрать ядро, предварительно настроив его командой:
make config или make menuconfig или make xconfig |
Чтобы использовать divert-сокеты, вам надо включить в ядро поддержку firewall и IP-firewall. На работу divert-сокетов влияют три опции сборки ядра:
Включает поддержку divert-сокетов в ядре.
Определяет поведение правил DIVERT: по умолчанию правило DIVERT, описанное в firewall, отбрасывает пакеты, при отсутствии программы на порте, определенном этим правилом, то есть действует аналогично правилу DENY.
При включении данной опции, эти пакеты будут проходить дальше через firewall. Опцию можно использовать, если вам нужно статическое правило в firewall, но вы не хотите, чтобы с портом divert-сокетов постоянно работала ваша программа.
Определяет, выполнять ли дефрагментирование при передаче данных в сокет. По умолчанию код divert-сокет получает отдельные фрагменты пакетов, имеющих больший, чем MTU, размер и посылает их в таком же виде программе. Задача дефрагментации в этом случае лежит на приложении, использующем divert-сокеты. Более того, приложение не может послать фрагмент пакета, больший, чем MTU - он сразу будет отброшен (это ограничение ядра, а не divert-сокетов - ядра Linux версии до 2.2.x НЕ фрагментируют пакеты с установленной опцией IP_HDRINCL). Обычно в таком поведении нет ничего страшного - в основном, вы просто пересылаете те же фрагменты пакетов, которые получили, и все прекрасно работает - в этом случае, размер фрагментов не будет больше MTU.
Если вы включите опцию always defragment, то все дефрагментирование будет производиться в ядре. Это сильно уменьшает производительность механизма перехвата - каждый большой пакет, который вы хотите перехватить, должен быть сначала собран из фрагментов, и только после этого будет передан вашей программе. Затем, когда вы захотите послать его дальше, он будет снова разбит на части (если в ядре включить эту опцию, то оно будет фрагментировать пакеты, имеющие флаг IP_HDRINCL)
В ядрах Linux версии 2.0.36 подобный выбор не предоставлялся из-за неправильной структуры кода firewall - он обрабатывал только первый фрагмент пакета, а с остальными фрагментами поступал так же, как и с первым, не обрабатывая их. В результате этого, если первый фрагмент был отброшен firewall, то и все остальные отбрасывались дефрагментатором. Поэтому для нормальной работы с divert-сокетами в этой версии ядра вы ДОЛЖНЫ были использовать опцию always defragment для того, чтобы получать весь пакет, а не только его первый фрагмент.
В версии 2.2.12 структура кода firewall была исправлена, и вы можете сами решать, будет ли ядро заниматься (де)фрагментацией, или вы будете делать этой в своей программе.
ВНИМАНИЕ: в версии 1.0.4 divert-сокетов функция дефрагментирования не реализована. Работа над этим продолжается.
В этой главе обсуждаются возможности применения divert-cокетов, и чем они отличаются от других существующих механизмов перехвата соединений.
Существуют другие пакеты, позволяющие производит перехват IP-пакетов. Ниже описано, чем они отличаются от divert-сокетов:
Сокеты Netlink могут перехватывать IP-пакеты так же, как и divert sockets - используя firewall. Для них существует специальный тип (AF_NETLINK) и с первого взгляда они ничем не отличаются от divert. Но, на самом деле, существуют два серьезных отличия:
В системе Netlink-сокетов нет портов, поэтому очень сложно иметь несколько процессов, перехватывающих разные потоки данных (в divert-сокетах встроено стандартное 16-битное пространство портов - соответственно, у вас может работать параллельно до 65535 процессов, перехватывающих разные потоки данных)
В системе Netlink-сокетов не существует простого способа посылать пакеты дальше (обратно в сеть), потому что в нее не встроена защита от повторного перехвата этих же пакетов. В divert-сокетах эта проверка производится автоматически
Использование RAW-сокетов - неплохой способ прослушивания сетевого потока (особенно в Linux, в котором RAW-сокеты могут прослушивать и TCP, и UDP-трафик - многие другие UNI*-ы этого не позволяют), но RAW-сокет не может помешать пакету продолжить свой путь до получателя - он просто предоставляет вам копию пакета. Нет никакого способа послать модифицированный пакет дальше, потому что исходный пакет не останавливается. Более того, вы можете фильтровать пакеты только по номеру протокола, который задается при открытии RAW-сокета. RAW-сокеты никоим образом не взаимодействуют с firewall.
Библиотека libpcap, и в частности ее наиболее известная утилита tcpdump, позволяет прослушивать трафик, идущий через сетевой интерфейс (это может быть ppp, eth, и т.п.). В ethernet вы можете включить на своей сетевой карте режим " promisc", и она станет обрабатывать весь IP-трафик, идущий по сети, а не только адресованные ей пакеты. Конечно, в libpcap не встроено способов перехвата пакетов, и нет способа их послать. На самом деле, libpcap и divert-сокеты служат для разных целей - их нельзя сравнивать.
В Linux существует три потока пакетов: входящий (input), исходящий (output) и проходящий (forward). Существуют также учетные потоки, но они нас не интересуют. В зависимости от происхождения пакета, он проходит через один или несколько из следующих потоков:
через него проходят все пакеты, попадающие в машину извне - пакеты, предназначенные для этой машины, и пакеты, которые будут переданы дальше.
через него проходят все пакеты, отправленные этой машиной, а также пересылаемые пакеты
через него проходят все пакеты, пересылаемые этой машиной.
Переадресованный пакет проходит через все три потока в следующем порядке:
Входящий
Проходящий
Исходящий
Для полной ясности надо придерживаться следующего правила - проходящий поток должен использоваться только для отбрасывания пакетов, не предназначенных для этой машины, и не посланных ей. Если вам интересны и пересылаемые пакеты и пакеты, относящиеся к этой машине, - используйте входящий или исходящий потоки. Перехват однотипных пакетов на входящем и исходящем потоках не только создаст проблемы с пересылкой, но и, что более важно, в этом просто нет необходимости.
Модифицированная версия ipchains, которую вы возьмете на указанном выше веб-сайте - это утилита, позволяющая изменять правила firewall из командной строки. Существует также способ задать эти правила из программы. В примере программы перехвата будет использоваться именно этот способ - настройка правила DIVERT аналогична настройке правила REDIRECT - указываете DIVERT в роли получателя, номер divert-порта, и у вас все готово.
Синтаксис команды ipchains для настройки правил firewall не изменился. Для использования правила DIVERT, вы должны использовать опцию -j DIVERT <port num> в роли получателя, а все остальное остается без изменений. Например команда
ipchains -A input -p ICMP -j DIVERT 1234 |
В следующей главе мы опишем, как использовать ipchains совместно с программой перехвата пакетов.
Здесь приведен пример программы, которая читает пакеты с divert-сокета, выводит их содержимое на экран и затем пересылает дальше. В командной строке ей надо указать номер divert-порта для перехвата.
#include <stdio.h> #include <errno.h> #include <limits.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <getopt.h> #include <netdb.h> #include <netinet/in.h> #include <sys/types.h> #include <signal.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/udp.h> #include <net/if.h> #include <sys/param.h> #include <linux/types.h> #include <linux/icmp.h> #include <linux/ip_fw.h> #define IPPROTO_DIVERT 254 #define BUFSIZE 65535 char *progname; #ifdef FIREWALL char *fw_policy="DIVERT"; char *fw_chain="output"; struct ip_fw fw; struct ip_fwuser ipfu; struct ip_fwchange ipfc; int fw_sock; /* удаляем все существующие правила firewall */ void intHandler (int signo) { if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_DELETE, &ipfc, sizeof(ipfc))==-1) { fprintf(stderr, "%s: невозможно удалить правило: %s\n", progname, strerror(errno)); exit(2); } close(fw_sock); exit(0); } #endif int main(int argc, char** argv) { int fd, rawfd, fdfw, ret, n; int on=1; struct sockaddr_in bindPort, sin; int sinlen; struct iphdr *hdr; unsigned char packet[BUFSIZE]; struct in_addr addr; int i, direction; struct ip_mreq mreq; if (argc!=2) { fprintf(stderr, "Использование: %s <port number>\n", argv[0]); exit(1); } progname=argv[0]; fprintf(stderr,"%s:Создание сокета\n",argv[0]); /* открываем divert-сокет */ fd=socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT); if (fd==-1) { fprintf(stderr,"%s:Невозможно открыть divert-сокет\n",argv[0]); exit(1); } bindPort.sin_family=AF_INET; bindPort.sin_port=htons(atol(argv[1])); bindPort.sin_addr.s_addr=0; fprintf(stderr,"%s:Подключение сокета\n",argv[0]); ret=bind(fd, &bindPort, sizeof(struct sockaddr_in)); if (ret!=0) { close(fd); fprintf(stderr, "%s: Ошибка bind(): %s",argv[0],strerror(ret)); exit(2); } #ifdef FIREWALL /* сначала заполняем поля правила */ bzero(&fw, sizeof (struct ip_fw)); fw.fw_proto=1; /* ICMP */ fw.fw_redirpt=htons(bindPort.sin_port); fw.fw_spts[1]=0xffff; fw.fw_dpts[1]=0xffff; fw.fw_outputsize=0xffff; /* заполняем структуру fwuser */ ipfu.ipfw=fw; memcpy(ipfu.label, fw_policy, strlen(fw_policy)); /* заполняем структуру fwchange */ ipfc.fwc_rule=ipfu; memcpy(ipfc.fwc_label, fw_chain, strlen(fw_chain)); /* открываем сокет */ if ((fw_sock=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1) { fprintf(stderr, "%s: невозможно создать raw-сокет: %s\n", argv[0], strerror(errno)); exit(2); } /* записываем правило */ if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_APPEND, &ipfc, sizeof(ipfc))==-1) { fprintf(stderr, "%s невозможно установить правило firewall: %s\n", argv[0], strerror(errno)); exit(2); } /* устанавливаем обработчик сигнала для удаления правила */ signal(SIGINT, intHandler); #endif /* FIREWALL */ printf("%s: Ожидание данных...\n",argv[0]); /* читаем данные */ sinlen=sizeof(struct sockaddr_in); while(1) { n=recvfrom(fd, packet, BUFSIZE, 0, &sin, &sinlen); hdr=(struct iphdr*)packet; printf("%s: Содержимое пакета:\n",argv[0]); for( i=0; i<40; i++) { printf("%02x ", (int)*(packet+i)); if (!((i+1)%16)) printf("\n"); }; printf("\n"); addr.s_addr=hdr->saddr; printf("%s: Адрес отправителя: %s\n",argv[0], inet_ntoa(addr)); addr.s_addr=hdr->daddr; printf("%s: Адрес получателя: %s\n", argv[0], inet_ntoa(addr)); printf("%s: IF-адрес получателя: %s\n", argv[0], inet_ntoa(sin.sin_addr)); printf("%s: Номер протокола: %i\n", argv[0], hdr->protocol); /* пересылка */ #ifdef MULTICAST if (IN_MULTICAST((ntohl(hdr->daddr)))) { printf("%s: Multicast-адрес!\n", argv[0]); addr.s_addr = hdr->saddr; errno = 0; if (sin.sin_addr.s_addr == 0) printf("%s: set_interface вернул %i ошибку номер =%i\n", argv[0], setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)), errno); } #endif #ifdef REINJECT printf("%s Пересылка DIVERT %i байт\n", argv[0], n); n=sendto(fd, packet, n ,0, &sin, sinlen); printf("%s: переслано %i байт.\n", argv[0], n); if (n<=0) printf("%s: Ошибка номер %i\n", argv[0], errno); if (errno == EBADRQC) printf("errno == EBADRQC\n"); if (errno == ENETUNREACH) printf("errno == ENETUNREACH\n"); #endif } } |
Вы можете просто скопировать эту программу и откомпилировать ее. Если вы хотите разрешить пересылку - соберите ее с флагом -DREINJECT, в противном случае, она будет только перехватывать пакеты.
Чтобы использовать эту программу, соберите ядро и ipchains-1.3.8 (как это сделать, описано выше. Установите правило в любой из потоков firewall: входящий, исходящий или проходящий, затем пошлите пакеты, соответствующие правилу, и смотрите их содержимое, которое будет появляться на экране. После этого пакеты будут посылаться дальше, если вы использовали соответствующую опцию при компиляции.
Например, после команды:
ipchains -A output -p TCP -s 172.16.128.10 -j DIVERT 4321 interceptor 4321 |
Если вы не использовали опцию pass-through при сборке ядра, то добавление в первой строчке правила firewall приведет к тому, что пакеты, отвечающие этому правилу, будут отбрасываться, если нет программы перехвата. Подробнее читайте выше.
Если вы хотите, чтобы правило firewall задавалось вашей программой - соберите ее с опцией -DFIREWALL, и она будет перехватывать все ICMP-пакеты из входящего потока. Она также автоматически удалит правило DIVERT по окончании работы. В этом случае опция pass-through ядра не влияет на поведение программы.
По моему мнению, область применения divert-сокетов может быть ограничена лишь пределами вашего воображения. Мне будет очень интересно услышать о программах, использующих divert-сокеты.
Удачи!
После перехвата пакета, вы можете изменить его содержимое перед тем, как переслать его дальше. Вам надо просто помнить о следующих правилах:
Перед пересылкой должна пересчитываться контрольная сумма в заголовке IP-пакета
Поле IP ID будет заполняться за вас, если вы оставите его равным 0.
Обновление длины пакета зависит от вас.
Абсолютно необязательно перехватывать пакет для того, чтобы послать его дальше. Вы можете сформировать свои собственные пакеты и послать их через открытый подключенный divert-сокет. Правила заполнения заголовка аналогичны тем, что описаны в предыдущем разделе.
В дополнение к этому, вы должны будете передать divert-сокету структуру sockaddr_in (см. программу), в которой будет описываться, куда передать пакет. Если вы заполните эту структуру нулями или передадите вместо нее NULL, то divert-сокет будет пытаться передать пакет наружу. Если же вы укажете в структуре sockaddr_in адрес одного из сетевых интерфейсов, то divert-сокет попытается послать пакет "внутрь" машины, как будто он пришел извне. Все адреса, конечно, должны быть заданы в соответствии с порядком байт в сетевом адресе.
Пересылка пакетов, которые выглядят так, как будто переадресованы вашей машиной, должны включать в себя адрес входящего интерфейса (на самом деле, подойдет любой правильный адрес интерфейса).
Как уже было упомянуто выше, divert-сокеты не поддерживают (де)фрагментацию перехваченных пакетов - вы всегда получаете фрагменты пакетов в том виде, в котором они были в сети, и вы не можете переслать пакет, имеющий размер больше PMTU. Предполагается, что поддержка фрагментации и дефрагментации пакетов будет реализована в ближайшем будущем.
Как уже было упомянуто выше, более подробная информация о divert-сокетах находится на веб-сайте Divert-сокетов для Linux http://www.anr.mcnc.org/~divert.
Существует список рассылки, который можно найти на веб-сайте. Чтобы подписаться на этот список, отправьте письмо с пустой темой, написав в нем:
subscribe divert |
Чтобы отказаться от подписки, пошлите по адресу anr-majordomo@list.anr.mcnc.org письмо с пустой темой, написав в нем:
unsubscribe divert |
Как уже было упомянуто в разделе "Ответственность", работа над divert-сокетами производится в рамках проекта разработки систем безопасности сетей, финансируемого DARPA. Мы продолжим работу по адаптации будущих версий ядра к divert-сокетам по мере возможности. По той причине, что ядра версии 2.4 уже недалеко, мы полностью пропустим серию ядер 2.3.x.