ifstat ====== Эта программа обрабатывает пакеты с сетевого интерфейса, собирает статистику (количество полученных пакетов, количество полученных байт, распределение количества пакетов по их размерам) о трафике установленного вида и отправляет броадкастом полученную статистику по [ubus][ubus]. ## Зависимости - актуальное ядро Linux с поддержкой eBPF и XDP (4.4+ должно подойти); - запущенный ubusd, установленные libubus (скомпиленный с поддержкой lua) и libuloop; - LLVM/Clang, luajit (устанавливаются из репозитория); - libbcc (может быть собрано в виде deb-пакета из репозитория); - tcpreplay (для тестов) ## TODO 1. Завести автоматические тесты с запуском `tcpreplay`, созданием виртуального сетевого интерфейса и валидацией полученной статистики; 2. Написать инструкцию/скрипт с точностью до команд, с информацией о том, как установить все необходимые зависимости для Debian; 3. Провести ручное нагрузочное тестирование на реальном железе с Debian и посмотреть на производительность; 4. Перейти с lua-based конфига на что-то, что можно адекватно валидировать (ini/yaml/toml). ## Запуск и конфигурация ```shell sudo ./ifstatd.lua # сервер (компиляция, инъекция eBPF, отправка) ./ifstat.lua # клиент (отображение статистики от сервера) ``` В качестве конфига используется находящийся рядом с `ifstatd.lua` файл `config.lua`. ```lua local _config = { delay_ms = 500, iface = "enp0s8", filters = { { filter_num = 0, -- номер фильтра (от 0 до 4 включительно) enabled = 1, -- включен (1) / выключен (0) ipproto = IPPROTO_TCP, -- IPPROTO_TCP, IPPROTO_UDP или ANY src_ip = "140.82.33.182", -- строка с IPv4 или ANY dst_ip = ANY, -- аналогично src_ip src_port = 22, -- номер UDP/TCP-порта или ANY dst_port = ANY -- аналогично src_port }, { filter_num = 1, enabled = 0, }, ... } ``` ## Архитектура Для того, чтобы собирать статистику максимально эффективно, используется интерфейс ядра [eBPF][ebpf]. Этот интерфейс даёт возможность встроить код прямо в ядро, который будет подсчитывать необходимую статистику. Кроме того, для наибольшей производительности eBPF вешается на XDP-хук (eXpress Data Path), что позволяет получить доступ до пакета даже раньше, чем он будет обработан сетевым стеком ядра. Основной код, задействованный в фильтрации и подсчёте статистики, написан на Си (`ifstat_kern.c`). Процессом компиляции и инъекцией этого кода в ядро занимается lua-скрипт, основанный на официальных байндингах проекта [bcc][bcc] (это фреймворк упрощает процесс создания утилит, использующих eBPF). Непосредственно компиляцией занимается LLVM/Clang. ## Почему я выбрал Lua в качестве вспомогательного языка? 1. Весь критичный к производительности код запускается в ядре и написан на Си, всё остальное (парсинг конфигов и командной строки, инъекция eBPF, вычитка готовых данных, отправка данных по ubus) можно было написать на любом подходящем для этих задач языке; 2. Проект bcc официально поддерживает нативный интерфейс (библиотеку libbcc) для инъекции eBPF в ядро и байндинги к нему поддерживаются непосредственно разработчиком только для Lua и Python -- выбор сильно сужается; 3. Проект ubus предоставляет только нативный интерфейс и Lua-байндинги; 4. Идея написать с нуля и поддерживать байндинги для bcc или ubus для других языков откидывается сразу. Таким образом, на выбор остаются только Си и Lua. Писать на Lua обработку конфигов и сериализацию/десериализацию данных значительно проще и приятнее, чем на Си, поэтому Lua и был выбран. ## Пример вывода утилиты ifstat ``` ------------------------------------------------------------------------------- # <= 64 65-127 128-255 256-511 512-1023 1024-151 >= 1513 Bytes Packets ------------------------------------------------------------------------------- 0 0 0 202 0 0 2 0 31708 204 Δ 0 0 0 0 0 0 0 0 0 ------------------------------------------------------------------------------- 1 0 0 202 0 0 2 0 31708 204 Δ 0 0 0 0 0 0 0 0 0 ------------------------------------------------------------------------------- 2 0 0 202 0 0 2 0 31708 204 Δ 0 0 0 0 0 0 0 0 0 ------------------------------------------------------------------------------- 3 0 0 202 0 0 2 0 31708 204 Δ 0 0 0 0 0 0 0 0 0 ------------------------------------------------------------------------------- 4 0 0 202 0 0 2 0 31708 204 Δ 0 0 0 0 0 0 0 0 0 ------------------------------------------------------------------------------- ``` [ubus]: https://oldwiki.archive.openwrt.org/doc/techref/ubus [bcc]: https://github.com/iovisor/bcc [ebpf]: https://lwn.net/Articles/740157/