mirror of
https://github.com/bol-van/zapret2.git
synced 2026-03-14 14:21:41 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7708021587 | ||
|
|
912aadf6ca | ||
|
|
420cc0c3ef | ||
|
|
6ce5829d06 | ||
|
|
a6d43af931 | ||
|
|
ca9898959e | ||
|
|
8cd2904614 | ||
|
|
0de1ab1b1b | ||
|
|
d1690aadcf | ||
|
|
2dd8533fb5 | ||
|
|
33ac18ea6b | ||
|
|
5c05c10f83 | ||
|
|
7de0995d4a | ||
|
|
a1c64e4dea | ||
|
|
92b66b1535 | ||
|
|
9bf4fb11e7 | ||
|
|
7deeb04207 | ||
|
|
776155a326 | ||
|
|
30423596ca | ||
|
|
27ef67adf9 | ||
|
|
bb604f111c | ||
|
|
e5174bc9ad | ||
|
|
6c29bf6702 | ||
|
|
976033be37 | ||
|
|
f9b2135688 | ||
|
|
844fa6ab47 | ||
|
|
dc0fe70bd6 | ||
|
|
2752c26795 | ||
|
|
1600b41135 | ||
|
|
2017889207 | ||
|
|
146ab847df | ||
|
|
cf9059ed22 | ||
|
|
c94264c79e | ||
|
|
04cb71150a | ||
|
|
378ee514c4 | ||
|
|
1a190fcf9e | ||
|
|
0f8a788351 | ||
|
|
4c00f11c15 | ||
|
|
0f8cfd7022 | ||
|
|
4563b6ddcb | ||
|
|
9ae6927a0e | ||
|
|
8540278c9b | ||
|
|
76b9ab5075 | ||
|
|
3a153035e8 | ||
|
|
2b5eb3cd2d | ||
|
|
2c35e8949a | ||
|
|
7edd428508 | ||
|
|
7749fce7dc | ||
|
|
ea6e1e0853 | ||
|
|
7d2f12cbad |
49
blockcheck2.d/standard/24-syndata.sh
Normal file
49
blockcheck2.d/standard/24-syndata.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
. "$TESTDIR/def.inc"
|
||||
|
||||
pktws_check_http()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
local PAYLOAD="--payload http_req" split
|
||||
|
||||
for split in '' multisplit multidisorder; do
|
||||
pktws_curl_test_update "$1" "$2" --lua-desync=syndata ${split:+$PAYLOAD --lua-desync=$split}
|
||||
pktws_curl_test_update "$1" "$2" --lua-desync=syndata:blob=fake_default_http $PAYLOAD ${split:+$PAYLOAD --lua-desync=$split}
|
||||
done
|
||||
}
|
||||
|
||||
pktws_check_https_tls()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
# $3 - PRE args for nfqws2
|
||||
|
||||
local PAYLOAD="--payload tls_client_hello" ok=0 pre="$3" split
|
||||
|
||||
for split in '' multisplit multidisorder; do
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=0x1603 ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,dupsid,rndsni ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,dupsid,sni=google.com ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
done
|
||||
|
||||
[ "$ok" = 1 ]
|
||||
}
|
||||
|
||||
pktws_check_https_tls12()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
|
||||
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
|
||||
}
|
||||
|
||||
pktws_check_https_tls13()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
pktws_check_https_tls "$1" "$2"
|
||||
}
|
||||
@@ -2,6 +2,6 @@ FOOLINGS46_TCP=${FOOLINGS46_TCP:-"tcp_md5 badsum tcp_seq=-3000 tcp_seq=1000000 t
|
||||
FOOLINGS6_TCP=${FOOLINGS6_TCP:-"ip6_hopbyhop ip6_hopbyhop:ip6_hopbyhop2 ip6_destopt ip6_routing ip6_ah"}
|
||||
FOOLINGS_TCP="$FOOLINGS46_TCP"
|
||||
[ "$IPV" = 6 ] && FOOLINGS_TCP="$FOOLINGS_TCP $FOOLINGS6_TCP"
|
||||
FOOLINGS_UDP="badsum"
|
||||
FOOLINGS_UDP="${FOOLINGS_UDP:-badsum}"
|
||||
|
||||
FAKE_REPEATS=${FAKE_REPEATS:-1}
|
||||
|
||||
@@ -26,7 +26,7 @@ CURL=${CURL:-curl}
|
||||
|
||||
TEST_DEFAULT=${TEST_DEFAULT:-standard}
|
||||
DOMAINS_DEFAULT=${DOMAINS_DEFAULT:-rutracker.org}
|
||||
QNUM=${QNUM:-59780}
|
||||
QNUM=${QNUM:-59781}
|
||||
SOCKS_PORT=${SOCKS_PORT:-1993}
|
||||
WS_UID=${WS_UID:-1}
|
||||
WS_GID=${WS_GID:-3003}
|
||||
@@ -1199,8 +1199,8 @@ test_runner()
|
||||
[ -f "$script" ] || continue
|
||||
unset -f $FUNC
|
||||
. "$script"
|
||||
echo
|
||||
existf $FUNC && {
|
||||
echo
|
||||
echo "* script : $TEST/$(basename "$script")"
|
||||
$FUNC "$@"
|
||||
}
|
||||
@@ -1354,7 +1354,7 @@ check_domain_http_udp()
|
||||
[ "$SKIP_PKTWS" = 1 ] || {
|
||||
echo
|
||||
echo preparing $PKTWSD redirection
|
||||
mdig_resolve_all $IPV ips $4
|
||||
mdig_resolve_all $IPV ips $3
|
||||
pktws_ipt_prepare_udp $2 "$ips"
|
||||
|
||||
pktws_check_domain_http3_bypass $1 $3
|
||||
|
||||
@@ -25,7 +25,7 @@ filter_apply_hostlist_target()
|
||||
{
|
||||
# $1 - var name of nfqws params
|
||||
|
||||
local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parmNA
|
||||
local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parm9 parm10 parmNA
|
||||
eval v="\$$1"
|
||||
if contains "$v" "$HOSTLIST_MARKER" || contains "$v" "$HOSTLIST_NOAUTO_MARKER"; then
|
||||
[ "$MODE_FILTER" = hostlist -o "$MODE_FILTER" = autohostlist ] &&
|
||||
@@ -40,10 +40,14 @@ filter_apply_hostlist_target()
|
||||
parm5="${AUTOHOSTLIST_FAIL_THRESHOLD:+--hostlist-auto-fail-threshold=$AUTOHOSTLIST_FAIL_THRESHOLD}"
|
||||
parm6="${AUTOHOSTLIST_FAIL_TIME:+--hostlist-auto-fail-time=$AUTOHOSTLIST_FAIL_TIME}"
|
||||
parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}"
|
||||
parm8="--hostlist=$HOSTLIST_AUTO"
|
||||
parm8="${AUTOHOSTLIST_RETRANS_MAXSEQ:+--hostlist-auto-retrans-maxseq=$AUTOHOSTLIST_RETRANS_MAXSEQ}"
|
||||
parm9="${AUTOHOSTLIST_INCOMING_MAXSEQ:+--hostlist-auto-incoming-maxseq=$AUTOHOSTLIST_INCOMING_MAXSEQ}"
|
||||
parm10="${AUTOHOSTLIST_UDP_IN:+--hostlist-auto-udp-in=$AUTOHOSTLIST_UDP_IN}"
|
||||
parm11="${AUTOHOSTLIST_UDP_OUT:+--hostlist-auto-udp-out=$AUTOHOSTLIST_UDP_OUT}"
|
||||
parm12="--hostlist=$HOSTLIST_AUTO"
|
||||
}
|
||||
parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}"
|
||||
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm8:+ $parm8}"
|
||||
parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}${parm8:+ $parm8}${parm9:+ $parm9}${parm10:+ $parm10}${parm11:+ $parm11}"
|
||||
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm10:+ $parm12}"
|
||||
}
|
||||
v="$(replace_str $HOSTLIST_NOAUTO_MARKER "$parmNA" "$v")"
|
||||
v="$(replace_str $HOSTLIST_MARKER "$parm" "$v")"
|
||||
|
||||
@@ -26,9 +26,13 @@ IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
|
||||
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
|
||||
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
|
||||
# options for auto hostlist
|
||||
AUTOHOSTLIST_INCOMING_MAXSEQ=4096
|
||||
AUTOHOSTLIST_RETRANS_MAXSEQ=65536
|
||||
AUTOHOSTLIST_RETRANS_THRESHOLD=3
|
||||
AUTOHOSTLIST_FAIL_THRESHOLD=3
|
||||
AUTOHOSTLIST_FAIL_TIME=60
|
||||
AUTOHOSTLIST_UDP_IN=1
|
||||
AUTOHOSTLIST_UDP_OUT=4
|
||||
# 1 = debug autohostlist positives to ipset/zapret-hosts-auto-debug.log
|
||||
AUTOHOSTLIST_DEBUGLOG=0
|
||||
|
||||
|
||||
@@ -61,3 +61,32 @@ v0.5
|
||||
* zapret-lib: http_reply, url and nld dissectors
|
||||
* zapret-lib: instance_cutoff_shim
|
||||
* zapret-auto: circular orchestrator
|
||||
|
||||
v0.5.1
|
||||
|
||||
* zapret-auto: separate failure detection logic
|
||||
* blockcheck2: fix broken http3 test
|
||||
|
||||
v0.6
|
||||
|
||||
* zapret-lib,zapret-antidpi: tls_mod_shim supports sni=%var subst
|
||||
* blockcheck2: syndata tests
|
||||
* nfqws2: reasm support negative overlaps. gaps are not supported.
|
||||
* nfqws2,zapret-auto: changed retransmission detection scheme.
|
||||
* zapret-auto: udp_in/udp_out failure detection
|
||||
|
||||
v0.6.1
|
||||
|
||||
* zapret-lib, zapret-auto: condition and stopif orchestrators
|
||||
* zapret-lib: detect_payload_str - sample lua payload detector
|
||||
* blockcheck2: unterminated string fix
|
||||
|
||||
v0.7
|
||||
|
||||
* nfqws2, zapret-lib : fix non-working % and # arg substitution under orchestrator
|
||||
* nfqws2, zapret-lib : structure conntrack in/out positions. pass in desync.track.pos.{client,server,direct,reverse} position tables
|
||||
* nfqws2: autohostlist: trigger RST and http redirect failures only within specified relative sequence
|
||||
* nfqws2: autohostlist: trigger http redirect failure if payload is http_req without connection proto check
|
||||
* nfqws2: push desync.track.pos.dt as float with nsec accuracy
|
||||
* zapret-auto: override host autostate key in automate_host_record
|
||||
* nfqws2: rewrite udp autohostlist failure detector logic
|
||||
|
||||
@@ -5,3 +5,7 @@ v2
|
||||
* removed "stun_binding_req" specialized payload. replaced with common "stun" - any stun packets, not only binding request.
|
||||
every LUA relying on desync.l7payload should be revised.
|
||||
nfqws2 --payload option and init.d custom scripts must be updated.
|
||||
|
||||
v3
|
||||
* restructured desync.track. pass positions in desync.track.pos.{client,server,direct,reverse}
|
||||
code relying on conntrack counters and sequence numbers must be rewritten
|
||||
|
||||
113
docs/manual.md
Normal file
113
docs/manual.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# МАНУАЛ В ПРОЦЕССЕ НАПИСАНИЯ
|
||||
|
||||
|
||||
# Введение
|
||||
|
||||
zapret2 является пакетным манипулятором, основная задача которого - совершение различных автономных атак на DPI в реальном времени
|
||||
с целью преодоления ограничений (блокировок) ресурсов или сетевых протоколов.
|
||||
Однако, этим возможности zapret2 не ограничиваются. Архитектура позволяет выполнять и другие виды пакетных манипуляций.
|
||||
Например, двусторонняя (клиент+сервер) обфускация протоколов с целью их сокрытия от DPI. Возможны и иные применения.
|
||||
|
||||
# Структура проекта
|
||||
|
||||
Главный компонент zapret2 - программа nfqws2 (dvtws2 на BSD, winws2 на Windows), написанная на C, которая и является пакетным манипулятором.
|
||||
Содержит функции по перехвату пакетов, базовой фильтрации, рапознавания основных протоколов и пейлоадов, поддержки хост и IP листов, автоматических хостлистов
|
||||
с распознаванием блокировок, систему множественных профилей (стратегий), возможности по отсылке raw пакетов и другие сервисные функции.
|
||||
Однако, там нет никаких возможностей собственно для воздействия на трафик. Это вынесено в код на языке LUA, вызываемый из nfqws2.
|
||||
|
||||
Поэтому следующая по важности часть проекта - LUA код. В базовый комплект входит библиотека функций-хелперов `zapret-lib.lua`,
|
||||
библиотека программ автономных атак на DPI `zapret-antidpi.lua`, библиотека функций принятия динамических решений (оркестрации) `zapret-auto.lua`.
|
||||
Дополнительно присутствует набор тестов C функций `zapret-tests.lua`, средство обфускации wireguard протокола `zapret-wgobfs.lua` и средство записи дампа пакетов в cap файлы `zapret-pcap.lua`.
|
||||
|
||||
Функции перенаправления трафика из ядра в Linux возложены на iptables и nftables, в FreeBSD - на ipfw, в OpenBSD - на pf, в Windows - встроены в сам процесс winws2 посредством драйвера windivert.
|
||||
Схема перехвата трафика из ядра , nfqws2 и lua код составляют минимально рабочее ядро проекта. Все остальное является дополнительным, второстепенным и опциональным.
|
||||
|
||||
Из второстепенных компонент - скрипты запуска под Linux - `init.d`, `common`, `ipset`, `install_easy.sh`, `uninstall_easy.sh` и средство автоматизации тестирования стратегий `blockcheck2`.
|
||||
Цель скриптов запуска - согласовать процесс поднятия таблиц и запуск инстансов nfqws2, учесть особенности интеграции в различные дистрибутивы (openwrt, systemd, openrc).
|
||||
Дополнительная функция - обеспечить поддержку и согласованное обновление различных листов и загрузку IP листов в пространство ядра - ipset.
|
||||
Все это можно сделать при желании и собственными средствами, если так удобнее или функционал скриптов запуска не подходит.
|
||||
Скрипты запуска выносят все настройки в файл config, лежащий в корне проекта. Этот конфиг относится только к ним, nfqws2 ничего о нем не знает.
|
||||
|
||||
Для обработки листов предусмотрены 2 программы, написанные на C. mdig - многопоточный ресолвер хостлистов неограниченного обьема.
|
||||
ip2net - программа для группировки отдельных IP адресов в подсети с целью сокращения их обьема.
|
||||
Эти программы испльзуются в скриптах запуска и в blockcheck2.
|
||||
|
||||
Скрипты запуска и инсталятор поддерживает установку на любые классические дистрибутивы Linux с systemd или openrc , из прошивок - на openwrt.
|
||||
Если система не удовлетворяет указанным требованиям - возможна самостоятельная "доприкрутка" к системе.
|
||||
|
||||
MacOS не поддерживается по причине отсутствия подходящего средства перехвата и управления пакетами. Стандартное для BSD средство ipdivert было убрано из ядра производителем.
|
||||
|
||||
# Схема обработки трафика
|
||||
|
||||
Сети работают с IP пакетами. Поэтому единицей обработки являются именно они. Приемом и отправкой пакетов занимается сетевая подсистема в ядре ОС.
|
||||
nfqws2 работает не в ядре (kernel mode), а является процессом пространства пользователя (user mode). Поэтому первая часть процесса обработки состоит
|
||||
в передаче пакетов из ядра ОС в процесс nfqws2. Все 4 средства перехвата обладают некоторыми возможностями фильтрации пакетов. Максимальные возможности - в Linux.
|
||||
Чем больше на этом этапе будет отсечено ненужного трафика, тем меньше будет нагрузка на процессор, поскольку передача пакетов из ядра в user mode и обратно сопряжена со значительными накладными расходами.
|
||||
|
||||
Пакет пришел в nfqws2. Первое, что он делает, это разбирает его по уровням OSI модели - выделяет ip , ipv6, tcp, udp заголовки и поле данных. Это называется диссекцией.
|
||||
Результатом диссекции является диссект - представление пакета в виде структур, которые можно адресовать по полям.
|
||||
|
||||
Далее задействуется встроенная в nfqws2 подсистема conntrack - система отслеживания потоков поверх отдельно взятых пакетов.
|
||||
Ищется уже имеющаяся запись о соединении на основании данных L3/L4 пакета. Если ее нет - создается. Старые записи, по которым давно нет активности, удаляются.
|
||||
conntrack отслеживает логическое направление пакетов в потоке (входящее/исходящее), ведет подсчет количество прошедших пакетов и байт в обе стороны,
|
||||
следит за sequence numbers для tcp. Он же используется в случае необходимости для сборки сообщений, передаваемых несколькими пакетами.
|
||||
|
||||
Сигнатурно определяется тип пейлоада - содержимого отдельно взятого пакета или группы пакетов. На основании типа пейлоада определяется тип протокола
|
||||
всего потока, который сохраняется за потоком до конца его существования. В рамках одного соединения могут проходить разные типы пейлоадов.
|
||||
Например, протокол потока xmpp обычно несет несколько видов специфических для xmpp сообщений и сообщения, связанные с tls.
|
||||
Тип протокола потока xmpp остается, но последующие пакеты получают различные типы пейлоада - как известные, так и неизвестные. Неизвестные пейлоады определяются как тип "unknown".
|
||||
|
||||
Если для конкретного пейлоада и типа протокола потока выясняется необходимость реконструкции сообщения из нескольких пакетов, nfqws2 начинает их накапливать в связи с записью в conntrack
|
||||
и запрещает их немедленную отправку. После приема всех пакетов сообщения происходит реконструкция и при необходимости дешифровка составного пейлоада.
|
||||
Дальнейшие решения принимаются уже на базе полностью собранного пейлоада - reasm или результата сборки и дешифровки - decrypt.
|
||||
|
||||
Когда необходимая информация о пейлоаде получена, наступает очередь системы классификации по профилям.
|
||||
Профили содержат систему фильтров и команды действия внутри профиля.
|
||||
Профили фильтруются по L3 - версия IP протокола, ipset-ы - списки IP адресов, L4 - порты tcp или udp, L6/L7 - тип протокола потока, списки доменов (хостлисты).
|
||||
Профили сканируются строго в порядке от первого к последнему. При первом совпадении условий фильтра профиля выбирается этот профиль, а сканирование прекращается.
|
||||
Если ни одно из условий не выполнено, выбирается профиль по умолчанию, в котором нет никаких действий.
|
||||
|
||||
Все дальнейшие действия выполняются уже в рамках выбранного профиля. Выбранный профиль кэшируется в записи conntrack, поэтому для каждого пакета поиск заново не выполняется.
|
||||
Повторный поиск выполняется в случае изменения исходных данных - при обнаружении L7 протокола и при обнаружении имени хоста. В последних случаях производится повторный поиск
|
||||
и при необходимости переключение профиля. Таких переключений может быть за соединение до двух, поскольку есть лишь 2 изменяемых параметра.
|
||||
|
||||
Профиль выбран. Из чего состоит его содержание, отвечающее за действия ?
|
||||
За действия отвечают LUA функции. В профиле их может быть произвольное количество.
|
||||
Каждый вызов LUA функции из профиля называется инстансом. Функция может быть одна, вызовов несколько - с разными параметрами.
|
||||
Поэтому и применяется понятие инстанса - экземпляра вызываемой функции, который идентифицируется номером профиля и порядковым номером вызова внутри профиля.
|
||||
Инстансы вызываются через параметры `--lua-desync`. Каждый инстанс получает набор произвольных параметров, задаваемых в `--lua-desync`.
|
||||
Порядок вызовов имеет принципиальное значение для логики стратегии и выполняется строго в порядке задания параметров `--lua-desync`.
|
||||
|
||||
Присутствуют и внутипрофильные фильтры. Их 3 типа - фильтр `--payload` - список принимаемых инстансом пейлоадов, и 2 диапазонных фильтра `--in-range` и `--out-range`,
|
||||
позволяющих задать диапазон позиций внутри потока, который интересен для инстанса. Внутрипрофильные фильтры после их определения действуют на все последующие инстансы
|
||||
до их переопределения. Главный смысл наличия внутрипрофильных фильтров - сократить число относительно медленных вызовов LUA , принимая максимум решений на стороне C кода.
|
||||
|
||||
Пакет пришел в LUA инстанс. Функция имеет 2 параметра - ctx и desync. ctx - это контекст для связи с некоторыми функциями на стороне C кода.
|
||||
desync - таблица, содержащая множество параметров обрабатываемого пакета. Прежде всего это диссект - подтаблица `dis`.
|
||||
Информация из записи conntrack - подтаблица `track`. Еще целый ряд параметров, который можно увидеть, выполнив `var_debug(desync)` или просто вызвав готовый инстанс `pktdebug`.
|
||||
|
||||
Если идет перепроигрывание задержанных пакетов (replay), LUA инстанс получает информацию о номере части, количестве частей исходного сообщения, позиции текущей части,
|
||||
reasm или decrypt при наличии.
|
||||
|
||||
LUA код может использовать глобальное пространство переменных для хранения данных, не относящихся к конкретному обрабатываемому пакету. Ему доступна таблица `desync.track.lua_state`,
|
||||
в которой он может хранить любую информацию, связанную с записью conntrack. При каждом новом пакете потока в LUA выдается одна и та же таблица.
|
||||
Таблицу desync можно использовать для генерации и хранения временных данных, актуальных в цепочке обработки текущего пакета.
|
||||
Следующие LUA инстансы получают ту же таблицу desync и тем самым могут принимать данные от предыдущих инстансов.
|
||||
|
||||
LUA инстанс может создавать копии текущего диссекта, вносить в них изменения, генерировать собственные диссекты, отправлять их через вызовы C кода.
|
||||
Итогом работы каждого инстанса является вердикт - VERDICT_PASS - не делать ничего с текущим диссектом, VERDICT_MODIFY - в конце всей цепочки отослать модифицированное содержимое диссекта,
|
||||
VERDICT_DROP - дропнуть текущий диссект. Вердикты всех инстансов аггрегируются - MODIFY замещает PASS, а DROP замещает и PASS, и MODIFY.
|
||||
|
||||
LUA инстанс может сам себя отключить от получения дальнейших пакетов потока по направлению in/out - это назвается instance cutoff.
|
||||
Может отключить направление in/out текущего потока от всей LUA обработки - lua cutoff.
|
||||
Может запросить отмену всей дальнейшей цепочки вызовов LUA инстансов по текущему диссекту. Инстанс, принимающий такое решение, берет на себя функцию координации дальнейших действий.
|
||||
Такой инстанс называется оркестратором. Он получает от C кода план дальнейшего выполнения со всеми фильтрами профиля и параметрами вызова всех оставшихся инстансов
|
||||
и сам принимает решения когда и при каких условиях их вызывать или не вызывать, менять их параметры. Так реализуются динамические сценарии без модификации основных составлящих кода стратегии.
|
||||
Например, определение блокировки ресурса и смена стратегии, если предыдущая не сработала.
|
||||
|
||||
Если все инстансы текущего профиля вошли в состояние cutoff по текущему потоку, либо текущая позиция потока находится за верхней границей range фильтров, значит по этому потоку больше не будет LUA
|
||||
вызовов. C код помечает такие потоки специальным признаком "lua cutoff", который проверяется максимально быстро без вызовов кода LUA. Тем самым экономятся ресурсы процессора.
|
||||
|
||||
После выполнения всей цепочки инстансов профиля C код получает итоговый вердикт - что делать с текущим диссектом. Отправить как есть, отправить модифицированный вариант или дропнуть.
|
||||
|
||||
В конце nfqws2 переходит к ожиданию следующего пакета, и цикл повторяется вновь.
|
||||
@@ -1,5 +1,3 @@
|
||||
# zapret2 v0.2
|
||||
|
||||
## Зачем это нужно
|
||||
|
||||
Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь
|
||||
|
||||
@@ -290,7 +290,7 @@ end
|
||||
-- nfqws1 : "--dpi-desync=syndata"
|
||||
-- standard args : fooling, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload. must fit to single packet. no segmentation possible. default - 16 zero bytes.
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>. sni=%var is supported
|
||||
function syndata(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
|
||||
@@ -298,7 +298,7 @@ function syndata(ctx, desync)
|
||||
dis.payload = blob(desync, desync.arg.blob, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
apply_fooling(desync, dis)
|
||||
if desync.arg.tls_mod then
|
||||
dis.payload = tls_mod(dis.payload, desync.arg.tls_mod, nil)
|
||||
dis.payload = tls_mod_shim(desync, dis.payload, desync.arg.tls_mod, nil)
|
||||
end
|
||||
if b_debug then DLOG("syndata: "..hexdump_dlog(dis.payload)) end
|
||||
if rawsend_dissect_ipfrag(dis, desync_opts(desync)) then
|
||||
@@ -340,7 +340,7 @@ end
|
||||
-- nfqws1 : "--dpi-desync=fake"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap . sni=%var is supported
|
||||
function fake(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
@@ -351,7 +351,7 @@ function fake(ctx, desync)
|
||||
end
|
||||
local fake_payload = blob(desync, desync.arg.blob)
|
||||
if desync.reasm_data and desync.arg.tls_mod then
|
||||
fake_payload = tls_mod(fake_payload, desync.arg.tls_mod, desync.reasm_data)
|
||||
fake_payload = tls_mod_shim(desync, fake_payload, desync.arg.tls_mod, desync.reasm_data)
|
||||
end
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("fake: "..hexdump_dlog(fake_payload)) end
|
||||
@@ -421,7 +421,7 @@ end
|
||||
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
|
||||
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
|
||||
-- arg : seqovl_pattern=<blob> . override pattern
|
||||
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
||||
-- arg : blob=<blob> - use this data instead of reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function multidisorder(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
@@ -600,7 +600,7 @@ end
|
||||
-- arg : pattern=<blob> . fill fake parts with this pattern
|
||||
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
|
||||
-- arg : seqovl_pattern=<blob> . override seqovl pattern
|
||||
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
||||
-- arg : blob=<blob> - use this data instead of reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function fakedsplit(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
-- standard automation/orchestration code
|
||||
-- this is related to making dynamic strategy decisions without rewriting or altering strategy function code
|
||||
-- orchestrators can decide which instances to call or not to call or pass them dynamic arguments
|
||||
-- failure detectors test potential block conditions for orchestrators
|
||||
-- arg: reqhost - require hostname, do not work with ip
|
||||
-- arg: key - a string - table name inside autostate table. to allow multiple orchestrator instances to use single host storage
|
||||
function automate_host_record(desync)
|
||||
local key
|
||||
local hostkey, askey
|
||||
if desync.arg.reqhost then
|
||||
key = desync.track and desync.track.hostname
|
||||
hostkey = desync.track and desync.track.hostname
|
||||
else
|
||||
key = host_or_ip(desync)
|
||||
hostkey = host_or_ip(desync)
|
||||
end
|
||||
if not key then
|
||||
if not hostkey then
|
||||
DLOG("automate: host record key unavailable")
|
||||
return nil
|
||||
end
|
||||
DLOG("automate: host record key '"..key.."'")
|
||||
askey = (desync.arg.key and #desync.arg.key>0) and desync.arg.key or desync.func_instance
|
||||
DLOG("automate: host record key 'autostate."..askey.."."..hostkey.."'")
|
||||
if not autostate then
|
||||
autostate = {}
|
||||
end
|
||||
if not autostate[key] then
|
||||
autostate[key] = {}
|
||||
if not autostate[askey] then
|
||||
autostate[askey] = {}
|
||||
end
|
||||
return autostate[key]
|
||||
if not autostate[askey][hostkey] then
|
||||
autostate[askey][hostkey] = {}
|
||||
end
|
||||
return autostate[askey][hostkey]
|
||||
end
|
||||
function automate_conn_record(desync)
|
||||
if not desync.track.lua_state.automate then
|
||||
@@ -69,24 +78,103 @@ function is_dpi_redirect(hostname, location)
|
||||
return false
|
||||
end
|
||||
|
||||
-- standard failure detector
|
||||
-- works with tcp and udp
|
||||
-- detected failures:
|
||||
-- incoming RST
|
||||
-- incoming http redirection
|
||||
-- outgoing retransmissions
|
||||
-- udp too much out with too few in
|
||||
-- arg: seq=<rseq> - tcp: if packet is beyond this relative sequence number treat this connection as successful. default is 64K
|
||||
-- arg: retrans=N - tcp: retrans count threshold. default is 3
|
||||
-- arg: rst=<rseq> - tcp: maximum relative sequence number to treat incoming RST as DPI reset. default is 1
|
||||
-- arg: no_http_redirect - tcp: disable http_reply dpi redirect trigger
|
||||
-- arg: udp_out - udp: >= outgoing udp packets. default is 4
|
||||
-- arg: udp_in - udp: with <= incoming udp packets. default is 1
|
||||
function standard_failure_detector(desync, crec, arg)
|
||||
if crec.nocheck then return false end
|
||||
|
||||
local seq_rst = tonumber(arg.rst) or 1
|
||||
local retrans = tonumber(arg.retrans) or 3
|
||||
local maxseq = tonumber(arg.seq) or 0x10000
|
||||
local udp_in = tonumber(arg.udp_in) or 1
|
||||
local udp_out = tonumber(arg.udp_out) or 4
|
||||
|
||||
local trigger = false
|
||||
if desync.dis.tcp then
|
||||
local seq = pos_get(desync,'s')
|
||||
if maxseq and seq>maxseq then
|
||||
DLOG("standard_failure_detector: s"..seq.." is beyond s"..maxseq..". treating connection as successful")
|
||||
crec.nocheck = true
|
||||
return false
|
||||
end
|
||||
|
||||
if desync.outgoing then
|
||||
if #desync.dis.payload>0 and retrans and (crec.retrans or 0)<retrans then
|
||||
if is_retransmission(desync) then
|
||||
crec.retrans = crec.retrans and (crec.retrans+1) or 1
|
||||
DLOG("standard_failure_detector: retransmission "..crec.retrans.."/"..retrans)
|
||||
trigger = crec.retrans>=retrans
|
||||
end
|
||||
end
|
||||
else
|
||||
if seq_rst and bitand(desync.dis.tcp.th_flags, TH_RST)~=0 then
|
||||
trigger = seq<=seq_rst
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: incoming RST s"..seq.." in range s"..seq_rst)
|
||||
else
|
||||
DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..seq_rst)
|
||||
end
|
||||
end
|
||||
elseif not arg.no_http_redirect and desync.l7payload=="http_reply" and desync.track.hostname then
|
||||
local hdis = http_dissect_reply(desync.dis.payload)
|
||||
if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then
|
||||
trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value)
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. looks like DPI redirect.")
|
||||
else
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. NOT a DPI redirect.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif desync.dis.udp then
|
||||
if desync.outgoing then
|
||||
if udp_out then
|
||||
local udp_in = udp_in or 0
|
||||
trigger = desync.track.pos.direct.pcounter>=udp_out and desync.track.pos.reverse.pcounter<=udp_in
|
||||
if trigger then
|
||||
crec.nocheck = true
|
||||
if b_debug then
|
||||
DLOG("standard_failure_detector: udp_out "..desync.track.pos.direct.pcounter..">="..udp_out.." udp_in "..desync.track.pos.reverse.pcounter.."<="..udp_in)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return trigger
|
||||
end
|
||||
|
||||
-- circularily change strategy numbers when failure count reaches threshold ('fails')
|
||||
-- detected failures: incoming RST, incoming http redirection, outgoing retransmissions
|
||||
-- works with tcp only
|
||||
-- this orchestrator requires redirection of incoming traffic to cache RST and http replies !
|
||||
-- each orchestrated instance must have strategy=N arg, where N starts from 1 and increment without gaps
|
||||
-- if 'final' arg is present in an orchestrated instance it stops rotation
|
||||
-- arg: fails=N - failture count threshold. default is 3
|
||||
-- arg: retrans=N - retrans count threshold. default is 3
|
||||
-- arg: seq=<rseq> - if packet is beyond this relative sequence number treat this connection as successful. default is 64K
|
||||
-- arg: rst=<rseq> - maximum relative sequence number to treat incoming RST as DPI reset. default is 1
|
||||
-- arg: time=<sec> - if last failure happened earlier than `maxtime` seconds ago - reset failure counter. default is 60.
|
||||
-- arg: reqhost - pass with no tampering if hostname is unavailable
|
||||
-- arg: detector - failure detector function name.
|
||||
-- args for failure detector - see standard_failure_detector or your own detector
|
||||
-- test case: nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function circular(ctx, desync)
|
||||
local function count_strategies(hrec, plan)
|
||||
local function count_strategies(hrec)
|
||||
if not hrec.ctstrategy then
|
||||
local uniq={}
|
||||
local n=0
|
||||
for i,instance in pairs(plan) do
|
||||
for i,instance in pairs(desync.plan) do
|
||||
if instance.arg.strategy then
|
||||
n = tonumber(instance.arg.strategy)
|
||||
if not n or n<1 then
|
||||
@@ -109,96 +197,47 @@ function circular(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
-- take over orchestration. prevent further instance execution in case of error
|
||||
execution_plan_cancel(ctx)
|
||||
-- take over execution. prevent further instance execution in case of error
|
||||
orchestrate(ctx, desync)
|
||||
|
||||
if not desync.dis.tcp then
|
||||
DLOG("circular: this orchestrator is tcp only")
|
||||
instance_cutoff(ctx)
|
||||
return
|
||||
end
|
||||
if not desync.track then
|
||||
DLOG_ERR("circular: conntrack is missing but required")
|
||||
return
|
||||
end
|
||||
|
||||
local plan = execution_plan(ctx)
|
||||
if #plan==0 then
|
||||
DLOG("circular: need some desync instances or useless")
|
||||
return
|
||||
end
|
||||
|
||||
local hrec = automate_host_record(desync)
|
||||
if not hrec then
|
||||
DLOG("circular: passing with no tampering")
|
||||
return
|
||||
end
|
||||
|
||||
count_strategies(hrec, plan)
|
||||
count_strategies(hrec)
|
||||
if hrec.ctstrategy==0 then
|
||||
error("circular: add strategy=N tag argument to each following instance ! N must start from 1 and increment")
|
||||
end
|
||||
|
||||
local rstseq = tonumber(desync.arg.rst) or 1
|
||||
local maxseq = tonumber(desync.arg.seq) or 0x10000
|
||||
local fails = tonumber(desync.arg.fails) or 3
|
||||
local retrans = tonumber(desync.arg.retrans) or 3
|
||||
local maxtime = tonumber(desync.arg.time) or 60
|
||||
local crec = automate_conn_record(desync)
|
||||
local pos = bitand(desync.track.tcp.seq - desync.track.tcp.seq0,0xFFFFFFFF)
|
||||
local trigger = false
|
||||
|
||||
if not hrec.nstrategy then
|
||||
DLOG("circular: start from strategy 1")
|
||||
hrec.nstrategy = 1
|
||||
end
|
||||
|
||||
if not crec.nocheck then
|
||||
local seq = pos_get(desync,'s')
|
||||
if seq>maxseq then
|
||||
DLOG("circular: s"..seq.." is beyond s"..maxseq..". treating connection as successful")
|
||||
crec.nocheck = true
|
||||
end
|
||||
end
|
||||
|
||||
local verdict = VERDICT_PASS
|
||||
if not crec.nocheck and hrec.final~=hrec.nstrategy then
|
||||
if desync.outgoing then
|
||||
if #desync.dis.payload>0 and (crec.retrans or 0)<retrans then
|
||||
if not crec.uppos then crec.uppos=0 end
|
||||
if desync.track.tcp.pos_orig<=crec.uppos then
|
||||
crec.retrans = crec.retrans and (crec.retrans+1) or 1
|
||||
DLOG("circular: retransmission "..crec.retrans.."/"..retrans)
|
||||
trigger = crec.retrans>=retrans
|
||||
end
|
||||
if desync.track.tcp.pos_orig>crec.uppos then
|
||||
crec.uppos=desync.track.tcp.pos_orig
|
||||
end
|
||||
if hrec.final~=hrec.nstrategy then
|
||||
local crec = automate_conn_record(desync)
|
||||
local fails = tonumber(desync.arg.fails) or 3
|
||||
local maxtime = tonumber(desync.arg.time) or 60
|
||||
local failure_detector
|
||||
if desync.arg.detector then
|
||||
if type(_G[desync.arg.detector])~="function" then
|
||||
error("circular: invalid failure detector function '"..desync.arg.detector.."'")
|
||||
end
|
||||
failure_detector = _G[desync.arg.detector]
|
||||
else
|
||||
if bitand(desync.dis.tcp.th_flags, TH_RST)~=0 then
|
||||
local seq=u32add(desync.track.tcp.ack, -desync.track.tcp.ack0)
|
||||
trigger = seq<=rstseq
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("circular: incoming RST s"..seq.." in range s"..rstseq)
|
||||
else
|
||||
DLOG("circular: not counting incoming RST s"..seq.." beyond s"..rstseq)
|
||||
end
|
||||
end
|
||||
elseif desync.l7payload=="http_reply" and desync.track.hostname then
|
||||
local hdis = http_dissect_reply(desync.dis.payload)
|
||||
if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then
|
||||
trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value)
|
||||
if trigger and b_debug then
|
||||
DLOG("circular: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'")
|
||||
end
|
||||
end
|
||||
end
|
||||
failure_detector = standard_failure_detector
|
||||
end
|
||||
if trigger then
|
||||
if failure_detector(desync,crec,desync.arg) then
|
||||
-- failure happened. count failures.
|
||||
if automate_failure_counter(hrec, crec, fails, maxtime) then
|
||||
-- circular strategy change
|
||||
-- counter reaches threshold. circular strategy change
|
||||
hrec.nstrategy = (hrec.nstrategy % hrec.ctstrategy) + 1
|
||||
DLOG("circular: rotate strategy to "..hrec.nstrategy)
|
||||
if hrec.nstrategy == hrec.final then
|
||||
@@ -209,22 +248,76 @@ function circular(ctx, desync)
|
||||
end
|
||||
|
||||
DLOG("circular: current strategy "..hrec.nstrategy)
|
||||
local dcopy = desync_copy(desync)
|
||||
for i=1,#plan do
|
||||
if plan[i].arg.strategy and tonumber(plan[i].arg.strategy)==hrec.nstrategy then
|
||||
apply_execution_plan(dcopy, plan[i])
|
||||
if cutoff_shim_check(dcopy) then
|
||||
DLOG("circular: not calling '"..dcopy.func_instance.."' because of voluntary cutoff")
|
||||
elseif not payload_match_filter(dcopy.l7payload, plan[i].payload_filter) then
|
||||
DLOG("circular: not calling '"..dcopy.func_instance.."' because payload '"..dcopy.l7payload.."' does not match filter '"..plan[i].payload_filter.."'")
|
||||
elseif not pos_check_range(dcopy, plan[i].range) then
|
||||
DLOG("circular: not calling '"..dcopy.func_instance.."' because pos "..pos_str(dcopy,plan[i].range.from).." "..pos_str(dcopy,plan[i].range.to).." is out of range '"..pos_range_str(plan[i].range).."'")
|
||||
else
|
||||
DLOG("circular: calling '"..dcopy.func_instance.."'")
|
||||
verdict = verdict_aggregate(verdict,_G[plan[i].func](nil, dcopy))
|
||||
end
|
||||
while true do
|
||||
local instance = plan_instance_pop(desync)
|
||||
if not instance then break end
|
||||
if instance.arg.strategy and tonumber(instance.arg.strategy)==hrec.nstrategy then
|
||||
verdict = plan_instance_execute(desync, verdict, instance)
|
||||
end
|
||||
end
|
||||
|
||||
return verdict
|
||||
end
|
||||
|
||||
-- test iff functions
|
||||
function cond_true(desync)
|
||||
return true
|
||||
end
|
||||
function cond_false(desync)
|
||||
return false
|
||||
end
|
||||
-- arg: percent - of true . 50 by default
|
||||
function cond_random(desync)
|
||||
return math.random(0,99)<(tonumber(desync.arg.percent) or 50)
|
||||
end
|
||||
-- this iif function detects packets having 'arg.pattern' string in their payload
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_payload_str:pattern=1234 --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
-- test case (true) : echo aaz1234zzz | ncat -4u 1.1.1.1 443
|
||||
-- test case (false) : echo aaze124zzz | ncat -4u 1.1.1.1 443
|
||||
function cond_payload_str(desync)
|
||||
if not desync.arg.pattern then
|
||||
error("cond_payload_str: missing 'pattern'")
|
||||
end
|
||||
return string.find(desync.dis.payload,desync.arg.pattern,1,true)
|
||||
end
|
||||
-- check iff function available. error if not
|
||||
function require_iff(desync, name)
|
||||
if not desync.arg.iff then
|
||||
error(name..": missing 'iff' function")
|
||||
end
|
||||
if type(_G[desync.arg.iff])~="function" then
|
||||
error(name..": invalid 'iff' function '"..desync.arg.iff.."'")
|
||||
end
|
||||
end
|
||||
-- execute further desync instances only if user-provided 'iff' function returns true
|
||||
-- for example, this can be used by custom protocol detectors
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_random --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
function condition(ctx, desync)
|
||||
require_iff(desync, "condition")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("condition: true")
|
||||
return replay_execution_plan(desync)
|
||||
else
|
||||
DLOG("condition: false")
|
||||
plan_clear(desync)
|
||||
end
|
||||
end
|
||||
-- clear execution plan if user provided 'iff' functions returns true
|
||||
-- can be used with other orchestrators to stop execution conditionally
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=stopif:iff=cond_random:strategy=1 --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function stopif(ctx, desync)
|
||||
require_iff(desync, "stopif")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("stopif: true")
|
||||
plan_clear(desync)
|
||||
else
|
||||
-- do not do anything. allow other orchestrator to finish the plan
|
||||
DLOG("stopif: false")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
HEXDUMP_DLOG_MAX = HEXDUMP_DLOG_MAX or 32
|
||||
NOT3=bitnot(3)
|
||||
NOT7=bitnot(7)
|
||||
math.randomseed(os.time())
|
||||
|
||||
-- xor pid,tid,sec,nsec
|
||||
math.randomseed(bitxor(getpid(),gettid(),clock_gettime()))
|
||||
|
||||
-- basic desync function
|
||||
-- execute given lua code. "desync" is temporary set as global var to be accessible to the code
|
||||
@@ -37,16 +37,24 @@ function pktdebug(ctx, desync)
|
||||
end
|
||||
-- basic desync function
|
||||
-- prints function args
|
||||
function argdebug(ctx,desync)
|
||||
function argdebug(ctx, desync)
|
||||
var_debug(desync.arg)
|
||||
end
|
||||
|
||||
-- basic desync function
|
||||
-- prints conntrack positions to DLOG
|
||||
function posdebug(ctx,desync)
|
||||
local s="posdebug:"
|
||||
for i,pos in pairs({'n','d','b','s'}) do
|
||||
s=s.." "..pos..pos_get(desync,pos)
|
||||
function posdebug(ctx, desync)
|
||||
if not desync.track then
|
||||
DLOG("posdebug: no track")
|
||||
return
|
||||
end
|
||||
local s="posdebug: "..(desync.outgoing and "out" or "in").." time +"..desync.track.pos.dt.."s direct"
|
||||
for i,pos in pairs({'n','d','b','s','p'}) do
|
||||
s=s.." "..pos..pos_get(desync, pos, false)
|
||||
end
|
||||
s=s.." reverse"
|
||||
for i,pos in pairs({'n','d','b','s','p'}) do
|
||||
s=s.." "..pos..pos_get(desync, pos, true)
|
||||
end
|
||||
s=s.." payload "..#desync.dis.payload
|
||||
if desync.reasm_data then
|
||||
@@ -61,6 +69,30 @@ function posdebug(ctx,desync)
|
||||
DLOG(s)
|
||||
end
|
||||
|
||||
-- basic desync function
|
||||
-- set l7payload to 'arg.payload' if reasm.data or desync.dis.payload contains 'arg.pattern' substring
|
||||
-- NOTE : this does not set payload on C code side !
|
||||
-- NOTE : C code will not see payload change. --payload args take only payloads known to C code and cause error if unknown.
|
||||
-- arg: pattern - substring for search inside reasm_data or desync.dis.payload
|
||||
-- arg: payload - set desync.l7payload to this if detected
|
||||
-- arg: undetected - set desync.l7payload to this if not detected
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua --lua-init=@zapret-auto.lua --lua-desync=detect_payload_str:pattern=1234:payload=my --lua-desync=fake:blob=0x1234:payload=my
|
||||
function detect_payload_str(ctx, desync)
|
||||
if not desync.arg.pattern then
|
||||
error("detect_payload_str: missing 'pattern'")
|
||||
end
|
||||
local data = desync.reasm_data or desync.dis.payload
|
||||
local b = string.find(data,desync.arg.pattern,1,true)
|
||||
if b then
|
||||
DLOG("detect_payload_str: detected '"..desync.arg.payload.."'")
|
||||
if desync.arg.payload then desync.l7payload = desync.arg.payload end
|
||||
else
|
||||
DLOG("detect_payload_str: not detected '"..desync.arg.payload.."'")
|
||||
if desync.arg.undetected then desync.l7payload = desync.arg.undetected end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- this shim is needed then function is orchestrated. ctx services not available
|
||||
-- have to emulate cutoff in LUA using connection persistent table track.lua_state
|
||||
function instance_cutoff_shim(ctx, desync, dir)
|
||||
@@ -102,28 +134,32 @@ end
|
||||
|
||||
|
||||
-- applies # and $ prefixes. #var means var length, %var means var value
|
||||
function apply_arg_prefix(arg)
|
||||
for a,v in pairs(arg) do
|
||||
function apply_arg_prefix(desync)
|
||||
for a,v in pairs(desync.arg) do
|
||||
local c = string.sub(v,1,1)
|
||||
if v=='#' then
|
||||
arg[a] = #_G[string.sub(v,2)]
|
||||
elseif v=='%' then
|
||||
arg[a] = _G[string.sub(v,2)]
|
||||
elseif v=='\\' then
|
||||
if c=='#' then
|
||||
local blb = blob(desync,string.sub(v,2))
|
||||
desync.arg[a] = (type(blb)=='string' or type(blb)=='table') and #blb or 0
|
||||
elseif c=='%' then
|
||||
desync.arg[a] = blob(desync,string.sub(v,2))
|
||||
elseif c=='\\' then
|
||||
c = string.sub(v,2,2);
|
||||
if c=='#' or c=='%' then
|
||||
arg[a] = string.sub(v,2)
|
||||
desync.arg[a] = string.sub(v,2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- copy instance identification and args from execution plan to desync table
|
||||
function apply_execution_plan(desync, plan)
|
||||
desync.func = plan.func
|
||||
desync.func_n = plan.func_n
|
||||
desync.func_instance = plan.func_instance
|
||||
desync.arg = deepcopy(plan.arg)
|
||||
apply_arg_prefix(desync.arg)
|
||||
-- NOTE : to not lose VERDICT_MODIFY dissect changes pass original desync table
|
||||
-- NOTE : if a copy was passed and VERDICT_MODIFY returned you must copy modified dissect back to desync table or resend it and return VERDICT_DROP
|
||||
-- NOTE : args and some fields are substituted. if you need them - make a copy before calling this.
|
||||
function apply_execution_plan(desync, instance)
|
||||
desync.func = instance.func
|
||||
desync.func_n = instance.func_n
|
||||
desync.func_instance = instance.func_instance
|
||||
desync.arg = deepcopy(instance.arg)
|
||||
apply_arg_prefix(desync)
|
||||
end
|
||||
-- produce resulting verdict from 2 verdicts
|
||||
function verdict_aggregate(v1, v2)
|
||||
@@ -139,25 +175,33 @@ function verdict_aggregate(v1, v2)
|
||||
end
|
||||
return v
|
||||
end
|
||||
-- redo what whould be done without orchestration
|
||||
function replay_execution_plan(desync, plan)
|
||||
local verdict = VERDICT_PASS
|
||||
for i=1,#plan do
|
||||
if cutoff_shim_check(desync) then
|
||||
DLOG("orchestrator: not calling '"..desync.func_instance.."' because of voluntary cutoff")
|
||||
elseif not payload_match_filter(desync.l7payload, plan[i].payload_filter) then
|
||||
DLOG("orchestrator: not calling '"..desync.func_instance.."' because payload '"..desync.l7payload.."' does not match filter '"..plan[i].payload_filter.."'")
|
||||
elseif not pos_check_range(desync, plan[i].range) then
|
||||
DLOG("orchestrator: not calling '"..desync.func_instance.."' because pos "..pos_str(desync,plan[i].range.from).." "..pos_str(desync,plan[i].range.to).." is out of range '"..pos_range_str(plan[i].range).."'")
|
||||
else
|
||||
apply_execution_plan(desync, plan[i])
|
||||
DLOG("orchestrator: calling '"..desync.func_instance.."'")
|
||||
verdict = verdict_aggregate(verdict,_G[plan[i].func](nil, desync))
|
||||
end
|
||||
function plan_instance_execute(desync, verdict, instance)
|
||||
apply_execution_plan(desync, instance)
|
||||
if cutoff_shim_check(desync) then
|
||||
DLOG("plan_instance_execute: not calling '"..desync.func_instance.."' because of voluntary cutoff")
|
||||
elseif not payload_match_filter(desync.l7payload, instance.payload_filter) then
|
||||
DLOG("plan_instance_execute: not calling '"..desync.func_instance.."' because payload '"..desync.l7payload.."' does not match filter '"..instance.payload_filter.."'")
|
||||
elseif not pos_check_range(desync, instance.range) then
|
||||
DLOG("plan_instance_execute: not calling '"..desync.func_instance.."' because pos "..pos_str(desync,instance.range.from).." "..pos_str(desync,instance.range.to).." is out of range '"..pos_range_str(instance.range).."'")
|
||||
else
|
||||
DLOG("plan_instance_execute: calling '"..desync.func_instance.."'")
|
||||
verdict = verdict_aggregate(verdict,_G[instance.func](nil, desync))
|
||||
end
|
||||
return verdict
|
||||
end
|
||||
|
||||
function plan_instance_pop(desync)
|
||||
return (desync.plan and #desync.plan>0) and table.remove(desync.plan, 1)
|
||||
end
|
||||
function plan_clear(desync)
|
||||
while table.remove(desync.plan) do end
|
||||
end
|
||||
-- this approach allows nested orchestrators
|
||||
function orchestrate(ctx, desync)
|
||||
if not desync.plan then
|
||||
execution_plan_cancel(ctx)
|
||||
desync.plan = execution_plan(ctx)
|
||||
end
|
||||
end
|
||||
-- copy desync preserving lua_state
|
||||
function desync_copy(desync)
|
||||
local dcopy = deepcopy(desync)
|
||||
@@ -165,41 +209,61 @@ function desync_copy(desync)
|
||||
-- preserve lua state
|
||||
dcopy.track.lua_state = desync.track.lua_state
|
||||
end
|
||||
if desync.plan then
|
||||
-- preserve execution plan
|
||||
dcopy.plan = desync.plan
|
||||
end
|
||||
return dcopy
|
||||
end
|
||||
|
||||
-- redo what whould be done without orchestration
|
||||
function replay_execution_plan(desync)
|
||||
local verdict = VERDICT_PASS
|
||||
while true do
|
||||
local instance = plan_instance_pop(desync)
|
||||
if not instance then break end
|
||||
verdict = plan_instance_execute(desync, verdict, instance)
|
||||
end
|
||||
return verdict
|
||||
end
|
||||
-- this function demonstrates how to stop execution of upcoming desync instances and take over their job
|
||||
-- this can be used, for example, for orchestrating conditional processing without modifying of desync functions code
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-desync=desync_orchestrator_example --lua-desync=pass --lua-desync=pass
|
||||
function desync_orchestrator_example(ctx, desync)
|
||||
local plan = execution_plan(ctx)
|
||||
if #plan>0 then
|
||||
DLOG("orchestrator: taking over upcoming desync instances")
|
||||
local dcopy = desync_copy(desync)
|
||||
execution_plan_cancel(ctx)
|
||||
return replay_execution_plan(dcopy, plan)
|
||||
end
|
||||
DLOG("orchestrator: taking over upcoming desync instances")
|
||||
orchestrate(ctx, desync)
|
||||
return replay_execution_plan(desync)
|
||||
end
|
||||
|
||||
-- these function duplicate range check logic from C code
|
||||
-- these functions duplicate range check logic from C code
|
||||
-- mode must be n,d,b,s,x,a
|
||||
-- pos is {mode,pos}
|
||||
-- range is {from={mode,pos}, to={mode,pos}, upper_cutoff}
|
||||
-- upper_cutoff = true means non-inclusive upper boundary
|
||||
function pos_get(desync, mode)
|
||||
if desync.track then
|
||||
function pos_get_pos(track_pos, mode)
|
||||
if track_pos then
|
||||
if mode=='n' then
|
||||
return desync.outgoing and desync.track.pcounter_orig or desync.track.pcounter_reply
|
||||
return track_pos.pcounter
|
||||
elseif mode=='d' then
|
||||
return desync.outgoing and desync.track.pdcounter_orig or desync.track.pdcounter_reply
|
||||
return track_pos.pdcounter
|
||||
elseif mode=='b' then
|
||||
return desync.outgoing and desync.track.pbcounter_orig or desync.track.pbcounter_reply
|
||||
elseif mode=='s' and desync.track.tcp then
|
||||
return desync.outgoing and u32add(desync.track.tcp.seq, -desync.track.tcp.seq0) or u32add(desync.track.tcp.ack, -desync.track.tcp.ack0)
|
||||
return track_pos.pbcounter
|
||||
elseif track_pos.tcp then
|
||||
if mode=='s' then
|
||||
return track_pos.tcp.rseq
|
||||
elseif mode=='p' then
|
||||
return track_pos.tcp.pos
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function pos_get(desync, mode, reverse)
|
||||
if desync.track then
|
||||
local track_pos = reverse and desync.track.pos.reverse or desync.track.pos.direct
|
||||
return pos_get_pos(track_pos,mode)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function pos_check_from(desync, range)
|
||||
if range.from.mode == 'x' then return false end
|
||||
if range.from.mode ~= 'a' then
|
||||
@@ -233,7 +297,9 @@ end
|
||||
function pos_str(desync, pos)
|
||||
return pos.mode..pos_get(desync, pos.mode)
|
||||
end
|
||||
|
||||
function is_retransmission(desync)
|
||||
return desync.track and desync.track.tcp and 0==bitand(u32add(desync.track.tcp.uppos_orig_prev, -desync.track.tcp.pos_orig), 0x80000000)
|
||||
end
|
||||
|
||||
-- prepare standard rawsend options from desync
|
||||
-- repeats - how many time send the packet
|
||||
@@ -305,6 +371,9 @@ function str_or_hex(s)
|
||||
return s
|
||||
end
|
||||
end
|
||||
function logical_xor(a,b)
|
||||
return a and not b or not a and b
|
||||
end
|
||||
-- print to DLOG any variable. tables are expanded in the tree form, unprintables strings are hex dumped
|
||||
function var_debug(v)
|
||||
local function dbg(v,level)
|
||||
@@ -567,6 +636,19 @@ function dissect_nld(domain, level)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- support sni=%var
|
||||
function tls_mod_shim(desync, blob, modlist, payload)
|
||||
local p1,p2 = string.find(modlist,"sni=%%[^,]+")
|
||||
if p1 then
|
||||
local var = string.sub(modlist,p1+5,p2)
|
||||
local val = desync[var] or _G[var]
|
||||
if not val then
|
||||
error("tls_mod_shim: non-existent var '"..var.."'")
|
||||
end
|
||||
modlist = string.sub(modlist,1,p1+3)..val..string.sub(modlist,p2+1)
|
||||
end
|
||||
return tls_mod(blob,modlist,payload)
|
||||
end
|
||||
|
||||
-- convert comma separated list of tcp flags to tcp.th_flags bit field
|
||||
function parse_tcp_flags(s)
|
||||
@@ -1310,16 +1392,3 @@ function ipfrag2(dis, ipfrag_options)
|
||||
|
||||
return {dis1,dis2}
|
||||
end
|
||||
|
||||
|
||||
-- location is url compatible with Location: header
|
||||
-- hostname is original hostname
|
||||
function is_dpi_redirect(hostname, location)
|
||||
local ds = dissect_url(location)
|
||||
if ds.domain then
|
||||
local sld1 = dissect_nld(hostname,2)
|
||||
local sld2 = dissect_nld(ds.domain,2)
|
||||
return sld2 and sld1~=sld2
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
165
nfq2/conntrack.c
165
nfq2/conntrack.c
@@ -37,7 +37,7 @@ void ConntrackClearHostname(t_ctrack *track)
|
||||
static void ConntrackClearTrack(t_ctrack *track)
|
||||
{
|
||||
ConntrackClearHostname(track);
|
||||
ReasmClear(&track->reasm_orig);
|
||||
ReasmClear(&track->reasm_client);
|
||||
rawpacket_queue_destroy(&track->delayed);
|
||||
luaL_unref(params.L, LUA_REGISTRYINDEX, track->lua_state);
|
||||
luaL_unref(params.L, LUA_REGISTRYINDEX, track->lua_instance_cutoff);
|
||||
@@ -102,8 +102,7 @@ static void ConntrackInitTrack(t_ctrack *t)
|
||||
{
|
||||
memset(t, 0, sizeof(*t));
|
||||
t->l7proto = L7_UNKNOWN;
|
||||
t->pos.scale_orig = t->pos.scale_reply = SCALE_NONE;
|
||||
time(&t->pos.t_start);
|
||||
t->pos.client.scale = t->pos.server.scale = SCALE_NONE;
|
||||
rawpacket_queue_init(&t->delayed);
|
||||
lua_newtable(params.L);
|
||||
t->lua_state = luaL_ref(params.L, LUA_REGISTRYINDEX);
|
||||
@@ -128,6 +127,36 @@ static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c)
|
||||
return ctnew;
|
||||
}
|
||||
|
||||
static void ConntrackApplyPos(const struct tcphdr *tcp, t_ctrack *t, bool bReverse, uint32_t len_payload)
|
||||
{
|
||||
uint8_t scale;
|
||||
uint16_t mss;
|
||||
t_ctrack_position *direct, *reverse;
|
||||
|
||||
direct = bReverse ? &t->pos.server : &t->pos.client;
|
||||
reverse = bReverse ? &t->pos.client : &t->pos.server;
|
||||
|
||||
scale = tcp_find_scale_factor(tcp);
|
||||
mss = ntohs(tcp_find_mss(tcp));
|
||||
|
||||
direct->seq_last = ntohl(tcp->th_seq);
|
||||
direct->pos = direct->seq_last + len_payload;
|
||||
reverse->pos = reverse->seq_last = ntohl(tcp->th_ack);
|
||||
if (t->pos.state == SYN)
|
||||
direct->uppos_prev = direct->uppos = direct->pos;
|
||||
else if (len_payload)
|
||||
{
|
||||
direct->uppos_prev = direct->uppos;
|
||||
if (!((direct->pos - direct->uppos) & 0x80000000))
|
||||
direct->uppos = direct->pos;
|
||||
}
|
||||
direct->winsize = ntohs(tcp->th_win);
|
||||
direct->winsize_calc = direct->winsize;
|
||||
if (direct->scale != SCALE_NONE) direct->winsize_calc <<= direct->scale;
|
||||
if (mss && !direct->mss) direct->mss = mss;
|
||||
if (scale != SCALE_NONE) direct->scale = scale;
|
||||
}
|
||||
|
||||
// non-tcp packets are passed with tcphdr=NULL but len_payload filled
|
||||
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload)
|
||||
{
|
||||
@@ -136,16 +165,16 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
|
||||
|
||||
if (bReverse)
|
||||
{
|
||||
t->pos.pcounter_reply++;
|
||||
t->pos.pdcounter_reply += !!len_payload;
|
||||
t->pos.pbcounter_reply += len_payload;
|
||||
t->pos.server.pcounter++;
|
||||
t->pos.server.pdcounter += !!len_payload;
|
||||
t->pos.server.pbcounter += len_payload;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
t->pos.pcounter_orig++;
|
||||
t->pos.pdcounter_orig += !!len_payload;
|
||||
t->pos.pbcounter_orig += len_payload;
|
||||
t->pos.client.pcounter++;
|
||||
t->pos.client.pdcounter += !!len_payload;
|
||||
t->pos.client.pbcounter += len_payload;
|
||||
}
|
||||
|
||||
if (tcphdr)
|
||||
@@ -153,16 +182,16 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
|
||||
if (tcp_syn_segment(tcphdr))
|
||||
{
|
||||
if (t->pos.state != SYN) ConntrackReInitTrack(t); // erase current entry
|
||||
t->pos.seq0 = ntohl(tcphdr->th_seq);
|
||||
t->pos.client.seq0 = ntohl(tcphdr->th_seq);
|
||||
}
|
||||
else if (tcp_synack_segment(tcphdr))
|
||||
{
|
||||
// ignore SA dups
|
||||
uint32_t seq0 = ntohl(tcphdr->th_ack) - 1;
|
||||
if (t->pos.state != SYN && t->pos.seq0 != seq0)
|
||||
if (t->pos.state != SYN && t->pos.client.seq0 != seq0)
|
||||
ConntrackReInitTrack(t); // erase current entry
|
||||
if (!t->pos.seq0) t->pos.seq0 = seq0;
|
||||
t->pos.ack0 = ntohl(tcphdr->th_seq);
|
||||
if (!t->pos.client.seq0) t->pos.client.seq0 = seq0;
|
||||
t->pos.server.seq0 = ntohl(tcphdr->th_seq);
|
||||
}
|
||||
else if (tcphdr->th_flags & (TH_FIN | TH_RST))
|
||||
{
|
||||
@@ -173,49 +202,28 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
|
||||
if (t->pos.state == SYN)
|
||||
{
|
||||
t->pos.state = ESTABLISHED;
|
||||
if (!bReverse && !t->pos.ack0) t->pos.ack0 = ntohl(tcphdr->th_ack) - 1;
|
||||
if (!bReverse && !t->pos.server.seq0) t->pos.server.seq0 = ntohl(tcphdr->th_ack) - 1;
|
||||
}
|
||||
}
|
||||
scale = tcp_find_scale_factor(tcphdr);
|
||||
mss = ntohs(tcp_find_mss(tcphdr));
|
||||
if (bReverse)
|
||||
{
|
||||
t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack);
|
||||
t->pos.ack_last = ntohl(tcphdr->th_seq);
|
||||
t->pos.pos_reply = t->pos.ack_last + len_payload;
|
||||
t->pos.winsize_reply = ntohs(tcphdr->th_win);
|
||||
t->pos.winsize_reply_calc = t->pos.winsize_reply;
|
||||
if (t->pos.scale_reply != SCALE_NONE) t->pos.winsize_reply_calc <<= t->pos.scale_reply;
|
||||
if (mss && !t->pos.mss_reply) t->pos.mss_reply = mss;
|
||||
if (scale != SCALE_NONE) t->pos.scale_reply = scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
t->pos.seq_last = ntohl(tcphdr->th_seq);
|
||||
t->pos.pos_orig = t->pos.seq_last + len_payload;
|
||||
t->pos.pos_reply = t->pos.ack_last = ntohl(tcphdr->th_ack);
|
||||
t->pos.winsize_orig = ntohs(tcphdr->th_win);
|
||||
t->pos.winsize_orig_calc = t->pos.winsize_orig;
|
||||
if (t->pos.scale_orig != SCALE_NONE) t->pos.winsize_orig_calc <<= t->pos.scale_orig;
|
||||
if (mss && !t->pos.mss_reply) t->pos.mss_orig = mss;
|
||||
if (scale != SCALE_NONE) t->pos.scale_orig = scale;
|
||||
}
|
||||
|
||||
ConntrackApplyPos(tcphdr, t, bReverse, len_payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bReverse)
|
||||
{
|
||||
t->pos.ack_last = t->pos.pos_reply;
|
||||
t->pos.pos_reply += len_payload;
|
||||
t->pos.server.seq_last = t->pos.server.pos;
|
||||
t->pos.server.pos += len_payload;
|
||||
}
|
||||
else
|
||||
{
|
||||
t->pos.seq_last = t->pos.pos_orig;
|
||||
t->pos.pos_orig += len_payload;
|
||||
t->pos.client.seq_last = t->pos.client.pos;
|
||||
t->pos.client.pos += len_payload;
|
||||
}
|
||||
}
|
||||
|
||||
time(&t->pos.t_last);
|
||||
clock_gettime(CLOCK_REALTIME, &t->pos.t_last);
|
||||
// make sure t_start gets exactly the same value as first t_last
|
||||
if (!t->t_start.tv_sec) t->t_start = t->pos.t_last;
|
||||
}
|
||||
|
||||
static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
|
||||
@@ -311,13 +319,15 @@ bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr
|
||||
|
||||
void ConntrackPoolPurge(t_conntrack *p)
|
||||
{
|
||||
time_t tidle, tnow = time(NULL);
|
||||
time_t tidle;
|
||||
struct timespec tnow;
|
||||
t_conntrack_pool *t, *tmp;
|
||||
|
||||
if ((tnow - p->t_last_purge) >= p->t_purge_interval)
|
||||
if (clock_gettime(CLOCK_REALTIME, &tnow)) return;
|
||||
if ((tnow.tv_sec - p->t_last_purge) >= p->t_purge_interval)
|
||||
{
|
||||
HASH_ITER(hh, p->pool, t, tmp) {
|
||||
tidle = tnow - t->track.pos.t_last;
|
||||
tidle = tnow.tv_sec - t->track.pos.t_last.tv_sec;
|
||||
if (t->track.b_cutoff ||
|
||||
(t->conn.l4proto == IPPROTO_TCP && (
|
||||
(t->track.pos.state == SYN && tidle >= p->timeout_syn) ||
|
||||
@@ -329,7 +339,7 @@ void ConntrackPoolPurge(t_conntrack *p)
|
||||
HASH_DEL(p->pool, t); ConntrackFreeElem(t);
|
||||
}
|
||||
}
|
||||
p->t_last_purge = tnow;
|
||||
p->t_last_purge = tnow.tv_sec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,29 +351,31 @@ static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsiz
|
||||
void ConntrackPoolDump(const t_conntrack *p)
|
||||
{
|
||||
t_conntrack_pool *t, *tmp;
|
||||
struct timespec tnow;
|
||||
char sa1[40], sa2[40];
|
||||
time_t tnow = time(NULL);
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &tnow)) return;
|
||||
HASH_ITER(hh, p->pool, t, tmp) {
|
||||
taddr2str(t->conn.l3proto, &t->conn.src, sa1, sizeof(sa1));
|
||||
taddr2str(t->conn.l3proto, &t->conn.dst, sa2, sizeof(sa2));
|
||||
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu orig=d%llu/n%llu/b%llu reply=d%llu/n%llu/b%lld ",
|
||||
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu client=d%llu/n%llu/b%llu server=d%llu/n%llu/b%lld ",
|
||||
proto_name(t->conn.l4proto),
|
||||
sa1, t->conn.sport, sa2, t->conn.dport,
|
||||
t->conn.l4proto == IPPROTO_TCP ? connstate_s[t->track.pos.state] : "-",
|
||||
(unsigned long long)t->track.pos.t_start, (unsigned long long)(t->track.pos.t_last - t->track.pos.t_start), (unsigned long long)(tnow - t->track.pos.t_last),
|
||||
(unsigned long long)t->track.pos.pdcounter_orig, (unsigned long long)t->track.pos.pcounter_orig, (unsigned long long)t->track.pos.pbcounter_orig,
|
||||
(unsigned long long)t->track.pos.pdcounter_reply, (unsigned long long)t->track.pos.pcounter_reply, (unsigned long long)t->track.pos.pbcounter_reply);
|
||||
(unsigned long long)t->track.t_start.tv_sec, (unsigned long long)(t->track.pos.t_last.tv_sec - t->track.t_start.tv_sec), (unsigned long long)(tnow.tv_sec - t->track.pos.t_last.tv_sec),
|
||||
(unsigned long long)t->track.pos.client.pdcounter, (unsigned long long)t->track.pos.client.pcounter, (unsigned long long)t->track.pos.client.pbcounter,
|
||||
(unsigned long long)t->track.pos.server.pdcounter, (unsigned long long)t->track.pos.server.pcounter, (unsigned long long)t->track.pos.server.pbcounter);
|
||||
if (t->conn.l4proto == IPPROTO_TCP)
|
||||
printf("seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u mss_orig=%u mss_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d",
|
||||
t->track.pos.seq0, t->track.pos.seq_last - t->track.pos.seq0, t->track.pos.pos_orig - t->track.pos.seq0,
|
||||
t->track.pos.ack0, t->track.pos.ack_last - t->track.pos.ack0, t->track.pos.pos_reply - t->track.pos.ack0,
|
||||
t->track.pos.mss_orig, t->track.pos.mss_reply,
|
||||
t->track.pos.winsize_orig, t->track.pos.scale_orig == SCALE_NONE ? -1 : t->track.pos.scale_orig,
|
||||
t->track.pos.winsize_reply, t->track.pos.scale_reply == SCALE_NONE ? -1 : t->track.pos.scale_reply);
|
||||
printf("seq0=%u rseq=%u client.pos=%u ack0=%u rack=%u server.pos=%u client.mss=%u server.mss=%u client.wsize=%u:%d server.wsize=%u:%d",
|
||||
t->track.pos.client.seq0, t->track.pos.client.seq_last - t->track.pos.client.seq0, t->track.pos.client.pos - t->track.pos.client.seq0,
|
||||
t->track.pos.server.seq0, t->track.pos.server.seq_last - t->track.pos.server.seq0, t->track.pos.server.pos - t->track.pos.server.seq0,
|
||||
t->track.pos.client.mss, t->track.pos.server.mss,
|
||||
t->track.pos.client.winsize, t->track.pos.client.scale == SCALE_NONE ? -1 : t->track.pos.client.scale,
|
||||
t->track.pos.server.winsize, t->track.pos.server.scale == SCALE_NONE ? -1 : t->track.pos.server.scale);
|
||||
else
|
||||
printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u",
|
||||
t->track.pos.seq_last, t->track.pos.pos_orig,
|
||||
t->track.pos.ack_last, t->track.pos.pos_reply);
|
||||
printf("rseq=%u client.pos=%u rack=%u server.pos=%u",
|
||||
t->track.pos.client.seq_last, t->track.pos.client.pos,
|
||||
t->track.pos.server.seq_last, t->track.pos.server.pos);
|
||||
printf(" req_retrans=%u cutoff=%u lua_in_cutoff=%u lua_out_cutoff=%u hostname=%s l7proto=%s\n",
|
||||
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_lua_in_cutoff, t->track.b_lua_out_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
|
||||
};
|
||||
@@ -394,17 +406,30 @@ bool ReasmResize(t_reassemble *reasm, size_t new_size)
|
||||
if (reasm->size_present > new_size) reasm->size_present = new_size;
|
||||
return true;
|
||||
}
|
||||
#define REASM_MAX_NEG 0x100000
|
||||
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len)
|
||||
{
|
||||
if (reasm->seq != seq) return false; // fail session if out of sequence
|
||||
|
||||
size_t szcopy;
|
||||
szcopy = reasm->size - reasm->size_present;
|
||||
if (len < szcopy) szcopy = len;
|
||||
memcpy(reasm->packet + reasm->size_present, payload, szcopy);
|
||||
reasm->size_present += szcopy;
|
||||
reasm->seq += (uint32_t)szcopy;
|
||||
uint32_t dseq = seq - reasm->seq;
|
||||
if (dseq && (dseq < REASM_MAX_NEG))
|
||||
return false; // fail session if a gap about to appear
|
||||
uint32_t neg_overlap = reasm->seq - seq;
|
||||
if (neg_overlap > REASM_MAX_NEG)
|
||||
return false; // too big minus
|
||||
|
||||
size_t szcopy, szignore;
|
||||
szignore = (neg_overlap > reasm->size_present) ? neg_overlap - reasm->size_present : 0;
|
||||
if (szignore>=len) return true; // everyting is before the starting pos
|
||||
szcopy = len - szignore;
|
||||
neg_overlap -= szignore;
|
||||
if ((reasm->size_present - neg_overlap + szcopy) > reasm->size)
|
||||
return false; // buffer overflow
|
||||
// in case of seq overlap new data replaces old - unix behavior
|
||||
memcpy(reasm->packet + reasm->size_present - neg_overlap, payload + szignore, szcopy);
|
||||
if (szcopy>neg_overlap)
|
||||
{
|
||||
reasm->size_present += szcopy - neg_overlap;
|
||||
reasm->seq += (uint32_t)szcopy - neg_overlap;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ReasmHasSpace(t_reassemble *reasm, size_t len)
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct
|
||||
// this structure helps to reassemble continuous packets streams. it does not support out-of-orders
|
||||
typedef struct {
|
||||
uint8_t *packet; // allocated for size during reassemble request. requestor must know the message size.
|
||||
uint32_t seq; // current seq number. if a packet comes with an unexpected seq - it fails reassemble session.
|
||||
uint32_t seq; // current seq number. if a packet comes with unsupported seq overlap - it fails reassemble session.
|
||||
size_t size; // expected message size. success means that we have received exactly 'size' bytes and have them in 'packet'
|
||||
size_t size_present; // how many bytes already stored in 'packet'
|
||||
} t_reassemble;
|
||||
@@ -54,15 +54,16 @@ typedef struct
|
||||
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
|
||||
uint8_t ipproto;
|
||||
|
||||
struct timespec t_start;
|
||||
|
||||
// this block of data can change between delayed (queued) packets. need to remeber this data for each packet for further replay
|
||||
t_ctrack_position pos;
|
||||
t_ctrack_positions pos;
|
||||
|
||||
struct desync_profile *dp; // desync profile cache
|
||||
bool dp_search_complete;
|
||||
|
||||
uint8_t req_retrans_counter; // number of request retransmissions
|
||||
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
|
||||
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
|
||||
bool failure_detect_finalized;
|
||||
|
||||
uint8_t incoming_ttl;
|
||||
|
||||
@@ -79,7 +80,7 @@ typedef struct
|
||||
int lua_state; // registry index of associated LUA object
|
||||
int lua_instance_cutoff; // registry index of per connection function instance cutoff table
|
||||
|
||||
t_reassemble reasm_orig;
|
||||
t_reassemble reasm_client;
|
||||
struct rawpacket_tailhead delayed;
|
||||
} t_ctrack;
|
||||
|
||||
|
||||
@@ -14,19 +14,26 @@ typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
time_t t_last, t_start;
|
||||
|
||||
uint64_t pcounter_orig, pcounter_reply; // packet counter
|
||||
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
|
||||
uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
|
||||
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
|
||||
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
|
||||
uint64_t pcounter; // packet counter
|
||||
uint64_t pdcounter; // data packet counter (with payload)
|
||||
uint64_t pbcounter; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
|
||||
uint32_t pos; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
|
||||
uint32_t uppos; // max seen position. useful to detect retransmissions
|
||||
uint32_t uppos_prev; // previous max seen position. useful to detect retransmissions
|
||||
uint32_t seq_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
|
||||
|
||||
// tcp only state, not used in udp
|
||||
t_connstate state;
|
||||
uint32_t seq0, ack0; // starting seq and ack
|
||||
uint16_t winsize_orig, winsize_reply; // last seen window size
|
||||
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
|
||||
uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size
|
||||
uint16_t mss_orig, mss_reply;
|
||||
uint32_t seq0; // starting seq and ack
|
||||
uint16_t winsize; // last seen window size
|
||||
uint8_t scale; // last seen window scale factor. SCALE_NONE if none
|
||||
uint32_t winsize_calc; // calculated window size
|
||||
uint16_t mss;
|
||||
} t_ctrack_position;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct timespec t_last;
|
||||
t_connstate state;
|
||||
t_ctrack_position client, server;
|
||||
}
|
||||
t_ctrack_positions;
|
||||
|
||||
343
nfq2/desync.c
343
nfq2/desync.c
@@ -240,31 +240,38 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_retransmission(const t_ctrack_position *pos)
|
||||
{
|
||||
return !((pos->uppos_prev - pos->pos) & 0x80000000);
|
||||
}
|
||||
|
||||
// return true if retrans trigger fires
|
||||
static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int threshold, const char *client_ip_port, t_l7proto l7proto)
|
||||
{
|
||||
if (ctrack && ctrack->dp && ctrack->hostname_ah_check && ctrack->req_retrans_counter != RETRANS_COUNTER_STOP)
|
||||
if (ctrack && ctrack->dp && ctrack->hostname_ah_check && !ctrack->failure_detect_finalized && ctrack->req_retrans_counter != RETRANS_COUNTER_STOP)
|
||||
{
|
||||
if (l4proto == IPPROTO_TCP)
|
||||
if (l4proto == IPPROTO_TCP && ctrack->pos.state!=SYN)
|
||||
{
|
||||
if (!ctrack->req_seq_finalized || ctrack->req_seq_abandoned)
|
||||
return false;
|
||||
if (!seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end))
|
||||
if (!seq_within(ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq))
|
||||
{
|
||||
DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
ctrack->failure_detect_finalized = true;
|
||||
DLOG("retrans : tcp seq %u not within range %u-%u. stop tracking.\n", ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname, client_ip_port, l7proto);
|
||||
return false;
|
||||
}
|
||||
if (!is_retransmission(&ctrack->pos.client))
|
||||
return false;
|
||||
}
|
||||
ctrack->req_retrans_counter++;
|
||||
if (ctrack->req_retrans_counter >= threshold)
|
||||
{
|
||||
DLOG("req retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
DLOG("retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
ctrack->failure_detect_finalized = true;
|
||||
return true;
|
||||
}
|
||||
DLOG("req retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
DLOG("retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -316,23 +323,50 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_client_ip_port(const struct sockaddr *client, char *client_ip_port, size_t client_ip_port_size)
|
||||
{
|
||||
if (*params.hostlist_auto_debuglog)
|
||||
ntop46_port((struct sockaddr*)client, client_ip_port, client_ip_port_size);
|
||||
else
|
||||
*client_ip_port = 0;
|
||||
}
|
||||
static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto, const struct sockaddr *client)
|
||||
{
|
||||
if (params.server) return; // no autohostlists in server mode
|
||||
|
||||
char client_ip_port[48];
|
||||
if (*params.hostlist_auto_debuglog)
|
||||
ntop46_port((struct sockaddr*)client, client_ip_port, sizeof(client_ip_port));
|
||||
else
|
||||
*client_ip_port = 0;
|
||||
fill_client_ip_port(client, client_ip_port, sizeof(client_ip_port));
|
||||
if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold, client_ip_port, ctrack->l7proto))
|
||||
{
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : retrans threshold reached", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(ctrack->dp), client_ip_port, l7proto_str(ctrack->l7proto));
|
||||
auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_udp_fail(t_ctrack *ctrack, const t_ctrack_positions *tpos, const struct sockaddr *client)
|
||||
{
|
||||
// no autohostlists in server mode
|
||||
if (!params.server && ctrack && ctrack->dp && ctrack->hostname && ctrack->hostname_ah_check &&
|
||||
!ctrack->failure_detect_finalized && ctrack->dp->hostlist_auto_udp_out)
|
||||
{
|
||||
if (!tpos) tpos = &ctrack->pos;
|
||||
//printf("UDP_POS %u %u\n",tpos->client.pcounter, tpos->server.pcounter);
|
||||
if (tpos->server.pcounter > ctrack->dp->hostlist_auto_udp_in)
|
||||
// success
|
||||
ctrack->failure_detect_finalized = true;
|
||||
else if (tpos->client.pcounter >= ctrack->dp->hostlist_auto_udp_out)
|
||||
{
|
||||
// failure
|
||||
char client_ip_port[48];
|
||||
ctrack->failure_detect_finalized = true;
|
||||
fill_client_ip_port(client, client_ip_port, sizeof(client_ip_port));
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : udp_in %u<=%u udp_out %u>=%u",
|
||||
ctrack->hostname, ctrack->dp->n, PROFILE_NAME(ctrack->dp), client_ip_port, l7proto_str(ctrack->l7proto),
|
||||
tpos->server.pcounter, ctrack->dp->hostlist_auto_udp_in,
|
||||
tpos->client.pcounter, ctrack->dp->hostlist_auto_udp_out);
|
||||
auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool send_delayed(t_ctrack *ctrack)
|
||||
{
|
||||
@@ -344,23 +378,22 @@ static bool send_delayed(t_ctrack *ctrack)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const t_ctrack_position *pos, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout)
|
||||
static bool rawpacket_queue_csum_fix(struct rawpacket_tailhead *q, const struct dissect *dis, const t_ctrack_positions *tpos, const struct sockaddr_storage* dst, uint32_t fwmark, uint32_t desync_fwmark, const char *ifin, const char *ifout)
|
||||
{
|
||||
// this breaks const pointer to l4 header
|
||||
if (dis->tcp)
|
||||
verdict_tcp_csum_fix(VERDICT_PASS, (struct tcphdr *)dis->tcp, dis->transport_len, dis->ip, dis->ip6);
|
||||
else if (dis->udp)
|
||||
verdict_udp_csum_fix(VERDICT_PASS, (struct udphdr *)dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||
return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload, pos);
|
||||
return rawpacket_queue(q, dst, fwmark, desync_fwmark, ifin, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload, tpos);
|
||||
}
|
||||
|
||||
|
||||
static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, uint32_t seq, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
ReasmClear(reasm);
|
||||
if (sz <= szMax)
|
||||
{
|
||||
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : 0;
|
||||
if (ReasmInit(reasm, sz, seq))
|
||||
{
|
||||
ReasmFeed(reasm, seq, data_payload, len_payload);
|
||||
@@ -374,15 +407,15 @@ static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, si
|
||||
DLOG("unexpected large payload for reassemble: size=%zu\n", sz);
|
||||
return false;
|
||||
}
|
||||
static bool reasm_orig_start(t_ctrack *ctrack, uint8_t proto, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
static bool reasm_client_start(t_ctrack *ctrack, uint8_t proto, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
return reasm_start(ctrack, &ctrack->reasm_orig, proto, sz, szMax, data_payload, len_payload);
|
||||
if (!ctrack) return false;
|
||||
return reasm_start(ctrack, &ctrack->reasm_client, proto, (proto == IPPROTO_TCP) ? ctrack->pos.client.seq_last : 0, sz, szMax, data_payload, len_payload);
|
||||
}
|
||||
static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, const uint8_t *data_payload, size_t len_payload)
|
||||
static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, uint32_t seq, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
if (ctrack && !ReasmIsEmpty(reasm))
|
||||
{
|
||||
uint32_t seq = (proto == IPPROTO_TCP) ? ctrack->pos.seq_last : (uint32_t)reasm->size_present;
|
||||
if (ReasmFeed(reasm, seq, data_payload, len_payload))
|
||||
{
|
||||
DLOG("reassemble : feeding data payload size=%zu. now we have %zu/%zu\n", len_payload, reasm->size_present, reasm->size);
|
||||
@@ -397,29 +430,30 @@ static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, con
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool reasm_orig_feed(t_ctrack *ctrack, uint8_t proto, const uint8_t *data_payload, size_t len_payload)
|
||||
static bool reasm_client_feed(t_ctrack *ctrack, uint8_t proto, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
return reasm_feed(ctrack, &ctrack->reasm_orig, proto, data_payload, len_payload);
|
||||
if (!ctrack) return false;
|
||||
return reasm_feed(ctrack, &ctrack->reasm_client, proto, (proto == IPPROTO_TCP) ? ctrack->pos.client.seq_last : (uint32_t)ctrack->reasm_client.size_present, data_payload, len_payload);
|
||||
}
|
||||
static void reasm_orig_stop(t_ctrack *ctrack, const char *dlog_msg)
|
||||
static void reasm_client_stop(t_ctrack *ctrack, const char *dlog_msg)
|
||||
{
|
||||
if (ctrack)
|
||||
{
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
DLOG("%s", dlog_msg);
|
||||
ReasmClear(&ctrack->reasm_orig);
|
||||
ReasmClear(&ctrack->reasm_client);
|
||||
}
|
||||
send_delayed(ctrack);
|
||||
}
|
||||
}
|
||||
static void reasm_orig_cancel(t_ctrack *ctrack)
|
||||
static void reasm_client_cancel(t_ctrack *ctrack)
|
||||
{
|
||||
reasm_orig_stop(ctrack, "reassemble session cancelled\n");
|
||||
reasm_client_stop(ctrack, "reassemble session cancelled\n");
|
||||
}
|
||||
static void reasm_orig_fin(t_ctrack *ctrack)
|
||||
static void reasm_client_fin(t_ctrack *ctrack)
|
||||
{
|
||||
reasm_orig_stop(ctrack, "reassemble session finished\n");
|
||||
reasm_client_stop(ctrack, "reassemble session finished\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +463,7 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect *
|
||||
// if used in postnat chain, dropping initial packet will cause conntrack connection teardown
|
||||
// so we need to workaround this.
|
||||
// SYN and SYN,ACK checks are for conntrack-less mode
|
||||
if (ctrack && (params.server ? ctrack->pos.pcounter_reply : ctrack->pos.pcounter_orig) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp)))
|
||||
if (ctrack && (params.server ? ctrack->pos.server.pcounter : ctrack->pos.client.pcounter) == 1 || dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp)))
|
||||
{
|
||||
if (dis->len_pkt > *len_mod_pkt)
|
||||
DLOG_ERR("linux postnat conntrack workaround cannot be applied\n");
|
||||
@@ -458,21 +492,22 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect *
|
||||
}
|
||||
|
||||
|
||||
static uint64_t pos_get(const t_ctrack_position *pos, char mode, bool bReply)
|
||||
static uint64_t pos_get(const t_ctrack_position *pos, char mode)
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 'n': return bReply ? pos->pcounter_reply : pos->pcounter_orig;
|
||||
case 'd': return bReply ? pos->pdcounter_reply : pos->pdcounter_orig;
|
||||
case 's': return bReply ? (pos->ack_last - pos->ack0) : (pos->seq_last - pos->seq0);
|
||||
case 'b': return bReply ? pos->pbcounter_reply : pos->pbcounter_orig;
|
||||
case 'n': return pos->pcounter;
|
||||
case 'd': return pos->pdcounter;
|
||||
case 's': return pos->seq_last - pos->seq0;
|
||||
case 'p': return pos->pos - pos->seq0;
|
||||
case 'b': return pos->pbcounter;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static bool check_pos_from(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
|
||||
static bool check_pos_from(const t_ctrack_position *pos, const struct packet_range *range)
|
||||
{
|
||||
uint64_t ps;
|
||||
if (range->from.mode == 'x') return false;
|
||||
@@ -480,7 +515,7 @@ static bool check_pos_from(const t_ctrack_position *pos, bool bReply, const stru
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
ps = pos_get(pos, range->from.mode, bReply);
|
||||
ps = pos_get(pos, range->from.mode);
|
||||
return ps >= range->from.pos;
|
||||
}
|
||||
else
|
||||
@@ -488,7 +523,7 @@ static bool check_pos_from(const t_ctrack_position *pos, bool bReply, const stru
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool check_pos_to(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
|
||||
static bool check_pos_to(const t_ctrack_position *pos, const struct packet_range *range)
|
||||
{
|
||||
uint64_t ps;
|
||||
if (range->to.mode == 'x') return false;
|
||||
@@ -496,7 +531,7 @@ static bool check_pos_to(const t_ctrack_position *pos, bool bReply, const struct
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
ps = pos_get(pos, range->to.mode, bReply);
|
||||
ps = pos_get(pos, range->to.mode);
|
||||
return (ps < range->to.pos) || !range->upper_cutoff && (ps == range->to.pos);
|
||||
}
|
||||
else
|
||||
@@ -504,14 +539,14 @@ static bool check_pos_to(const t_ctrack_position *pos, bool bReply, const struct
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool check_pos_cutoff(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
|
||||
static bool check_pos_cutoff(const t_ctrack_position *pos, const struct packet_range *range)
|
||||
{
|
||||
bool bto = check_pos_to(pos, bReply, range);
|
||||
return pos ? !bto : (!bto || !check_pos_from(pos, bReply, range));
|
||||
bool bto = check_pos_to(pos, range);
|
||||
return pos ? !bto : (!bto || !check_pos_from(pos, range));
|
||||
}
|
||||
static bool check_pos_range(const t_ctrack_position *pos, bool bReply, const struct packet_range *range)
|
||||
static bool check_pos_range(const t_ctrack_position *pos, const struct packet_range *range)
|
||||
{
|
||||
return check_pos_from(pos, bReply, range) && check_pos_to(pos, bReply, range);
|
||||
return check_pos_from(pos, range) && check_pos_to(pos, range);
|
||||
}
|
||||
|
||||
|
||||
@@ -645,7 +680,7 @@ static uint8_t desync(
|
||||
const char *ifout,
|
||||
bool bIncoming,
|
||||
t_ctrack *ctrack,
|
||||
const t_ctrack_position *pos,
|
||||
const t_ctrack_positions *tpos,
|
||||
t_l7payload l7payload,
|
||||
t_l7proto l7proto,
|
||||
const struct dissect *dis,
|
||||
@@ -663,6 +698,7 @@ static uint8_t desync(
|
||||
struct packet_range *range;
|
||||
size_t l;
|
||||
char instance[256];
|
||||
const t_ctrack_position *pos, *rpos;
|
||||
|
||||
if (ctrack)
|
||||
{
|
||||
@@ -677,8 +713,10 @@ static uint8_t desync(
|
||||
DLOG("lua out cutoff\n");
|
||||
return verdict;
|
||||
}
|
||||
if (!pos) pos = &ctrack->pos;
|
||||
if (!tpos) tpos = &ctrack->pos;
|
||||
}
|
||||
pos = tpos ? (bIncoming ^ params.server) ? &tpos->server : &tpos->client : NULL;
|
||||
rpos = tpos ? (bIncoming ^ params.server) ? &tpos->client : &tpos->server : NULL;
|
||||
|
||||
LUA_STACK_GUARD_ENTER(params.L)
|
||||
|
||||
@@ -700,12 +738,12 @@ static uint8_t desync(
|
||||
{
|
||||
if (lua_instance_cutoff_check(&ctx, bIncoming))
|
||||
DLOG("* lua '%s' : voluntary cutoff\n", instance);
|
||||
else if (check_pos_cutoff(pos, bIncoming, range))
|
||||
else if (check_pos_cutoff(pos, range))
|
||||
{
|
||||
DLOG("* lua '%s' : %s pos %c%llu %c%llu is beyond range %c%u%c%c%u (ctrack %s)\n",
|
||||
instance, sDirection,
|
||||
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
|
||||
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
|
||||
range->from.mode, pos_get(pos, range->from.mode),
|
||||
range->to.mode, pos_get(pos, range->to.mode),
|
||||
range->from.mode, range->from.pos,
|
||||
range->upper_cutoff ? '<' : '-',
|
||||
range->to.mode, range->to.pos,
|
||||
@@ -728,7 +766,7 @@ static uint8_t desync(
|
||||
// create arg table that persists across multiple desync function calls
|
||||
lua_newtable(params.L);
|
||||
lua_pushf_dissect(dis);
|
||||
lua_pushf_ctrack(ctrack, pos);
|
||||
lua_pushf_ctrack(ctrack, tpos, bIncoming);
|
||||
lua_pushf_int("profile_n", dp->n);
|
||||
if (dp->name) lua_pushf_str("profile_name", dp->name);
|
||||
if (dp->n_tpl) lua_pushf_int("template_n", dp->n_tpl);
|
||||
@@ -760,8 +798,8 @@ static uint8_t desync(
|
||||
if (dis->tcp)
|
||||
{
|
||||
// recommended mss value for generated packets
|
||||
if (pos && pos->mss_orig)
|
||||
lua_pushf_int("tcp_mss", pos->mss_orig);
|
||||
if (rpos && rpos->mss)
|
||||
lua_pushf_int("tcp_mss", rpos->mss);
|
||||
else
|
||||
lua_pushf_global("tcp_mss", "DEFAULT_MSS");
|
||||
}
|
||||
@@ -777,12 +815,12 @@ static uint8_t desync(
|
||||
if (!lua_instance_cutoff_check(&ctx, bIncoming))
|
||||
{
|
||||
range = bIncoming ? &func->range_in : &func->range_out;
|
||||
if (check_pos_range(pos, bIncoming, range))
|
||||
if (check_pos_range(pos, range))
|
||||
{
|
||||
DLOG("* lua '%s' : %s pos %c%llu %c%llu in range %c%u%c%c%u\n",
|
||||
instance, sDirection,
|
||||
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
|
||||
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
|
||||
range->from.mode, pos_get(pos, range->from.mode),
|
||||
range->to.mode, pos_get(pos, range->to.mode),
|
||||
range->from.mode, range->from.pos,
|
||||
range->upper_cutoff ? '<' : '-',
|
||||
range->to.mode, range->to.pos);
|
||||
@@ -799,7 +837,7 @@ static uint8_t desync(
|
||||
}
|
||||
lua_pushlightuserdata(params.L, &ctx);
|
||||
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
|
||||
lua_pushf_args(&func->args, -1);
|
||||
lua_pushf_args(&func->args, -1, true);
|
||||
lua_pushf_str("func", func->func);
|
||||
lua_pushf_int("func_n", ctx.func_n);
|
||||
lua_pushf_str("func_instance", instance);
|
||||
@@ -827,8 +865,8 @@ static uint8_t desync(
|
||||
else
|
||||
DLOG("* lua '%s' : %s pos %c%llu %c%llu out of range %c%u%c%c%u\n",
|
||||
instance, sDirection,
|
||||
range->from.mode, pos_get(pos, range->from.mode, bIncoming),
|
||||
range->to.mode, pos_get(pos, range->to.mode, bIncoming),
|
||||
range->from.mode, pos_get(pos, range->from.mode),
|
||||
range->to.mode, pos_get(pos, range->to.mode),
|
||||
range->from.mode, range->from.pos,
|
||||
range->upper_cutoff ? '<' : '-',
|
||||
range->to.mode, range->to.pos);
|
||||
@@ -927,7 +965,7 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset,
|
||||
uint32_t fwmark,
|
||||
const char *ifin, const char *ifout,
|
||||
const t_ctrack_position *pos,
|
||||
const t_ctrack_positions *tpos,
|
||||
const struct dissect *dis,
|
||||
uint8_t *mod_pkt, size_t *len_mod_pkt)
|
||||
{
|
||||
@@ -1084,49 +1122,48 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
// process reply packets for auto hostlist mode
|
||||
// by looking at RSTs or HTTP replies we decide whether original request looks like DPI blocked
|
||||
// we only process first-sequence replies. do not react to subsequent redirects or RSTs
|
||||
if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->pos.ack_last - ctrack->pos.ack0) == 1)
|
||||
if (!params.server && ctrack && ctrack->hostname_ah_check && !ctrack->failure_detect_finalized && dp->hostlist_auto_incoming_maxseq)
|
||||
{
|
||||
bool bFail = false;
|
||||
|
||||
char client_ip_port[48];
|
||||
if (*params.hostlist_auto_debuglog)
|
||||
ntop46_port((struct sockaddr*)&dst, client_ip_port, sizeof(client_ip_port));
|
||||
else
|
||||
*client_ip_port = 0;
|
||||
|
||||
if (dis->tcp->th_flags & TH_RST)
|
||||
uint32_t rseq = ctrack->pos.server.seq_last - ctrack->pos.server.seq0;
|
||||
if (rseq)
|
||||
{
|
||||
DLOG("incoming RST detected for hostname %s\n", ctrack->hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : incoming RST", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
|
||||
bFail = true;
|
||||
}
|
||||
else if (dis->len_payload && l7proto == L7_HTTP)
|
||||
{
|
||||
if (l7payload == L7P_HTTP_REPLY)
|
||||
char client_ip_port[48];
|
||||
fill_client_ip_port((struct sockaddr*)&dst, client_ip_port, sizeof(client_ip_port));
|
||||
if (seq_within(ctrack->pos.server.seq_last, ctrack->pos.server.seq0 + 1, ctrack->pos.server.seq0 + dp->hostlist_auto_incoming_maxseq))
|
||||
{
|
||||
DLOG("incoming HTTP reply detected for hostname %s\n", ctrack->hostname);
|
||||
bFail = HttpReplyLooksLikeDPIRedirect(dis->data_payload, dis->len_payload, ctrack->hostname);
|
||||
bool bFail = false;
|
||||
|
||||
if (dis->tcp->th_flags & TH_RST)
|
||||
{
|
||||
DLOG("incoming RST detected for hostname %s rseq %u\n", ctrack->hostname, rseq);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : rseq %u : incoming RST", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), rseq);
|
||||
bFail = true;
|
||||
}
|
||||
else if (dis->len_payload && l7payload == L7P_HTTP_REPLY)
|
||||
{
|
||||
DLOG("incoming HTTP reply detected for hostname %s rseq %u\n", ctrack->hostname, rseq);
|
||||
bFail = HttpReplyLooksLikeDPIRedirect(dis->data_payload, dis->len_payload, ctrack->hostname);
|
||||
if (bFail)
|
||||
{
|
||||
DLOG("redirect to another domain detected. possibly DPI redirect.\n");
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : rseq %u : redirect to another domain", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), rseq);
|
||||
}
|
||||
else
|
||||
DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n");
|
||||
}
|
||||
if (bFail)
|
||||
{
|
||||
DLOG("redirect to another domain detected. possibly DPI redirect.\n");
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : redirect to another domain", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto));
|
||||
auto_hostlist_failed(dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, l7proto);
|
||||
ctrack->failure_detect_finalized = true;
|
||||
}
|
||||
else
|
||||
DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// received not http reply. do not monitor this connection anymore
|
||||
DLOG("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname);
|
||||
// incoming_maxseq exceeded. treat connection as successful
|
||||
auto_hostlist_reset_fail_counter(dp, ctrack->hostname, client_ip_port, l7proto);
|
||||
ctrack->failure_detect_finalized = true;
|
||||
}
|
||||
}
|
||||
if (bFail)
|
||||
auto_hostlist_failed(dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, l7proto);
|
||||
else
|
||||
if (dis->len_payload)
|
||||
auto_hostlist_reset_fail_counter(dp, ctrack->hostname, client_ip_port, l7proto);
|
||||
if (dis->tcp->th_flags & TH_RST)
|
||||
ctrack->hostname_ah_check = false; // do not react to further dup RSTs
|
||||
}
|
||||
}
|
||||
// not reverse
|
||||
@@ -1135,18 +1172,17 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
struct blob_collection_head *fake;
|
||||
uint8_t *p, *phost = NULL;
|
||||
int i;
|
||||
|
||||
bool bHaveHost = false, bHostIsIp = false;
|
||||
|
||||
if (replay_piece_count)
|
||||
{
|
||||
rdata_payload = ctrack_replay->reasm_orig.packet;
|
||||
rlen_payload = ctrack_replay->reasm_orig.size_present;
|
||||
rdata_payload = ctrack_replay->reasm_client.packet;
|
||||
rlen_payload = ctrack_replay->reasm_client.size_present;
|
||||
}
|
||||
else if (reasm_orig_feed(ctrack, IPPROTO_TCP, dis->data_payload, dis->len_payload))
|
||||
else if (reasm_client_feed(ctrack, IPPROTO_TCP, dis->data_payload, dis->len_payload))
|
||||
{
|
||||
rdata_payload = ctrack->reasm_orig.packet;
|
||||
rlen_payload = ctrack->reasm_orig.size_present;
|
||||
rdata_payload = ctrack->reasm_client.packet;
|
||||
rlen_payload = ctrack->reasm_client.size_present;
|
||||
}
|
||||
|
||||
process_retrans_fail(ctrack, IPPROTO_TCP, (struct sockaddr*)&src);
|
||||
@@ -1161,7 +1197,7 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
}
|
||||
|
||||
// we do not reassemble http
|
||||
reasm_orig_cancel(ctrack);
|
||||
reasm_client_cancel(ctrack);
|
||||
|
||||
bHaveHost = HttpExtractHost(rdata_payload, rlen_payload, host, sizeof(host));
|
||||
if (!bHaveHost)
|
||||
@@ -1169,17 +1205,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
DLOG("not applying tampering to HTTP without Host:\n");
|
||||
goto pass;
|
||||
}
|
||||
if (ctrack)
|
||||
{
|
||||
// we do not reassemble http
|
||||
if (!ctrack->req_seq_present)
|
||||
{
|
||||
ctrack->req_seq_start = ctrack->pos.seq_last;
|
||||
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
|
||||
ctrack->req_seq_present = ctrack->req_seq_finalized = true;
|
||||
DLOG("req retrans : tcp seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsTLSClientHello(rdata_payload, rlen_payload, TLS_PARTIALS_ENABLE))
|
||||
{
|
||||
@@ -1198,29 +1223,14 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
if (ctrack && !(params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable)))
|
||||
{
|
||||
// do not reasm retransmissions
|
||||
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !ctrack->req_seq_abandoned &&
|
||||
!(ctrack->req_seq_finalized && seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
|
||||
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_client) && !is_retransmission(&ctrack->pos.client))
|
||||
{
|
||||
// do not reconstruct unexpected large payload (they are feeding garbage ?)
|
||||
if (!reasm_orig_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload))
|
||||
if (!reasm_client_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload))
|
||||
goto pass_reasm_cancel;
|
||||
}
|
||||
if (!ctrack->req_seq_finalized)
|
||||
{
|
||||
if (!ctrack->req_seq_present)
|
||||
{
|
||||
// lower bound of request seq interval
|
||||
ctrack->req_seq_start = ctrack->pos.seq_last;
|
||||
ctrack->req_seq_present = true;
|
||||
}
|
||||
// upper bound of request seq interval
|
||||
// it can grow on every packet until request is complete. then interval is finalized and never touched again.
|
||||
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
|
||||
DLOG("req retrans : seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
ctrack->req_seq_finalized |= bReqFull;
|
||||
}
|
||||
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
|
||||
{
|
||||
@@ -1231,16 +1241,16 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
DLOG_ERR("rawpacket_queue failed !\n");
|
||||
goto pass_reasm_cancel;
|
||||
}
|
||||
if (ReasmIsFull(&ctrack->reasm_orig))
|
||||
if (ReasmIsFull(&ctrack->reasm_client))
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
reasm_client_fin(ctrack);
|
||||
}
|
||||
return VERDICT_DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ctrack && (ctrack->pos.seq_last - ctrack->pos.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
|
||||
else if (ctrack && (ctrack->pos.client.seq_last - ctrack->pos.client.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
|
||||
{
|
||||
DLOG("packet contains telegram mtproto2 initial\n");
|
||||
// mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable.
|
||||
@@ -1259,12 +1269,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
};
|
||||
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
|
||||
}
|
||||
if (ctrack && ctrack->req_seq_finalized)
|
||||
{
|
||||
uint32_t dseq = ctrack->pos.seq_last - ctrack->req_seq_end;
|
||||
// do not react to 32-bit overflowed sequence numbers. allow 16 Mb grace window then cutoff.
|
||||
if (dseq >= 0x1000000 && !(dseq & 0x80000000)) ctrack->req_seq_abandoned = true;
|
||||
}
|
||||
|
||||
if (bHaveHost)
|
||||
{
|
||||
@@ -1327,7 +1331,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
DLOG("desync profile changed by revealed l7 protocol or hostname !\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp))
|
||||
{
|
||||
if (!bCheckDone)
|
||||
@@ -1367,19 +1370,19 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2));
|
||||
DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload));
|
||||
}
|
||||
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, l7proto, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0);
|
||||
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, tpos, l7payload, l7proto, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, rdata_payload, rlen_payload, NULL, 0);
|
||||
|
||||
pass:
|
||||
return (!bReverseFixed && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict;
|
||||
pass_reasm_cancel:
|
||||
reasm_orig_cancel(ctrack);
|
||||
reasm_client_cancel(ctrack);
|
||||
goto pass;
|
||||
}
|
||||
|
||||
// return : true - should continue, false - should stop with verdict
|
||||
static void quic_reasm_cancel(t_ctrack *ctrack, const char *reason)
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
reasm_client_cancel(ctrack);
|
||||
DLOG("%s\n", reason);
|
||||
}
|
||||
|
||||
@@ -1388,7 +1391,7 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset,
|
||||
uint32_t fwmark,
|
||||
const char *ifin, const char *ifout,
|
||||
const t_ctrack_position *pos,
|
||||
const t_ctrack_positions *tpos,
|
||||
const struct dissect *dis,
|
||||
uint8_t *mod_pkt, size_t *len_mod_pkt)
|
||||
{
|
||||
@@ -1574,8 +1577,8 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
|
||||
if (replay_piece_count)
|
||||
{
|
||||
clean_len = ctrack_replay->reasm_orig.size_present;
|
||||
pclean = ctrack_replay->reasm_orig.packet;
|
||||
clean_len = ctrack_replay->reasm_client.size_present;
|
||||
pclean = ctrack_replay->reasm_client.packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1585,13 +1588,13 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
if (pclean)
|
||||
{
|
||||
bool reasm_disable = params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable);
|
||||
if (ctrack && !reasm_disable && !ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (ctrack && !reasm_disable && !ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
if (ReasmHasSpace(&ctrack->reasm_orig, clean_len))
|
||||
if (ReasmHasSpace(&ctrack->reasm_client, clean_len))
|
||||
{
|
||||
reasm_orig_feed(ctrack, IPPROTO_UDP, clean, clean_len);
|
||||
pclean = ctrack->reasm_orig.packet;
|
||||
clean_len = ctrack->reasm_orig.size_present;
|
||||
reasm_client_feed(ctrack, IPPROTO_UDP, clean, clean_len);
|
||||
pclean = ctrack->reasm_client.packet;
|
||||
clean_len = ctrack->reasm_client.size_present;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1616,13 +1619,13 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
|
||||
if (ctrack && !reasm_disable)
|
||||
{
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len))
|
||||
if (!reasm_client_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len))
|
||||
goto pass_reasm_cancel;
|
||||
}
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
|
||||
{
|
||||
@@ -1636,7 +1639,7 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
if (bReqFull)
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
reasm_client_fin(ctrack);
|
||||
}
|
||||
return ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt);
|
||||
}
|
||||
@@ -1658,10 +1661,10 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
DLOG("QUIC initial contains CRYPTO with partial fragment coverage\n");
|
||||
if (ctrack && !reasm_disable)
|
||||
{
|
||||
if (ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
if (ReasmIsEmpty(&ctrack->reasm_client))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len))
|
||||
if (!reasm_client_start(ctrack, IPPROTO_UDP, UDP_MAX_REASM, UDP_MAX_REASM, clean, clean_len))
|
||||
goto pass_reasm_cancel;
|
||||
}
|
||||
if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout))
|
||||
@@ -1695,7 +1698,7 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
|
||||
reasm_orig_cancel(ctrack);
|
||||
reasm_client_cancel(ctrack);
|
||||
|
||||
t_protocol_probe testers[] = {
|
||||
{L7P_DISCORD_IP_DISCOVERY,L7_DISCORD,IsDiscordIpDiscoveryRequest,false},
|
||||
@@ -1797,20 +1800,14 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
else
|
||||
{
|
||||
if (ctrack_replay)
|
||||
{
|
||||
ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded;
|
||||
if (ctrack_replay->hostname_ah_check)
|
||||
{
|
||||
// first request is not retrans
|
||||
if (!bDiscoveredHostname && !reasm_offset)
|
||||
process_retrans_fail(ctrack_replay, IPPROTO_UDP, (struct sockaddr*)&src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
process_udp_fail(ctrack_replay, tpos, (struct sockaddr*)&src);
|
||||
} // len_payload
|
||||
|
||||
if (bCheckDone && !bCheckResult)
|
||||
{
|
||||
DLOG("not applying tampering because of negative hostlist check\n");
|
||||
@@ -1823,12 +1820,12 @@ static uint8_t dpi_desync_udp_packet_play(
|
||||
ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2));
|
||||
DLOG("dpi desync src=%s dst=%s track_direction=%s fixed_direction=%s connection_proto=%s payload_type=%s\n", s1, s2, bReverse ? "in" : "out", bReverseFixed ? "in" : "out", l7proto_str(l7proto), l7payload_str(l7payload));
|
||||
}
|
||||
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, pos, l7payload, l7proto, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt);
|
||||
verdict = desync(dp, fwmark, ifin, ifout, bReverseFixed, ctrack_replay, tpos, l7payload, l7proto, dis, sdip4, sdip6, sdport, mod_pkt, len_mod_pkt, replay_piece, replay_piece_count, reasm_offset, NULL, 0, data_decrypt, len_decrypt);
|
||||
|
||||
pass:
|
||||
return (!bReverse && (verdict & VERDICT_MASK) == VERDICT_DROP) ? ct_new_postnat_fix(ctrack, dis, mod_pkt, len_mod_pkt) : verdict;
|
||||
pass_reasm_cancel:
|
||||
reasm_orig_cancel(ctrack);
|
||||
reasm_client_cancel(ctrack);
|
||||
goto pass;
|
||||
}
|
||||
|
||||
@@ -1872,7 +1869,7 @@ static void packet_debug(bool replay, const struct dissect *dis)
|
||||
|
||||
static uint8_t dpi_desync_packet_play(
|
||||
unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, uint32_t fwmark, const char *ifin, const char *ifout,
|
||||
const t_ctrack_position *pos,
|
||||
const t_ctrack_positions *tpos,
|
||||
const uint8_t *data_pkt, size_t len_pkt,
|
||||
uint8_t *mod_pkt, size_t *len_mod_pkt)
|
||||
{
|
||||
@@ -1888,7 +1885,7 @@ static uint8_t dpi_desync_packet_play(
|
||||
case IPPROTO_TCP:
|
||||
if (dis.tcp)
|
||||
{
|
||||
verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt);
|
||||
verdict = dpi_desync_tcp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, tpos, &dis, mod_pkt, len_mod_pkt);
|
||||
// we fix csum before pushing to replay queue
|
||||
if (!replay_piece_count) verdict_tcp_csum_fix(verdict, (struct tcphdr *)dis.tcp, dis.transport_len, dis.ip, dis.ip6);
|
||||
}
|
||||
@@ -1896,7 +1893,7 @@ static uint8_t dpi_desync_packet_play(
|
||||
case IPPROTO_UDP:
|
||||
if (dis.udp)
|
||||
{
|
||||
verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, pos, &dis, mod_pkt, len_mod_pkt);
|
||||
verdict = dpi_desync_udp_packet_play(replay_piece, replay_piece_count, reasm_offset, fwmark, ifin, ifout, tpos, &dis, mod_pkt, len_mod_pkt);
|
||||
// we fix csum before pushing to replay queue
|
||||
if (!replay_piece_count) verdict_udp_csum_fix(verdict, (struct udphdr *)dis.udp, dis.transport_len, dis.ip, dis.ip6);
|
||||
}
|
||||
@@ -1926,7 +1923,7 @@ static bool replay_queue(struct rawpacket_tailhead *q)
|
||||
{
|
||||
DLOG("REPLAYING delayed packet #%u offset %zu\n", i+1, offset);
|
||||
modlen = sizeof(mod);
|
||||
uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->pos_present ? &rp->pos : NULL, rp->packet, rp->len, mod, &modlen);
|
||||
uint8_t verdict = dpi_desync_packet_play(i, count, offset, rp->fwmark_orig, rp->ifin, rp->ifout, rp->tpos_present ? &rp->tpos : NULL, rp->packet, rp->len, mod, &modlen);
|
||||
switch (verdict & VERDICT_MASK)
|
||||
{
|
||||
case VERDICT_MODIFY:
|
||||
|
||||
@@ -514,7 +514,7 @@ bool pf_is_empty(const port_filter *pf)
|
||||
|
||||
bool packet_pos_parse(const char *s, struct packet_pos *pos)
|
||||
{
|
||||
if (*s!='n' && *s!='d' && *s!='s' && *s!='b' && *s!='x' && *s!='a') return false;
|
||||
if (*s!='n' && *s!='d' && *s!='s' && *s!='p' && *s!='b' && *s!='x' && *s!='a') return false;
|
||||
pos->mode=*s;
|
||||
if (pos->mode=='x' || pos->mode=='a')
|
||||
{
|
||||
|
||||
131
nfq2/lua.c
131
nfq2/lua.c
@@ -841,7 +841,7 @@ static int luacall_execution_plan(lua_State *L)
|
||||
range = ctx->incoming ? &func->range_in : &func->range_out;
|
||||
lua_pushinteger(params.L, n - ctx->func_n);
|
||||
lua_createtable(params.L, 0, 6);
|
||||
lua_pushf_args(&func->args, -1);
|
||||
lua_pushf_args(&func->args, -1, false);
|
||||
lua_pushf_str("func", func->func);
|
||||
lua_pushf_int("func_n", ctx->func_n);
|
||||
lua_pushf_str("func_instance", instance);
|
||||
@@ -929,6 +929,18 @@ void lua_pushi_lint(lua_Integer idx, int64_t v)
|
||||
lua_pushlint(params.L, v);
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
void lua_pushf_number(const char *field, lua_Number v)
|
||||
{
|
||||
lua_pushstring(params.L, field);
|
||||
lua_pushnumber(params.L, v);
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
void lua_pushi_number(lua_Integer idx, lua_Number v)
|
||||
{
|
||||
lua_pushinteger(params.L, idx);
|
||||
lua_pushnumber(params.L, v);
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
void lua_pushf_bool(const char *field, bool b)
|
||||
{
|
||||
lua_pushstring(params.L, field);
|
||||
@@ -1260,23 +1272,44 @@ void lua_pushf_dissect(const struct dissect *dis)
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
|
||||
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos)
|
||||
void lua_pushf_ctrack_pos(const t_ctrack *ctrack, const t_ctrack_position *pos)
|
||||
{
|
||||
LUA_STACK_GUARD_ENTER(params.L)
|
||||
|
||||
if (!pos) pos = &ctrack->pos;
|
||||
lua_pushf_lint("pcounter", pos->pcounter);
|
||||
lua_pushf_lint("pdcounter", pos->pdcounter);
|
||||
lua_pushf_lint("pbcounter", pos->pbcounter);
|
||||
if (ctrack->ipproto == IPPROTO_TCP)
|
||||
{
|
||||
lua_pushliteral(params.L, "tcp");
|
||||
lua_createtable(params.L, 0, 10);
|
||||
lua_pushf_lint("seq0", pos->seq0);
|
||||
lua_pushf_lint("seq", pos->seq_last);
|
||||
lua_pushf_lint("rseq", pos->seq_last - pos->seq0);
|
||||
lua_pushf_int("pos", pos->pos - pos->seq0);
|
||||
lua_pushf_int("uppos", pos->uppos - pos->seq0);
|
||||
lua_pushf_int("uppos_prev", pos->uppos_prev - pos->seq0);
|
||||
lua_pushf_int("winsize", pos->winsize);
|
||||
lua_pushf_int("winsize_calc", pos->winsize_calc);
|
||||
lua_pushf_int("scale", pos->scale);
|
||||
lua_pushf_int("mss", pos->mss);
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
|
||||
LUA_STACK_GUARD_LEAVE(params.L, 0)
|
||||
}
|
||||
|
||||
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_positions *tpos, bool bIncoming)
|
||||
{
|
||||
LUA_STACK_GUARD_ENTER(params.L)
|
||||
|
||||
if (!tpos) tpos = &ctrack->pos;
|
||||
|
||||
lua_pushliteral(params.L, "track");
|
||||
if (ctrack)
|
||||
{
|
||||
lua_createtable(params.L, 0, 13 + (ctrack->ipproto == IPPROTO_TCP));
|
||||
lua_createtable(params.L, 0, 9);
|
||||
|
||||
lua_pushf_lint("pcounter_orig", pos->pcounter_orig);
|
||||
lua_pushf_lint("pdcounter_orig", pos->pdcounter_orig);
|
||||
lua_pushf_lint("pbcounter_orig", pos->pbcounter_orig);
|
||||
lua_pushf_lint("pcounter_reply", pos->pcounter_reply);
|
||||
lua_pushf_lint("pdcounter_reply", pos->pdcounter_reply);
|
||||
lua_pushf_lint("pbcounter_reply", pos->pbcounter_reply);
|
||||
if (ctrack->incoming_ttl)
|
||||
lua_pushf_int("incoming_ttl", ctrack->incoming_ttl);
|
||||
else
|
||||
@@ -1287,27 +1320,38 @@ void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos)
|
||||
lua_pushf_reg("lua_state", ctrack->lua_state);
|
||||
lua_pushf_bool("lua_in_cutoff", ctrack->b_lua_in_cutoff);
|
||||
lua_pushf_bool("lua_out_cutoff", ctrack->b_lua_out_cutoff);
|
||||
lua_pushf_lint("t_start", (lua_Number)ctrack->t_start.tv_sec + ctrack->t_start.tv_nsec/1000000000.);
|
||||
|
||||
if (ctrack->ipproto == IPPROTO_TCP)
|
||||
{
|
||||
lua_pushliteral(params.L, "tcp");
|
||||
lua_createtable(params.L, 0, 14);
|
||||
lua_pushf_lint("seq0", pos->seq0);
|
||||
lua_pushf_lint("seq", pos->seq_last);
|
||||
lua_pushf_lint("ack0", pos->ack0);
|
||||
lua_pushf_lint("ack", pos->ack_last);
|
||||
lua_pushf_int("pos_orig", pos->pos_orig - pos->seq0);
|
||||
lua_pushf_int("winsize_orig", pos->winsize_orig);
|
||||
lua_pushf_int("winsize_orig_calc", pos->winsize_orig_calc);
|
||||
lua_pushf_int("scale_orig", pos->scale_orig);
|
||||
lua_pushf_int("mss_orig", pos->mss_orig);
|
||||
lua_pushf_int("pos_reply", pos->pos_reply - pos->ack0);
|
||||
lua_pushf_int("winsize_reply", pos->winsize_reply);
|
||||
lua_pushf_int("winsize_reply_calc", pos->winsize_reply_calc);
|
||||
lua_pushf_int("scale_reply", pos->scale_reply);
|
||||
lua_pushf_int("mss_reply", pos->mss_reply);
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
lua_pushliteral(params.L, "pos");
|
||||
lua_createtable(params.L, 0, 5);
|
||||
|
||||
// orig, reply related to connection logical direction
|
||||
// for tcp orig is client (who connects), reply is server (who listens).
|
||||
// for orig is the first seen party, reply is another party
|
||||
lua_pushf_number("dt",
|
||||
(lua_Number)tpos->t_last.tv_sec - (lua_Number)ctrack->t_start.tv_sec +
|
||||
(tpos->t_last.tv_nsec - ctrack->t_start.tv_nsec)/1000000000.);
|
||||
|
||||
lua_pushliteral(params.L, "client");
|
||||
lua_newtable(params.L);
|
||||
lua_pushf_ctrack_pos(ctrack, &tpos->client);
|
||||
lua_rawset(params.L,-3);
|
||||
|
||||
lua_pushliteral(params.L, "server");
|
||||
lua_newtable(params.L);
|
||||
lua_pushf_ctrack_pos(ctrack, &tpos->server);
|
||||
lua_rawset(params.L,-3);
|
||||
|
||||
// direct and reverse are adjusted for server mode. in server mode orig and reply are exchanged.
|
||||
lua_pushliteral(params.L, "direct");
|
||||
lua_getfield(params.L, -2, (params.server ^ bIncoming) ? "server" : "client");
|
||||
lua_rawset(params.L,-3);
|
||||
|
||||
lua_pushliteral(params.L, "reverse");
|
||||
lua_getfield(params.L, -2, (params.server ^ bIncoming) ? "client" : "server");
|
||||
lua_rawset(params.L,-3);
|
||||
|
||||
lua_rawset(params.L,-3);
|
||||
}
|
||||
else
|
||||
lua_pushnil(params.L);
|
||||
@@ -1316,7 +1360,7 @@ void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos)
|
||||
LUA_STACK_GUARD_LEAVE(params.L, 0)
|
||||
}
|
||||
|
||||
void lua_pushf_args(const struct str2_list_head *args, int idx_desync)
|
||||
void lua_pushf_args(const struct str2_list_head *args, int idx_desync, bool subst_prefix)
|
||||
{
|
||||
// var=val - pass val string
|
||||
// var=%val - subst 'val' blob
|
||||
@@ -1337,17 +1381,22 @@ void lua_pushf_args(const struct str2_list_head *args, int idx_desync)
|
||||
{
|
||||
var = arg->str1;
|
||||
val = arg->str2 ? arg->str2 : "";
|
||||
if (val[0]=='\\' && (val[1]=='%' || val[1]=='#'))
|
||||
// escape char
|
||||
lua_pushf_str(var, val+1);
|
||||
else if (val[0]=='%')
|
||||
lua_pushf_blob(idx_desync, var, val+1);
|
||||
else if (val[0]=='#')
|
||||
if (subst_prefix)
|
||||
{
|
||||
lua_push_blob(idx_desync, val+1);
|
||||
lua_Integer len = lua_rawlen(params.L, -1);
|
||||
lua_pop(params.L,1);
|
||||
lua_pushf_int(var, len);
|
||||
if (val[0]=='\\' && (val[1]=='%' || val[1]=='#'))
|
||||
// escape char
|
||||
lua_pushf_str(var, val+1);
|
||||
else if (val[0]=='%')
|
||||
lua_pushf_blob(idx_desync, var, val+1);
|
||||
else if (val[0]=='#')
|
||||
{
|
||||
lua_push_blob(idx_desync, val+1);
|
||||
lua_Integer len = lua_rawlen(params.L, -1);
|
||||
lua_pop(params.L,1);
|
||||
lua_pushf_int(var, len);
|
||||
}
|
||||
else
|
||||
lua_pushf_str(var, val);
|
||||
}
|
||||
else
|
||||
lua_pushf_str(var, val);
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#endif
|
||||
|
||||
// pushing and not popping inside luacall cause memory leak
|
||||
// these macros ensure correct stack position or throw error if not
|
||||
#define LUA_STACK_GUARD_ENTER(L) int _lsg=lua_gettop(L);
|
||||
#define LUA_STACK_GUARD_LEAVE(L,N) if ((_lsg+N)!=lua_gettop(L)) luaL_error(L,"stack guard failure");
|
||||
#define LUA_STACK_GUARD_RETURN(L,N) LUA_STACK_GUARD_LEAVE(L,N); return N;
|
||||
@@ -68,6 +69,8 @@ void lua_pushf_int(const char *field, lua_Integer v);
|
||||
void lua_pushi_int(lua_Integer idx, lua_Integer v);
|
||||
void lua_pushf_lint(const char *field, int64_t v);
|
||||
void lua_pushi_lint(lua_Integer idx, int64_t v);
|
||||
void lua_pushf_number(const char *field, lua_Number v);
|
||||
void lua_pushi_number(lua_Integer idx, lua_Number v);
|
||||
void lua_push_raw(const void *v, size_t l);
|
||||
void lua_pushf_raw(const char *field, const void *v, size_t l);
|
||||
void lua_pushi_raw(lua_Integer idx, const void *v, size_t l);
|
||||
@@ -86,8 +89,8 @@ void lua_pushf_iphdr(const struct ip *ip, size_t len);
|
||||
void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len);
|
||||
void lua_push_dissect(const struct dissect *dis);
|
||||
void lua_pushf_dissect(const struct dissect *dis);
|
||||
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos);
|
||||
void lua_pushf_args(const struct str2_list_head *args, int idx_desync);
|
||||
void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_positions *tpos, bool bIncoming);
|
||||
void lua_pushf_args(const struct str2_list_head *args, int idx_desync, bool subst_prefix);
|
||||
void lua_pushf_pos(const char *name, const struct packet_pos *pos);
|
||||
void lua_pushf_range(const char *name, const struct packet_range *range);
|
||||
void lua_pushf_global(const char *field, const char *global);
|
||||
|
||||
33
nfq2/nfqws.c
33
nfq2/nfqws.c
@@ -1434,11 +1434,15 @@ static void exithelp(void)
|
||||
" --hostlist-auto-fail-threshold=<int>\t\t\t; how many failed attempts cause hostname to be added to auto hostlist (default : %d)\n"
|
||||
" --hostlist-auto-fail-time=<int>\t\t\t; all failed attemps must be within these seconds (default : %d)\n"
|
||||
" --hostlist-auto-retrans-threshold=<int>\t\t; how many request retransmissions cause attempt to fail (default : %d)\n"
|
||||
" --hostlist-auto-retrans-maxseq=<int>\t\t\t; count retransmissions only within this relative sequence (default : %u)\n"
|
||||
" --hostlist-auto-incoming-maxseq=<int>\t\t\t; treat tcp connection as successful if incoming relative sequence exceedes this threshold (default : %u)\n"
|
||||
" --hostlist-auto-udp-out=<int>\t\t\t\t; udp failure condition : sent at least `udp_out` packets (default : %u)\n"
|
||||
" --hostlist-auto-udp-in=<int>\t\t\t\t; udp failure condition : received not more than `udp_in` packets (default : %u)\n"
|
||||
" --hostlist-auto-debug=<logfile>\t\t\t; debug auto hostlist positives (global parameter)\n"
|
||||
"\nLUA PACKET PASS MODE:\n"
|
||||
" --payload=type[,type]\t\t\t\t\t; set payload types following LUA functions should process : %s\n"
|
||||
" --out-range=[(n|a|d|s)<int>](-|<)[(n|a|d|s)<int>]\t; set outgoing packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, b - byte count, x - never, a - always\n"
|
||||
" --in-range=[(n|a|d|s)<int>](-|<)[(n|a|d|s)<int>]\t; set incoming packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, b - byte count, x - never, a - always\n"
|
||||
" --out-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]\t; set outgoing packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
|
||||
" --in-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]\t; set incoming packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
|
||||
"\nLUA DESYNC ACTION:\n"
|
||||
" --lua-desync=<functon>[:param1=val1[:param2=val2]]\t; call LUA function when packet received\n",
|
||||
#if defined(__linux__) || defined(SO_USER_COOKIE)
|
||||
@@ -1448,7 +1452,10 @@ static void exithelp(void)
|
||||
IPCACHE_LIFETIME,
|
||||
LUA_GC_INTERVAL,
|
||||
all_protos,
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT,
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT,
|
||||
HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT,
|
||||
HOSTLIST_AUTO_RETRANS_MAXSEQ, HOSTLIST_AUTO_INCOMING_MAXSEQ,
|
||||
HOSTLIST_AUTO_UDP_OUT, HOSTLIST_AUTO_UDP_IN,
|
||||
all_payloads
|
||||
);
|
||||
exit(1);
|
||||
@@ -1545,6 +1552,10 @@ enum opt_indices {
|
||||
IDX_HOSTLIST_AUTO_FAIL_THRESHOLD,
|
||||
IDX_HOSTLIST_AUTO_FAIL_TIME,
|
||||
IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD,
|
||||
IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ,
|
||||
IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ,
|
||||
IDX_HOSTLIST_AUTO_UDP_IN,
|
||||
IDX_HOSTLIST_AUTO_UDP_OUT,
|
||||
IDX_HOSTLIST_AUTO_DEBUG,
|
||||
IDX_NEW,
|
||||
IDX_SKIP,
|
||||
@@ -1629,6 +1640,10 @@ static const struct option long_options[] = {
|
||||
[IDX_HOSTLIST_AUTO_FAIL_THRESHOLD] = {"hostlist-auto-fail-threshold", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_FAIL_TIME] = {"hostlist-auto-fail-time", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD] = {"hostlist-auto-retrans-threshold", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ] = {"hostlist-auto-retrans-maxseq", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ] = {"hostlist-auto-incoming-maxseq", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_UDP_IN] = {"hostlist-auto-udp-in", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_UDP_OUT] = {"hostlist-auto-udp-out", required_argument, 0, 0},
|
||||
[IDX_HOSTLIST_AUTO_DEBUG] = {"hostlist-auto-debug", required_argument, 0, 0},
|
||||
[IDX_NEW] = {"new", no_argument, 0, 0},
|
||||
[IDX_SKIP] = {"skip", no_argument, 0, 0},
|
||||
@@ -2077,6 +2092,18 @@ int main(int argc, char **argv)
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ:
|
||||
dp->hostlist_auto_retrans_maxseq = (uint32_t)atoi(optarg);
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ:
|
||||
dp->hostlist_auto_incoming_maxseq = (uint32_t)atoi(optarg);
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_UDP_OUT:
|
||||
dp->hostlist_auto_udp_out = atoi(optarg);
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_UDP_IN:
|
||||
dp->hostlist_auto_udp_in = atoi(optarg);
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_DEBUG:
|
||||
{
|
||||
FILE *F = fopen(optarg, "a+t");
|
||||
|
||||
@@ -12,4 +12,4 @@ extern bool bQuit;
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
// when something changes that can break LUA compatibility this version should be increased
|
||||
#define LUA_COMPAT_VER 2
|
||||
#define LUA_COMPAT_VER 3
|
||||
|
||||
@@ -26,7 +26,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
|
||||
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
|
||||
}
|
||||
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos)
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_positions *tpos)
|
||||
{
|
||||
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
|
||||
if (!rp) return NULL;
|
||||
@@ -54,13 +54,13 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock
|
||||
rp->len_payload=len_payload;
|
||||
|
||||
// make a copy for replay
|
||||
if (pos)
|
||||
if (tpos)
|
||||
{
|
||||
rp->pos = *pos;
|
||||
rp->pos_present = true;
|
||||
rp->tpos = *tpos;
|
||||
rp->tpos_present = true;
|
||||
}
|
||||
else
|
||||
rp->pos_present = false;
|
||||
rp->tpos_present = false;
|
||||
|
||||
TAILQ_INSERT_TAIL(q, rp, next);
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ struct rawpacket
|
||||
uint32_t fwmark;
|
||||
size_t len, len_payload;
|
||||
uint8_t *packet;
|
||||
t_ctrack_position pos;
|
||||
bool pos_present;
|
||||
t_ctrack_positions tpos;
|
||||
bool tpos_present;
|
||||
TAILQ_ENTRY(rawpacket) next;
|
||||
};
|
||||
TAILQ_HEAD(rawpacket_tailhead, rawpacket);
|
||||
@@ -26,6 +26,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q);
|
||||
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
|
||||
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
|
||||
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_position *pos);
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_positions *tpos);
|
||||
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
|
||||
void rawpacket_free(struct rawpacket *rp);
|
||||
|
||||
@@ -342,6 +342,10 @@ void dp_init(struct desync_profile *dp)
|
||||
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
|
||||
dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
|
||||
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
|
||||
dp->hostlist_auto_retrans_maxseq = HOSTLIST_AUTO_RETRANS_MAXSEQ;
|
||||
dp->hostlist_auto_incoming_maxseq = HOSTLIST_AUTO_INCOMING_MAXSEQ;
|
||||
dp->hostlist_auto_udp_out = HOSTLIST_AUTO_UDP_OUT;
|
||||
dp->hostlist_auto_udp_in = HOSTLIST_AUTO_UDP_IN;
|
||||
dp->filter_ipv4 = dp->filter_ipv6 = true;
|
||||
}
|
||||
static void dp_clear_dynamic(struct desync_profile *dp)
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
|
||||
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
|
||||
#define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3
|
||||
#define HOSTLIST_AUTO_RETRANS_MAXSEQ 65536
|
||||
#define HOSTLIST_AUTO_INCOMING_MAXSEQ 4096
|
||||
#define HOSTLIST_AUTO_UDP_OUT 4
|
||||
#define HOSTLIST_AUTO_UDP_IN 1
|
||||
|
||||
#define IPCACHE_LIFETIME 7200
|
||||
|
||||
@@ -39,7 +43,7 @@
|
||||
#define BLOB_EXTRA_BYTES 128
|
||||
|
||||
// this MSS is used for ipv6 in windows and linux
|
||||
#define DEFAULT_MSS 1360
|
||||
#define DEFAULT_MSS 1220
|
||||
|
||||
#define RECONSTRUCT_MAX_SIZE 16384
|
||||
|
||||
@@ -78,6 +82,8 @@ struct desync_profile
|
||||
// pointer to autohostlist. NULL if no autohostlist for the profile.
|
||||
struct hostlist_file *hostlist_auto;
|
||||
int hostlist_auto_fail_threshold, hostlist_auto_fail_time, hostlist_auto_retrans_threshold;
|
||||
int hostlist_auto_udp_in, hostlist_auto_udp_out;
|
||||
uint32_t hostlist_auto_retrans_maxseq, hostlist_auto_incoming_maxseq;
|
||||
|
||||
hostfail_pool *hostlist_auto_fail_counters;
|
||||
|
||||
|
||||
@@ -368,8 +368,10 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
|
||||
|
||||
// extract 2nd level domains
|
||||
const char *dhost, *drhost;
|
||||
if (!FindNLD((uint8_t*)host,strlen(host),2,(const uint8_t**)&dhost,NULL) || !FindNLD((uint8_t*)redirect_host,strlen(redirect_host),2,(const uint8_t**)&drhost,NULL))
|
||||
if (!FindNLD((uint8_t*)redirect_host,strlen(redirect_host),2,(const uint8_t**)&drhost,NULL))
|
||||
return false;
|
||||
if (!FindNLD((uint8_t*)host,strlen(host),2,(const uint8_t**)&dhost,NULL))
|
||||
return true; // no SLD redirects to SLD
|
||||
|
||||
// compare 2nd level domains
|
||||
return strcasecmp(dhost, drhost)!=0;
|
||||
@@ -848,7 +850,7 @@ bool TLSMod(const struct fake_tls_mod *tls_mod, const uint8_t *payload, size_t p
|
||||
{
|
||||
if (tls_mod->mod & FAKE_TLS_MOD_DUP_SID)
|
||||
{
|
||||
if (IsTLSClientHello(payload, payload_len, false))
|
||||
if (IsTLSClientHelloPartial(payload, payload_len))
|
||||
{
|
||||
if (payload_len < 44)
|
||||
{
|
||||
|
||||
@@ -127,7 +127,7 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
|
||||
|
||||
struct fake_tls_mod
|
||||
{
|
||||
char sni[128];
|
||||
char sni[256];
|
||||
uint32_t mod;
|
||||
};
|
||||
#define FAKE_TLS_MOD_RND 0x01
|
||||
|
||||
Reference in New Issue
Block a user