TcpDumper: DIY устройство для исследования сети Ethernet

в 12:00, , рубрики: Raspberry Pi, никто не читает теги
TcpDumper: DIY устройство для исследования сети Ethernet - 1

Привет! Решил представить тут небольшое и дешёвое устройство, которое на данный момент умеет:
1. Пинговать заданный IP
2. Смотреть пакеты с помощью tcpdump
3. Сканировать сеть через ARP

А главное, всё это работает полностью автономно на Raspberry PI 2 без GUI и потребляет всего 50 Мб ОЗУ!
В статье будет: описание самого устройства и идеи; описание различных приколов и багов в процессе разработки; инструкция как сделать такое самому; и вопрос знатокам, нужно ли это вообще кому-нибудь :)

Как я к этому пришел

В процессе разработки коммутатора на работе я столкнулся с тем, что было бы прикольно иметь небольшое настольное устройство для, например, генерации пакетов с заданными QoS метками; пакетов с таким-то VLAN'ом, или просто для просмотра "идут пакеты или нет".

Постепенно в голове начали формироваться следующие требования к устройству:
1. Дешевизна (забегая вперед, это устройство получилось в районе 20$)
2. Наличие собственного экрана (3.5 дюйма SPI экран с резистивным тач-скрином)
3. Наличие линукса на борту
4. И один или несколько портов Ethernet.

Я долго искал одноплатники с двумя портами Ethernet, но находил только дорогие, заточенные под создание собственного маршрутизатора. В итоге остановился на Raspberry pi 2B, потому что во-первых, она была в наличии, а во-вторых, не возникнет проблем с подключением экрана (думал он). Плюс, при желании добавить один порт можно просто купить дешевый переходник USB - Ethernet, открывая кучу возможностей.

Описание возможностей

Итак, что имеем сейчас в получившемся устройстве (подчеркиваю, это только proof-of-concept):

  • Можно пингануть другое устройство в сети.

  • Можно посмотреть пакеты, приходящие на интерфейс.

  • Можно узнать какие девайсы есть в сети. При этом устройство посылает arp-запросы на маску /24 (да, она захардкожена. PoC же), и слушает ответы, генерируя список соответствия IP-адреса MAC-адресу.

  • И можно посмотреть информацию о своём интерфейсе (на данный момент это просто вывод "ip address show dev eth0".

Гифки (~10 мб)
TcpDumper: DIY устройство для исследования сети Ethernet - 2
TcpDumper: DIY устройство для исследования сети Ethernet - 3
TcpDumper: DIY устройство для исследования сети Ethernet - 4
TcpDumper: DIY устройство для исследования сети Ethernet - 5

Как это было сделано

А теперь, к вопросу как это все сделать без GUI.

Компоненты

Их всего два. Raspberry pi 2b можно взять на авито примерно за 10 долларов; экранчик брал как в спойлере, с учетом доставки и прочего получаются те же 10$.

Фото
Экран

Экран
Малинка
Малинка

Образ ОС

В качестве ОС я выбрал Raspbian lite (32х битную), без десктопного окружения, потому что зачем, когда у нас есть терминал :) Да и вторая малинка с трудом тянет полноценную графическую систему.

Подключение экрана

Итак, экран начал подключать по инструкции здесь. Там всё просто, клонируем репу, запускаем ./LCD35-show, наслаждаемся. Но не тут-то было, т.к. далее стоял вопрос как откалибровать его. Не помню как именно, но тернистыми путями вышел на библиотеку, в которой было всё что нужно для калибровки.

Написание собственно программы

Существует шикарная библиотека для быстрого написания интерфейсов на плюсах Dear ImGui. Однако существует менее известная библиотека ImTui, которая позволяет использовать ImGui для написания приложений в терминале с использованием ncurses.

Про автора:

Автор ImTui -- ggerganov -- какой-то монстр: он сделал мегапопулярную библиотеку для LLM llama.cpp, порт Whisper на С++, ImTui... А о его активности можно слагать легенды:

TcpDumper: DIY устройство для исследования сети Ethernet - 8

Склонировав репу ImTui и попробовав запустить, я столкнулся со следующим: не регистрирует нажатия. Потыкавшись с линуксовыми инпутами, я решил проблему просто: линкуем себе в приложение libts, лезем в исходник imtui и добавляем обработку:

// imtui-impl-ncurses.cpp
#include "tslib.h"
...
static struct tsdev* ts;
...
ImTui::TScreen * ImTui_ImplNcurses_Init() {
    ...
    ts = ts_setup(nullptr, 1);
}

bool ImTui_ImplNcurses_NewFrame() {
    struct ts_sample samp;
    int ret = ts_read(ts, &samp, 1);
    if (samp.pressure > 0) { // Если давление на экран больше ноля
        // и если данные от экрана "более-менее" норм
        if (samp.x > 0 && samp.x < 1000 && samp.y > 0 && samp.y < 1000) {
            // масштабируем и выдаем координаты нажатия
            mx = (double)samp.x * 30 / 240;
            my = (double)samp.y * 20 / 160;
            lbut = 1;
            hasInput = true;
        } else {
            lbut = 0;
        }
    } else {
        lbut = 0;
    }
    ...
}

Весь код лежит на гитхабе.
Был также момент с масштабированием. По умолчанию текст получался очень мелкий, было сложно что-то разглядеть. Однако драйвер к экрану не поддерживает масштабирование. Поэтому я просто задал разрешение экрана в 1.5 раза меньше, и текст увеличился. Это неидеальное решение, он получился немного размытым, надо бы больше поковырять этот момент.

Приколы в коде и баги

Для пинга и просмотра пакетов используются команды ping и tcpdump, вывод которых перенаправляется в pipe и отображается. При этом у tcpdump есть флаг -U, который, согласно ману, означает "вывод будет записан в stdout когда закончится очередной пакет". Но это не работало! Пакеты выводились пачкой раз в секунд 10. Зато опция -l "вывод записывается в stdout по окончанию очередной строки" работала! Хотя даже в man tcpdump говорится что опции идентичны.

Залез в исходники. Флаг -l вызывает setvbuf(stdout, NULL, _IOLBF, 0) где _IOLBF и означает строковую буферизацию. Флаг -U вызывает в конце каждого пакета функцию pcap_dump_flush, которая является частью API libpcap. Лезем в код libpcap. Находим:

int
pcap_dump_flush(pcap_dumper_t *p)
{
	if (fflush((FILE *)p) == EOF)
		return (-1);
	else
		return (0);
}

Fflush чистит все user-space буферы, но не чистит kernel-space. При этом размер pipe обычно 4 кибибайта, а средний пакет в выводе tcpdump ~ 100 байт. Вот и получалось что он печатал пачками по ~40 пакетов. Причина найдена, поэтому идем дальше.

Был прикол с тем, что если в .bashrc прописать что-то типа

if [[ $(tty) == /dev/tty1 ]]; then
    /root/TcpDumper/build/bin/TcpDumper
fi

То при открытии приложения будет чёрный экран. Я не знаю сколько бы я с этим бодался, но подсказала ChatGPT: для ncurses приложения необходима переменная среды TERM! Добавив в код выше export TERM=xterm-256color, все заработало.

С файлом .profile был ещё один мем. Когда я первый раз добавил запуск приложения сразу после запуска ОС, я по-глупости написал это вот так:

# файл /root/.profile
...
exec /root/TcpDumper/build/bin/TcpDumper

И сохранил. Решил проверить. Перезахожу по ssh. Меня встречает приложение, работает, всё хорошо. Жму Ctrl+C чтобы его закрыть и... сессия ssh закрывается. О нет. Ведь exec замещает процесс баша приложением в его аргументе, и назад в баш вернуться не было возможности. От пользователя pi пароль я забыл, команда вида ssh root@192.168.0.114 'echo privet > /root/.profile' завершается ошибкой...

Начал думать что делать. Точно, нужно просто достать sd-карточку и поменять на ней этот файл! Но есть проблема. Файловая система у малинок отформатирована в ext4, а винда такой формат не поддерживает. Другого устройства на линуксе дома нет.
Нашел под винду программу просмотра файлов с карточек ext2fsd, но она умеет их только читать, а не писать. Но у меня же есть WSL, подумал я, нашёл программу для проброса usb флешек в WSL -- usb-ipd. Но она тоже отказалась монтировать флешку. Погуглив, нашел информацию что нужно пересобрать ядро WSL с поддержкой нужных дров, но решил остановиться и... поспать. На завтра занёс её на работу, где проблема решилась за 10 секунд :) Можно ещё было залить на флешку образ линукса и загрузиться в live cd на ноуте, но тогда я почему-то отверг этот вариант (может просто флешки не было еще одной).

Ещё был баг, если зайти на малинку через ssh, и открыть сразу две копии приложения через openvt, то все начинает глючить. Не знаю зачем вам эта информация :)

Инструкция по программированию

Если вы не собираетесь повторять девайс, этот раздел можно пропустить, тут не будет ничего интересного.

Установка ОС

  1. Перейдите на https://www.raspberrypi.com/software, скачайте raspberry pi imager под вашу ОС

  2. В Imager: выберите вашу модель Raspberry, ОС - Raspberry pi OS lite, в настройках включите удаленный доступ (ssh).

  3. Установите выбранную ОС на sd-карточку.

Установка драйверов для дисплея и калибровка

  1. Установите необходимые пакеты: apt install libts-dev cmake

  2. Склонируйте репозиторий с драйвером дисплея: git clone https://github.com/goodtft/LCD-show.git

  3. cd LCD-show; ./LCD35-show

  4. Поменяйте (уменьшите) разрешение дисплея. Для этого в файле /boot/config.txt добавьте (если их нет) в конец следующие строки:
    framebuffer_width=240
    framebuffer_height=160

  5. reboot

  6. Далее, необходимо выставить следующие переменные среды для работы libts: export TSLIB_FBDEVICE=/dev/fb0;
    export TSLIB_TSDEVICE=/dev/input/event0

  7. Откалибруйте дисплей: ts_calibrate

Сборка программы

  1. Склонируйте репозиторий:
    cd ~;
    git clone https://github.com/Dima-Makarov/TcpDumper.git --recurse-submodules

  2. Можно собирать и запускать:
    cd TcpDumper;
    mkdir build;
    cmake -S . -B build;
    cmake --build build;
    build/bin/TcpDumper

  3. Для того, чтобы оно автоматически запускалось при старте малинки, добавьте следующие строки в ~/.profile:
    export TSLIB_FBDEVICE=/dev/fb0
    export TSLIB_TSDEVICE=/dev/input/event0
    if [[ $(tty) == /dev/tty1 ]]; then
    export TERM=xterm-256color
    openvt -f -c 1 /root/TcpDumper/build/bin/TcpDumper
    fi

  4. Также нужно сделать так, чтобы экран запускался с пользователем root. Для этого:
    nano /etc/systemd/system/getty@tty1.service.d/autologin.conf
    В строке после слова autologin измените слово pi на root
    Выйдите из редактора:
    reboot

  5. Готово!

Если появятся какие-то проблемы - смело пишите в лс, обещаю оперативно помочь.

Инструкция по сборке

Простите
Соедините детали как показано на видео

Соедините детали как показано на видео

Описание улучшений которые можно было бы реализовать

  1. Не зря я упомянул про адаптер USB-Ethernet в начале, ведь с ним можно сделать следующее: втыкаем его в usb, соединяем два сетевых интерфейса в bridge, и вуаля, получаем сниффер, который можем включить в разрыв между двумя устройствами и смотреть через tcpdump что они там шлют друг другу.

  2. На поздних "малинках" есть wi-fi, и с ним можно сделать много чего интересного, и всё из того же TUI интерфейса. Как минимум - переходник "медь-воздух", wi-fi сканер, you name it.

  3. Можно реализовать сценарии разных атак на сеть - arp spoof, mac spoof, проверить включен ли port-security на коммутаторе, попытавшись загадить ему mac-таблицу :), и прочее и прочее.

  4. Также, через wi-fi можно реализовать управление этим устройством со смартфона, и там уже воротить все что вздумается.

Вопросы знатокам

Мои вопросы: заинтересован ли кто-нибудь в подобном устройстве? Какие бы фичи вам были бы полезны? Удобный ли форм-фактор, на ваш взгляд? Добро пожаловать в комменты!

Скрытый текст

Стоп он реально не оставил ссылки на свой тг канал???

Автор: Duke_nukeum

Источник

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


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js