aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 63969122e38dd45f00beb85359139d663948cd8a (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
ifstat
======

Эта программа обрабатывает пакеты с сетевого интерфейса, собирает
статистику (количество полученных пакетов, количество полученных
байт, распределение количества пакетов по их размерам) о трафике
установленного вида и отправляет броадкастом полученную статистику по
[ubus][ubus].

## Зависимости

  - актуальное ядро 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). Непосредственно компиляцией
занимается 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/