Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-17 07:09:44 +00:00

17 Commits
v0.6.1 ... v0.7

Author SHA1 Message Date
bol-van
7708021587 nfqws2: rewrite autohostlist udp failure detector logic 2025-12-11 15:19:31 +03:00
bol-van
912aadf6ca zapret-auto: override host autostate key 2025-12-11 13:41:04 +03:00
bol-van
420cc0c3ef nfqws2: fix crash 2025-12-11 13:38:10 +03:00
bol-van
6ce5829d06 zapret-auto: override host autostate key 2025-12-11 12:57:32 +03:00
bol-van
a6d43af931 nfqws2: autohostlist do not react to rseq 0 2025-12-11 01:03:25 +03:00
bol-van
ca9898959e nfqws2: remove commented test code 2025-12-11 00:23:20 +03:00
bol-van
8cd2904614 nfqws2: push desync.track.pos.dt as float with nsec accuracy 2025-12-11 00:21:22 +03:00
bol-van
0de1ab1b1b init.d: AUTOHOSTLIST_INCOMING_MAXSEQ 2025-12-10 23:21:30 +03:00
bol-van
d1690aadcf nfqws2: autohostlist incoming failure triggers change 2025-12-10 23:11:28 +03:00
bol-van
2dd8533fb5 nfqws2,zapret-lib.lua,zapret-auto.lua: restructure conntrack record 2025-12-10 19:36:31 +03:00
bol-van
33ac18ea6b zapret-lib,zapret-auto: do not use desync copy to not lose VERDICT_MODIFY changes 2025-12-10 13:43:39 +03:00
bol-van
5c05c10f83 zapret-lib: return 0 if #val refers to non-string and non-table type 2025-12-10 10:49:46 +03:00
bol-van
7de0995d4a nfqws2,zapret-lib: fix non-working # and % arg subst under orchestrator 2025-12-10 10:28:48 +03:00
bol-van
a1c64e4dea update doc 2025-12-09 18:08:36 +03:00
bol-van
92b66b1535 update doc 2025-12-09 18:08:01 +03:00
bol-van
9bf4fb11e7 update doc 2025-12-09 18:05:08 +03:00
bol-van
7deeb04207 start writing manual.md 2025-12-09 18:00:24 +03:00
22 changed files with 604 additions and 363 deletions

View File

@@ -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 parm9 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 ] &&
@@ -41,10 +41,13 @@ filter_apply_hostlist_target()
parm6="${AUTOHOSTLIST_FAIL_TIME:+--hostlist-auto-fail-time=$AUTOHOSTLIST_FAIL_TIME}"
parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}"
parm8="${AUTOHOSTLIST_RETRANS_MAXSEQ:+--hostlist-auto-retrans-maxseq=$AUTOHOSTLIST_RETRANS_MAXSEQ}"
parm9="--hostlist=$HOSTLIST_AUTO"
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}${parm8:+ $parm8}"
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm9:+ $parm9}"
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")"

View File

@@ -26,10 +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

View File

@@ -80,3 +80,13 @@ 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

View File

@@ -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
View 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 переходит к ожиданию следующего пакета, и цикл повторяется вновь.

View File

@@ -1,5 +1,3 @@
# zapret2 v0.2
## Зачем это нужно
Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь

View File

@@ -2,27 +2,31 @@
-- 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
@@ -85,7 +89,7 @@ end
-- 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 3
-- 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
@@ -94,7 +98,7 @@ function standard_failure_detector(desync, crec, arg)
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 3
local udp_out = tonumber(arg.udp_out) or 4
local trigger = false
if desync.dis.tcp then
@@ -141,11 +145,11 @@ function standard_failure_detector(desync, crec, arg)
if desync.outgoing then
if udp_out then
local udp_in = udp_in or 0
trigger = desync.track.pcounter_orig>=udp_out and desync.track.pcounter_reply<=udp_in
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.pcounter_orig..">="..udp_out.." udp_in "..desync.track.pcounter_reply.."<="..udp_in)
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
@@ -244,12 +248,11 @@ function circular(ctx, desync)
end
DLOG("circular: current strategy "..hrec.nstrategy)
local dcopy = desync_copy(desync)
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(dcopy, verdict, instance)
verdict = plan_instance_execute(desync, verdict, instance)
end
end

View File

@@ -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
@@ -126,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
-- 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.arg)
apply_arg_prefix(desync)
end
-- produce resulting verdict from 2 verdicts
function verdict_aggregate(v1, v2)
@@ -205,12 +217,11 @@ function desync_copy(desync)
end
-- redo what whould be done without orchestration
function replay_execution_plan(desync)
local dcopy = desync_copy(desync)
local verdict = VERDICT_PASS
while true do
local instance = plan_instance_pop(dcopy)
local instance = plan_instance_pop(desync)
if not instance then break end
verdict = plan_instance_execute(dcopy, verdict, instance)
verdict = plan_instance_execute(desync, verdict, instance)
end
return verdict
end
@@ -228,20 +239,31 @@ end
-- 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

View File

@@ -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,65 +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.ack_last = ntohl(tcphdr->th_seq);
t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack);
t->pos.pos_reply = t->pos.ack_last + len_payload;
if (t->pos.state == SYN)
t->pos.uppos_reply_prev = t->pos.uppos_reply = t->pos.pos_reply;
else if (len_payload)
{
t->pos.uppos_reply_prev = t->pos.uppos_reply;
if (!((t->pos.pos_reply - t->pos.uppos_reply) & 0x80000000))
t->pos.uppos_reply = t->pos.pos_reply;
}
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);
if (t->pos.state == SYN)
t->pos.uppos_orig_prev = t->pos.uppos_orig = t->pos.pos_orig;
else if (len_payload)
{
t->pos.uppos_orig_prev = t->pos.uppos_orig;
if (!((t->pos.pos_orig - t->pos.uppos_orig) & 0x80000000))
t->pos.uppos_orig = t->pos.pos_orig;
}
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)
@@ -327,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) ||
@@ -345,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;
}
}
@@ -357,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));
};

View File

@@ -54,14 +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 retrans_detect_finalized;
bool failure_detect_finalized;
uint8_t incoming_ttl;
@@ -78,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;

View File

@@ -14,21 +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 uppos_orig, uppos_reply; // max seen position. useful to detect retransmissions
uint32_t uppos_orig_prev, uppos_reply_prev; // previous max seen position. useful to detect retransmissions
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;

View File

@@ -240,29 +240,27 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch
}
}
static bool is_retransmission(const t_ctrack *ctrack)
static bool is_retransmission(const t_ctrack_position *pos)
{
return !((ctrack->pos.uppos_orig_prev - ctrack->pos.pos_orig) & 0x80000000);
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->retrans_detect_finalized)
return false;
if (!seq_within(ctrack->pos.seq_last, ctrack->pos.seq0, ctrack->pos.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq))
if (!seq_within(ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq))
{
ctrack->retrans_detect_finalized = true;
DLOG("retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->pos.seq0, ctrack->pos.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq);
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))
if (!is_retransmission(&ctrack->pos.client))
return false;
}
ctrack->req_retrans_counter++;
@@ -270,7 +268,7 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho
{
DLOG("retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold);
ctrack_stop_retrans_counter(ctrack);
ctrack->retrans_detect_finalized = true;
ctrack->failure_detect_finalized = true;
return true;
}
DLOG("retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold);
@@ -325,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)
{
@@ -353,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);
@@ -383,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);
@@ -406,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");
}
@@ -438,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");
@@ -467,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;
@@ -489,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
@@ -497,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;
@@ -505,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
@@ -513,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);
}
@@ -654,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,
@@ -672,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)
{
@@ -686,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)
@@ -709,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,
@@ -737,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);
@@ -769,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");
}
@@ -786,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);
@@ -808,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);
@@ -836,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);
@@ -936,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)
{
@@ -1093,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
@@ -1144,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);
@@ -1170,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)
@@ -1196,14 +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) && !is_retransmission(ctrack))
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 (!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))
{
@@ -1214,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.
@@ -1343,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);
}
@@ -1364,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)
{
@@ -1550,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
{
@@ -1561,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
{
@@ -1592,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))
{
@@ -1612,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);
}
@@ -1634,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))
@@ -1671,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},
@@ -1773,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");
@@ -1799,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;
}
@@ -1848,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)
{
@@ -1864,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);
}
@@ -1872,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);
}
@@ -1902,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:

View File

@@ -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')
{

View File

@@ -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,31 +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, 18);
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("uppos_orig", pos->uppos_orig - pos->seq0);
lua_pushf_int("uppos_orig_prev", pos->uppos_orig_prev - 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("uppos_reply", pos->uppos_reply - pos->ack0);
lua_pushf_int("uppos_reply_prev", pos->uppos_reply_prev - 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);
@@ -1320,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
@@ -1341,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);

View File

@@ -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);

View File

@@ -1435,11 +1435,14 @@ static void exithelp(void)
" --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)
@@ -1450,7 +1453,9 @@ static void exithelp(void)
LUA_GC_INTERVAL,
all_protos,
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT,
HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT, HOSTLIST_AUTO_RETRANS_MAXSEQ,
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);
@@ -1548,6 +1553,9 @@ enum opt_indices {
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,
@@ -1633,6 +1641,9 @@ static const struct option long_options[] = {
[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},
@@ -1695,22 +1706,6 @@ static const struct option long_options[] = {
int main(int argc, char **argv)
{
/*
t_reassemble t;
ReasmInit(&t,16,-10);
memset(t.packet,0,16);
bool b;
b=ReasmFeed(&t,-10,"0123456789",10);
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
b=ReasmFeed(&t,0,"YOREK",5);
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
b=ReasmFeed(&t,-12,"XOR",3);
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
b=ReasmFeed(&t,3,"abc",3);
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
return 0;
*/
#ifdef __CYGWIN__
if (service_run(argc, argv))
{
@@ -2100,6 +2095,15 @@ int main(int argc, char **argv)
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");

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -343,6 +343,9 @@ void dp_init(struct desync_profile *dp)
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)

View File

@@ -31,6 +31,9 @@
#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
@@ -40,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
@@ -79,7 +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;
uint32_t hostlist_auto_retrans_maxseq;
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;

View File

@@ -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;