zapret README

The project's README from github.com/bol-van/zapret. As one markdown file: README.md.

ВНИМАНИЕ, остерегайтесь мошенников

zapret является свободным и open source. Всякий, кто понуждает вас скачивать zapret только с его ресурса, требует удалить ссылки, видео, файлы, обосновывая эти требования авторскими правами, сам нарушает лицензию. Однако, это не исключает добровольные пожертвования.

zapret2

Эта версия zapret более не развивается и находится в режиме EOL (End-Of-Life). Никаких новых функций больше не будет. Только багфиксы. Pull реквесты с новыми фичами не принимаются.

Актуальная версия - zapret 2

Multilanguage README

en ru



Зачем это нужно

Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь обойти блокировки или замедление сайтов HTTP(S), сигнатурный анализ TCP и UDP протоколов, например, с целью блокировки VPN.

Проект нацелен прежде всего на маломощные embedded устройства - роутеры, работающие под OpenWrt. Поддерживаются традиционные Linux-системы, FreeBSD, OpenBSD, частично macOS. В некоторых случаях возможна самостоятельная прикрутка решения к различным прошивкам.

Большая часть функционала работает на Windows.

Быстрый старт

Как это работает

В самом простейшем случае вы имеете дело с пассивным DPI. Пассивный DPI может читать трафик из потока, может инжектить свои пакеты, но не может блокировать проходящие пакеты. Если запрос "плохой", пассивный DPI инжектит пакет RST, опционально дополняя его пакетом HTTP redirect. Если фейк пакет инжектится только для клиента, в этом случае можно обойтись командами iptables для дропа RST и/или редиректа на заглушку по определённым условиям, которые нужно подбирать для каждого провайдера индивидуально. Так мы обходим последствия срабатывания триггера запрета. Если пассивный DPI направляет пакет RST в том числе и серверу, то вы ничего с этим не сможете сделать. Ваша задача — не допустить срабатывания триггера запрета. Одними iptables уже не обойтись. Этот проект нацелен именно на предотвращение срабатывания запрета, а не ликвидацию его последствий.

Активный DPI ставится в разрез провода и может дропать пакеты по любым критериям, в том числе распознавать TCP-потоки и блокировать любые пакеты, принадлежащие потоку.

Как не допустить срабатывания триггера запрета? Послать то, на что DPI не рассчитывает и что ломает ему алгоритм распознавания запросов и их блокировки.

Некоторые DPI не могут распознать HTTP-запрос, если он разделен на TCP-сегменты. Например, запрос вида GET / HTTP/1.1\r\nHost: kinozal.tv...... мы посылаем двумя частями: сначала идет GET, затем / HTTP/1.1\r\nHost: kinozal.tv...... Другие DPI спотыкаются, когда заголовок Host: пишется в другом регистре: например, host:. Кое-где работает добавление дополнительного пробела после метода: GET / → GET / или добавление точки в конце имени хоста: Host: kinozal.tv.

Существует и более продвинутая магия, направленная на преодоление DPI на пакетном уровне.

Подробнее про DPI:
https://habr.com/ru/post/335436 или https://web.archive.org/web/20230331233644/https://habr.com/ru/post/335436/
https://geneva.cs.umd.edu/papers/geneva_ccs19.pdf

Что сейчас происходит в России

Раньше, до внедрения повсеместных систем ТСПУ, использовался зоопарк различных DPI у провайдеров. Какие-то были активными, какие-то пассивными. Сейчас время простых iptables окончательно ушло. Везде активный DPI ТСПУ, но кое-где могут оставаться невыключенными дополнительные старые DPI из зоопарка. В этом случае приходится обходить сразу несколько DPI. Все больше становится внереестровых блокировок, о которых вы узнаете только по факту недоступности чего-либо, в списках этого нет. Применяются блокировки некоторых диапазонов ip адресов (автономный обход невозможен) и протоколов (VPN). На некоторых диапазонах IP используется более строгий фильтр, распознающий попытки обмана через сегментацию. Должно быть это связано с некоторыми сервисами, которые пытаются таким образом обмануть DPI.

Как это реализовать на практике в системе linux

Если кратко, то варианты можно классифицировать по следующей схеме :

  1. Пассивный DPI, не отправляющий RST серверу. Помогут индивидуально настраиваемые под провайдера команды iptables. На rutracker в разделе "обход блокировок - другие способы" по этому вопросу существует отдельная тема. В данном проекте не рассматривается. Если вы не допустите срабатывание триггера запрета, то и не придется бороться с его последствиями.
  2. Модификация TCP соединения на уровне потока. Реализуется через proxy или transparent proxy.
  3. Модификация TCP соединения на уровне пакетов. Реализуется через обработчик очереди NFQUEUE и raw сокеты.

Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно. Чтобы они работали, необходимо их запустить с нужными параметрами и перенаправить на них определенный трафик средствами iptables или nftables.

Когда это работать не будет

nfqws

Эта программа - модификатор пакетов и обработчик очереди NFQUEUE. Для BSD систем существует адаптированный вариант - dvtws, собираемый из тех же исходников (см. документация BSD).

@<config_file>|$<config_file>                             ; читать конфигурацию из файла. опция должна быть первой. остальные опции игнорируются.

--debug=0|1                                               ; 1=выводить отладочные сообщения
--dry-run                                                 ; проверить опции командной строки и выйти. код 0 - успешная проверка.
--version                                                 ; вывести версию и выйти
--comment                                                 ; любой текст (игнорируется)
--daemon                                                  ; демонизировать прогу
--pidfile=<file>                                          ; сохранить PID в файл
--user=<username>                                         ; менять uid процесса
--uid=uid[:gid]                                           ; менять uid процесса
--qnum=N                                                  ; номер очереди N
--bind-fix4                                               ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv4 пакетов
--bind-fix6                                               ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv6 пакетов
--ctrack-timeouts=S:E:F[:U]                               ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60
--ctrack-disable=[0|1]                                    ; 1 или отсутствие аргумента отключает conntrack
--ipcache-lifetime=<int>                                  ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1]                                  ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--wsize=<winsize>[:<scale_factor>]                        ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !)
--wssize=<winsize>[:<scale_factor>]                       ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !)
--wssize-cutoff=[n|d|s]N                                  ; изменять server window size в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--wssize-forced-cutoff=0|1                                ; 1(default)=автоматически отключать wssize в случае обнаружения известного протокола
--synack-split=[syn|synack|acksyn]                        ; выполнить tcp split handshake. вместо SYN,ACK отсылать только SYN, SYN+ACK или ACK+SYN
--orig-ttl=<int>                                          ; модифицировать TTL оригинального пакета
--orig-ttl6=<int>                                         ; модифицировать ipv6 hop limit оригинальных пакетов.  если не указано, используется значение --orig-ttl
--orig-autottl=[<delta>[:<min>[-<max>]]|-]                ; режим auto ttl для ipv4 и ipv6. по умолчанию: +5:3-64. "0:0-0" или "-" отключает функцию
--orig-autottl6=[<delta>[:<min>[-<max>]]|-]               ; переопределение предыдущего параметра для ipv6
--orig-tcp-flags-set=<int|0xHEX|flaglist>                 ; устанавливать указанные tcp флаги (flags |= value). число , либо список через запятую : FIN,SYN,RST,PSH,ACK,URG,ECE,CWR,AE,R1,R2,R3
--orig-tcp-flags-unset=<int|0xHEX|flaglist>               ; удалять указанные tcp флаги (flags &= ~value)
--orig-mod-start=[n|d|s]N                                 ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--orig-mod-cutoff=[n|d|s]N                                ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--dup=<int>                                               ; высылать N дубликатов до оригинала
--dup-replace=[0|1]                                       ; 1 или отсутствие аргумента блокирует отправку оригинала. отправляются только дубликаты.
--dup-ttl=<int>                                           ; модифицировать TTL дубликатов
--dup-ttl6=<int>                                          ; модифицировать ipv6 hop limit дубликатов. если не указано, используется значение --dup-ttl
--dup-autottl=[<delta>[:<min>[-<max>]]|-]                 ; режим auto ttl для ipv4 и ipv6. по умолчанию: +1:3-64. "0:0-0" или "-" отключает функцию
--dup-autottl6=[<delta>[:<min>[-<max>]]|-]                ; переопределение предыдущего параметра для ipv6
--dup-tcp-flags-set=<int|0xHEX|flaglist>                  ; устанавливать указанные tcp флаги (flags |= value). число , либо список через запятую : FIN,SYN,RST,PSH,ACK,URG,ECE,CWR,AE,R1,R2,R3
--dup-tcp-flags-unset=<int|0xHEX|flaglist>                ; удалять указанные tcp флаги (flags &= ~value)
--dup-fooling=<fooling>                                   ; дополнительные методики как сделать, чтобы дубликат не дошел до сервера. none md5sig badseq badsum datanoack ts hopbyhop hopbyhop2
--dup-ts-increment=<int|0xHEX>                            ; инкремент TSval для ts. по умолчанию -600000
--dup-badseq-increment=<int|0xHEX>                        ; инкремент sequence number для badseq. по умолчанию -10000
--dup-badack-increment=<int|0xHEX>                        ; инкремент ack sequence number для badseq. по умолчанию -66000
--dup-ip-id=same|zero|seq|rnd                             ; режим назначения ip_id для пакетов dup
--dup-start=[n|d|s]N                                      ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dup-cutoff=[n|d|s]N                                     ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostcase                                                ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace                                             ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
--methodeol                                               ; добавить перевод строки в unix стиле ('\n') перед методом и убрать пробел из Host: : "GET / ... Host: domain.com" => "\nGET  / ... Host:domain.com"
--hostspell=HoST                                          ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
--domcase                                                 ; домен после Host: сделать таким : TeSt.cOm
--ip-id=seq|seqgroup|rnd|zero                             ; режим назначения ip_id для генерированных пакетов
--dpi-desync=[<mode0>,]<mode>[,<mode2]                    ; атака по десинхронизации DPI. mode : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 multisplit multidisorder fakedsplit hostfakesplit fakeddisorder ipfrag2 udplen tamper
--dpi-desync-fwmark=<int|0xHEX>                           ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000
--dpi-desync-ttl=<int>                                    ; установить ttl для десинхронизирующих пакетов
--dpi-desync-ttl6=<int>                                   ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение --dpi-desync-ttl
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-]          ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. "0:0-0" или "-" отключает функцию
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-]         ; переопределение предыдущего параметра для ipv6
--dpi-desync-tcp-flags-set=<int|0xHEX|flaglist>           ; устанавливать указанные tcp флаги (flags |= value). число , либо список через запятую : FIN,SYN,RST,PSH,ACK,URG,ECE,CWR,AE,R1,R2,R3
--dpi-desync-tcp-flags-unset=<int|0xHEX|flaglist>         ; удалять указанные tcp флаги (flags &= ~value)
--dpi-desync-fooling=<fooling>                            ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack ts hopbyhop hopbyhop2
--dpi-desync-repeats=<N>                                  ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
--dpi-desync-skip-nosni=0|1                               ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
--dpi-desync-split-pos=N|-N|marker+N|marker-N             ; список через запятую маркеров для tcp сегментации в режимах split и disorder
--dpi-desync-split-seqovl=N|-N|marker+N|marker-N          ; единичный маркер, определяющий величину перекрытия sequence в режимах split и disorder. для split поддерживается только положительное число.
--dpi-desync-split-seqovl-pattern=[+ofs]@<filename>|0xHEX ; чем заполнять фейковую часть overlap
--dpi-desync-fakedsplit-pattern=[+ofs]@<filename>|0xHEX   ; чем заполнять фейки в fakedsplit/fakeddisorder
--dpi-desync-fakedsplit-mod=mod[,mod]                     ; может быть none, altorder=0|1|2|3 + 0|8|16
--dpi-desync-hostfakesplit-midhost=marker+N|marker-N      ; маркер дополнительного разреза сегмента с оригинальным хостом. должен попадать в пределы хоста.
--dpi-desync-hostfakesplit-mod=mod[,mod]                  ; может быть none, host=<hostname>, altorder=0|1
--dpi-desync-ipfrag-pos-tcp=<8..9216>                     ; позиция ip фрагментации tcp, начиная с транспортного заголовка. должно быть кратно 8, по умолчанию - 32.
--dpi-desync-ipfrag-pos-udp=<8..9216>                     ; позиция ip фрагментации udp, начиная с транспортного заголовка. должно быть кратно 8, по умолчанию - 8.
--dpi-desync-ts-increment=<int|0xHEX>                     ; инкремент TSval для ts. по умолчанию -600000
--dpi-desync-badseq-increment=<int|0xHEX>                 ; инкремент sequence number для badseq. по умолчанию -10000
--dpi-desync-badack-increment=<int|0xHEX>                 ; инкремент ack sequence number для badseq. по умолчанию -66000
--dpi-desync-any-protocol=0|1                             ; 0(default)=работать только по http request и tls clienthello  1=по всем непустым пакетам данных
--dpi-desync-fake-tcp-mod=mod[,mod]                       ; список через запятую режимов runtime модификации tcp фейков (любых) : none, seq
--dpi-desync-fake-http=[+ofs]@<filename>|0xHEX	          ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному www.iana.org
--dpi-desync-fake-tls=[+ofs]@<filename>|0xHEX|![+offset]  ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному. '!' = стандартный фейк
--dpi-desync-fake-tls-mod=mod[,mod]                       ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--dpi-desync-fake-unknown=[+ofs]@<filename>|0xHEX         ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт
--dpi-desync-fake-syndata=[+ofs]@<filename>|0xHEX         ; файл, содержащий фейковый пейлоад пакета SYN для режима десинхронизации syndata
--dpi-desync-fake-quic=[+ofs]@<filename>|0xHEX            ; файл, содержащий фейковый QUIC Initial
--dpi-desync-fake-wireguard=[+ofs]@<filename>|0xHEX       ; файл, содержащий фейковый wireguard handshake initiation
--dpi-desync-fake-dht=[+ofs]@<filename>|0xHEX             ; файл, содержащий фейковый пейлоад DHT протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-fake-discord=[+ofs]@<filename>|0xHEX         ; файл, содержащий фейковый пейлоад Discord протокола нахождения IP адреса для голосовых чатов для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-fake-stun=[+ofs]@<filename>|0xHEX            ; файл, содержащий фейковый пейлоад STUN протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-fake-unknown-udp=[+ofs]@<filename>|0xHEX     ; файл, содержащий фейковый пейлоад неизвестного udp протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-udplen-increment=<int>                       ; на сколько увеличивать длину udp пейлоада в режиме udplen
--dpi-desync-udplen-pattern=[+ofs]@<filename>|0xHEX       ; чем добивать udp пакет в режиме udplen. по умолчанию - нули
--dpi-desync-start=[n|d|s]N                               ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dpi-desync-cutoff=[n|d|s]N                              ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostlist=<filename>                                     ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
                                                          ; в файле должен быть хост на каждой строке.
                                                          ; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
                                                          ; при изменении времени модификации файла он перечитывается автоматически по необходимости
                                                          ; список может быть запакован в gzip. формат автоматически распознается и разжимается
                                                          ; списков может быть множество. пустой общий лист = его отсутствие
                                                          ; хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello.
--hostlist-domains=<domain_list>                          ; фиксированный список доменов через зяпятую. можно использовать # в начале для комментирования отдельных доменов.
--hostlist-exclude=<filename>                             ; не применять дурение к доменам из листа. может быть множество листов. схема аналогична include листам.
--hostlist-exclude-domains=<domain_list>                  ; фиксированный список доменов через зяпятую. можно использовать # в начале для комментирования отдельных доменов.
--hostlist-auto=<filename>                                ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика)
--hostlist-auto-fail-threshold=<int>                      ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3)
--hostlist-auto-fail-time=<int>                           ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60)
--hostlist-auto-retrans-threshold=<int>                   ; сколько ретрансмиссий запроса считать блокировкой (по умолчанию: 3)
--hostlist-auto-debug=<logfile>                           ; лог положительных решений по autohostlist. позволяет разобраться почему там появляются хосты.
--new                                                     ; начало новой стратегии (новый профиль)
--skip                                                    ; не использовать этот профиль . полезно для временной деактивации профиля без удаления параметров.
--filter-l3=ipv4|ipv6                                     ; фильтр версии ip для текущей стратегии
--filter-tcp=[~]port1[-port2]|*                           ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. установка фильтра tcp и неустановка фильтра udp запрещает udp. поддерживается список через запятую.
--filter-udp=[~]port1[-port2]|*                           ; фильтр портов udp для текущей стратегии. ~ означает инверсию. установка фильтра udp и неустановка фильтра tcp запрещает tcp. поддерживается список через запятую.
--filter-l7=<proto>                                       ; фильтр протокола L6-L7. поддерживается несколько значений через запятую. proto : http tls quic wireguard dht discord stun unknown
--filter-ssid=ssid1[,ssid2,ssid3,...]                     ; фильтр по имени wifi сети (только для linux)
--ipset=<filename>                                        ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-ip=<ip_list>                                      ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.
--ipset-exclude=<filename>                                ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-exclude-ip=<ip_list>                              ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.

--debug позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования опций. --debug лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа --debug еще дело не дошло, то сообщения не будут выведены в файл или syslog. При логировании в файл процесс не держит файл открытым. Ради каждой записи файл открывается и потом закрывается. Так что файл можно удалить в любой момент, и он будет создан заново при первом же сообщении в лог. Но имейте в виду, что если вы запускаете процесс под root, то будет сменен UID на не-root. В начале на лог файл меняется owner, иначе запись будет невозможна. Если вы потом удалите файл, и у процесса не будет прав на создание файла в его директории, лог больше не будет вестись. Вместо удаления лучше использовать truncate. В шелле это можно сделать через команду ": >filename"

Многие параметры, загружающие двоичные данные из файлов, поддерживают загрузку из hex-строки или из файла. hex строка начинается с "0x". Имя файла можно писать как есть или использовать префикс "@". Если перед префиксом "@" указано "+<число>", то это означает смещение полезных данных внутри файла. Файл может загружаться целиком с нулевой позиции, к нему могут применяться модификации, требующие полного файла (TLS), но передача пойдет с позиции offset. offset должен быть меньше длины файла. Если к блоку данных применяется мод, который уменьшает размер данных, и offset окажется не меньше новой длины данных, будет ошибка.

АТАКА ДЕСИНХРОНИЗАЦИИ DPI

Суть ее в следующем. Берется оригинальный запрос, модифицируется, добавляется поддельная информация (фейки) таким образом, чтобы ОС сервера передала серверному процессу оригинальный запрос в неизменном виде, а DPI увидел другое. То, что он блокировать не станет. Сервер видит одно, DPI - другое. DPI не понимает, что передается запрещенный запрос и не блокирует его.

Есть арсенал возможностей, чтобы достичь такого результата. Это может быть передача фейк пакетов, чтобы они дошли до DPI, но не дошли до сервера. Может использоваться фрагментация на уровне TCP (сегментация) или на уровне IP. Есть атаки, основанные на игре с tcp sequence numbers или с перепутыванием порядка следования tcp сегментов. Методы могут сочетаться в различных вариантах.

ФЕЙКИ

Фейки - это отдельные сгенерированные nfqws пакеты, несущие ложную информацию для DPI. Они либо не должны дойти до сервера, либо могут дойти, но должны быть им отброшены. Иначе получается слом tcp соединения или нарушение целостности передаваемого потока, что гарантированно приводит к поломке ресурса. Есть ряд методов для решения этой задачи.

Режимы дурения могут сочетаться в любых комбинациях. --dpi-desync-fooling берет множество значений через запятую.

Возможно задание множества фейков через повторение параметров --dpi-desync-fake-???, кроме --dpi-desync-fake-syndata. Фейки будут отосланы в указанном порядке. --dpi-desync-repeats повторяет каждый отосланный фейк. Итоговый порядок будет такой : fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....

МОДИФИКАЦИЯ ФЕЙКОВ

Любые tcp фейки отправляются с исходным sequence по умолчанию, даже если их несколько. Если задать --dpi-desync-fake-tcp-mod=seq, то несколько фейков будут отправлены с увеличением sequence number таким образом, как будто они являются tcp сегментами одного фейка.

В nfqws зашит базовый вариант фейка для TLS. Его можно переопределить опцией --dpi-desync-fake-tls. Переопределение фейков дает возможность использовать любые данные в качестве фейка для TLS. Можно использовать фейковый Client Hello с любым фингерпринтом и с любым SNI.

Некоторые модификации можно делать в процессе выполнения с помощью --dpi-desync-fake-tls-mod. Часть из них работает при обработке каждого TLS Client Hello и может подстраиваться под отправляемые данные. Модификации требуют наличия полного валидного TLS Client Hello в качестве фейка, они не работают с произвольными данными.

По умолчанию если не задан собственный фейк для TLS используются модификации rnd,rndsni,dupsid. Если фейк задан, используется none. Это соответствует поведению программы более старых версий с добавлением функции dupsid.

Если задан режим модификации и имеется множество TLS фейков, к каждому из них применяется последний режим модификации. Если режим модификации задан после фейка, то он замещает предыдущий режим. Таким образом можно использовать разные режимы модификации для разных фейков. При невозможности модифицировать фейк на этапе запуска программа завершается с ошибкой.

Если сначала идет TLS фейк, для него задан режим однократной модификации, затем идет не TLS фейк, то будет ошибка. Нужно использовать `--dpi-desync-fake-tls-mod=none'.

Пример : `--dpi-desync-fake-tls=iana_org.bin --dpi-desync-fake-tls-mod=rndsni --dpi-desync-fake-tls=0xaabbccdd --dpi-desync-fake-tls-mod=none'

TCP СЕГМЕНТАЦИЯ

Для fakedsplit и fakeddisorder предусмотрены вариации порядка следования сегментов. Параметр --dpi-desync-fakedsplit-mod=altorder=N задает число, влияющее на наличие отдельных фейков :

Режимы altorder для fakedsplit для части многопакетного запроса, где есть сплит-позиция :

Режимы altorder для fakeddisorder для части многопакетного запроса, где есть сплит-позиция :

Режимы altorder для fakedsplit и fakeddisorder для части многопакетного запроса, где нет сплит-позиции :

Итоговое число altorder=N вычисляется как сумма чисел из этих двух групп. По умолчанию altorder=0.

Содержимое фейков в fakedsplit/fakeddisorder определяется параметром --dpi-desync-fakedsplit-pattern (по умолчанию 0x00). Данные фейков берутся из паттерна со смещением, соответствующим смещению отсылаемых частей, учитывая смещения пакетов в многопакетных запросах. Размеры фейков соответствуют длинам отсылаемых частей. Цель этих режимов - максимально усложнить выявление оригинальных данных среди фейков.

Использование fakedsplit или fakeddisorder на TLS kyber с md5sig fooling может привести к ошибкам "message too long", если позиция сплита мала, поскольку будет превышение MTU из-за md5 tcp option.

Режим 'hostfakesplit' имеет задачу минимального вмешательства фейком - как раз по той части запроса, на основании которой DPI принимает решение о блокировке. Конкретно - имени хоста. По умолчанию фейк хоста генерируется каждый раз случайно из набора [a-z0-9]. При длине более 7 символов за 3 символа до конца ставится точка, имитируя TLD, а последние 3 символа заполняются одним из нескольких известных TLD.

Можно переопределить шаблон генерации с помощью --dpi-desync-hostfakesplit-mod=host=<hostname>. В последнем случае справа всегда будет указанный hostname. Слева он будет дополнен до размера оригинального хоста как поддомен со случайными символами. Пример : "www.networksolutions.com" -> "h8xmdba4tv7a8.google.com". Если размер оригинального хоста меньше шаблона, шаблон будет порезан : "habr.com" -> "ogle.com". Если размер оригинального хоста больше шаблона на 1, получится инвалидный пустой поддомен : "www.xxx.com" => ".google.com". Поэтому стоит использовать максимально короткие хосты из разрешенных : "ya.ru", "vk.com".

--dpi-desync-hostfakesplit-mod=altorder=1 позволяет сменить порядок следования частей на альтернативный вариант. altorder=1 шлет фрагменты в таком порядке, чтобы при последовательной сборке сегментов на DPI он получил полностью собранный оригинал запроса с подмененным хостом. Реальный хост идет отдельным сегментом уже после. То есть в этом варианте применяется разновидность disorder. Сервер принимает фрагменты с нарушенным порядком sequence.

Опционально можно разрезать оригинальный хост. Например, --dpi-desync-hostfakesplit-midhost=midsld. Позиция нарезки должна попадать внутрь хоста. Многопакетные запросы поддерживаются только, если исходная нарезка пакетов не включает позиции имени хоста. В последнем случае дурение отменяется.

Вариант fakedsplit имеет несколько альтернативных порядков нарезки - от 0 до 3. Режим задается в параметре --dpi-desync-fakedsplit-mod=altorder=N. Каждый следующий altorder убирает часть фейков.

Для определения позиций нарезки используются маркеры.

Относительные позиции :

Пример списка маркеров : 100,midsld,sniext+1,endhost-2,-10.

При разбиении пакета первым делом происходит ресолвинг маркеров - нахождение всех указанных относительных позиций и применение смещений. Если относительная позиция отсутствует в текущем протоколе, такие позиции не применяются и отбрасываются. Дальше происходит нормализация позиций относительно смещения текущего пакета в группе пакетов (многопакетные запросы TLS с kyber, например). Выкидываются все позиции, выходящие за пределы текущего пакета. Оставшиеся сортируются в порядке возрастания и удаляются дубли. В вариантах multisplit и multidisorder если не осталось ни одной позиции, разбиение не происходит.

Варианты fakedsplit и fakeddisorder применяют только одну позицию сплита. Ее поиск среди списка --dpi-desync-split-pos осуществляется особым образом. Сначала сверяются все относительные маркеры. Если среди них найден подходящий, применяется он. В противном случае сверяются все абсолютные маркеры. Если и среди них ничего не найдено, применяется позиция 1.

Например, можно написать --dpi-desync-split-pos=method+2,midsld,5. Если протокол http, разбиение будет на позиции method+2. Если протокол TLS - на позиции midsld. Если протокол неизвестен и включено --dpi-desync-any-protocol, разбиение будет на позиции 5. Чтобы все было однозначнее, можно использовать разные профили для разных протоколов и указывать только одну позицию, которая точно есть в этом протоколе.

ПЕРЕКРЫТИЕ SEQUENCE NUMBERS

seqovl добавляет в начало одного из TCP сегментов seqovl байт со смещенным в минус sequence number на величину seqovl. Для split - в начало первого сегмента, для disorder - в начало предпоследнего отсылаемого сегмента (второго в оригинальном порядке следования).

В случае split расчет идет на то, что предыдущий отсыл, если он был, уже попал в сокет серверного приложения, поэтому новая пришедшая часть лишь частично находится в пределах текущего окна (in-window). Спереди фейковая часть отбрасывается, а оставшаяся часть содержит оригинал и начинается с начала window, поэтому попадает в сокет. Серверное приложение получает все, что реально отсылает клиент, отбрасывая фейковую out-of-window часть. Но DPI не может этого понять, поэтому у него происходит sequence десинхронизация. Обязательно, чтобы первый сегмент вместе с seqovl не превысили длину MTU. Эта ситуация распознается автоматически в Linux, и seqovl отменяется. В остальных системах ситуация не распознается, и это приведет к поломке соединения. Поэтому выбирайте первую позицию сплита и seqovl таким образом, чтобы MTU не был превышен в любом случае. Иначе дурение может не работать или работать хаотично.

Для disorder overlap идет на предпоследнюю отсылаемую часть пакета. Для простоты будем считать, что разбиение идет на 2 части, шлются они в порядке "2 1" при оригинальном порядке "1 2". Обязательно, чтобы seqovl был меньше позиции первого сплита, иначе все отосланное будет передано в сокет сразу же, включая фейк, ломая протокол прикладного уровня. Такая ситуация легко обнаруживается программой, и seqovl отменяется. Увеличение размера пакета невозможно в принципе. При соблюдении условия 2-я часть пакета является полностью in-window, поэтому серверная ОС принимает ее целиком, включая фейк. Но поскольку начальная часть данных из 1 пакета еще не принята, то фейк и реальные данные остаются в памяти ядра, не отправляясь в серверное приложение. Как только приходит 1-я часть пакета, она переписывает фейковую часть в памяти ядра. Ядро получает данные из 1 и 2 части, поэтому далее идет отправка в сокет приложения. Таково поведение всех unix ОС, кроме solaris - оставлять последние принятые данные. Windows оставляет старые данные, поэтому disorder с seqovl будет приводить к зависаниям соединения при работе с Windows серверами. Solaris практически мертв, windows серверов очень немного. Можно использовать листы при необходимости. Метод позволяет обойтись без fooling и TTL. Фейки перемешаны с реальным данными. fakedsplit/fakeddisorder по-прежнему добавляют дополнительные отдельные фейки.

seqovl в варианте split может быть только абсолютным положительным значением, поскольку применяется только в первому пакету. В варианте disorder допустимо применение всех вариантов маркеров. Они автоматически нормализуются к текущему пакету в серии. Можно сплитать на midsld и делать seqovl на midsld-1.

НАЗНАЧЕНИЕ IP_ID

Некоторые DPI секут поле ipv4 заголовка ip_id. Защита заключается в распознавании нехарактерного для разных ОС порядка назначения ip_id, но характерного для некоторого anti-DPI софта. Обычно ОС инкрементируют ip_id для каждого следующего пакета. Например, на ТСПУ повторение ненулевых ip_id фейка и не фейка вызывает триггер блока на диапазонах IP googlevideo.com.

Если отсылаются фейки или дополнительные tcp сегменты, то в любом случае последовательность будет нарушена, поскольку ОС ничего не будет знать о всунутых фейках и не увеличит свой счетчик ip_id на количество фейков или дополнительных tcp сегментов. Чтобы сохранить последовательность, потребовалось бы перехватывать все соединение до конца, что очень затратно по ресурсам. Поэтому после отработки серии генерированных пакетов ip_id возвращается к тому значению, о котором знает ОС.

Параметр ip-id относится к профилю и задает режим назначения ip_id при отсылке генерированных в nfqws пакетов.

В заголовках ipv6 поле ip_id отсутствует, параметр игнорируется для ipv6.

СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6

Режимы десинхронизации hopbyhop, destopt и ipfrag1 (не путать с fooling !) относятся только к ipv6 и заключается в добавлении хедера hop-by-hop options, destination options или fragment во все пакеты, попадающие под десинхронизацию. Здесь надо обязательно понимать, что добавление хедера увеличивает размер пакета, потому не может быть применено к пакетам максимального размера. Это имеет место при передаче больших сообщений. В случае невозможности отослать пакет дурение будет отменено, пакет будет выслан в оригинале. Расчет идет на то, что DPI увидит 0 в поле next header основного заголовка ipv6 и не будет скакать по extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет без анализа. Возможно, какие-то DPI на это купятся. Может сочетаться с любыми режимами 2-й фазы, кроме варианта ipfrag1+ipfrag2. Например, hopbyhop,multisplit означает разбить tcp пакет на несколько сегментов, в каждый из них добавить hop-by-hop. При hopbyhop,ipfrag2 последовательность хедеров будет : ipv6,hop-by-hop,fragment,tcp/udp. Режим ipfrag1 может срабатывать не всегда без специальной подготовки. См. раздел IP фрагментация.

МОДИФИКАЦИЯ ОРИГИНАЛА

Параметры --orig-ttl и --orig-ttl6 позволяют изменить TTL оригинальных пакетов. Если дальнейшие манипуляции связаны с оригиналом, например, идет TCP сегментация, то исходными данными являются измененные оригинальные пакеты. То есть в данном примере TCP сегменты пойдут с измененным TTL.

Вариант --orig-autottl и --orig-autottl6 работает аналогично dpi-desync-autottl, но по оригинальным пакетам. Дельту стоит указывать положительную с унарным знаком +, иначе оригинал не дойдет до сервера, и вы вообще ничего не получите. Пример : --orig-autottl=+5:3-64.

--orig-mod-start и --orig-mod-cutoff задают ограничитель по началу и концу модификации оригинала. Схема аналогична --dpi-desync-start и --dpi-desync-cutoff.

Функция может быть полезна, когда DPI охотится за фейками и блокирует соединение при наличии подозрительных признаков, в частности, измененный TTL у фейка относительно оригинала.

ДУБЛИКАТЫ

Дубликаты - это копии оригинальных пакетов, высылаемые перед ними. Включаются параметром --dup=N, где N - количество дублей, не включающее оригинал. --dup-replace отключает отсылку оригинала.

Отсылка дублей имеет место только в тех случаях, когда высылается и оригинал без реконструкции. Например, если случилась TCP сегментация, то оригинал фактически дропается и заменяется искусственно сконструированными сегментами. Дубли высланы не будут. Это же касается изменения состава хедеров ipv6, режима tamper для DHT и других.

Возможно применение всех вариантов дурения, как и для desync : --dup-ttl. --dup-ttl6, --dup-fooling. Нужно ли, чтобы эти пакеты доходили до сервера и в каком виде, решаете вы согласно задуманной стратегии.

Вариант --dup-autottl и --dup-autottl6 работает аналогично dpi-desync-autottl, но по дублям. Дельту можно указывать положительную с унарным знаком +, а можно и отрицательную. Зависит от вашей задумки. Пример : --dup-autottl=-2:3-64.

--dup-start и --dup-cutoff задают ограничитель по началу и концу применения стратегии дубликатов. Схема аналогична --dpi-desync-start и --dpi-desync-cutoff.

Функция может помочь, когда DPI сечет разницу в характеристиках фейков и оригинала. Дубликатами можно попытаться заставить DPI принять , что весь сеанс идет аномальным. Например, у нас имеется TCP сеанс с MD5 сразу с первого SYN пакета. Значит последующие MD5 будут восприниматься нормально.

КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ

В параметре dpi-desync можно указать до 3 режимов через запятую.

Режимы требуют указания в порядке возрастания номеров фаз.

КЭШ IP

ipcache представляет собой структуру в памяти процесса, позволяющую по ключу IP адреса и имени интерфейса запоминать некоторую информацию, которую впоследствии можно извлечь и использовать как недостающие данные. На текущий момент это применяются в следующих ситуациях :

  1. IP,interface => hop count . Кэшируется количество хопов до сервера для последующего применения в autottl прямо с первого пакета, когда еще ответа не было. Пока записи в кэше нет, autottl не будет применен сразу. При повторном запросе до истечения времени жизни записи autottl будет применение сразу.

  2. IP => hostname . Кэшируется имя хоста, вне привязки к интерфейсу, для последующего применения в стратегиях нулевой фазы. Режим отключен по умолчанию и включается через параметры ipcache-hostname. Данная техника является экспериментальной. Ее проблема в том, что как такового нет однозначного соответствия между доменом и IP. Множество доменов могут ссылаться на тот же IP адрес. При коллизии происходит замещение имени хоста на последний вариант. Домен может скакать по разным IP на CDN. Сейчас один адрес, через час - другой. Эта проблема решается через время жизни записей кэша : --ipcache-lifetime. По умолчанию 2 часа. Однако, может случиться и так, что в вашем случае применение техники несет больше пользы, чем проблем. Будьте готовы к непонятному на первый взгляд поведению, которое может быть исследовано только через --debug лог.

При подаче сигнала SIGUSR2 процесс выводит содержимое ipcache на консоль.

РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА

Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены. Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1. В варианте disorder обычно приходит сперва частичное подтверждение (SACK), потом полный ACK. Если вместо ACK или SACK идет RST пакет с минимальной задержкой, то DPI вас отсекает еще на этапе вашего запроса. Если RST идет после полного ACK спустя задержку, равную примерно пингу до сервера, тогда вероятно DPI реагирует на ответ сервера. DPI может отстать от потока, если ClientHello его удовлетворил и не проверять ServerHello. Тогда вам повезло. Вариант fake может сработать. Если же он не отстает и упорно проверяет ServerHello, то можно попробовать заставить сервер высылать ServerHello частями через параметр --wssize (см. conntrack). Если и это не помогает, то сделать с этим что-либо вряд ли возможно без помощи со стороны сервера. Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде. Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI.

РЕЖИМ SYNACK

В документации по geneva это называется "TCB turnaround". Попытка ввести DPI в заблуждение относительно ролей клиента и сервера.

Поскольку режим нарушает работу NAT, техника может сработать только если между атакующим устройством и DPI нет NAT. Атака не сработает через NAT роутер, но может сработать с него. Для реализации атаки на проходящий трафик требуются nftables и схема POSTNAT.

РЕЖИМ SYNDATA

Тут все просто. Добавляются данные в пакет SYN. Все ОС их игнорируют, если не используется TCP fast open (TFO), а DPI может воспринять, не разобравшись есть там TFO или нет. Оригинальные соединения с TFO не трогаются, поскольку это их точно сломает. Без уточняющего параметра добавляются 16 нулевых байтов.

ВИРТУАЛЬНЫЕ МАШИНЫ

Изнутри VM от virtualbox и vmware в режиме NAT не работают многие техники пакетной магии nfqws. Принудительно заменяется ttl, не проходят фейк пакеты. Необходимо настроить сеть в режиме bridge.

CONNTRACK

nfqws оснащен ограниченной реализацией слежения за состоянием tcp соединений (conntrack). Он включается для реализации некоторых методов противодействия DPI. conntrack способен следить за фазой соединения : SYN,ESTABLISHED,FIN, количеством пакетов в каждую сторону, sequence numbers. conntrack способен "кормиться" пакетами в обе или только в одну сторону. Соединение попадает в таблицу при обнаружении пакетов с выставленными флагами SYN или SYN,ACK. Поэтому если необходим conntrack, в правилах перенаправления iptables соединение должно идти на nfqws с самого первого пакета, хотя затем может обрываться по фильтру connbytes. Для UDP инициатором попадания в таблицу является первый UDP пакет. Он же и определяет направление потока. Считается, что первый UDP пакет исходит от клиента к серверу. Далее все пакеты с совпадающими src_ip,src_port,dst_ip,dst_port считаются принадлежащими этому потоку до истечения времени неактивности. conntrack - простенький, он не писался с учетом всевозможных атак на соединение, он не проверяет пакеты на валидность sequence numbers или чексумму. Его задача - лишь обслуживание нужд nfqws, он обычно кормится только исходящим трафиком, потому нечувствителен к подменам со стороны внешней сети. Соединение удаляется из таблицы, как только отпадает нужда в слежении за ним или по таймауту неактивности. Существуют отдельные таймауты на каждую фазу соединения. Они могут быть изменены параметром --ctrack-timeouts.

--wssize позволяет изменить с клиента размер tcp window для сервера, чтобы он послал следующие ответы разбитыми на части. Чтобы это подействовало на все серверные ОС, необходимо менять window size в каждом исходящем с клиента пакете до отсылки сообщения, ответ на которое должен быть разбит (например, TLS ClientHello). Именно поэтому и необходим conntrack, чтобы знать когда надо остановиться. Если не остановиться и все время устанавливать низкий wssize, скорость упадет катастрофически. В linux это может быть купировано через connbytes, но в BSD системах такой возможности нет. В случае http(s) останавливаемся сразу после отсылки первого http запроса или TLS ClientHello. Если вы имеете дело с не http(s), то вам потребуется параметр --wssize-cutoff. Он устанавливает предел, с которого действие wssize прекращается. Префикс d перед номером означает учитывать только пакеты с data payload, префикс s - relative sequence number, проще говоря количество переданных клиентом байтов + 1. Если проскочит пакет с http request или TLS ClientHello, действие wssize прекращается сразу же, не дожидаясь wssize-cutoff, если не указан параметр --wssize-forced-cutoff=0. Если ваш протокол склонен к долгому бездействию, следует увеличить таймаут фазы ESTABLISHED через параметр --ctrack-timeouts. Таймаут по умолчанию низкий - всего 5 минут. Не забывайте, что nfqws кормится приходящими на него пакетами. Если вы ограничили поступление пакетов через connbytes, то в таблице могут остаться повисшие соединения в фазе ESTABLISHED, которые отвалятся только по таймауту. Для диагностики состояния conntrack пошлите сигнал SIGUSR1 процессу nfqws : killall -SIGUSR1 nfqws. Текущая таблица будет выведена nfqws в stdout.

Обычно в SYN пакете клиент отсылает кроме window size еще и TCP extension scaling factor. scaling factor представляет из себя степень двойки, на которую умножается window size : 0=>1, 1=>2, 2=>4, ..., 8=>256, ... В параметре wssize scaling factor указывается через двоеточие. Scaling factor может только снижаться, увеличение заблокировано, чтобы не допустить превышение размера окна со стороны сервера. Для принуждения сервера к фрагментации ServerHello, чтобы избежать просекание имени сервера из сертификата сервера на DPI, лучше всего использовать --wssize=1:6. Основное правило - делать scale_factor как можно больше, чтобы после восстановления window size итоговый размер окна стал максимально возможным. Если вы сделаете 64:0, будет очень медленно. С другой стороны нельзя допустить, чтобы ответ сервера стал достаточно большим, чтобы DPI нашел там искомое.

--wssize не работает в профилях с хостлистами, поскольку он действует с самого начала соединения, когда еще нельзя принять решение о попадании в лист. Однако, профиль с auto hostlist может содержать --wssize. --wssize может замедлять скорость и/или увеличивать время ответа сайтов, поэтому если есть другие работающие способы обхода DPI, лучше применять их.

--dpi-desync-cutoff позволяет задать предел, при достижении которого прекращается применение dpi-desync. Доступны префиксы n,d,s по аналогии с --wssize-cutoff. Полезно совместно с --dpi-desync-any-protocol=1. На склонных к бездействию соединениях следует изменить таймауты conntrack. Если соединение выпало из conntrack и задана опция --dpi-desync-cutoff, dpi desync применяться не будет.

РЕАССЕМБЛИНГ

nfqws поддерживает реассемблинг некоторых видов запросов. На текущий момент это TLS и QUIC ClientHello. Они бывают длинными, если в chrome включить пост-квантовую криптографию tls-kyber, и занимают, как правило, 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124. chrome рандомизирует фингерпринт TLS. SNI может оказаться как в начале, так и в конце, то есть попасть в любой пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом принимает решение о блокировке. В случае получения TLS или QUIC пакета с частичным ClientHello начинается процесс сборки, а пакеты задерживаются и не отсылаются до ее окончания. По окончании сборки пакеты проходит через десинхронизацию на основании полностью собранного ClientHello. При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется.

Есть специальная поддержка всех вариантов tcp сплита для многосегментного TLS. Если указать позицию сплита больше длины первого пакета, то разбивка происходит не обязательно первого пакета, а того, на который пришлась итоговая позиция. Если, допустим, клиент послал TLS ClientHello длиной 2000, SNI начинается с 1700, и заданы опции fake,multisplit, то перед первым пакетом идет fake, затем первый пакет в оригинале, а последний пакет разбивается на 2 сегмента. В итоге имеем фейк в начале и 3 реальных сегмента.

ПОДДЕРЖКА UDP

Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip.

Для UDP действуют только режимы десинхронизации fake, fakeknown, hopbyhop, destopt, ipfrag1, ipfrag2, udplen, tamper. Режимами первой фазы являются fake, fakeknown, hopbyhop, destopt, ipfrag1. Второй фазы - ipfrag2, udplen, tamper. Как обычно, возможно сочетание режимов первой и второй фазы, но не двух режимов одной фазы.

udplen увеличивает размер udp пакета на указанное в --dpi-desync-udplen-increment количество байтов. Паддинг заполняется нулями по умолчанию, но можно задать свой паттерн. Предназначено для обмана DPI, ориентирующегося на размеры пакетов. Может сработать, если пользовательский протокол не привязан жестко к размеру udp пейлоада. Режим tamper означает модификацию пакетов известных протоколов особенным для протокола образом. На текущий момент работает только с DHT. Поддерживается определение пакетов QUIC Initial с расшифровкой содержимого и имени хоста, то есть параметр --hostlist будет работать. Определяются пакеты wireguard handshake initiation, DHT (начинается с 'd1', кончается 'e'), STUN и Discord Voice IP Discovery. Для десинхронизации других протоколов обязательно указывать --dpi-desync-any-protocol. Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp можно изменить 4-м параметром в --ctrack-timeouts. Атака fake полезна только для stateful DPI, она бесполезна для анализа на уровне отдельных пакетов. По умолчанию fake наполнение - 64 нуля. Можно указать файл в --dpi-desync-fake-unknown-udp.

IP ФРАГМЕНТАЦИЯ

Современная сеть практически не пропускает фрагментированные tcp на уровне ip. На udp с этим дело получше, поскольку некоторые udp протоколы могут опираться на этот механизм (IKE старых версий). Однако, кое-где бывает, что режут и фрагментированный udp. Роутеры на базе linux могут самопроизвольно собирать или перефрагментировать пакеты. Позиция фрагментации задается отдельно для tcp и udp. По умолчанию 24 и 8 соответственно, должна быть кратна 8. Смещение считается с транспортного заголовка.

Существует ряд моментов вокруг работы с фрагментами на Linux, без понимания которых может ничего не получиться.

ipv4 : Linux дает отсылать ipv4 фрагменты, но стандартные настройки iptables в цепочке OUTPUT могут вызывать ошибки отправки.

ipv6 : Нет способа для приложения гарантированно отослать фрагменты без дефрагментации в conntrack. На разных системах получается по-разному. Где-то нормально уходят, где-то пакеты дефрагментируются. Для ядер <4.16 похоже, что нет иного способа решить эту проблему, кроме как выгрузить модуль nf_conntrack, который подтягивает зависимость nf_defrag_ipv6. Он то как раз и выполняет дефрагментацию. Для ядер 4.16+ ситуация чуть лучше. Из дефрагментации исключаются пакеты в состоянии NOTRACK. Чтобы не загромождать описание, смотрите пример решения этой проблемы в blockcheck.sh.

Иногда требуется подгружать модуль ip6table_raw с параметром raw_before_defrag=1. В OpenWrt параметры модулей указываются через пробел после их названий в файлах /etc/modules.d. В традиционных системах посмотрите используется ли iptables-legacy или iptables-nft. Если legacy, то нужно создать файл /etc/modprobe.d/ip6table_raw.conf с содержимым :

options ip6table_raw raw_before_defrag=1

В некоторых традиционных дистрибутивах можно изменить текущий ip6tables через : update-alternatives --config ip6tables Если вы хотите оставаться на iptables-nft, вам придется пересобрать патченную версию. Патч совсем небольшой. В nft.c найдите фрагмент:

        {
            .name	= "PREROUTING",
            .type	= "filter",
            .prio	= -300,	/* NF_IP_PRI_RAW */
            .hook	= NF_INET_PRE_ROUTING,
        },
        {
            .name	= "OUTPUT",
            .type	= "filter",
            .prio	= -300,	/* NF_IP_PRI_RAW */
            .hook	= NF_INET_LOCAL_OUT,
        },

и замените везде -300 на -450.

Это нужно сделать вручную, никакой автоматики в blockcheck.sh нет.

Либо можно раз и навсегда избавиться от этой проблемы, используя nftables. Там можно создать netfilter hook с любым приоритетом. Используйте приоритет -401 и ниже.

При использовании iptables и NAT, похоже, что нет способа прицепить обработчик очереди после NAT. Пакет попадает в nfqws с source адресом внутренней сети, затем фрагментируется и уже не обрабатывается NAT. Так и уходит во внешнюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает. Видимо единственный рабочий метод - отказаться от iptables и использовать nftables. Хук должен быть с приоритетом 101 или выше.

МНОЖЕСТВЕННЫЕ СТРАТЕГИИ

nfqws способен по-разному реагировать на различные запросы и применять разные стратегии дурения. Это реализовано посредством поддержки множества профилей дурения. Профили разделяются в командной строке параметром --new. Первый профиль создается автоматически. Для него не нужно --new. Каждый профиль имеет фильтр. По умолчанию он пуст, то есть профиль удовлетворяет любым условиям. Фильтр может содержать жесткие параметры: версия ip протокола, ipset и порты tcp/udp. Они всегда однозначно идентифицируются даже на нулевой фазе десинхронизации, когда еще хост и L7 неизвестны. В качестве мягкого фильтра могут выступать хост-листы и протокол прикладного уровня (l7). L7 протокол становится известен обычно после первого пакета с данными. При поступлении запроса идет проверка профилей в порядке от первого до последнего до достижения первого совпадения с фильтром. Жесткие параметры фильтра сверяются первыми. При несовпадении идет сразу же переход к следующему профилю. Если какой-то профиль удовлетворяет жесткому фильтру и L7 фильтру и содержит авто-хостлист, он выбирается сразу. Если профиль удовлетворяет жесткому фильтру и L7 фильтру, для него задан хостлист, и у нас еще нет имени хоста, идет переход к следующему профилю. В противном случае идет проверка по хостлистам этого профиля. Если имя хоста удовлетворяет листам, выбирается этот профиль. Иначе идет переход к следующему. Может так случиться, что до получения имени хоста или узнавания L7 протокола соединение идет по одному профилю, а при выяснении этих параметров профиль меняется на лету. Это может произойти даже дважды - при выяснении L7 и имени хоста. Чаще всего это выяснение совмещается в одно действие, поскольку по одному пакету, как правило, узнается и L7, и хост. Поэтому если у вас есть параметры дурения нулевой фазы, тщательно продумывайте что может произойти при переключении стратегии. Смотрите debug log, чтобы лучше понять что делает nfqws. Нумерация профилей идет с 1 до N. Последним в цепочке создается пустой профиль с номером 0. Он используется, когда никакие условия фильтров не совпали.

[!IMPORTANT] Множественные стратегии создавались только для случаев, когда невозможно обьединить имеющиеся стратегии для разных ресурсов. Копирование стратегий из blockcheck для разных сайтов во множество профилей без понимания как они работают приведет к нагромождению параметров, которые все равно не покроют все возможные заблокированные ресурсы. Вы только увязните в этой каше.

[!IMPORTANT] user-mode реализация ipset создавалась не как удобная замена *nix версии, реализованной в ядре. Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без поддержки ipset в ядре. Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.

ФИЛЬТРАЦИЯ ПО WIFI

Имя wifi сети никак не связано с сетевым интерфейсом адаптера wifi. Интерфейс один, подключиться можно к любой сети. Для разных сетей разные стратегии. Стратегия от сети A не работает или ломает сеть B. Что делать ?

Можно вручную запускать и снимать инстансы nfqws. Но можно поступить иначе. В windows версии winws есть глобальный фильтр --ssid-filter. Он включает или отключает инстанс winws в зависимости от подключенности любого адаптера к конкретной wifi сети. При этом не учитывается маршрутизация. Такой подход возможен потому, что к windivert можно прицепить несколько инстансов winws на пересекающихся фильтрах. При смене wifi сети одни будут включаться, другие выключаться.

Для linux применяется иное решение. Фильтр --filter-ssid относится к конкретному профилю. Невозможно повесить несколько инстансов nfqws на одну и ту же очередь или направить один и тот же трафик на несколько очередей. Подключение и отключение от очереди разных инстансов сопряжено со сложностями синхронизации между ними. Поэтому обрабатывать трафик должен один инстанс, и он должен уметь работать с разными wifi сетями. Это и реализовано в параметре --filter-ssid. Он берет список имен wifi сетей (SSID) через запятую аналогично --ssid-filter для winws. При выборе профиля имеет значение куда идет конкретный обрабатываемый пакет. На какой интерфейс. Или с какого интерфейса пакет пришел, если он считается входящим. Поэтому даже если у вас часть трафика идет на одну сеть, часть на другую, а часть вообще не идет по wifi, то все это можно настроить.

Сканируются все wifi интерфейсы, составляется список interface->SSID. Он обновляется по мере поступления пакетов, но не чаще 1 раза в секунду.

IPTABLES ДЛЯ NFQWS

[!CAUTION] Начиная с ядер Linux 6.17 присутствует параметр конфигурации ядра CONFIG_NETFILTER_XTABLES_LEGACY, который по умолчанию в дистрибутиве может быть "not set". Отсутствие этой настройки выключает iptables-legacy. Это часть процесса депрекации iptables. Тем не менее iptables-nft будут работать, поскольку используют backend nftables.

iptables для задействования атаки на первые пакеты данных в tcp соединении :

iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass

Этот вариант применяем, когда DPI не следит за всеми запросами http внутри keep-alive сессии. Если следит, направляем только первый пакет от https и все пакеты от http :

iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass
iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass

mark нужен, чтобы сгенерированный поддельный пакет не попал опять к нам на обработку. nfqws выставляет fwmark при его отсылке. Хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes, чтобы не допустить изменения порядка следования пакетов. Процессинг очереди - процесс отложенный. Если ядро имеет пакеты на отсылку вне очереди - оно их отправляет незамедлительно. Изменение правильного порядка следования пакетов при десинхронизации ломает всю идею. Так же были замечены дедлоки при достаточно большой отсылке пакетов из nfqws и отсутствии mark фильтра. Процесс может зависнуть. Поэтому наличие фильтра по mark в ip/nf tables можно считать обязательным.

Почему --connbytes 1:6 :

Для режима autottl необходимо перенаправление входящего SYN,ACK пакета или первого пакета соединения (что обычно есть то же самое). Для режима autohostlist необходимы входящие RST и http redirect. Можно построить фильтр на tcp flags для выделения SYN,ACK и модуле u32 для поиска характерных паттернов http redirect, но проще использовать connbytes для выделения нескольких начальных входящих пакетов.

iptables -t mangle -I PREROUTING -i <внешний интерфейс> -p tcp -m multiport --sports 80,443 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:3 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass

Для quic :

iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p udp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass

6 пакетов берется, чтобы покрыть случаи возможных ретрансмиссий quic initial в случае плохой связи или если сервер плохо себя чувствует, а приложение настаивает именно на quic, не переходя на tcp. А так же для работы autohostlist по quic. Однако, autohostlist для quic не рекомендуется.

NFTABLES ДЛЯ NFQWS

Можно начать с базовой конфигурации.

IFACE_WAN=wan

nft create table inet ztest

nft add chain inet ztest post "{type filter hook postrouting priority mangle;}"
nft add rule inet ztest post oifname $IFACE_WAN meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-6 queue num 200 bypass
nft add rule inet ztest post oifname $IFACE_WAN meta mark and 0x40000000 == 0 udp dport 443 ct original packets 1-6 queue num 200 bypass

# auto hostlist with avoiding wrong ACK numbers in RST,ACK packets sent by russian DPI
sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1 
nft add chain inet ztest pre "{type filter hook prerouting priority filter;}"
nft add rule inet ztest pre iifname $IFACE_WAN tcp sport "{80,443}" ct reply packets 1-3 queue num 200 bypass

Для задействования IP фрагментации и datanoack на проходящие пакеты требуется особая конфигурация цепочек, перенаправляющая пакеты после NAT. В скриптах zapret эта схема называется POSTNAT, и она возможна только на nftables. Сгенерированные nfqws пакеты требуется на раннем этапе помечать как notrack, чтобы они не были испорчены NAT.

IFACE_WAN=wan

nft create table inet ztest

nft add chain inet ztest postnat "{type filter hook postrouting priority srcnat+1;}"
nft add rule inet ztest postnat oifname $IFACE_WAN meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-6 queue num 200 bypass
nft add rule inet ztest postnat oifname $IFACE_WAN meta mark and 0x40000000 == 0 udp dport 443 ct original packets 1-6 queue num 200 bypass

nft add chain inet ztest predefrag "{type filter hook output priority -401;}"
nft add rule inet ztest predefrag "mark & 0x40000000 != 0x00000000 notrack"

Удаление тестовой таблицы :

nft delete table inet ztest

FLOW OFFLOADING

Если ваше устройство поддерживает аппаратное ускорение (flow offloading, hardware nat, hardware acceleration), то iptables могут не работать. При включенном offloading пакет не проходит по обычному пути netfilter. Необходимо или его отключить, или выборочно им управлять.

В новых ядрах присутствует software flow offloading (SFO). Пакеты, проходящие через SFO, так же проходят мимо большей части механизмов iptables. При включенном SFO работает DNAT/REDIRECT (tpws). Эти соединения исключаются из offloading. Однако, остальные соединения идут через SFO, потому NFQUEUE будет срабатывать только до помещения соединения в flowtable. Практически это означает, что почти весь функционал nfqws работать не будет. Offload включается через специальный target в iptables FLOWOFFLOAD или через flowtable в nftables.

Не обязательно пропускать весь трафик через offload. tpws и так обходит offload "by design", а для отработки nfqws достаточно первых нескольких пакетов в tcp соединении или udp сеансе. Пока сеанс не направлен на offload, он процессится обычным образом через полноценный netfilter. Как только срабатывает правило offload по любому входящему или исходящему пакету, весь сеанс окончательно уходит из netfilter в offload. Поэтому скрипты zapret берут правила для NFQUEUE, что они создали, и из них создают exemption правила, которые не дают раньше времени попасть сеансу в offload, а потом его "отпускают". При этом входящим пакетам не дают начать offload, триггером выступают только исходящие пакеты. Эта схема обеспечивает практически нулевой негативный эффект на скорость, одновременно покрывая нужды nfqws и упрощая правила таблиц.

OpenWrt не предусматривает выборочного управления offload, поэтому скрипты zapret поддерживают свою систему выборочного управления.

iptables target FLOWOFFLOAD - это проприетарное изобретение OpenWrt. Управление offload в nftables реализовано в базовом ядре linux без патчей. nftables - единственный способ включения offload на классическом Linux.

ОСОБЕННОСТИ ЖЕЛЕЗОК

На устройствах mediatek замечены 2 проблемы.

Драйвер mediatek ethernet отбрасывает tcp и udp пакеты с неверной чексуммой на аппаратном уровне, это не отключается. Как следствие не будет работать fooling badsum через роутер, но будет с него.

Другая проблема mediatek, затрагивающая как ethernet, так и wireless, проявляется на udp, когда включен offload rx-gro-list. Пока отсутствует nfqueue, все хорошо. Как только nfqueue появляется, часть пакетов выпадает. Особенно заметно это проявляется на дурении QUIC с kyber.

shell код лечения
append_separator_list()
{
    # $1 - var name to receive result
    # $2 - separator
    # $3 - quoter
    # $4,$5,... - elements
    local _var="$1" sep="$2" quo="$3" i

    eval i="\$$_var"
    shift; shift; shift
    while [ -n "$1" ]; do
        if [ -n "$i" ] ; then
            i="$i$sep$quo$1$quo"
        else
            i="$quo$1$quo"
        fi
        shift
    done
    eval $_var="\$i"
}
resolve_lower_devices()
{
    # $1 - bridge interface name
    [ -d "/sys/class/net/$1" ] && {
        find "/sys/class/net/$1" -follow -maxdepth 1 -name "lower_*" |
        {
            local l lower lowers
            while read lower; do
                lower="$(basename "$lower")"
                l="${lower#lower_*}"
                [  "$l" != "$lower" ] && append_separator_list lowers ' ' '' "$l"
            done
            printf "$lowers"
        }
    }
}

# it breaks nfqueue
lans=$(resolve_lower_devices br-lan)
for int in $lans; do
    ethtool -K $int rx-gro-list off
done

Этот код нужно вызывать после вставания интерфейса LAN, когда все bridge members уже занесены в bridge. Можно использовать хук в /etc/hotplug.d/iface. Должен быть установлен ethtool.

Проблемы mediatek были подтверждены на MT7621 (TP-Link Archer C6U v1) и MT7981 (Xiaomi AX3000T). Другие чипсеты могут быть так же подвержены проблеме, а могут и не быть. Более широкой статистики нет.

ДУРЕНИЕ СО СТОРОНЫ СЕРВЕРА

Это тоже возможно. nfqws рассчитан на атаку со стороны клиента, поэтому он распознает прямой и обратный трафик на основании роли в установлении tcp соединения. Если проходит SYN, то source IP - это клиент. Если проходит SYN,ACK , то source IP - это сервер. Для UDP клиентом считается source IP первого прошедшего пакета по двум связкам ip-port. На сервере трафиком клиента будет считаться принятый трафик, а трафиком сервера - исходящий.

--wsize работает в любом случае, он может использоваться как на клиенте, так и на сервере. Остальные техники работают только если nfqws считает трафик трафиком клиента. Поэтому для их применения по исходящему с сервера трафику conntrack нужно выключить параметром --ctrack-disable. Если пакет не найден в conntrack, по нему идет работа как по пакету клиента.

Большинство протоколов опознаваться не будет, потому что система их опознавания рассчитана на содержание пакетов от клиента. Чтобы задействовать техники типа fake или multisplit нужно использовать --dpi-desync-any-protocol с ограничителем connbytes или с ограничителем на основании содержания пакета или его заголовков. start/cutoff недоступны, поскольку завязаны на conntrack.

Техника synack-split позволяет разбить tcp сегмент SYN,ACK на отдельные части с SYN и с ACK. В ответ на это клиент шлет SYN,ACK , что обычно характеризует сервер. У некоторых DPI от этого может ломаться алгоритм, и они перестают блокировать запрещенный контент. Здесь подробное описание что есть split handshake.

Перенаправление трафика обычно идет по номеру source портов и направлению original. original - это исходящий с системы трафик, reply - входящий.

tpws

tpws - это transparent proxy.

@<config_file>|$<config_file>                     ; читать конфигурацию из файла. опция должна быть первой. остальные опции игнорируются.

--debug=0|1|2|syslog|@<filename>                  ; 0,1,2 = логирование на косоль : 0=тихо, 1(default)=подробно, 2=отладка.
--debug-level=0|1|2                               ; указать уровень логирования для syslog и @<filename>
--dry-run                                         ; проверить опции командной строки и выйти. код 0 - успешная проверка.
--version                                         ; вывести версию и выйти

--daemon                                          ; демонизировать прогу
--pidfile=<file>                                  ; сохранить PID в файл
--user=<username>                                 ; менять uid процесса
--uid=uid[:gid]                                   ; менять uid процесса
--bind-addr                                       ; на каком адресе слушать. может быть ipv4 или ipv6 адрес
                                                  ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan
--bind-linklocal=no|unwanted|prefer|force         ; no : биндаться только на global ipv6
                                                  ; unwanted (default) : предпочтительно global, если нет - LL
                                                  ; prefer : предпочтительно LL, если нет - global
                                                  ; force : биндаться только на LL
--bind-iface4=<iface>                             ; слушать на первом ipv4 интерфейса iface
--bind-iface6=<iface>                             ; слушать на первом ipv6 интерфейса iface
--bind-wait-ifup=<sec>                            ; ждать до N секунд появления и поднятия интерфейса
--bind-wait-ip=<sec>                              ; ждать до N секунд получения IP адреса (если задан --bind-wait-ifup - время идет после поднятия интерфейса)
--bind-wait-ip-linklocal=<sec>
                                                  ; имеет смысл только при задании --bind-wait-ip
                                                  ; --bind-linklocal=unwanted	: согласиться на LL после N секунд
                                                  ; --bind-linklocal=prefer	: согласиться на global address после N секунд
--bind-wait-only                                  ; подождать все бинды и выйти. результат 0 в случае успеха, иначе не 0.
--connect-bind-addr                               ; с какого адреса подключаться во внешнюю сеть. может быть ipv4 или ipv6 адрес
                                                  ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan
                                                  ; опция может повторяться для v4 и v6 адресов
                                                  ; опция не отменяет правил маршрутизации ! выбор интерфейса определяется лишь правилами маршрутизации, кроме случая v6 link local.
--socks                                           ; вместо прозрачного прокси реализовать socks4/5 proxy
--no-resolve                                      ; запретить ресолвинг имен через socks5
--resolve-threads                                 ; количество потоков ресолвера
--port=<port>                                     ; на каком порту слушать
--maxconn=<max_connections>                       ; максимальное количество соединений от клиентов к прокси
--maxfiles=<max_open_files>                       ; макс количество файловых дескрипторов (setrlimit). мин требование (X*connections+16), где X=6 в tcp proxy mode, X=4 в режиме тамперинга.
                                                  ; стоит сделать запас с коэффициентом как минимум 1.5. по умолчанию maxfiles (X*connections)*1.5+16
--max-orphan-time=<sec>                           ; если вы запускаете через tpws торрент-клиент с множеством раздач, он пытается установить очень много исходящих соединений,
                                                  ; большая часть из которых отваливается по таймауту (юзера сидят за NAT, firewall, ...)
                                                  ; установление соединения в linux может длиться очень долго. локальный конец отвалился, перед этим послав блок данных,
                                                  ; tpws ждет подключения удаленного конца, чтобы отослать ему этот блок, и зависает надолго.
                                                  ; настройка позволяет сбрасывать такие подключения через N секунд, теряя блок данных. по умолчанию 5 сек. 0 означает отключить функцию
                                                  ; эта функция не действует на успешно подключенные ранее соединения

--local-rcvbuf=<bytes>                            ; SO_RCVBUF для соединений client-proxy
--local-sndbuf=<bytes>                            ; SO_SNDBUF для соединений client-proxy
--remote-rcvbuf=<bytes>                           ; SO_RCVBUF для соединений proxy-target
--remote-sndbuf=<bytes>                           ; SO_SNDBUF для соединений proxy-target
--nosplice                                        ; не использовать splice на linux системах
--skip-nodelay                                    ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split.
--local-tcp-user-timeout=<seconds>                ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение)
--remote-tcp-user-timeout=<seconds>               ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение)
--fix-seg=<int>                                   ; исправлять неудачи tcp сегментации ценой задержек для всех клиентов и замедления. ждать до N мс. по умолчанию 30 мс.
--ipcache-lifetime=<int>                          ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1]                          ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы

--split-pos=N|-N|marker+N|marker-N                ; список через запятую маркеров для tcp сегментации
--split-any-protocol                              ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS)
--disorder[=http|tls]                             ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса
--oob[=http|tls]                                  ; отправить байт out-of-band data (OOB) в конце первой части сплита
--oob-data=<char>|0xHEX                           ; переопределить байт OOB. по умолчанию 0x00.
--hostcase                                        ; менять регистр заголовка "Host:". по умолчанию на "host:".
--hostspell=HoST                                  ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
--hostdot                                         ; добавление точки после имени хоста : "Host: kinozal.tv."
--hosttab                                         ; добавление табуляции после имени хоста : "Host: kinozal.tv\t"
--hostnospace                                     ; убрать пробел после "Host:"
--hostpad=<bytes>                                 ; добавить паддинг-хедеров общей длиной <bytes> перед Host:
--domcase                                         ; домен после Host: сделать таким : TeSt.cOm
--methodspace                                     ; добавить пробел после метода : "GET /" => "GET  /"
--methodeol                                       ; добавить перевод строки перед методом  : "GET /" => "\r\nGET  /"
--unixeol                                         ; конвертировать 0D0A в 0A и использовать везде 0A
--tlsrec=N|-N|marker+N|marker-N                   ; разбивка TLS ClientHello на 2 TLS records на указанной позиции. Минимальное смещение - 6.
--mss=<int>                                       ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
--tamper-start=[n]<pos>                           ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока)
--tamper-cutoff=[n]<pos>                          ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока)
--hostlist=<filename>                             ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
                                                  ; в файле должен быть хост на каждой строке.
                                                  ; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
                                                  ; при изменении времени модификации файла он перечитывается автоматически по необходимости
                                                  ; список может быть запакован в gzip. формат автоматически распознается и разжимается
                                                  ; списков может быть множество. пустой общий лист = его отсутствие
                                                  ; хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello.
--hostlist-domains=<domain_list>                  ; фиксированный список доменов через зяпятую. можно использовать # в начале для комментирования отдельных доменов.
--hostlist-exclude=<filename>                     ; не применять дурение к доменам из листа. может быть множество листов. схема аналогична include листам.
--hostlist-exclude-domains=<domain_list>          ; фиксированный список доменов через зяпятую. можно использовать # в начале для комментирования отдельных доменов.
--hostlist-auto=<filename>                        ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика)
--hostlist-auto-fail-threshold=<int>              ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3)
--hostlist-auto-fail-time=<int>                   ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60)
--hostlist-auto-debug=<logfile>                   ; лог положительных решений по autohostlist. позволяет разобраться почему там появляются хосты.
--new                                             ; начало новой стратегии (новый профиль)
--skip                                            ; не использовать этот профиль . полезно для временной деактивации профиля без удаления параметров.
--filter-l3=ipv4|ipv6                             ; фильтр версии ip для текущей стратегии
--filter-tcp=[~]port1[-port2]|*                   ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. поддерживается список через запятую.
--filter-l7=[http|tls|quic|wireguard|dht|unknown] ; фильтр протокола L6-L7. поддерживается несколько значений через запятую.
--ipset=<filename>                                ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-ip=<ip_list>                              ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.
--ipset-exclude=<filename>                        ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-exclude-ip=<ip_list>                      ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.

TCP СЕГМЕНТАЦИЯ В TPWS

tpws, как и nfqws, поддерживает множественную сегментацию запросов. Сплит позиции задаются в --split-pos. Указываются маркеры через запятую. Описание маркеров см в разделе nfqws.

На прикладном уровне в общем случае нет гарантированного средства заставить ядро выплюнуть блок данных, порезанным в определенном месте. ОС держит буфер отсылки (SNDBUF) у каждого сокета. Если у сокета включена опция TCP_NODELAY и буфер пуст, то каждый send приводит к отсылке отдельного ip пакета или группы пакетов, если блок не вмещается в один ip пакет. Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему, никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии, что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI. Разбиение будет производиться согласно MSS, который зависит от MTU исходящего интерфейса. Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае. Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда, когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан, но и принят другой стороной, а следовательно буфер отсылки пуст, и следующие 2 send приведут к отсылке сегментов данных разными ip пакетами.

Таким образом tpws обеспечивает сплит только за счет раздельных вызовов send, и это обычно работает надежно, если разбивать не на слишком много частей и не на слишком мелкие подряд следующие части. В последнем случае Linux все же может обьединить некоторые части, что приведет к несоответствию реальной сегментации указанным сплит позициям. Другие ОС в этом вопросе ведут себя более предсказуемо. Спонтанного обьединения замечено не было. Поэтому не стоит злоупотреблять сплитами и в особенности мелкими соседними пакетами.

Как показывается практика, проблемы могут начаться , если количество сплитов более одного. На каких-то системах наблюдался стабильный результат до 8 сплитов, на других проблемы уже начинались после 2 сплитов. Один сплит работает стабильно, если не является частью массивной потоковой передачи. При неудаче сегментации будет выводиться сообщение WARNING ! segmentation failed. Если вы его видите, это повод снизить количество сплит позиций. Если это не вариант, для ядер Linux >=4.6 есть параметр --fix-seg. Он позволяет подождать завершение отсылки перед отправкой следующей части. Но этот вариант ломает модель асинхронной обработки событий. Пока идет ожидание, все остальные соединения не обрабатываются и кратковременно подвисают. На практике это может быть совсем небольшое ожидание - менее 10 мс. Выполняется оно только , если происходит split, и в ожидании есть реальная необходимость. В высоконагруженных системах данный вариант не рекомендуется. Но для домашнего использования может подойти, и вы эти задержки даже не заметите.

Если вы пытаетесь сплитнуть массивную передачу с --split-any-protocol, когда информация поступает быстрее отсылки, то без --fix-seg ошибки сегментации будут сыпаться сплошным потоком. Работа по массивному потоку без ограничителей --tamper-start и --tamper-cutoff обычно лишена смысла.

tpws работает на уровне сокетов, поэтому длинный запрос, не вмещающийся в 1 пакет (TLS с kyber), он получает целым блоком. На каждую сплит часть он делает отдельный вызов send(). Но ОС не сможет отослать данные в одном пакете, если размер превысит MTU. В случае слишком большого сегмента ОС дополнительно его порежет на более мелкие. Результат должен быть аналогичен nfqws.

--disorder заставляет слать каждый 2-й пакет с TTL=1, начиная с первого. К серверу приходят все четные пакеты сразу. На остальные ОС делает ретрансмиссию, и они приходят потом. Это само по себе создает дополнительную задержку (200 мс в linux для первой ретрансмиссии). Иным способом сделать disorder в сокет варианте не представляется возможным. Итоговый порядок для 6 сегментов получается 2 4 6 1 3 5.

--oob высылает 1 байт out-of-band data после первого сплит сегмента. oob в каждом сегменте сплита показал себя ненадежным. Сервер получает oob в сокет.

Сочетание oob и disorder возможно только в Linux. Остальные ОС не умеют с таким справляться. Флаг URG теряется при ретрансмиссиях. Сервер получает oob в сокет. Сочетание этих параметров в ос, кроме Linux, вызывает ошибку на этапе запуска.

TLSREC

--tlsrec позволяют внутри одного tcp сегмента разрезать TLS ClientHello на 2 TLS records. Можно использовать стандартный механизм маркеров для задания относительных позиций.

--tlsrec ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах без проблем принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN или системы ddos-защиты. Поэтому применение --tlsrec без ограничителей вряд ли целесообразно. В РФ --tlsrec обычно не работает с TLS 1.2, потому что цензор парсит сертификат сервера из ServerHello. Работает только с TLS 1.3, поскольку там эта информация шифруется. Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного.

MSS

--mss устанавливает опцию сокета TCP_MAXSEG. Клиент выдает это значение в tcp опциях SYN пакета. Сервер в ответ в SYN,ACK выдает свой MSS. На практике сервера обычно снижают размеры отсылаемых ими пакетов, но они все равно не вписываются в низкий MSS, указанный клиентом. Обычно чем больше указал клиент, тем больше шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет, это может обмануть DPI, секущий ответ сервера. Схема может значительно снизить скорость и сработать не на всех сайтах.

С фильтром по hostlist совместимо только в некоторых случаях, когда возможно узнать имя хоста на момент применения дурения.

Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже. Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в 3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который может привести к реакции DPI. Использовать только когда нет ничего лучше или для отдельных ресурсов. Для http использовать смысла нет, поэтому заводите отдельный desync profile с фильтром по порту 443. Работает только на Linux, не работает на BSD и MacOS.

ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ

Параметр --hostpad=<bytes> добавляет паддинг-хедеров перед Host: на указанное количество байтов. Если размер <bytes> слишком большой, то идет разбивка на разные хедеры по 2K. Общий буфер приема http запроса - 64K, больший паддинг не поддерживается, да и http сервера такое уже не принимают. Полезно против DPI, выполняющих реассемблинг TCP с ограниченным буфером. Если техника работает, то после некоторого количества bytes http запрос начнет проходить до сайта. Если при этом критический размер padding около MTU, значит скорее всего DPI не выполняет реассемблинг пакетов, и лучше будет использовать обычные опции TCP сегментации. Если все же реассемблинг выполняется, то критический размер будет около размера буфера DPI. Он может быть 4K или 8K, возможны и другие значения.

МНОЖЕСТВЕННЫЕ СТРАТЕГИИ

Работают аналогично nfqws, кроме некоторых моментов. Нет параметра --filter-udp, поскольку tpws udp не поддерживает. Методы нулевой фазы (--mss) могут работать по хостлисту только в двух случаях: если используется режим socks и удаленный ресолвинг хостов через прокси, либо используется система кэша IP для запоминания соответствия IP->hostname. Работоспособность вашей настройки в одном и том же режиме может зависеть от того, применяет ли клиент удаленный ресолвинг. Это может быть неочевидно. В одной программе работает, в другой - нет.

Если вы используете профиль с хостлистом , и вам нужен mss всегда, укажите mss в профиле с хостлистом, создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss. Тогда при любом раскладе будет выполняться mss.

Если вам нужен mss по хостлисту, указывайте --mss только в профиле с хостлистом и убедитесь в наличии любого из необходимых условий работы в таком режиме.

Используйте curl --socks5 и curl --socks5-hostname для проверки вашей стратегии. Смотрите вывод --debug, чтобы убедиться в правильности настроек.

СЛУЖЕБНЫЕ ПАРАМЕТРЫ

--debug позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования опций. --debug лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа --debug еще дело не дошло, то сообщения не будут выведены в файл или syslog. --debug=0|1|2 позволяют сразу в одном параметре включить логирование на консоль и указать уровень. Сохранено для совместимости с более старыми версиями. Для выбора уровня в режиме syslog или file используйте отдельный параметр --debug-level. Если в этих режимах --debug не указывать уровень через --debug-level, то автоматически назначается уровень 1. При логировании в файл процесс не держит файл открытым. Ради каждой записи файл открывается и потом закрывается. Так что файл можно удалить в любой момент, и он будет создан заново при первом же сообщении в лог. Но имейте в виду, что если вы запускаете процесс под root, то будет сменен UID на не-root. В начале на лог файл меняется owner, иначе запись будет невозможна. Если вы потом удалите файл, и у процесса не будет прав на создание файла в его директории, лог больше не будет вестись. Вместо удаления лучше использовать truncate. В шелле это можно сделать через команду ": >filename"

tpws может биндаться на множество интерфейсов и IP адресов (до 32 шт). Порт всегда только один. Параметры --bind-iface* и --bind-addr создают новый бинд. Остальные параметры --bind-* относятся к последнему бинду. Для бинда на все ipv4 укажите --bind-addr "0.0.0.0", на все ipv6 - "::". --bind-addr="" - биндаемся на все ipv4 и ipv6. Выбор режима использования link local ipv6 адресов (fe80::/8) :

--bind-iface6 --bind-linklocal=no : сначала приватный адрес fc00::/7, затем глобальный адрес
--bind-iface6 --bind-linklocal=unwanted : сначала приватный адрес fc00::/7, затем глобальный адрес, затем link local.
--bind-iface6 --bind-linklocal=prefer : сначала link local, затем приватный адрес fc00::/7, затем глобальный адрес.
--bind-iface6 --bind-linklocal=force : только link local

Если не указано ни одного бинда, то создается бинд по умолчанию на все адреса всех интерфейсов. Для бинда на конкретный link-local address делаем так : --bind-iface6=fe80::aaaa:bbbb:cccc:dddd%iface-name Параметры --bind-wait* могут помочь в ситуациях, когда нужно взять IP с интерфейса, но его еще нет, он не поднят или не сконфигурирован. В разных системах события ifup ловятся по-разному и не гарантируют, что интерфейс уже получил IP адрес определенного типа. В общем случае не существует единого механизма повеситься на событие типа "на интерфейсе X появился link local address". Для бинда на известный ip, когда еще интерфейс не сконфигурирован, нужно делать так: --bind-addr=192.168.5.3 --bind-wait-ip=20 В режиме transparent бинд возможен на любой несуществующий адрес, в режиме socks - только на существующий.

Параметры rcvbuf и sndbuf позволяют установить setsockopt SO_RCVBUF SO_SNDBUF для локального и удаленного соединения.

--skip-nodelay может быть полезен, когда tpws используется без дурения, чтобы привести MTU к MTU системы, на которой работает tpws. Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.

--local-tcp-user-timeout и --remote-tcp-user-timeout устанавливают значение таймаута в секундах для соединений клиент-прокси и прокси-сервер. Этот таймаут соответствует опции сокета linux TCP_USER_TIMEOUT. Под таймаутом подразумевается время, в течение которого буферизированные данные не переданы или на переданные данные не получено подтверждение (ACK) от другой стороны. Этот таймаут никак не касается времени отсутствия какой-либо передачи через сокет лишь потому, что данных для передачи нет. Полезно для сокращения время закрытия подвисших соединений. Поддерживается только на Linux и MacOS.

Режим --socks не требует повышенных привилегий (кроме бинда на привилегированные порты 1..1023). Поддерживаются версии socks 4 и 5 без авторизации. Версия протокола распознается автоматически. Подключения к IP того же устройства, на котором работает tpws, включая localhost, запрещены. socks5 позволяет удаленно ресолвить хосты (curl : --socks5-hostname firefox : socks_remote_dns=true). tpws поддерживает эту возможность асинхронно, не блокируя процессинг других соединений, используя многопоточный пул ресолверов. Количество потоков определяется автоматически в зависимости от --maxconn, но можно задать и вручную через параметр --resolver-threads. Запрос к socks выставляется на паузу, пока домен не будет преобразован в ip адрес в одном из потоков ресолвера. Ожидание может быть более длинным, если все потоки заняты. Если задан параметр --no-resolve, то подключения по именам хостов запрещаются, а пул ресолверов не создается. Тем самым экономятся ресурсы.

IPTABLES ДЛЯ TPWS

Для перенаправления tcp соединения на transparent proxy используются команды следующего вида :

iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988
iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988

Первая команда для соединений с самой системы, вторая - для проходящих через роутер соединений.

DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра route_localnet :

sysctl -w net.ipv4.conf.<внутренний_интерфейс>.route_localnet=1

Можно использовать -j REDIRECT --to-port 988 вместо DNAT, однако в этом случае процесс transparent proxy должен слушать на ip адресе входящего интерфейса или на всех адресах. Слушать на всех - не есть хорошо с точки зрения безопасности. Слушать на одном (локальном) можно, но в случае автоматизированного скрипта придется его узнавать, потом динамически вписывать в команду. В любом случае требуются дополнительные усилия. Использование route_localnet тоже имеет потенциальные проблемы с безопасностью. Вы делаете доступным все, что висит на 127.0.0.0/8 для локальной подсети < внутренний_интерфейс>. Службы обычно привязываются к 127.0.0.1, поэтому можно средствами iptables запретить входящие на 127.0.0.1 не с интерфейса lo, либо повесить tpws на любой другой IP из 127.0.0.0/8, например на 127.0.0.127, и разрешить входящие не с lo только на этот IP.

iptables -A INPUT ! -i lo -d 127.0.0.127 -j ACCEPT
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j DROP

Фильтр по owner необходим для исключения рекурсивного перенаправления соединений от самого tpws. tpws запускается под пользователем tpws, для него задается исключающее правило.

ip6tables работают почти точно так же, как и ipv4, но есть ряд важных нюансов. В DNAT следует брать адрес --to в квадратные скобки. Например :

ip6tables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to [::1]:988

Параметра route_localnet не существует для ipv6. DNAT на localhost (::1) возможен только в цепочке OUTPUT. В цепочке PREROUTING DNAT возможен на любой global address или на link local address того же интерфейса, откуда пришел пакет. NFQUEUE работает без изменений.

NFTABLES ДЛЯ TPWS

Базовая конфигурация :

IFACE_WAN=wan
IFACE_LAN=br-lan

sysctl -w net.ipv4.conf.$IFACE_LAN.route_localnet=1

nft create table inet ztest

nft create chain inet ztest localnet_protect
nft add rule inet ztest localnet_protect ip daddr 127.0.0.127 return
nft add rule inet ztest localnet_protect ip daddr 127.0.0.0/8 drop
nft create chain inet ztest input "{type filter hook input priority filter - 1;}"
nft add rule inet ztest input iif != "lo" jump localnet_protect

nft create chain inet ztest dnat_output "{type nat hook output priority dstnat;}"
nft add rule inet ztest dnat_output meta skuid != tpws oifname $IFACE_WAN tcp dport { 80, 443 } dnat ip to 127.0.0.127:988
nft create chain inet ztest dnat_pre "{type nat hook prerouting priority dstnat;}"
nft add rule inet ztest dnat_pre meta iifname $IFACE_LAN tcp dport { 80, 443 } dnat ip to 127.0.0.127:988

Удаление таблицы :

nft delete table inet ztest

ip2net

Утилита ip2net предназначена для преобразования ipv4 или ipv6 списка ip в список подсетей с целью сокращения размера списка. Входные данные берутся из stdin, выходные выдаются в stdout.

-4                             ; лист - ipv4 (по умолчанию)
-6                             ; лист - ipv6
--prefix-length=min[-max]      ; диапазон рассматриваемых длин префиксов. например : 22-30 (ipv4), 56-64 (ipv6)
--v4-threshold=mul/div         ; ipv4 : включать подсети, в которых заполнено по крайней мере mul/div адресов. например : 3/4
--v6-threshold=N               ; ipv6 : минимальное количество ip для создания подсети

В списке могут присутствовать записи вида ip/prefix и ip1-ip2. Такие записи выкидываются в stdout без изменений. Они принимаются командой ipset. ipset умеет для листов hash:net из ip1-ip2 делать оптимальное покрытие ip/prefix. ipfw из FreeBSD понимает ip/prefix, но не понимает ip1-ip2. ip2net фильтрует входные данные, выкидывая неправильные IP адреса.

Выбирается подсеть, в которой присутствует указанный минимум адресов. Для ipv4 минимум задается как процент от размера подсети (mul/div. например, 3/4), для ipv6 минимум задается напрямую.

Размер подсети выбирается следующим алгоритмом: Сначала в указанном диапазоне длин префиксов ищутся подсети, в которых количество адресов - максимально. Если таких сетей найдено несколько, берется наименьшая сеть (префикс больше). Например, заданы параметры v6_threshold=2 prefix_length=32-64, имеются следующие ipv6 :

1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5
Результат будет :
1234:5678:aaa8::/45

Эти адреса так же входят в подсеть /32. Однако, нет смысла проходиться ковровой бомбардировкой, когда те же самые адреса вполне влезают в /45 и их ровно столько же. Если изменить v6_threshold=4, то результат будет:

1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5

То есть ip не объединятся в подсеть, потому что их слишком мало. Если изменить prefix_length=56-64, результат будет:

1234:5678:aaaa::/64
1234:5678:aaac::5

Требуемое процессорное время для вычислений сильно зависит от ширины диапазона длин префиксов, размера искомых подсетей и длины листа. Если ip2net думает слишком долго, не используйте слишком большие подсети и уменьшите диапазон длин префиксов. Учтите, что арифметика mul/div - целочисленная. При превышении разрядной сетки 32 bit результат непредсказуем. Не надо делать такое: 5000000/10000000. 1/2 - гораздо лучше.

mdig

Программа предназначена для многопоточного ресолвинга больших листов через системный DNS. Она берет из stdin список доменов и выводит в stdout результат ресолвинга. Ошибки выводятся в stderr.

--threads=<threads_number>	; количество потоков. по умолчанию 1.
--family=<4|6|46>              ; выбор семейства IP адресов : ipv4, ipv6, ipv4+ipv6
--verbose                      ; дебаг-лог на консоль
--stats=N                      ; выводить статистику каждые N доменов
--log-resolved=<file>          ; сохранять успешно отресолвленные домены в файл
--log-failed=<file>            ; сохранять неудачно отресолвленные домены в файл
--dns-make-query=<domain>      ; вывести в stdout бинарный DNS запрос по домену. если --family=6, запрос будет AAAA, иначе A.
--dns-parse-query              ; распарсить бинарный DNS ответ и выдать все ivp4 и ipv6 адреса из него в stdout

Параметры --dns-make-query и --dns-parse-query позволяют провести ресолвинг одного домена через произвольный канал. Например, следующим образом можно выполнить DoH запрос, используя лишь mdig и curl :

mdig --family=6 --dns-make-query=rutracker.org | curl --data-binary @- -H "Content-Type: application/dns-message" https://cloudflare-dns.com/dns-query | mdig --dns-parse-query

Способы получения списка заблокированных IP

!!! nftables не могут работать с ipset-ами. Собственный аналогичный механизм требует огромного количество RAM !!! для загрузки больших листов. Например, для загона 100K записей в nfset не хватает даже 256 Mb. !!! Если вам нужны большие листы на домашних роутерах, откатывайтесь на iptables+ipset.

  1. Внесите заблокированные домены в ipset/zapret-hosts-user.txt и запустите ipset/get_user.sh На выходе получите ipset/zapret-ip-user.txt с IP адресами.

Cкрипты с названием get_reestr_* оперируют дампом реестра заблокированных сайтов :

  1. ipset/get_reestr_resolve.sh получает список доменов от rublacklist и дальше их ресолвит в ip адреса в файл ipset/zapret-ip.txt.gz. В этом списке есть готовые IP адреса, но судя во всему они там в точности в том виде, что вносит в реестр РосКомПозор. Адреса могут меняться, позор не успевает их обновлять, а провайдеры редко банят по IP : вместо этого они банят http запросы с "нехорошим" заголовком "Host:" вне зависимости от IP адреса. Поэтому скрипт ресолвит все сам, хотя это и занимает много времени. Используется мультипоточный ресолвер mdig (собственная разработка).

  2. ipset/get_reestr_preresolved.sh. то же самое, что и 2), только берется уже заресолвленый список со стороннего ресурса.

  3. ipset/get_reestr_preresolved_smart.sh. то же самое, что и 3), с добавлением всего диапазона некоторых автономных систем (прыгающие IP адреса из cloudflare, facebook, ...) и некоторых поддоменов блокируемых сайтов

Cкрипты с названием get_antifilter_* оперируют списками адресов и масок подсетей с сайтов antifilter.network и antifilter.download :

  1. ipset/get_antifilter_ip.sh. получает лист https://antifilter.download/list/ip.lst.

  2. ipset/get_antifilter_ipsmart.sh. получает лист https://antifilter.network/download/ipsmart.lst. умная суммаризация отдельных адресов из ip.lst по маскам от /32 до /22

  3. ipset/get_antifilter_ipsum.sh. получает лист https://antifilter.download/list/ipsum.lst. суммаризация отдельных адресов из ip.lst по маске /24

  4. ipset/get_antifilter_ipresolve.sh. получает лист https://antifilter.download/list/ipresolve.lst. пре-ресолвленный список, аналогичный получаемый при помощи get_reestr_resolve. только ipv4.

  5. ipset/get_antifilter_allyouneed.sh. получает лист https://antifilter.download/list/allyouneed.lst. Суммарный список префиксов, созданный из ipsum.lst и subnet.lst.

  6. ipset/get_refilter_ipsum.sh. Список берется отсюда : https://github.com/1andrevich/Re-filter-lists

Все варианты рассмотренных скриптов автоматически создают и заполняют ipset. Варианты 2-10 дополнительно вызывают вариант 1.

  1. ipset/get_config.sh. этот скрипт вызывает то, что прописано в переменной GETLIST из файла config Если переменная не определена, то ресолвятся лишь листы для ipset nozapret/nozapret6.

Листы РКН все время изменяются. Возникают новые тенденции. Требования к RAM могут меняться. Поэтому необходима нечастая, но все же регулярная ревизия что же вообще у вас происходит на роутере. Или вы можете узнать о проблеме лишь когда у вас начнет постоянно пропадать wifi, и вам придется его перезагружать каждые 2 часа (метод кувалды).

Самые щадящие варианты по RAM - get_antifilter_allyouneed.sh, get_antifilter_ipsum.sh, get_refilter_*.sh.

Листы zapret-ip.txt и zapret-ipban.txt сохраняются в сжатом виде в файлы .gz. Это позволяет снизить их размер во много раз и сэкономить место на роутере. Отключить сжатие листов можно параметром конфига GZIP_LISTS=0.

На роутерах не рекомендуется вызывать эти скрипты чаще раза за 2 суток, поскольку сохранение идет либо во внутреннюю флэш память роутера, либо в случае extroot - на флэшку. В обоих случаях слишком частая запись может убить флэшку, но если это произойдет с внутренней флэш памятью, то вы просто убьете роутер.

Принудительное обновление ipset выполняет скрипт ipset/create_ipset.sh. Если передан параметр no-update, скрипт не обновляет ipset, а только создает его при его отсутствии и заполняет. Это полезно, когда могут случиться несколько последовательных вызовов скрипта. Нет смысла несколько раз перезаполнять ipset, это длительная операция на больших листах. Листы можно обновлять раз в несколько суток, и только тогда вызывать create_ipset без параметра no-update. Во всех остальных случаях стоит применять no-update.

Список РКН уже достиг внушительных размеров в сотни тысяч IP адресов. Поэтому для оптимизации ipset применяется утилита ip2net. Она берет список отдельных IP адресов и пытается интеллектуально создать из него подсети для сокращения количества адресов. ip2net отсекает неправильные записи в листах, гарантируя отсутствие ошибок при их загрузке. ip2net написан на языке C, поскольку операция ресурсоемкая. Иные способы роутер может не потянуть.

Можно внести список доменов в ipset/zapret-hosts-user-ipban.txt. Их ip адреса будут помещены в отдельный ipset ipban. Он может использоваться для принудительного завертывания всех соединений на прозрачный proxy redsocks или на VPN.

IPV6 : если включен ipv6, то дополнительно создаются листы с таким же именем, но с "6" на конце перед расширением. zapret-ip.txt => zapret-ip6.txt Создаются ipset-ы zapret6 и ipban6. Листы с antifilter не содержат список ipv6 адресов.

СИСТЕМА ИСКЛЮЧЕНИЯ IP. Все скрипты ресолвят файл zapret-hosts-user-exclude.txt, создавая zapret-ip-exclude.txt и zapret-ip-exclude6.txt. Они загоняются в ipset-ы nozapret и nozapret6. Все правила, создаваемые init скриптами, создаются с учетом этих ipset. Помещенные в них IP не участвуют в процессе. zapret-hosts-user-exclude.txt может содержать домены, ipv4 и ipv6 адреса или подсети.

FreeBSD. Скрипты ipset/*.sh работают так же на FreeBSD. Вместо ipset они создают lookup таблицы ipfw с аналогичными именами. ipfw таблицы в отличие от ipset могут содержать как ipv4, так и ipv6 адреса и подсети в одной таблице, поэтому разделения нет.

Параметр конфига LISTS_RELOAD задает произвольную команду для перезагрузки листов. Это особенно полезно на BSD системах с PF. LISTS_RELOAD=- отключает перезагрузку листов.

Фильтрация по именам доменов

Альтернативой ipset является использование tpws или nfqws со списком доменов. Оба демона принимают неограниченное количество листов include (--hostlist) и exclude (--hostlist-exclude). Прежде всего проверяются exclude листы. При вхождении в них происходит отказ от дурения. Далее при наличии include листов проверяется домен на вхождение в них. При невхождении в список отказ от дурения. Если все include листы пустые, это приравнивается к отсутствию include листов. Ограничение перестает работать. В иных случаях происходит дурение. Нет ни одного списка - дурение всегда. Есть только exclude список - дурение всех, кроме. Есть только include список - дурение только их. Есть оба - дурение только include, кроме exclude.

В системе запуска это обыграно следующим образом. Присутствуют 2 include списка : ipset/zapret-hosts-user.txt.gz или ipset/zapret-hosts-user.txt, ipset/zapret-hosts.txt.gz или ipset/zapret-hosts.txt и 1 exclude список ipset/zapret-hosts-user-exclude.txt.gz или ipset/zapret-hosts-user-exclude.txt

При режимах фильтрации MODE_FILTER=hostlist или MODE_FILTER=autohostlist система запуска передает nfqws или tpws все листы, файлы которых присутствуют. Передача происходит через замену маркеров <HOSTLIST> и <HOSTLIST_NOAUTO> на реальные параметры --hostlist, --hostlist-exclude, --hostlist-auto. Если вдруг листы include присутствуют, но все они пустые, то работа аналогична отсутствию include листа. Файл есть, но несмотря на это дурится все, кроме exclude. Если вам нужен именно такой режим - не обязательно удалять zapret-hosts-users.txt. Достаточно сделать его пустым.

Поддомены учитываются автоматически. Например, строчка "ru" вносит в список "*.ru". Строчка "*.ru" в списке не сработает. Можно использовать символ ^ в начале хоста, чтобы отказаться от автоматического учета поддоменов.

Список доменов РКН может быть получен скриптами

ipset/get_reestr_hostlist.sh
ipset/get_antizapret_domains.sh
ipset/get_reestr_resolvable_domains.sh
ipset/get_refilter_domains.sh

Он кладется в ipset/zapret-hosts.txt.gz.

При изменении времени модификации или размера файлов списки перечитываются автоматически. После неатомарных операций изменения можно послать tpws/nfqws сигнал HUP для принудительной перечитки всех листов.

При фильтрации по именам доменов демон должен запускаться без фильтрации по ipset. tpws и nfqws решают нужно ли применять дурение в зависимости от хоста, полученного из протокола прикладного уровня (http, tls, quic). При использовании больших списков, в том числе списка РКН, оцените объем RAM на роутере ! Если после запуска демона RAM под завязку или случаются oom, значит нужно отказаться от таких больших списков.

Режим фильтрации autohostlist

Этот режим позволяет проанализировать как запросы со стороны клиента, так и ответы от сервера. Если хост еще не находится ни в каких листах и обнаруживается ситуация, похожая на блокировку, происходит автоматическое добавление хоста в список autohostlist как в памяти, так и в файле. nfqws или tpws сами ведут этот файл. Чтобы какой-то хост не смог попась в autohostlist используйте hostlist-exclude. Если он все-же туда попал - удалите запись из файла вручную. Процессы автоматически перечитают файл. tpws/nfqws сами назначают владельцем файла юзера, под которым они работают после сброса привилегий, чтобы иметь возможность обновлять лист.

В случае nfqws данный режим требует перенаправления в том числе и входящего трафика. Крайне рекомендовано использовать ограничитель connbytes, чтобы nfqws не обрабатывал гигабайты. По этой же причине не рекомендуется использование режима на BSD системах. Там нет фильтра connbytes.

На linux системах при использовании nfqws и фильтра connbytes может понадобиться : sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1 Было замечено, что некоторые DPI в России возвращают RST с неверным ACK. Это принимается tcp/ip стеком linux, но через раз приобретает статус INVALID в conntrack. Поэтому правила с connbytes срабатывают через раз, не пересылая RST пакет nfqws.

Как вообще могут вести себя DPI, получив "плохой запрос" и приняв решение о блокировке:

  1. Зависание: просто отмораживается, блокируя прохождение пакетов по TCP каналу.
  2. RST: отправляет RST клиенту и/или серверу
  3. Редирект: (только для http) отправляет редирект на сайт-заглушку
  4. Подмена сертификата: (только для https) полный перехват TLS сеанса с попыткой всунуть что-то свое клиенту. Применяется нечасто, поскольку броузеры на такое ругаются.

nfqws и tpws могут сечь варианты 1-3, 4 они не распознают. В силу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации по-разному. Что считается ситуацией, похожей на блокировку :

  1. nfqws Несколько ретрансмиссий первого запроса в TCP сеансе, в котором имеется host.
  2. nfqws,tpws RST, пришедший в ответ на первый запрос с хостом.
  3. nfqws,tpws HTTP редирект, пришедший в ответ на первый запрос с хостом, на глобальный адрес с доменом 2 уровня, не совпадающим с доменом 2 уровня оригинального запроса.
  4. tpws закрытие соединения клиентом после отправки первого запроса с хостом, если не было на него ответа со стороны сервера. Это обычно случается по таймауту, когда нет ответа (случай "зависание").

Чтобы снизить вероятность ложных срабатываний, имеется счетчик ситуаций, похожих на блокировку. Если за определенное время произойдет более определенного их количества, хост считается заблокированным и заносится в autohostlist. По нему сразу же начинает работать стратегия по обходу блокировки. Если в процессе счета вебсайт отвечает без признаков блокировки, счетчик сбрасывается. Вероятно, это был временный сбой сайта.

На практике работа с данным режимом выглядит так. Первый раз пользователь заходит на сайт и получает заглушку, сброс соединения или броузер подвисает, вываливаясь по таймауту с сообщением о невозможности загрузить страницу. Надо долбить F5, принуждая броузер повторять попытки. После некоторой попытки сайт начинает работать, и дальше он будет работать всегда.

С этим режимом можно использовать техники обхода, ломающие значительное количество сайтов. Если сайт не ведет себя как заблокированный, значит обход применен не будет. В противном случае терять все равно нечего. Однако, могут быть временные сбои сервера, приводящие к ситуации, аналогичной блокировке. Могут происходить ложные срабатывания. Если такое произошло, стратегия может начать ломать незаблокированный сайт. Эту ситуацию, увы, придется вам контролировать вручную. Заносите такие домены в ipset/zapret-hosts-user-exclude.txt, чтобы избежать повторения. Чтобы впоследствии разобраться почему домен был занесен в лист, можно включить autohostlist debug log. Он полезен тем, что работает без постоянного просмотра вывода nfqws в режиме debug. В лог заносятся только основные события, ведущие к занесению хоста в лист. По логу можно понять как избежать ложных срабатываний и подходит ли вообще вам этот режим.

Можно использовать один autohostlist с множеством процессов. Все процессы проверяют время модификации файла. Если файл был изменен в другом процессе, происходит его перечитывание. Все процессы должны работать под одним uid, чтобы были права доступа на файл.

Скрипты zapret ведут autohostlist в ipset/zapret-hosts-auto.txt. install_easy.sh при апгрейде zapret сохраняет этот файл. Режим autohostlist включает в себя режим hostlist. Можно вести ipset/zapret-hosts-user.txt, ipset/zapret-hosts-user-exclude.txt.

Проверка провайдера

Перед настройкой нужно провести исследование какую бяку устроил вам ваш провайдер.

Нужно выяснить не подменяет ли он DNS и какой метод обхода DPI работает. В этом вам поможет скрипт blockcheck.sh.

Если DNS подменяется, но провайдер не перехватывает обращения к сторонним DNS, поменяйте DNS на публичный. Например: 8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1, 9.9.9.9 Если DNS подменяется и провайдер перехватывает обращения к сторонним DNS, настройте dnscrypt. Еще один эффективный вариант - использовать ресолвер от yandex 77.88.8.88 на нестандартном порту 1253. Многие провайдеры не анализируют обращения к DNS на нестандартных портах. blockcheck если видит подмену DNS автоматически переключается на DoH сервера.

Следует прогнать blockcheck по нескольким заблокированным сайтам и выявить общий характер блокировок. Разные сайты могут быть заблокированы по-разному, нужно искать такую технику, которая работает на большинстве. Чтобы записать вывод blockcheck.sh в файл, выполните: ./blockcheck.sh | tee /tmp/blockcheck.txt.

Проанализируйте какие методы дурения DPI работают, в соответствии с ними настройте /opt/zapret/config.

Имейте в виду, что у провайдеров может быть несколько DPI или запросы могут идти через разные каналы по методу балансировки нагрузки. Балансировка может означать, что на разных ветках разные DPI или они находятся на разных хопах. Такая ситуация может выражаться в нестабильности работы обхода. Дернули несколько раз curl. То работает, то connection reset или редирект. blockcheck.sh выдает странноватые результаты. То split работает на 2-м. хопе, то на 4-м. Достоверность результата вызывает сомнения. В этом случае задайте несколько повторов одного и того же теста. Тест будет считаться успешным только, если все попытки пройдут успешно.

При использовании autottl следует протестировать как можно больше разных доменов. Эта техника может на одних провайдерах работать стабильно, на других потребуется выяснить при каких параметрах она стабильна, на третьих полный хаос, и проще отказаться.

Blockcheck имеет 3 уровня сканирования.

Есть ряд других параметров, которые не будут спрашиваться в диалоге, но которые можно переопределить через переменные.

CURL - замена программы curl
CURL_MAX_TIME - время таймаута curl в секундах
CURL_MAX_TIME_QUIC - время таймаута curl для quic. если не задано, используется значение CURL_MAX_TIME
CURL_MAX_TIME_DOH - время таймаута curl для DoH серверов
CURL_CMD=1 - показывать команды curl
CURL_OPT - дополнительные параметры curl. `-k` - игнор сертификатов. `-v` - подробный вывод протокола
CURL_HTTPS_GET=1 - использовать метод GET вместо HEAD для https
DOMAINS - список тестируемых доменов через пробел
IPVS=4|6|46 - тестируемые версии ip протокола
ENABLE_HTTP=0|1 - включить тест plain http
ENABLE_HTTPS_TLS12=0|1 - включить тест https TLS 1.2
ENABLE_HTTPS_TLS13=0|1 - включить тест https TLS 1.3
ENABLE_HTTP3=0|1 - включить тест QUIC
REPEATS - количество попыток тестирования
PARALLEL=0|1 - включить параллельные попытки. может обидеть сайт из-за долбежки и привести к неверному результату
SCANLEVEL=quick|standard|force - уровень сканирования
BATCH=1 - пакетный режим без вопросов и ожидания ввода в консоли
HTTP_PORT, HTTPS_PORT, QUIC_PORT - номера портов для соответствующих протоколов
SKIP_DNSCHECK=1 - отказ от проверки DNS
SKIP_IPBLOCK=1 - отказ от тестов блокировки по порту или IP
SKIP_TPWS=1 - отказ от тестов tpws
SKIP_PKTWS=1 - отказ от тестов nfqws/dvtws/winws
PKTWS_EXTRA, TPWS_EXTRA - дополнительные параметры nfqws/dvtws/winws и tpws, указываемые после основной стратегии
PKTWS_EXTRA_1 .. PKTWS_EXTRA_9, TPWS_EXTRA_1 .. TPWS_EXTRA_9 - отдельно дополнительные параметры, содержащие пробелы
PKTWS_EXTRA_PRE - дополнительные параметры для nfqws/dvtws/winws, указываемые перед основной стратегией
PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 - отдельно дополнительные параметры, содержащие пробелы
SECURE_DNS=0|1 - принудительно выключить или включить DoH
DOH_SERVERS - список URL DoH через пробел для автоматического выбора работающего сервера
DOH_SERVER - конкретный DoH URL, отказ от поиска
UNBLOCKED_DOM - незаблокированный домен, который используется для тестов IP block
MIN_TTL,MAX_TTL - пределы тестов с TTL. MAX_TTL=0 отключает тесты.
MIN_AUTOTTL_DELTA,MAX_AUTOTTL_DELTA - пределы тестов с autottl по дельте. MAX_AUTOTTL_DELTA=0 отключает тесты.
SIMULATE=1 - включить режим симуляции для отладки логики скрипта. отключаются реальные запросы через curl, заменяются рандомным результатом.
SIM_SUCCESS_RATE=<percent> - вероятность успеха симуляции в процентах

Пример запуска с переменными:
SECURE_DNS=1 SKIP_TPWS=1 CURL_MAX_TIME=1 CURL=/tmp/curl ./blockcheck.sh

СКАН ПОРТОВ
Если в системе присутствует совместимый netcat (ncat от nmap или openbsd ncat. в OpenWrt по умолчанию нет), то выполняется сканирование портов http или https всех IP адресов домена. Если ни один IP не отвечает, то результат очевиден. Можно останавливать сканирование. Автоматически оно не остановится, потому что netcat-ы недостаточно подробно информируют о причинах ошибки. Если доступна только часть IP, то можно ожидать хаотичных сбоев, т.к. подключение идет к случайному адресу из списка.

ПРОВЕРКА НА ЧАСТИЧНЫЙ IP block
Под частичным блоком подразумевается ситуация, когда коннект на порты есть, но по определенному транспортному или прикладному протоколу всегда идет реакция DPI вне зависимости от запрашиваемого домена. Эта проверка так же не выдаст автоматического вердикта/решения, потому что может быть очень много вариаций. Вместо этого анализ происходящего возложен на самого пользователя или тех, кто будет читать лог. Суть этой проверки в попытке дернуть неблокированный IP с блокированным доменом и наоборот, анализируя при этом реакцию DPI. Реакция DPI обычно проявляется в виде таймаута (зависание запроса), connection reset или http redirect на заглушку. Любой другой вариант скорее всего говорит об отсутствии реакции DPI. В частности, любые http коды, кроме редиректа, ведущего именно на заглушку, а не куда-то еще. На TLS - ошибки handshake без задержек. Ошибка сертификата может говорить как о реакции DPI с MiTM атакой (подмена сертификата), так и о том, что принимающий сервер неблокированного домена все равно принимает ваш TLS handshake с чужим доменом, пытаясь при этом выдать сертификат без запрошенного домена. Требуется дополнительный анализ. Если на заблокированный домен есть реакция на всех IP адресах, значит есть блокировка по домену. Если на неблокированный домен есть реакция на IP адресах блокированного домена, значит имеет место блок по IP. Соответственно, если есть и то, и другое, значит есть и блок по IP, и блок по домену. Неблокированный домен первым делом проверяется на доступность на оригинальном адресе. При недоступности тест отменяется, поскольку он будет неинформативен.

Если выяснено, что есть частичный блок по IP на DPI, то скорее всего все остальные тесты будут провалены вне зависимости от стратегий обхода. Но бывают и некоторые исключения. Например, пробитие через ipv6 option headers. Или сделать так, чтобы он не мог распознать протокол прикладного уровня. Дальнейшие тесты могут быть не лишены смысла.

ПРИМЕРЫ БЛОКИРОВКИ ТОЛЬКО ПО ДОМЕНУ БЕЗ БЛОКА ПО IP

> testing iana.org on it's original
!!!!! AVAILABLE !!!!!
> testing rutracker.org on 192.0.43.8 (iana.org)
curl: (28) Operation timed out after 1002 milliseconds with 0 bytes received
> testing iana.org on 172.67.182.196 (rutracker.org)
HTTP/1.1 409 Conflict
> testing iana.org on 104.21.32.39 (rutracker.org)
HTTP/1.1 409 Conflict

> testing iana.org on it's original ip
!!!!! AVAILABLE !!!!!
> testing rutracker.org on 192.0.43.8 (iana.org)
curl: (28) Connection timed out after 1001 milliseconds
> testing iana.org on 172.67.182.196 (rutracker.org)
curl: (35) OpenSSL/3.2.1: error:0A000410:SSL routines::ssl/tls alert handshake failure
> testing iana.org on 104.21.32.39 (rutracker.org)
curl: (35) OpenSSL/3.2.1: error:0A000410:SSL routines::ssl/tls alert handshake failure

> testing iana.org on it's original ip
!!!!! AVAILABLE !!!!!
> testing rutracker.org on 192.0.43.8 (iana.org)
HTTP/1.1 307 Temporary Redirect
Location: https://www.gblnet.net/blocked.php
> testing iana.org on 172.67.182.196 (rutracker.org)
HTTP/1.1 409 Conflict
> testing iana.org on 104.21.32.39 (rutracker.org)
HTTP/1.1 409 Conflict

> testing iana.org on it's original ip
!!!!! AVAILABLE !!!!!
> testing rutracker.org on 192.0.43.8 (iana.org)
curl: (35) Recv failure: Connection reset by peer
> testing iana.org on 172.67.182.196 (rutracker.org)
curl: (35) OpenSSL/3.2.1: error:0A000410:SSL routines::ssl/tls alert handshake failure
> testing iana.org on 104.21.32.39 (rutracker.org)
curl: (35) OpenSSL/3.2.1: error:0A000410:SSL routines::ssl/tls alert handshake failure

ПРИМЕР ПОЛНОГО IP БЛОКА ИЛИ БЛОКА TCP ПОРТА ПРИ ОТСУТСТВИИ БЛОКА ПО ДОМЕНУ

* port block tests ipv4 startmail.com:80
  ncat -z -w 1 145.131.90.136 80
  145.131.90.136 does not connect. netcat code 1
  ncat -z -w 1 145.131.90.152 80
  145.131.90.152 does not connect. netcat code 1

* curl_test_http ipv4 startmail.com
- checking without DPI bypass
  curl: (28) Connection timed out after 2002 milliseconds
  UNAVAILABLE code=28

- IP block tests (requires manual interpretation)

> testing iana.org on it's original ip
!!!!! AVAILABLE !!!!!
> testing startmail.com on 192.0.43.8 (iana.org)
HTTP/1.1 302 Found
Location: https://www.iana.org/
> testing iana.org on 145.131.90.136 (startmail.com)
curl: (28) Connection timed out after 2002 milliseconds
> testing iana.org on 145.131.90.152 (startmail.com)
curl: (28) Connection timed out after 2002 milliseconds

Выбор параметров

Файл /opt/zapret/config используется различными компонентами системы и содержит основные настройки. Его нужно просмотреть и при необходимости отредактировать.

На linux системах можно выбрать использовать iptables или nftables. По умолчанию на традиционных linux выбирается nftables, если установлен nft. На OpenWrt по умолчанию выбирается nftables на новых версиях с firewall4.

FWTYPE=iptables

На nftables можно отключить стандартную схему перехвата трафика после NAT и перейти на перехват до NAT. Это сделает невозможным применение некоторых методов дурения на проходящем трафике как в случае с iptables. nfqws начнет получать адреса пакетов из локальной сети и отображать их в логах.

POSTNAT=0

Существует 3 стандартных опции запуска, настраиваемых раздельно и независимо: tpws-socks, tpws, nfqws. Их можно использовать как по отдельности, так и вместе. Например, вам надо сделать комбинацию из методов, доступных только в tpws и только в nfqws. Их можно задействовать вместе. tpws будет прозрачно локализовывать трафик на системе и применять свое дурение, nfqws будет дурить трафик, исходящий с самой системы после обработки на tpws. А можно на эту же систему повесить без параметров socks proxy, чтобы получать доступ к обходу блокировок через прокси. Таким образом, все 3 режима вполне могут задействоваться вместе. Так же безусловно и независимо, в добавок к стандартным опциям, применяются все custom скрипты в init.d/{sysv,openwrt,macos}/custom.d.

Однако, при комбинировании tpws и nfqws с пересечением по L3/L4 протоколам не все так просто , как может показаться на первый взгляд. Первым всегда работает tpws, за ним - nfqws. На nfqws попадает уже "задуренный" трафик от tpws. Получается, что дурилка дурит дурилку, и дурилка не срабатывает, потому что ее задурили. Вот такой веселый момент. nfqws перестает распознавать протоколы и применять методы. Некоторые методы дурения от tpws nfqws в состоянии распознать и отработать корректно, но большинство - нет. Решение - использование --dpi-desync-any-protocol в nfqws и работа как с неизвестным протоколом. Комбинирование tpws и nfqws является продвинутым вариантом, требующим глубокого понимания происходящего. Очень желательно проанализировать действия nfqws по --debug логу. Все ли так, как вы задумали.

Одновременное использование tpws и nfqws без пересечения по L3/L4 (то есть nfqws - udp, tpws - tcp или nfqws - port 443, tpws - port 80 или nfqws - ipv4, tpws - ipv6) проблем не представляет.

tpws-socks требует настройки параметров tpws, но не требует перехвата трафика. Остальные опции требуют раздельно настройки перехвата трафика и опции самих демонов. Каждая опция предполагает запуск одного инстанса соответствующего демона. Все различия методов дурения для http, https, quic и т.д. должны быть отражены через схему мультистратегий. В этом смысле настройка похожа на вариант winws на Windows, а перенос конфигов не должен представлять больших сложностей.

Основное правило настройки перехвата - перехватывайте только необходимый минимум. Любой перехват лишнего - это бессмысленная нагрузка на вашу систему. Опции демонов --ipset использовать нужно с умом. Не стоит перехватывать весь трафик, чтобы потом по параметру --ipset выделить лишь горстку IP. Это будет работать, но очень неэффективно с точки зрения нагрузки на систему. Используйте ipset-ы режима ядра. При необходимости пишите и задействуйте custom scripts. Но если у вас и так идет работа по всем IP, и нужно написать небольшую специализацию по IP, то --ipset вполне уместен.

Настройки демонов можно для удобства писать на нескольких строках, используя двойные или одинарные кавычки. Чтобы задействовать стандартные обновляемые хост-листы из каталога ipset, используйте маркер . Он будет заменен на параметры, соответствующие режиму MODE_FILTER, и будут подставлены реально существующие файлы. Если MODE_FILTER не предполагает стандартного хостлиста, будет заменен на пустую строку. Стандартные хостлисты следует вставлять в финальных стратегиях (стратегиях по умолчанию), закрывающих цепочки по группе параметров фильтра. Таких мест может быть несколько. Не нужно использовать в узких специализациях и в тех профилях, по которым точно не будет проходить трафик с известными протоколами, откуда поддерживается извлечение имени хоста (http, tls, quic). - это вариация, при которой стандартный автолист используется как обычный. То есть на этом профиле не происходит автоматическое добавление заблокированных доменов. Но если на другом профиле что-то будет добавлено, то этот профиль примет изменения автоматически.

Изменение бита mark для предотвращения зацикливания
DESYNC_MARK=0x40000000

Изменение бита mark для пометки пакетов, проходящих по POSTNAT схеме (только nftables)
DESYNC_MARK_POSTNAT=0x20000000

Если раскоментировано, пометка пакетов, которые должны быть обработаны zapret.
#FILTER_MARK=0x10000000

Бит должен быть установлен вашими собственными правилами.

Критерии пометки любые. Например, IP адрес или интерфейс источника. Это ответ на вопрос "как мне сделать, чтобы телик не ходил через zapret или чтобы через него ходил только мой комп".

Включение стандартной опции tpws в режиме socks
TPWS_SOCKS_ENABLE=0

На каком порту будет слушать tpws socks. прослушивается только localhost и LAN
TPPORT_SOCKS=987

Параметры tpws для режима socks

TPWS_SOCKS_OPT="
--filter-tcp=80 --methodeol <HOSTLIST> --new
--filter-tcp=443 --split-pos=1,midsld --disorder <HOSTLIST>"

Включение стандартной опции tpws в прозрачном режиме
TPWS_ENABLE=0

Какие tcp порты следует перенаправлять на tpws
TPWS_PORTS=80,443

Параметры tpws для прозрачного режима

TPWS_OPT="
--filter-tcp=80 --methodeol <HOSTLIST> --new
--filter-tcp=443 --split-pos=1,midsld --disorder <HOSTLIST>"

Включение стандартной опции nfqws
NFQWS_ENABLE=0

Какие tcp и udp порты следует перенаправлять на nfqws с использованием connbytes ограничителя

connbytes позволяет из каждого соединения перенаправить только заданное количество начальных пакетов по каждому направлению - на вход и на выход. Это более эффективная kernel-mode замена параметра nfqws --dpi-desync-cutoff=nX.

NFQWS_PORTS_TCP=80,443
NFQWS_PORTS_UDP=443

Сколько начальных входящих и исходящих пакетов нужно перенаправлять на nfqws по каждому направлению

NFQWS_TCP_PKT_OUT=$((6+$AUTOHOSTLIST_RETRANS_THRESHOLD))
NFQWS_TCP_PKT_IN=3
NFQWS_UDP_PKT_OUT=$((6+$AUTOHOSTLIST_RETRANS_THRESHOLD))
NFQWS_UDP_PKT_IN=0

Задать порты для перенаправления на nfqws без connbytes ограничителя
Есть трафик, исходящий сеанс для которого необходимо перенаправлять весь без ограничителей. Типичное применение - поддержка http keepalives на stateless DPI. Это существенно нагружает процессор. Использовать только если понимаете зачем. Чаще всего это не нужно. Входящий трафик ограничивается по connbytes через

zapret (bol-van/zapret)

Searching Database…

16.3K ·GitHub
这是什么东西?一个不需要第三方服务器的自主式 DPI(深度包检测)绕过工具。它可以帮助绕过对 HTTP(S) 网站的封锁或限速,以及基于特征识别的 TCP 和 UDP 协议封锁,比如对 VPN 的封禁。
那它是一个 VPN 服务,还是在我自己电脑上运行的程序?是你自己运行的东西:它是一套本地工具,不是服务。它的程序会在传输途中修改你的流量,让真正的服务器收到原始请求,而 DPI 看到的却是它不会拦截的内容。
我该怎么把它跑起来?克隆仓库,按照针对 Linux/OpenWrt 或 Windows 的 Quick start 操作(在 Linux 上用 make 构建)。然后用合适的参数运行 nfqws 或 tpws,并通过 iptables 或 nftables 规则把流量重定向过去。
有没有它完全帮不上忙的情况?有:DNS 被替换(这部分很容易单独解决)、按 IP 地址封锁,以及完全按规范完整重组 TCP 连接的过滤器,比如 squid。整个技巧都依赖于 DPI 只对流量做部分解析。
我需要一个特殊的路由器吗,还是我的笔记本就行?笔记本完全可以,但这个项目的主要目标是 OpenWrt 路由器这类低功耗嵌入式设备。它也能在普通 Linux、FreeBSD、OpenBSD 上运行,macOS 部分支持,大部分功能在 Windows 上可用;README 里还讲到了 Android。
对于它自己解决不了的封锁,买一台 VPS 是不是更现实的办法?是的,这正是 README 自己给出的建议:有些封锁针对 IP 段或整个协议,自主绕过是不可能的,而且 README 里还有专门一章讲为什么投资一台 VPS 是值得的。
它真的可以免费使用吗?是的 —— README 强调 zapret 是免费开源的,不过没有写明具体的许可证名称。
这个版本还在开发吗?不在了。这个仓库已进入生命周期终结(EOL)状态:只修复 bug,不再增加新功能,也不接受带新功能的 pull request。活跃开发已转移到 zapret 2。
这个包里包含哪些工具?nfqws,一个数据包修改器和 NFQUEUE 处理器(它的 BSD 版本叫 dvtws);tpws,一个透明代理;还有 ip2net 和 mdig 两个辅助工具。
为什么 README 开头会有防诈骗警告?作者警告说,任何人如果施压让你只能从他的资源下载 zapret,并以版权为由要求下架,那他自己在违反许可证,因为这个项目本来就是免费开源的。
默认设置在任何运营商那里都能直接用吗?不一定。README 说每个运营商的情况都需要单独挑选合适的方案,而且有些方法(比如 badsum 伪造数据包)在某些 NAT、路由器或网卡驱动后面会失效。
Creator
bol-van
Size
19.7 MB
Stars
16.3K
Age
10y
Commits · 90 Days