Raw sockets � linux
Уровень IPv4 генерирует заголовок IP при посылке пакета, если только для сокета не включён параметр IP_HDRINCL. Если он включён, то пакет должен содержать заголовок IP. Принимаемые пакеты всегда содержат заголовок IP.
Открывать неструктурированные сокеты могут только процессы с идентификатором эффективного пользователя, равным 0, или имеющие мандат CAP_NET_RAW.
Все пакеты или ошибки, совпадающие с номером протокола protocol, указанным неструктурированному сокету, передаются этому сокету. Список допустимых протоколов назначается списком IANA и содержится в getprotobyname(3).
Протокол IPPROTO_RAW подразумевает включённый параметр IP_HDRINCL и позволяет отправлять любой протокол IP, который указан в передаваемом заголовке. Приём любых протоколов IP посредством IPPROTO_RAW через неструктурированные сокеты невозможен.
Поля заголовка IP, изменяемые IP_HDRINCL при передаче | |
IP Checksum | всегда заполняется |
Source Address | заполняется, если значение равно нулю |
Packet Id | заполняется, если значение равно нулю |
Total Length | всегда заполняется |
Если указан IP_HDRINCL и заголовок IP имеет ненулевой адрес назначения, то для маршрутизации пакета используется адрес назначения сокета. Если указан MSG_DONTROUTE, то адрес назначения должен ссылаться на локальный интерфейс, иначе в любом случае будет производиться поиск в таблице маршрутизации, но маршруты со шлюзами будут игнорироваться.
Если IP_HDRINCL не указан, то параметры заголовка IP в неструктурированных сокетах могут быть установлены с помощью setsockopt(2); более подробная информация приведена в ip(7).
Начиная с Linux 2.2 все поля заголовка IP и параметры могут быть заданы с помощью параметров сокета IP. Это означает, что неструктурированные сокеты, как правило, необходимы лишь для новых протоколов или протоколов без интерфейса пользователя (например, ICMP).
Принятый пакет передаётся всем подключенным (bound) к этому протоколу неструктурированным сокетам до того, как он будет передан другим обработчикам протоколов (например, протокольным модулям ядра).
Формат адреса
Параметры сокета
Кроме этого, поддерживаются все датаграммные параметры сокетов ip(7) для IPPROTO_IP.
Обработка ошибок
ОШИБКИ
ВЕРСИИ
Код неструктурированных сокетов в версии Linux 2.0 при установленном параметре SO_BSDCOMPAT был специально сделан совместимым с BSD «вплоть до ошибок». Из Linux 2.2 это было удалено.
ЗАМЕЧАНИЯ
Неструктурированный сокет может быть подключён к определённому локальному адресу с помощью вызова bind(2). Если он не подключён, то принимаются все пакеты указанного протокола IP. Кроме того, неструктурированный сокет может быть подключён к определённому сетевому устройству с помощью SO_BINDTODEVICE; смотрите socket(7).
Сокет IPPROTO_RAW предназначен только для отправки. Если вы хотите получать все пакеты IP, используйте сокет packet(7) с протоколом ETH_P_IP. Заметим, что пакетные сокеты, в отличие от неструктурированных сокетов, не собирают пакеты из фрагментов IP.
Если вы хотите получать все пакеты ICMP для датаграммного сокета, то лучше использовать IP_RECVERR на определённом сокете; смотрите ip(7).
Неструктурированные сокеты могут перехватывать в Linux все протоколы IP, даже имеющие протокольный модуль в ядре (такие, как ICMP или TCP). В этом случае пакеты передаются как модулю ядра, так и неструктурированным сокетам. На это нельзя закладываться в переносимых программах, так как многие другие реализации сокетов BSD так не делают.
Linux никогда не изменяет заголовки, полученные от пользователя (за исключением обнуления некоторых полей, как описано в случае с IP_HDRINCL). Такое поведение отличается от поведения многих других реализаций неструктурированных сокетов.
Неструктурированные сокеты, в общем случае, не являются переносимыми, поэтому следует избегать их использования в переносимых программах.
Передача через неструктурированный сокет должна осуществляться с помощью протокола IP из sin_port; эта возможность исчезла в Linux 2.2. Метод обхода — использовать IP_HDRINCL.
ДЕФЕКТЫ
Если установлен параметр IP_HDRINCL, датаграммы не будут фрагментированы и их размер будет ограничен MTU интерфейса.
В Linux 2.2 отсутствует настройка протокола IP для отправки sin_port — всегда используется тот протокол, к которому был подключён сокет или который был указан при первом вызове socket(2).
Источник
Using Linux Raw Sockets
Sid Shanker — May 28, 2018
In an effort to learn how TCP/IP works, I decided to start playing around with a low-level TCP/IP library, smoltcp. Some of the examples (particularly, the clone of tcpdump ) require using raw sockets, which need to be run as the root user. Running network programs as root is pretty dangerous, and is something that one should probably never do.
In this post, I’ll be discussing how to use raw sockets without having to run the whole program as root. And while the library I’m working with is written in Rust, I’ll be using examples written in C.
Why does it mean to access a raw socket?
Sockets are the means by which programs on Linux way talk to the internet. The socket system call creates a file descriptor that can be written to and read from. The connect system call can then be used to connect the socket to some remote address. After that, writing to the socket sends data to that remote address, while reading from the socket file descriptor reads data sent from the remote address.
There are two main types of sockets, stream sockets, and datagram sockets. I won’t get into the details of these now, but streaming sockets are used for applications that use TCP, while datagram sockets are for applications that use UDP. These are both transport-level protocols that follow the network-level IP protocol.
If you are interested in writing your own implementations of one of these protocols, or need to use a different transport-layer protocol, you’ll need to use raw sockets. Raw sockets operate at the network OSI level, which means that transport-level headers such as TCP or UDP headers will not be automatically decoded. If you are implementing a a transport-level protocol, you’ll have to write code to decode and encode the transport-level headers in your application.
Raw Sockets and Security
An important thing worth noting about this is that there is important security-related information stored in TCP and UDP headers:
For instance, the destination port is stored in the TCP headers. This means that packets read from raw sockets don’t have any notion of “port”.
To step back a little bit, an application using TCP or UDP must, when opening up a STREAM or DGRAM socket, declare a port to receive data on. When the application reads data from that socket, they will only see data that was sent to that particular port.
Now, with raw sockets, because network-level IP packets do not have a notion of “port”, all packets coming in over the server’s network device can be read. Applications that open raw sockets have to do the work of filtering out packets that are not relevant themselves, by parsing the TCP headers. The security implications of this are pretty serious–it means that applications with a raw socket open can read any inbound network packets, including those headed to other applications running on the system, which may or may not be run as the same unix user as the application with the raw socket open.
To prevent this from happening, Linux requires that any program that accesses raw sockets be run as root. Actually running network programs as root is dangerous, especially for a sufficiently complicated program, like a TCP implementation.
In this post I’ll cover a couple different strategies for circumventing this.
Attempting to read from a raw socket
To see what happens when we read data from sockets, let’s use this C program that simply sniffs packets entering the system and prints out some information about the packet as an example:
Источник
Настоящее sock_raw’вище
Raw сокеты предоставляют программисту более широкие возможности по сравнению с остальной частью сокетного API. Все знают об этих «широких» возможностях, но более или менее систематизированное их описание встречается в Интернете нечасто. Попробуем, восполнить данный пробел и разобраться с предназначением raw cокетов и вариантами их применения в сетевом ПО.
Итак, raw сокеты — особый вид сокетов, доступный при использовании системного вызова типа:
Не удержусь и сделаю небольшое отступление по поводу возможных аргументов в вызове 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).
При отправке данных через 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 сокеты незаменимы при написании сетевых сканеров. К примеру, мы можем использовать вызов типа:
Далее нам остается только сконструировать TCP-сегмент и передать его на отправку raw сокету. Не забываем так же и о возможности подделки source IP-адреса, которая в данном контексте придется очень кстати. Аналогичным образом, можно писать программы для фаззинга сетевого стека ядра. К примеру, конструируемые пакеты транспортного уровня могут содержать заведомо невалидные значения в нужных нам полях.
При использовании протокола TCP через raw сокет стоит понимать следующую вещь. Мы не можем полностью воссоздать работу протокола TCP, да у нас это и не получится, исходя из возможностей, предоставляемых raw сокетами. Максимум, что мы сможем сделать — это принимать входящий трафик этого типа (в Linux) и отправлять отдельно взятые исходящие TCP-сегменты, якобы являющиеся частью установленного или устанавливаемого TCP-соединения.
Наконец, стоит упомянуть еще один аспект использования raw сокетов. Существуют протоколы, работа которых контролируется, главным образом, самим ядром, например ICMP и IGMP. В ряде случаев, доступ к этим протоколам из пользовательского приложения может быть весьма полезен:
Именно так написаны такие утилиты как ping, классический tracert (ICMP), а также утилиты, позволяющие манипулировать подпиской по IGMP.
Таким образом, собрав воедино все факты, можно попытаться сделать вывод о предназначении raw сокетов. Данный механизм пригодится нам, если нужно реализовать:
- приложение, которое будет «спуфить» source IP-адрес в отправляемых дейтаграммах
- приложение для посылки отдельно взятых исходящих TCP-сегментов и UDP-дейтаграмм (находит применение в сетевых сканерах)
- приложение которое сможет захватывать входящий трафик некоторого типа (включая IP-заголовок)
- приложение для фаззинга сетевого стека ядра
- приложение, которое работает со служебными протоколами, которые в остальных случаях контролируются самим ядром (ICMP, IGMP и т.п.)
Сложно себе представить, что функционал, предоставляемый raw сокетами, был изначально призван поощрить написание софта для проведения сетевых атак. Скорее обоснованием необходимости данного механизма послужили достаточно мирные цели. Однако возможности raw сокетов, в конечном счете, нашли применение и в хакерском софте.
Ссылки на полезные ресурсы по теме
Источник