ifstat ====== Эта программа обрабатывает пакеты с сетевого интерфейса, собирает статистику (количество полученных пакетов, количество полученных байт, распределение количества пакетов по их размерам) о трафике установленного вида и отправляет броадкастом полученную статистику по [ubus][ubus]. ## Пример вывода утилиты ifstat ![Demo](doc/demo.gif) ## Зависимости - актуальное ядро Linux с поддержкой eBPF и XDP (проверено на 4.18); - ubox и ubus с поддержкой Lua; - luajit; - libbcc. ## TODO 1. Завести автоматические тесты с запуском `tcpreplay`, созданием виртуального сетевого интерфейса и валидацией полученной статистики; 2. Провести ручное нагрузочное тестирование на реальном железе с Debian и посмотреть на производительность; 3. Перейти с lua-based конфига на что-то, что можно адекватно валидировать (ini/yaml/toml). ## Установка Целевая система: Debian 9 (stretch), amd64 Команды установки и запуска проверены вручную через Vagrant (образ `debian/stretch64`) ```shell echo "deb http://deb.debian.org/debian stretch-backports main contrib non-free" | sudo tee /etc/apt/sources.list.d/backports.list && \ sudo apt-get update && \ sudo apt-get install -y linux-image-4.18.0-0.bpo.1-amd64 \ linux-headers-4.18.0-0.bpo.1-amd64 \ linux-compiler-gcc-6-x86=4.18.6-1~bpo9+1 \ luajit libjson-c3 git && \ wget http://tmp.nazaryev.ru/ifstat/{libbcc_0.7.0-1_all.deb,ubox-0.1.1-Linux.deb,ubus-0.1.1-Linux.deb} && \ sudo dpkg -i libbcc_0.7.0-1_all.deb ubox-0.1.1-Linux.deb ubus-0.1.1-Linux.deb && \ rm -f libbcc_0.7.0-1_all.deb ubox-0.1.1-Linux.deb ubus-0.1.1-Linux.deb && \ git clone https://github.com/3ap/ifstat && cd ifstat && \ git submodule init && \ git submodule update sudo reboot # для перехода на новую версию ядра ``` ## Запуск и конфигурация ```shell cd ifstat vi config.lua # настройка фильтров (формат см. ниже) sudo /usr/sbin/ubusd & sudo ./ifstatd.lua # запуск сервера (компиляция, инъекция eBPF, отправка) sudo ./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). ## Почему я выбрал Lua в качестве вспомогательного языка? 1. Весь критичный к производительности код запускается в ядре и написан на Си, всё остальное (парсинг конфигов и командной строки, инъекция eBPF, вычитка готовых данных, отправка данных по ubus) можно написать на любом подходящем для этих задач языке; 2. Проект `bcc` официально поддерживает нативный интерфейс (библиотеку libbcc) для инъекции eBPF в ядро и байндинги к нему поддерживаются непосредственно разработчиком только для Lua и Python -- выбор сильно сужается; 3. Проект `ubus` предоставляет только нативный интерфейс и Lua-байндинги; 4. Идея написать с нуля и поддерживать байндинги для `bcc` или `ubus` для других языков откидывается сразу. Таким образом на выбор остаются только Си и Lua. Писать на Lua обработку конфигов и сериализацию/десериализацию данных значительно проще и приятнее, чем на Си, поэтому Lua и был выбран. [ubus]: https://oldwiki.archive.openwrt.org/doc/techref/ubus [bcc]: https://github.com/iovisor/bcc [ebpf]: https://lwn.net/Articles/740157/