Zapret: what's new

Результат профилирования nfqws2, слинкованного с libluajit, на x86_64 системе на сплошном потоке выгрузки iperf
без ограничителей connbytes
2 инстанса - multisplit и fake без ограничителя standard payload. без внутрипрофильных фильтров на оба направления

valgrind --tool=callgrind --callgrind-out-file=callgrind.out 
/opt/zapret2/nfq2/nfqws2 --user=root --qnum 200
--lua-init=@/opt/zapret2/lua/zapret-lib.lua
--lua-init=@/opt/zapret2/lua/zapret-antidpi.lua
--in-range=a
--lua-desync=multisplit:payload=all
--lua-desync=fake:ip_ttl=1:badsum:blob=0x10000000:payload=all
--debug=0
gprof2dot.py -f callgrind callgrind.out | dot -Tpng -o 1.png

Что здесь примечательно.

  1. Вся логика C кода без процедуры desync занимает всего 5% cpu. desync - это анализ какие есть lua функции, какие у них внутрипрофильные фильтры, проверить фильтры, проверить условие cutoff, если проходит - запушить содержимое desync и вызывать через pcall саму функцию
  2. Само выполнение кода lua в варианте JIT занимает всего 15% cpu.
  3. Остальное - это накладные расходы, чтобы каждый раз пушить всю таблицу desync. lua API работает очень небыстро, львиная доля сьедается на подготовке структуры до передачи в функцию. Затем и был придуман FFI в luajit, но я его не использую, потому что не будет совместимости со стандартным lua и отлетят некоторые платформы. А 2 кода писать нет желания. Да и не очень хотел бы давать возможности FFI непонятно кем писанном скриптам. Уж слишком там low level все. Загоняю в песочницы, а тут возможность вызвать любую C функцию.
  4. При lua cutoff 95% сразу отлетает - не выполняется. Понимаете теперь зачем нужно ставить внутрипрофильные фильтры по range, если нет connbytes ? При превышении up limit по range всех инстансов наступает тишина - lua больше нет !

Вот что творится без lua совсем, когда все ушло в cutoff или функций просто нет.
Большие проценты начинают занимать C функции, которые в lua варианте отьедали доли процента

Почему lua работает так медленно ? Любую строку он уникализирует.
Видимо считает какой-то хэш и сравнивает с базой строк, и если уже есть, то пишет просто указатель.
Потому pushstring работает очень нешустро. string - это не только “abc123”, а любые бинарные блобы, которые могут быть несколько кб
Да и tables не особо скачут.
Там чуть что сразу malloc, копирование памяти.
А пушить приходится много - структура desync довольно обширна

Но NFQUEUE еще больше жрет. Сам процесс перехвата, переключение контекстов, копирование в user mode, возврат вердикта.
Совсем без lua функций - жрет 45% ядра, упираясь в возможности роутера принимать 600-700 мбит.
С 2 инстансами - жрет 55% ядра, выдавая ту же скорость.
Получается, что вся обработка в lua сьедает около 20% от NFQUEUE.
Видите как важно не гнать лишнее на nfqws ?

tun, кстати, по той же причине тормоз. разница openvpn через tun и wireguard - 5-10 раз

Итак :

  1. Главный монстр - NFQUEUE
  2. Монстр поменьше - LUA
  3. C код - на закуску