aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: be9123645b03b1ed7824426723b2564887b5070a (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
135
136
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/