aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: fbe53c21477dbd1eccd8cc9350b99addac3552e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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/