Сокеты в ОС Linux

a85e29d05acc130319b3cf7b277de2c0

В данной статье будет рассмотрено понятие сокета в операционной системе Linux: основные структуры данных, как они работают и можно ли управлять состоянием сокета с помощью приложения. В качестве практики будут рассмотрены инструменты netcat и socat.

Что такое сокет?

Сокет — это абстракция сетевого взаимодействия в операционной системе Linux. Каждому сокету соответствует пара IP-адрес + номер порта. Это стандартное определение, к которому привыкли все, спасибо вики. Хотя нет, вот здесь лучше описано. Поскольку сокет является только лишь абстракцией, то связка IP-адрес + номер порта — это уже имплементация в ОС. Верное название этой имплементации — «Интернет сокет». Абстракция используется для того, чтобы операционная система могла работать с любым типом канала передачи данных. Именно поэтому в ОС Linux Интернет сокет — это дескриптор, с которым система работает как с файлом. Типов сокетов, конечно же, намного больше. В ядре ОС Linux сокеты представлены тремя основными структурами:

  1. struct socket — представление сокета BSD, того вида сокета, который стал основой для современных «Интернет сокетов»;

  2. struct sock — собственная оболочка, которая в Linux называется «INET socket»;

  3. struct sk_buff — «хранилище» данных, которые передает или получает сокет;

Как видно по исходным кодам, все структуры достаточно объемны. Работа с ними возможна при использовании языка программирования или специальных оберток и написания приложения. Для эффективного управления этими структурами нужно знать, какие типы операций над сокетами существуют и когда их применять. Для сокетов существует набор стандартных действий:

  • socket — создание сокета;

  • bind — действие используется на стороне сервера. В стандартных терминах — это открытие порта на прослушивание, используя указанный интерфейс;

  • listen — используется для перевода сокета в прослушивающее состояние. Применяется к серверному сокету;

  • connect — используется для инициализации соединения;

  • accept — используется сервером, создает новое соединение для клиента;

  • send/recv — используется для работы с отправкой/приемом данных;

  • close — разрыв соединения, уничтожение сокета.

Если о структурах, которые описаны выше, заботится ядро операционной системы, то в случае команд по управлению соединением ответстсвенность берет на себя приложение, которое хочет пересылать данные по сети. Попробуем использовать знания о сокетах для работы с приложениями netcat и socat.

netcat

Оригинальная утилита появилась 25 лет назад больше не поддерживается. На cегодняшний день существуют порты, которые поддерживаются различными дистрибутивами: Debian, Ubuntu, FreeBSD, MacOS. В операционной системе утилиту можно вызвать с помощью команды nc, nc.traditional или ncat в зависимости от ОС. Утилита позволяет из коробки работать с сокетами, которые используют в качестве транспорта TCP и UDP протоколы. Примеры сценариев использования, которые, по мнению автора, наиболее интересны:

  • перенаправление входящих/исходящих запросов;

  • трансляция данных на экран в шестнадцатеричном формате.

Опробуем операции в действии. Задача будет состоять в том, что необходимо отправить TCP данные через netcat в UDP соединение. Для лабораторной будет использоваться следующая топология сети:

ea777279258b5f1e7a55bbadc99d180d.png

Проведем трансляцию:

  1. Введем команду на открытие порта на машине Destination: nc -u lvvp 7878

  2. Настроим машину Repeater. Так как передача из одного интерфейса этой машины будет происходить по протоколу TCP, а на другой интерфейс будет осуществляться передача по протоколу UDP, то для таких действий необходимо сделать соединитель, который сможет накапливать данные и пересылать их между открытыми портами. На такую роль отлично подходит FIFO файл. Поэтому команда для запуска будет выглядеть так: sudo mkfifo /tmp/repeater #создать FIFO файл sudo nc -l -p 4545 < /tmp/repeater | nc -u -l 10.0.3.5 7878 > tmp/repeater IP адрес 10.0.3.5 — адрес машины Destination. Символы »|» и »><" представляют собой пайп и редирект данных соответственно. Функция предоставляется оболочкой терминала.

  3. Запускаем соединение из машины Source: nc 10.0.2.4 4545

В итоге получаем возможность читать данные от машины Source:

01f3bc299965907b19e1c787ae20b6da.png

В машине Destination:

52f48d3691d26d0bd61bbe2e86db470a.png

Пример с трансляцией данных в шестнадцатеричном формате можно провести так же, но заменить команду на Destination или добавить еще один пайп на Repeater:

nc -l -p 4545 -o file

В результате будет создан файл, в котором можно будет обнаружить передаваемые данные в шестнадцатеричном формате:

2ba0156b62ada2e94388a22a1ca5f9af.png

Как видно из тестового сценария использования, netcat не дает контролировать практически ничего, кроме направления данных. Нет ни разграничения доступа к ресурсам, которые пересылаются, ни возможности без дополнительных ухищрений работать с двумя сокетами, ни возможности контролировать действия сокета. Протестируем socat.

socat

Инструмент, который до сих пор поддерживается и имеет весьма обширный функционал по склейке каналов для взаимодействия. Разработчиками инструмент именуется как netcat++. Ниже приведем небольшой список того что можно перенаправить через socat:

  • STDIO → TCP Socket;

  • FILE → TCP Socket;

  • TCP Socket → Custom Application;

  • UDP Socket → Custom Application;

  • Socket → Socket.

Для повседневного использования достаточно опций, но если понадобится когда-то работать напрямую с серийным портом или виртуальным терминалом, то socat тоже умеет это делать. Полный перечень опций можно вызвать с помощью команды:

socat -h

Помимо редиректов socat также можно использовать как универсальный сервер для расшаривания ресурсов, через него можно как через chroot ограничивать привелегии и доступ к директориям системы.

Чтобы комфортно пользоваться этим инструментом, нужно запомнить шаблон командной строки, который ожидает socat:

socat additionalOptions addr1 addr2

  • additionalOptions — опции, которые могут добавлять возможности логирования информации, управления направлением передачи данных;

  • addr1 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;

  • addr2 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;

Попробуем провести трансляцию данных из сокета в сокет. Будем использовать для этого 1 машину. Перед началом эксперимента стоит отметить, что особенностью socat является то, что для его корректной работы нужно обязательно писать 2 адреса. Причем адрес не обязательно должен быть адресом, это может быть и приложение, и стандартный вывод на экран.

Например, чтобы использовать socat как netcat в качестве TCP сервера, можно запустить вот такую команду:

socat TCP-LISTEN:4545, STDOUT

Для коннекта можно использовать netcat:

nc localhost 4545

При таком использовании, socat дает возможность пересылать сообщения в обе стороны, но если добавить флаг »-u», то общение будет только от клиента к серверу. Все серверные сообшения пересылаться не будут:

406ce650e271616c28f4089ab52b5279.png

Настроим более тонко наш сервер, добавив новые опции через запятую после используемого действия:

socat TCP-LISTEN:4545,reuseaddr,keepalive,fork STDOUT

Дополнительные параметры распространяются на те действия, которые socat может выполнять по отношению к адресу. Полный список опций можно найти здесь в разделе «SOCKET option group».

Таким образом socat дает практически полный контроль над состоянием сокетов и расшариваемых ресурсов.

Статья написана в преддверии старта курса Network engineer. Basic. Всех, кто желает подробнее узнать о курсе и карьерных перспективах, приглашаем записаться на день открытых дверей, который пройдет уже 4 февраля.

© Habrahabr.ru