Настоящее sock_raw’вище

в 9:53, , рубрики: linux, network, socket, Блог компании Smart-Soft, метки: , ,

image

Raw сокеты предоставляют программисту более широкие возможности по сравнению с остальной частью сокетного API. Все знают об этих «широких» возможностях, но более или менее систематизированное их описание встречается в Интернете нечасто. Попробуем, восполнить данный пробел и разобраться с предназначением raw cокетов и вариантами их применения в сетевом ПО.

Итак, raw сокеты — особый вид сокетов, доступный при использовании системного вызова типа:

int socket = socket(AF_INET, SOCK_RAW, IPPROTO_*);

Не удержусь, и сделаю небольшое отступление по поводу возможных аргументов в вызове socket и их концептуального значения. Все мы знаем, что сокетный API задумывался как генерализированный интерфейс программирования, т.е. интерфейс, посредством которого можно будет работать с самыми различными механизмами сетевого, а то и просто внутрихостового взаимодействия. Первый аргумент вызова выбирает т.н. коммуникационный домен. Создатели сокетного API отвели под межсетевое взаимодействие на базе TCP/IP (IPv4) коммуникационный домен AF_INET, который мы и указываем в нашем вызове. Второй аргумент задает тип сокета — сокеты типизируются в соответствии с семантикой передачи данных, которую они обеспечивают (SOCK_STREAM — потоковая передача, SOCK_DGRAM — дейтаграммная передача). В нашем случае используется тип сокета SOCK_RAW — наверное это единственное отступление от правил, когда второй аргумент означает не столько семантику передачи, сколько доступ к специальному низкоуровневому функционалу. Наконец, третий аргумент задает протокол. Список констант, большинство из которых можно использовать в качестве третьего аргумента в нашем вызове, можно посмотреть в файле /usr/src/linux-*/include/linux/in.h, где * — версия ядра. Наиболее часто используются константы IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP, IPPOROTO_IGMP, IPPROTO_RAW.

Когда начинаешь работать с raw сокетами, сразу замечаешь, по крайней мере, две их особенности — то как осуществляется инкапсуляция данных и наличие доступа к IP-заголовкам. Посылаемые с помощью raw сокета данные инкапсулируются непосредственно в поле полезной нагрузки IP-дейтаграммы, минуя транспортный уровень. Что касается заголовков, то тут удобно выделить две ситуации – отправка и прием дейтаграммы. Пользовательское приложение, использующее raw сокеты, всегда получает IP-дейтаграмму целиком, включая её заголовок. При этом все необходимые проверки валидности полей заголовка дейтаграммы все равно осуществляются ядром, и приложение никогда не получит через raw сокет дейтаграмму, в которой указана, к примеру, невалидная версия протокола IP (т.е. версия отличная от IPv4).

image

При отправке данных через raw сокет у программиста есть выбор – переложить задачу создания IP заголовков на ядро или создавать заголовки самому. Второе возможно при использовании сокетной опции IP_HDRINCL (плюс в Linux есть ситуация, когда данная опция будет включена автоматически, если в качестве третьего аргумента в вызове socket() использовать константу IPPROTO_RAW). Однако, даже в случае использования опции IP_HDRINCL ядро все равно будет “ассистировать” программисту. Так, поля заголовка дейтаграммы Total Length и Header Checksum всегда автоматически заполняются ядром, а поля Source Address и Identification будут заполнены ядром, если программист оставит их значения равными нулю. Возможность создать свой заголовок дейтаграммы используется в основном для отправки пакетов с «проспуфленным» source IP-адресом.

Raw сокеты могут использоваться как несколько ограниченный в своих возможностях механизм packet capture. Ограничен он по ряду причин. Во-первых, через raw сокет можно получать только копию входящего пакета, но не генерируемого данной машиной и не транзитируемого (маршрутизируемого). Во-вторых, через raw сокет можно получать только IP-дейтаграммы с заданным Protocol ID (Protocol ID соответствует третьему аргументу в системном вызове socket(), использовавшимся при создании сокета), а не весь IP-трафик. Наконец, отличительной особенностью raw сокетов во FreeBSD (но не Linux) является то, что мы сможем получить IP-дейтаграмму, только если ядро не знает как ее обрабатывать (исключение — пакеты протоколов ICMP и IGMP, о которых скажем ниже). Здесь имеется ввиду, что в ядре не реализован код, предназначенный для обработки IP-дейтаграммы с указанным в ней Protocol ID. Также, интересен следующий факт — если создано более одного raw сокета с аналогичными параметрами, каждый сокет будет получать по собственной копии входящего пакета.

Raw сокеты незаменимы при написании сетевых сканеров. К примеру, мы можем использовать вызов типа:

int socket = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);

Далее нам остается только сконструировать TCP-сегмент и передать его на отправку raw сокету. Не забываем так же и о возможности подделки source IP-адреса, которая в данном контексте придется очень кстати. Аналогичным образом, можно писать программы для фаззинга сетевого стека ядра. К примеру, конструируемые пакеты транспортного уровня могут содержать заведомо невалидные значения в нужных нам полях.

При использовании протокола TCP через raw сокет стоит понимать следующую вещь. Мы не можем полностью воссоздать работу протокола TCP, да у нас это и не получится, исходя из возможностей, предоставляемых raw сокетами. Максимум, что мы сможем сделать — это принимать входящий трафик этого типа (в Linux) и отправлять отдельно взятые исходящие TCP-сегменты, якобы являющиеся частью установленного или устанавливаемого TCP-соединения.

Наконец, стоит упомянуть еще один аспект использования raw сокетов. Существуют протоколы, работа которых контролируется, главным образом, самим ядром, например ICMP и IGMP. В ряде случаев, доступ к этим протоколам из пользовательского приложения может быть весьма полезен:

int socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
int socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);

Именно так написаны такие утилиты как ping, классический tracert (ICMP), а также утилиты, позволяющие манипулировать подпиской по IGMP.

Таким образом, собрав воедино все факты, можно попытаться сделать вывод о предназначении raw сокетов. Данный механизм пригодится нам, если нужно реализовать:

  • приложение, которое будет «спуфить» source IP-адрес в отправляемых дейтаграммах
  • приложение для посылки отдельно взятых исходящих TCP-сегментов и UDP-дейтаграмм (находит применение в сетевых сканерах)
  • приложение которое сможет захватывать входящий трафик некоторого типа (включая IP-заголовок)
  • приложение для фаззинга сетевого стека ядра
  • приложение, которое работает со служебными протоколами, которые в остальных случаях контролируются самим ядром (ICMP, IGMP и т.п.)

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

Ссылки на полезные ресурсы по теме

SOCK_RAW Demystified
http://sock-raw.org/papers/sock_raw

4.2bsd Networking Implementation Notes (Revised July, 1983)
http://www.eecs.berkeley.edu/Pubs/TechRpts/1983/CSD-83-146

Linux man pages, SOCKET(2)
http://man7.org/linux/man-pages/man2/socket.2.html

Linux man pages, SOCKET(7)
ttp://man7.org/linux/man-pages/man7/socket.7.html

Linux man pages, RAW(7)
http://man7.org/linux/man-pages/man7/raw.7.html

Автор: Denis_msk

Источник

Поделиться

* - обязательные к заполнению поля