mirror of
https://github.com/bol-van/zapret.git
synced 2025-04-19 05:22:58 +03:00
Merge branch 'bol-van:master' into master
This commit is contained in:
commit
8555b22e6a
14
docs/bsd.md
14
docs/bsd.md
@ -143,7 +143,7 @@ $ ipfw -q -f flush
|
|||||||
zapret, добавив в параметры `--daemon`. Например так:
|
zapret, добавив в параметры `--daemon`. Например так:
|
||||||
```sh
|
```sh
|
||||||
$ pkill ^dvtws$
|
$ pkill ^dvtws$
|
||||||
$ /opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=split2
|
$ /opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
Для перезапуска фаервола и демонов достаточно будет сделать:
|
Для перезапуска фаервола и демонов достаточно будет сделать:
|
||||||
@ -209,7 +209,7 @@ $ ipfw delete 100
|
|||||||
$ ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0
|
$ ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0
|
||||||
# required for autottl mode only
|
# required for autottl mode only
|
||||||
$ ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted recv em0
|
$ ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted recv em0
|
||||||
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2
|
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Трафик только на таблицу zapret, за исключением таблицы nozapret
|
#### Трафик только на таблицу zapret, за исключением таблицы nozapret
|
||||||
@ -220,7 +220,7 @@ $ ipfw add 100 allow tcp from me to table\(nozapret\) 80,443
|
|||||||
$ ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0
|
$ ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0
|
||||||
# required for autottl mode only
|
# required for autottl mode only
|
||||||
$ ipfw add 100 divert 989 tcp from table\(zapret\) 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0
|
$ ipfw add 100 divert 989 tcp from table\(zapret\) 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0
|
||||||
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2
|
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ sysctl net.inet6.ip6.pfil.inbound=ipfw,pf
|
|||||||
ipfw delete 100
|
ipfw delete 100
|
||||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0
|
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0
|
||||||
pkill ^dvtws$
|
pkill ^dvtws$
|
||||||
dvtws --daemon --port 989 --dpi-desync=split2
|
dvtws --daemon --port 989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
|
|
||||||
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state
|
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state
|
||||||
pfctl -d ; pfctl -e
|
pfctl -d ; pfctl -e
|
||||||
@ -357,7 +357,7 @@ rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::20c:29ff:5ae3:4821 por
|
|||||||
```sh
|
```sh
|
||||||
$ pfctl -a zapret -f /etc/zapret.anchor
|
$ pfctl -a zapret -f /etc/zapret.anchor
|
||||||
$ pkill ^tpws$
|
$ pkill ^tpws$
|
||||||
$ tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-http-req=method --split-pos=2
|
$ tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
4. После перезагрузки проверьте, что правила создались:
|
4. После перезагрузки проверьте, что правила создались:
|
||||||
@ -424,7 +424,7 @@ pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no sta
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ pfctl -f /etc/pf.conf
|
$ pfctl -f /etc/pf.conf
|
||||||
$ ./dvtws --port=989 --dpi-desync=split2
|
$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Трафик только на таблицу zapret, за исключением таблицы nozapret
|
#### Трафик только на таблицу zapret, за исключением таблицы nozapret
|
||||||
@ -456,7 +456,7 @@ pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-p
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ pfctl -f /etc/pf.conf
|
$ pfctl -f /etc/pf.conf
|
||||||
$ ./dvtws --port=989 --dpi-desync=split2
|
$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ pass in quick on em0 proto tcp from port {80,443} flags SA/SA divert-packet por
|
|||||||
pass in quick on em0 proto tcp from port {80,443} no state
|
pass in quick on em0 proto tcp from port {80,443} no state
|
||||||
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state
|
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state
|
||||||
pfctl -f /etc/pf.conf
|
pfctl -f /etc/pf.conf
|
||||||
./dvtws --port=989 --dpi-desync=split2
|
./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2
|
||||||
|
|
||||||
; dvtws with table limitations : to zapret,zapret6 but not to nozapret,nozapret6
|
; dvtws with table limitations : to zapret,zapret6 but not to nozapret,nozapret6
|
||||||
; reload tables : pfctl -f /etc/pf.conf
|
; reload tables : pfctl -f /etc/pf.conf
|
||||||
|
@ -363,3 +363,29 @@ nfqws,tpws: use alternate $ sign for $<config_file>
|
|||||||
repo: binaries removed from repo. git actions binaries build in releases.
|
repo: binaries removed from repo. git actions binaries build in releases.
|
||||||
uninstall_easy.sh: offer to remove dependencies in openwrt
|
uninstall_easy.sh: offer to remove dependencies in openwrt
|
||||||
install_easy.sh: allow to download lists in autohostlist filter mode
|
install_easy.sh: allow to download lists in autohostlist filter mode
|
||||||
|
|
||||||
|
v69:
|
||||||
|
|
||||||
|
nfqws, tpws: multisplit/multidisorder support.
|
||||||
|
nfqws: name change split->fakedsplit, disorder->fakeddisorder. compat : old names are synonyms
|
||||||
|
nfqws: --dpi-desync-split-http-req, --dpi-desync-split-tls deprecated. compat : these parameters add split point to multisplit.
|
||||||
|
nfqws: --dpi-desync=split2|disorder2 deprecated. compat: they are now synonyms for multisplit/multidisorder
|
||||||
|
nfqws: cancel seqovl if MTU is exceeded (linux only). cancel seqovl for disorder if seqovl>=first_part_size.
|
||||||
|
nfqws: fixed splits in multiple TLS segments.
|
||||||
|
tpws: --split-tls,--split-tls deprecated. compat : these parameters add split point to multisplit.
|
||||||
|
tpws: --tlsrec now takes pos markers. compat : old names are converted to pos markers
|
||||||
|
tpws: --tlsrec-pos deprecated. compat : sets absolute pos marker
|
||||||
|
nfqws,tpws: chown autohostlist, autohostlist debug log and debug log files after options parse
|
||||||
|
nfqws,tpws: set EXEDIR env var to use in @config (won't work for stadalone winws without /bin/sh)
|
||||||
|
dvtws: set random/increasing ip_id value in generated packets
|
||||||
|
mdig: fixed parsing of DNS reply in windows (stdin is opened as text, not binary)
|
||||||
|
tpws: support compile for android NDK api level >= 21 (Android 5.0)
|
||||||
|
tpws: --fix-seg segmentation fixer
|
||||||
|
repo: build for android NDK api level 21 (Android 5.0)
|
||||||
|
install_easy: support for APK package manager in openwrt
|
||||||
|
blockcheck: removed ignore CA question
|
||||||
|
blockcheck: removed IGNORE_CA, CURL_VERBOSE
|
||||||
|
blockcheck: added CURL_OPT
|
||||||
|
blockcheck: new strategies support
|
||||||
|
blockcheck: test sequence rework
|
||||||
|
blockcheck: view all working strategies in summary
|
||||||
|
16
docs/compile/build_howto_unix.txt
Normal file
16
docs/compile/build_howto_unix.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
debian,ubuntu :
|
||||||
|
|
||||||
|
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev
|
||||||
|
make -C /opt/zapret
|
||||||
|
|
||||||
|
FreeBSD :
|
||||||
|
|
||||||
|
make -C /opt/zapret
|
||||||
|
|
||||||
|
OpenBSD :
|
||||||
|
|
||||||
|
make -C /opt/zapret bsd
|
||||||
|
|
||||||
|
MacOS :
|
||||||
|
|
||||||
|
make -C /opt/zapret mac
|
29
docs/compile/build_howto_windows.txt
Normal file
29
docs/compile/build_howto_windows.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Windows x64
|
||||||
|
|
||||||
|
1) Download latest cygwin for windows 7
|
||||||
|
|
||||||
|
curl -O https://www.cygwin.com/setup-x86_64.exe
|
||||||
|
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215
|
||||||
|
|
||||||
|
2) During setup install packages : make gcc-core zlib-devel
|
||||||
|
|
||||||
|
3) Run Cygwin.bat
|
||||||
|
|
||||||
|
4) cd to %ZAPRET_BASE%/nfq
|
||||||
|
|
||||||
|
cd C:/Users/user/Downloads/zapret/nfq
|
||||||
|
|
||||||
|
5) Compile
|
||||||
|
|
||||||
|
make cygwin64
|
||||||
|
|
||||||
|
use winws.exe
|
||||||
|
|
||||||
|
6) Take windivert.dll and windivert64.sys here : https://reqrypt.org/download
|
||||||
|
Choose version 2.2.2 for Windows 10 and 2.2.0 for Windows 7.
|
||||||
|
|
||||||
|
7) Copy cygwin1.dll, winws.exe, windivert.dll and windivert64.sys to one folder.
|
||||||
|
|
||||||
|
8) Run winws.exe from cmd.exe running as administrator.
|
||||||
|
winws will not run from cygwin shell with cygwin1.dll copy in it's folder.
|
||||||
|
winws will not run without cygwin1.dll outside of cygwin shell.
|
@ -1,4 +1,4 @@
|
|||||||
# zapret v.68
|
# zapret v.69
|
||||||
|
|
||||||
# SCAMMER WARNING
|
# SCAMMER WARNING
|
||||||
|
|
||||||
|
746
docs/readme.md
746
docs/readme.md
@ -1,4 +1,4 @@
|
|||||||
# zapret v.68
|
# zapret v.69
|
||||||
|
|
||||||
# ВНИМАНИЕ, остерегайтесь мошенников
|
# ВНИМАНИЕ, остерегайтесь мошенников
|
||||||
|
|
||||||
@ -17,12 +17,15 @@ zapret является свободным и open source.
|
|||||||
- [Как это работает](#как-это-работает)
|
- [Как это работает](#как-это-работает)
|
||||||
- [Что сейчас происходит в России](#что-сейчас-происходит-в-россии)
|
- [Что сейчас происходит в России](#что-сейчас-происходит-в-россии)
|
||||||
- [Как это реализовать на практике в системе linux](#как-это-реализовать-на-практике-в-системе-linux)
|
- [Как это реализовать на практике в системе linux](#как-это-реализовать-на-практике-в-системе-linux)
|
||||||
- [Особенности применения ip6tables](#особенности-применения-ip6tables)
|
|
||||||
- [Особенности применения nftables](#особенности-применения-nftables)
|
|
||||||
- [Когда это работать не будет](#когда-это-работать-не-будет)
|
- [Когда это работать не будет](#когда-это-работать-не-будет)
|
||||||
- [nfqws](#nfqws)
|
- [nfqws](#nfqws)
|
||||||
- [АТАКА ДЕСИНХРОНИЗАЦИИ DPI](#атака-десинхронизации-dpi)
|
- [АТАКА ДЕСИНХРОНИЗАЦИИ DPI](#атака-десинхронизации-dpi)
|
||||||
|
- [ФЕЙКИ](#фейки)
|
||||||
|
- [TCP СЕГМЕНТАЦИЯ](#tcp-сегментация)
|
||||||
|
- [ПЕРЕКРЫТИЕ SEQUENCE NUMBERS](#перекрытие-sequence-numbers)
|
||||||
|
- [СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6](#специфические-режимы-ipv6)
|
||||||
- [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации)
|
- [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации)
|
||||||
|
- [РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА](#реакция-dpi-на-ответ-сервера)
|
||||||
- [РЕЖИМ SYNACK](#режим-synack)
|
- [РЕЖИМ SYNACK](#режим-synack)
|
||||||
- [РЕЖИМ SYNDATA](#режим-syndata)
|
- [РЕЖИМ SYNDATA](#режим-syndata)
|
||||||
- [ВИРТУАЛЬНЫЕ МАШИНЫ](#виртуальные-машины)
|
- [ВИРТУАЛЬНЫЕ МАШИНЫ](#виртуальные-машины)
|
||||||
@ -31,8 +34,18 @@ zapret является свободным и open source.
|
|||||||
- [ПОДДЕРЖКА UDP](#поддержка-udp)
|
- [ПОДДЕРЖКА UDP](#поддержка-udp)
|
||||||
- [IP ФРАГМЕНТАЦИЯ](#ip-фрагментация)
|
- [IP ФРАГМЕНТАЦИЯ](#ip-фрагментация)
|
||||||
- [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии)
|
- [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии)
|
||||||
|
- [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws)
|
||||||
|
- [NFTABLES ДЛЯ NFQWS](#nftables-для-nfqws)
|
||||||
|
- [FLOW OFFLOADING](#flow-offloading)
|
||||||
- [tpws](#tpws)
|
- [tpws](#tpws)
|
||||||
|
- [TCP СЕГМЕНТАЦИЯ В TPWS](#tcp-сегментация-в-tpws)
|
||||||
|
- [TLSREC](#tlsrec)
|
||||||
|
- [MSS](#mss)
|
||||||
|
- [ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ](#другие-параметры-дурения)
|
||||||
- [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии-1)
|
- [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии-1)
|
||||||
|
- [СЛУЖЕБНЫЕ ПАРАМЕТРЫ](#служебные-параметры)
|
||||||
|
- [IPTABLES ДЛЯ TPWS](#iptables-для-tpws)
|
||||||
|
- [NFTABLES ДЛЯ TPWS](#nftables-для-tpws)
|
||||||
- [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip)
|
- [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip)
|
||||||
- [ip2net](#ip2net)
|
- [ip2net](#ip2net)
|
||||||
- [mdig](#mdig)
|
- [mdig](#mdig)
|
||||||
@ -126,107 +139,6 @@ DPI. Все больше становится внереестровых бло
|
|||||||
Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно. Чтобы они работали, необходимо их запустить с
|
Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно. Чтобы они работали, необходимо их запустить с
|
||||||
нужными параметрами и перенаправить на них определенный трафик средствами iptables или nftables.
|
нужными параметрами и перенаправить на них определенный трафик средствами iptables или nftables.
|
||||||
|
|
||||||
Для перенаправления tcp соединения на transparent proxy используются команды следующего вида :
|
|
||||||
|
|
||||||
проходящий трафик:
|
|
||||||
`iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988`
|
|
||||||
|
|
||||||
исходящий трафик:
|
|
||||||
`iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -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`, для него задается исключающее правило.
|
|
||||||
|
|
||||||
tpws может использоваться в режиме socks proxy. В этом случае iptables не нужны, а нужно прописать socks в настройки
|
|
||||||
программы (например, броузера), с которой будем обходить блокировки. transparent proxy отличается от socks именно тем,
|
|
||||||
что в варианте transparent настраивать клиентские программы не нужно.
|
|
||||||
|
|
||||||
Для перенаправления на очередь NFQUEUE исходящего и проходящего в сторону внешнего интерфейса трафика используются
|
|
||||||
команды следующего вида :
|
|
||||||
|
|
||||||
`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass`
|
|
||||||
|
|
||||||
Чтобы не трогать трафик на незаблокированные адреса, можно взять список заблокированных хостов, заресолвить его в IP
|
|
||||||
адреса и загнать в ipset zapret, затем добавить фильтр в команду :
|
|
||||||
|
|
||||||
`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass`
|
|
||||||
|
|
||||||
DPI может ловить только первый http запрос, игнорируя последующие запросы в keep-alive сессии. Тогда можем уменьшить
|
|
||||||
нагрузку на проц, отказавшись от процессинга ненужных пакетов.
|
|
||||||
|
|
||||||
`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass`
|
|
||||||
|
|
||||||
Фильтр по mark нужен для отсечения от очереди пакетов, сгенерированных внутри nfqws. Если применяется фильтр по
|
|
||||||
connbytes 1:6, то обязательно добавлять в iptables и фильтр по mark. Иначе возможно перепутывание порядка следования
|
|
||||||
пакетов, что приведет к неработоспособности метода. Так же возможно зависание nfqws по deadlock.
|
|
||||||
|
|
||||||
Для некоторых атак на DPI требуется перенаправлять один или несколько входящих пакетов от соединения :
|
|
||||||
|
|
||||||
`iptables -t mangle -I PREROUTING -i <внешний_интерфейс> -p tcp --sport 80 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:6 -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass`
|
|
||||||
|
|
||||||
Получаемые пакеты будут фильтроваться по входящему интерфейсу, порту и IP источника, то есть наоборот прямому правилу.
|
|
||||||
|
|
||||||
Некоторые техники, ломающие NAT, не всегда можно реализовать через iptables. Требуются nftables.
|
|
||||||
|
|
||||||
Если ваше устройство поддерживает аппаратное ускорение (flow offloading, hardware nat, hardware acceleration), то
|
|
||||||
iptables могут не работать. При включенном offloading пакет не проходит по обычному пути netfilter. Необходимо или его
|
|
||||||
отключить, или выборочно им управлять.
|
|
||||||
|
|
||||||
В новых ядрах (и в более старых, openwrt портировал изменение на 4.14) присутствует software flow offloading (SFO).
|
|
||||||
Пакеты, проходящие через SFO, так же проходят мимо большей части механизмов iptables. При включенном SFO работает
|
|
||||||
DNAT/REDIRECT (tpws). Эти соединения исключаются из offloading. Однако, остальные соединения идут через SFO, потому
|
|
||||||
NFQUEUE будет срабатывать только до помещения соединения в flowtable. Практически это означает, что nfqws будет работать
|
|
||||||
на window size changing, но не будут работать опции по модификации содержимого пакетов. Offload включается через
|
|
||||||
специальный target в iptables `FLOWOFFLOAD`. Не обязательно пропускать весь трафик через offload. Можно исключить из
|
|
||||||
offload соединения, которые должны попасть на tpws или nfqws. openwrt не предусматривает выборочного управления offload.
|
|
||||||
Поэтому скрипты zapret поддерживают свою систему выборочного управления offload в openwrt.
|
|
||||||
|
|
||||||
## Особенности применения ip6tables
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Более подробно преимущества и недостатки nftables применительно к данной системе описаны в [docs/nftables_notes.txt](./nftables_notes.txt) Если
|
|
||||||
коротко, то в nftables невозможно работать с большими ip листами на системах с малым количеством RAM. Остальные
|
|
||||||
рассматриваемые здесь функции могут быть перенесены на nftables.
|
|
||||||
|
|
||||||
Рекомендуется версия nft `1.0.2` или выше. Но чем выше версия, тем лучше. В nft регулярно находят баги.
|
|
||||||
|
|
||||||
Относительно старые версии ядра и/или утилиты nft могут вызывать ошибки. В частности, на ubuntu 18.04 с ядром 4.15 будут
|
|
||||||
проблемы. В 20.04 - работает.
|
|
||||||
|
|
||||||
Некоторые техники можно полноценно использовать только с nftables. Например, в iptables невозможно перенаправить пакеты
|
|
||||||
на nfqws после NAT. Следовательно, при использовании NAT невозможно произвести атаку, не совместимую с NAT. В nftables
|
|
||||||
этой проблемы не существует, потому что приоритеты хука выставляете вы сами, а не привязаны к фиксированным значениям,
|
|
||||||
соответствующим разным таблицам iptables. В iptables нет таблицы, способной перехватить пакеты после MASQUERDADE.
|
|
||||||
|
|
||||||
## Когда это работать не будет
|
## Когда это работать не будет
|
||||||
|
|
||||||
* Если подменяется DNS. С этой проблемой легко справиться.
|
* Если подменяется DNS. С этой проблемой легко справиться.
|
||||||
@ -265,7 +177,7 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
|
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
|
||||||
--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
|
--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
|
||||||
--domcase ; домен после Host: сделать таким : TeSt.cOm
|
--domcase ; домен после Host: сделать таким : TeSt.cOm
|
||||||
--dpi-desync=[<mode0>,]<mode>[,<mode2] ; атака по десинхронизации DPI. mode : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen tamper
|
--dpi-desync=[<mode0>,]<mode>[,<mode2] ; атака по десинхронизации DPI. mode : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper
|
||||||
--dpi-desync-fwmark=<int|0xHEX> ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000
|
--dpi-desync-fwmark=<int|0xHEX> ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000
|
||||||
--dpi-desync-ttl=<int> ; установить ttl для десинхронизирующих пакетов
|
--dpi-desync-ttl=<int> ; установить ttl для десинхронизирующих пакетов
|
||||||
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl
|
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl
|
||||||
@ -274,10 +186,8 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
--dpi-desync-fooling=<fooling> ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
|
--dpi-desync-fooling=<fooling> ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
|
||||||
--dpi-desync-repeats=<N> ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
|
--dpi-desync-repeats=<N> ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
|
||||||
--dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
|
--dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
|
||||||
--dpi-desync-split-pos=<1..1500> ; (только для split*, disorder*) разбивать пакет на указанной позиции
|
--dpi-desync-split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации в режимах split и disorder
|
||||||
--dpi-desync-split-http-req=method|host ; разбивка http request на указанном логическом месте
|
--dpi-desync-split-seqovl=N|-N|marker+N|marker-N ; единичный маркер, определяющий величину перекрытия sequence в режимах split и disorder. для split поддерживается только положительное число.
|
||||||
--dpi-desync-split-tls=sni|sniext ; разбивка tls client hello на указанном логическом месте
|
|
||||||
--dpi-desync-split-seqovl=<int> ; использовать sequence overlap перед первым отсылаемым оригинальным tcp сегментом
|
|
||||||
--dpi-desync-split-seqovl-pattern=<filename>|0xHEX ; чем заполнять фейковую часть overlap
|
--dpi-desync-split-seqovl-pattern=<filename>|0xHEX ; чем заполнять фейковую часть overlap
|
||||||
--dpi-desync-badseq-increment=<int|0xHEX> ; инкремент sequence number для badseq. по умолчанию -10000
|
--dpi-desync-badseq-increment=<int|0xHEX> ; инкремент sequence number для badseq. по умолчанию -10000
|
||||||
--dpi-desync-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
|
--dpi-desync-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
|
||||||
@ -315,12 +225,6 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
|
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
|
||||||
```
|
```
|
||||||
|
|
||||||
Параметры манипуляции могут сочетаться в любых комбинациях.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> **ЗАМЕЧАНИЕ.** Параметр `--wsize` считается устаревшим и более не поддерживается в скриптах. Функции сплита выполняются в
|
|
||||||
> рамках атаки десинхронизации. Это быстрее и избавляет от целого ряда недостатков wsize.
|
|
||||||
|
|
||||||
`--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования
|
`--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования
|
||||||
опций. `--debug` лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при
|
опций. `--debug` лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при
|
||||||
проверке опции, а до анализа `--debug` еще дело не дошло, то сообщения не будут выведены в файл или syslog. При
|
проверке опции, а до анализа `--debug` еще дело не дошло, то сообщения не будут выведены в файл или syslog. При
|
||||||
@ -332,18 +236,24 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
|
|
||||||
### АТАКА ДЕСИНХРОНИЗАЦИИ DPI
|
### АТАКА ДЕСИНХРОНИЗАЦИИ DPI
|
||||||
|
|
||||||
Суть ее в следующем. После выполнения tcp 3-way handshake идет первый пакет с данными от клиента. Там обычно `GET / ...`
|
Суть ее в следующем. Берется оригинальный запрос, модифицируется, добавляется поддельная информация (фейки)
|
||||||
или TLS ClientHello. Мы дропаем этот пакет, заменяя чем-то другим. Это может быть поддельная версия с безобидным, но
|
таким образом, чтобы ОС сервера передала серверному процессу оригинальный запрос в неизменном виде, а DPI увидел другое.
|
||||||
валидным запросом http или https (вариант `fake`), пакет сброса соединения (варианты `rst`, `rstack`), разбитый на части
|
То, что он блокировать не станет. Сервер видит одно, DPI - другое. DPI не понимает, что передается запрещенный запрос и не блокирует его.
|
||||||
оригинальный пакет с перепутанным порядком следования сегментов + обрамление первого сегмента фейками (`disorder`), то
|
|
||||||
же самое без перепутывания порядка сегментов (`split`). fakeknown отличается от fake тем, что применяется только к
|
|
||||||
распознанному протоколу. В литературе такие атаки еще называют **TCB desynchronization** и **TCB teardown**. Надо, чтобы
|
|
||||||
фейковые пакеты дошли до DPI, но не дошли до сервера. На вооружении есть следующие возможности : установить низкий TTL,
|
|
||||||
посылать пакет с инвалидной чексуммой, добавлять tcp option **MD5 signature**, испортить sequence numbers. Все они не
|
|
||||||
лишены недостатков.
|
|
||||||
|
|
||||||
* `md5sig` работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux.
|
Есть арсенал возможностей, чтобы достичь такого результата.
|
||||||
* `badsum` не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее
|
Это может быть передача фейк пакетов, чтобы они дошли до DPI, но не дошли до сервера. Может использоваться фрагментация на уровне TCP (сегментация) или на уровне IP.
|
||||||
|
Есть атаки, основанные на игре с tcp sequence numbers или с перепутыванием порядка следования tcp сегментов.
|
||||||
|
Методы могут сочетаться в различных вариантах.
|
||||||
|
|
||||||
|
### ФЕЙКИ
|
||||||
|
|
||||||
|
Фейки - это отдельные сгенерированные nfqws пакеты, несущие ложную информацию для DPI.
|
||||||
|
Они либо не должны дойти до сервера, либо могут дойти, но должны быть им отброшены.
|
||||||
|
Иначе получается слом tcp соединения или нарушение целостности передаваемого потока, что гарантированно приводит к поломке ресурса.
|
||||||
|
Есть ряд методов для решения этой задачи.
|
||||||
|
|
||||||
|
* `md5sig` добавляет TCP опцию **MD5 signature**. Работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux.
|
||||||
|
* `badsum` портит контрольную сумму TCP. Не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее
|
||||||
распространенная настройка NAT роутера в Linux их не пропускает. На Linux построено большинство домашних роутеров.
|
распространенная настройка NAT роутера в Linux их не пропускает. На Linux построено большинство домашних роутеров.
|
||||||
Непропускание обеспечивается так : настройка ядра sysctl по умолчанию
|
Непропускание обеспечивается так : настройка ядра sysctl по умолчанию
|
||||||
`net.netfilter.nf_conntrack_checksum=1` заставляет conntrack проверять tcp и udp чексуммы входящих пакетов и
|
`net.netfilter.nf_conntrack_checksum=1` заставляет conntrack проверять tcp и udp чексуммы входящих пакетов и
|
||||||
@ -359,7 +269,8 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
себя ведут некоторые роутеры на базе mediatek. badsum пакеты уходят с клиентской ОС, но роутером не видятся в br-lan
|
себя ведут некоторые роутеры на базе mediatek. badsum пакеты уходят с клиентской ОС, но роутером не видятся в br-lan
|
||||||
через tcpdump. При этом если nfqws выполняется на самом роутере, обход может работать. badsum нормально уходят с
|
через tcpdump. При этом если nfqws выполняется на самом роутере, обход может работать. badsum нормально уходят с
|
||||||
внешнего интерфейса.
|
внешнего интерфейса.
|
||||||
* Пакеты с `badseq` будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence
|
* `badseq` увеличивает TCP sequence number на определенное значение, выводя его тем самым из TCP window.
|
||||||
|
Такие пакеты будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence
|
||||||
numbers. По умолчанию смещение seq выбирается -10000. Практика показала, что некоторые DPI не пропускают seq вне
|
numbers. По умолчанию смещение seq выбирается -10000. Практика показала, что некоторые DPI не пропускают seq вне
|
||||||
определенного окна. Однако, такое небольшое смещение может вызвать проблемы при существенной потоковой передаче и
|
определенного окна. Однако, такое небольшое смещение может вызвать проблемы при существенной потоковой передаче и
|
||||||
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобится установить badseq increment
|
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобится установить badseq increment
|
||||||
@ -383,7 +294,8 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
может ломать NAT и не всегда работает с iptables, если используется masquerade, даже с локальной системы (почти всегда
|
может ломать NAT и не всегда работает с iptables, если используется masquerade, даже с локальной системы (почти всегда
|
||||||
на роутерах ipv4). На системах c iptables без masquerade и на nftables работает без ограничений. Экспериментально
|
на роутерах ipv4). На системах c iptables без masquerade и на nftables работает без ограничений. Экспериментально
|
||||||
выяснено, что многие провайдерские NAT не отбрасывают эти пакеты, потому работает даже с внутренним провайдерским IP.
|
выяснено, что многие провайдерские NAT не отбрасывают эти пакеты, потому работает даже с внутренним провайдерским IP.
|
||||||
Но linux NAT оно не пройдет, так что за домашним роутером эта техника не сработает, но может сработать с него.
|
Но linux NAT оно не пройдет, так что за домашним роутером эта техника скорее всего не сработает, но может сработать с него.
|
||||||
|
Может сработать и через роутер, если подключение по проводу, и на роутере включено аппаратное ускорение.
|
||||||
* `autottl`. Суть режима в автоматическом определении TTL, чтобы он почти наверняка прошел DPI и немного не дошел до
|
* `autottl`. Суть режима в автоматическом определении TTL, чтобы он почти наверняка прошел DPI и немного не дошел до
|
||||||
сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет
|
сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет
|
||||||
(да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается `delta` (1 по
|
(да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается `delta` (1 по
|
||||||
@ -396,44 +308,64 @@ dvtws, собираемый из тех же исходников (см. [док
|
|||||||
|
|
||||||
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
|
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
|
||||||
|
|
||||||
Для режимов fake, rst, rstack после фейка отправляем оригинальный пакет.
|
### TCP СЕГМЕНТАЦИЯ
|
||||||
|
|
||||||
Режим disorder делит оригинальный пакет на 2 части и отправляет следующую комбинацию в указанном порядке :
|
* `multisplit`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях.
|
||||||
|
* `multidisorder`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях и отправляем в обратном порядке.
|
||||||
|
* `fakedsplit`. нарезаем запрос на 2 части, обрамляя его фейками : фейк 1-й части, 1 часть, фейк 1-й части, 2 часть
|
||||||
|
* `fakeddisorder`. нарезаем запрос на 2 части, обрамляя его фейками : 2 часть, фейк 1-й части, 1 часть, фейк 1 части.
|
||||||
|
|
||||||
1. 2-я часть пакета
|
Для определения позиций нарезки используются маркеры.
|
||||||
2. поддельная 1-я часть пакета, поле данных заполнено нулями
|
|
||||||
3. 1-я часть пакета
|
|
||||||
4. поддельная 1-я часть пакета, поле данных заполнено нулями. отсылка 2-й раз. Оригинальный пакет дропается всегда.
|
|
||||||
|
|
||||||
Параметр `--dpi-desync-split-pos` позволяет указать байтовую позицию, на которой происходит разбивка. По умолчанию - 2.
|
* **Абсолютный положительный маркер** - числовое смещение внутри пакета или группы пакетов от начала.
|
||||||
Если позиция больше длины пакета, позиция выбирается 1. Этой последовательностью для DPI максимально усложняется задача
|
* **Абсолютный отрицательный маркер** - числовое смещение внутри пакета или группы пакетов от следующего за концом байта. -1 указывает на последний байт.
|
||||||
реконструкции начального сообщения, по которому принимается решение о блокировке. Некоторым DPI хватит и tcp сегментов в
|
* **Относительный маркер** - положительное или отрицательное смещение относительно логической позиции внутри пакета или группы пакетов.
|
||||||
неправильном порядке, поддельные части сделаны для дополнительной надежности и более сложных алгоритмов реконструкции.
|
|
||||||
Режим `disorder2` отключает отправку поддельных частей.
|
|
||||||
|
|
||||||
Режим `split` очень похож на disorder, только нет изменения порядка следования сегментов :
|
Относительные позиции :
|
||||||
|
|
||||||
1. поддельная 1-я часть пакета, поле данных заполнено нулями
|
* **method** - начало метода HTTP ('GET', 'POST', 'HEAD', ...). Метод обычно всегда находится на позиции 0, но поддерживается и нахождение метода после дурение методом `--methodeol` от tpws. Тогда позиция может стать 1 или 2.
|
||||||
2. 1-я часть пакета
|
* **host** - начало имени хоста в известном протоколе (http, TLS)
|
||||||
3. поддельная 1-я часть пакета, поле данных заполнено нулями. отсылка 2-й раз.
|
* **endhost** - конец имени хоста
|
||||||
4. 2-я часть пакета Режим split2 отключает отправку поддельных частей. Он может быть использован как более быстрая
|
* **sld** - начало домена 2 уровня в имени хоста
|
||||||
альтернатива --wsize.
|
* **endsld** - конец домена 2 уровня в имени хоста
|
||||||
|
* **midsld** - середина домена 2 уровня в имени хоста
|
||||||
|
* **sniext** - начало поля данных SNI extension в TLS. Любой extension состоит из 2-байтовых полей type и length, за ними идет поле данных.
|
||||||
|
|
||||||
`disorder2` и `split2` не предполагают отсылку фейк пакетов, поэтому опции ttl и fooling неактуальны.
|
Пример списка маркеров : `100,midsld,sniext+1,endhost-2,-10`.
|
||||||
|
|
||||||
`seqovl` добавляет в начало первой отсылаемой части оригинального пакета (1 часть для split и 2 часть для disorder)
|
При разбиении пакета первым делом происходит ресолвинг маркеров - нахождение всех указанных относительных позиций и применение смещений.
|
||||||
`seqovl` байт со смещенным в минус sequence number на величину seqovl. В случае `split2` расчет идет на то, что предыдущий
|
Если относительная позиция отсутствует в текущем протоколе, такие позиции не применяются и отбрасываются.
|
||||||
отсыл, если он был, уже попал в сокет серверного приложения, поэтому новая пришедшая часть лишь частично находится в
|
Дальше происходит нормализация позиций относительно смещения текущего пакета в группе пакетов (многопакетные запросы 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). Спереди фейковая часть отбрасывается, а оставшаяся часть содержит оригинал и
|
пределах текущего окна (in-window). Спереди фейковая часть отбрасывается, а оставшаяся часть содержит оригинал и
|
||||||
начинается с начала window, поэтому попадает в сокет. Серверное приложение получает все, что реально отсылает клиент,
|
начинается с начала window, поэтому попадает в сокет. Серверное приложение получает все, что реально отсылает клиент,
|
||||||
отбрасывая фейковую out-of-window часть. Но DPI не может этого понять, поэтому у него происходит sequence
|
отбрасывая фейковую out-of-window часть. Но DPI не может этого понять, поэтому у него происходит sequence десинхронизация.
|
||||||
десинхронизация.
|
Обязательно, чтобы первый сегмент вместе с `seqovl` не превысили длину MTU. Эта ситуация распознается автоматически в Linux, и `seqovl` отменяется.
|
||||||
|
В остальных системах ситуация не распознается, и это приведет к поломке соединения. Поэтому выбирайте первую позицию сплита и `seqovl` таким образом, чтобы MTU не был превышен в любом случае.
|
||||||
|
Иначе дурение может не работать или работать хаотично.
|
||||||
|
|
||||||
Для `disorder2` overlap идет на 2-ю часть пакета. Обязательно, чтобы `seqovl` был меньше `split_pos`, иначе
|
Для `disorder` overlap идет на предпоследнюю отсылаемую часть пакета.
|
||||||
все отосланное будет передано в сокет сразу же, включая фейк, ломая протокол прикладного уровня.
|
Для простоты будем считать, что разбиение идет на 2 части, шлются они в порядке "2 1" при оригинальном порядке "1 2".
|
||||||
При соблюдении этого условия 2-я часть пакета является полностью in-window,
|
Обязательно, чтобы `seqovl` был меньше позиции первого сплита, иначе все отосланное будет передано в сокет сразу же, включая фейк, ломая протокол прикладного уровня.
|
||||||
поэтому серверная ОС принимает ее целиком, включая фейк. Но поскольку начальная часть данных из 1 пакета
|
Такая ситуация легко обнаруживается программой, и `seqovl` отменяется. Увеличение размера пакета невозможно в принципе.
|
||||||
еще не принята, то фейк и реальные данные остаются в памяти ядра, не отправляясь в серверное приложение.
|
При соблюдении условия 2-я часть пакета является полностью in-window, поэтому серверная ОС принимает ее целиком, включая фейк.
|
||||||
|
Но поскольку начальная часть данных из 1 пакета еще не принята, то фейк и реальные данные остаются в памяти ядра, не отправляясь в серверное приложение.
|
||||||
Как только приходит 1-я часть пакета, она переписывает фейковую часть в памяти ядра.
|
Как только приходит 1-я часть пакета, она переписывает фейковую часть в памяти ядра.
|
||||||
Ядро получает данные из 1 и 2 части, поэтому далее идет отправка в сокет приложения.
|
Ядро получает данные из 1 и 2 части, поэтому далее идет отправка в сокет приложения.
|
||||||
Таково поведение всех unix ОС, кроме solaris - оставлять последние принятые данные.
|
Таково поведение всех unix ОС, кроме solaris - оставлять последние принятые данные.
|
||||||
@ -441,7 +373,13 @@ Windows оставляет старые данные, поэтому disorder с
|
|||||||
при работе с Windows серверами. Solaris практически мертв, windows серверов очень немного.
|
при работе с Windows серверами. Solaris практически мертв, windows серверов очень немного.
|
||||||
Можно использовать листы при необходимости.
|
Можно использовать листы при необходимости.
|
||||||
Метод позволяет обойтись без fooling и TTL. Фейки перемешаны с реальным данными.
|
Метод позволяет обойтись без fooling и TTL. Фейки перемешаны с реальным данными.
|
||||||
`split/disorder` вместо `split2/disorder2` по-прежнему добавляют дополнительные отдельные фейки.
|
`fakedsplit/fakeddisorder` по-прежнему добавляют дополнительные отдельные фейки.
|
||||||
|
|
||||||
|
`seqovl` в варианте `split` может быть только абсолютным положительным значением, поскольку применяется только в первому пакету.
|
||||||
|
В варианте `disorder` допустимо применение всех вариантов маркеров.
|
||||||
|
Они автоматически нормализуются к текущему пакету в серии. Можно сплитать на `midsld` и делать seqovl на `midsld-1`.
|
||||||
|
|
||||||
|
### СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6
|
||||||
|
|
||||||
Режимы десинхронизации `hopbyhop`, `destopt` и `ipfrag1` (не путать с fooling !) относятся только к `ipv6` и заключается
|
Режимы десинхронизации `hopbyhop`, `destopt` и `ipfrag1` (не путать с fooling !) относятся только к `ipv6` и заключается
|
||||||
в добавлении хедера `hop-by-hop options`, `destination options` или `fragment` во все пакеты, попадающие под десинхронизацию.
|
в добавлении хедера `hop-by-hop options`, `destination options` или `fragment` во все пакеты, попадающие под десинхронизацию.
|
||||||
@ -452,10 +390,22 @@ Windows оставляет старые данные, поэтому disorder с
|
|||||||
extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет
|
extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет
|
||||||
без анализа. Возможно, какие-то DPI на это купятся.
|
без анализа. Возможно, какие-то DPI на это купятся.
|
||||||
Может сочетаться с любыми режимами 2-й фазы, кроме варианта `ipfrag1+ipfrag2`.
|
Может сочетаться с любыми режимами 2-й фазы, кроме варианта `ipfrag1+ipfrag2`.
|
||||||
Например, `hopbyhop,split2` означает разбить tcp пакет на 2 сегмента, в каждый из них добавить hop-by-hop.
|
Например, `hopbyhop,multisplit` означает разбить tcp пакет на несколько сегментов, в каждый из них добавить hop-by-hop.
|
||||||
При `hopbyhop,ipfrag2` последовательность хедеров будет : `ipv6,hop-by-hop`,`fragment`,`tcp/udp`.
|
При `hopbyhop,ipfrag2` последовательность хедеров будет : `ipv6,hop-by-hop`,`fragment`,`tcp/udp`.
|
||||||
Режим `ipfrag1` может срабатывать не всегда без специальной подготовки. См. раздел `IP фрагментация`.
|
Режим `ipfrag1` может срабатывать не всегда без специальной подготовки. См. раздел `IP фрагментация`.
|
||||||
|
|
||||||
|
### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ
|
||||||
|
|
||||||
|
В параметре dpi-desync можно указать до 3 режимов через запятую.
|
||||||
|
|
||||||
|
* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata` `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](((#множественные-стратегии))).
|
||||||
|
* 1 фаза - отсылка чего-либо до оригинального пакета данных : `fake`, `rst`, `rstack`.
|
||||||
|
* 2 фаза - отсылка в модифицированном виде оригинального пакета данных (например, `fakedsplit` или `ipfrag2`).
|
||||||
|
|
||||||
|
Режимы требуют указания в порядке возрастания номеров фаз.
|
||||||
|
|
||||||
|
### РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА
|
||||||
|
|
||||||
Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены.
|
Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены.
|
||||||
Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1.
|
Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1.
|
||||||
В варианте disorder обычно приходит сперва частичное подтверждение (SACK), потом полный ACK.
|
В варианте disorder обычно приходит сперва частичное подтверждение (SACK), потом полный ACK.
|
||||||
@ -470,44 +420,6 @@ DPI может отстать от потока, если ClientHello его у
|
|||||||
Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде.
|
Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде.
|
||||||
Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI.
|
Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI.
|
||||||
|
|
||||||
Хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello.
|
|
||||||
Субдомены учитываются автоматически. Поддерживаются листы gzip.
|
|
||||||
|
|
||||||
iptables для задействования атаки на первый пакет данных :
|
|
||||||
|
|
||||||
`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 :
|
|
||||||
1 - для работы методов десинхронизации 0-й фазы и wssize
|
|
||||||
2 - иногда данные идут в 3-м пакете 3-way handshake
|
|
||||||
3 - стандартная ситуация приема одного пакета запроса
|
|
||||||
4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например)
|
|
||||||
|
|
||||||
### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ
|
|
||||||
|
|
||||||
В параметре dpi-desync можно указать до 3 режимов через запятую.
|
|
||||||
0 фаза предполагает работу на этапе установления соединения. Может быть `synack` или `syndata`.
|
|
||||||
На 0 фазу не действует фильтр по hostlist.
|
|
||||||
Последующие режимы отрабатывают на пакетах с данными.
|
|
||||||
Режим 1-й фазы может быть `fake`, `rst`, `rstack`. Режим 2-й фазы может быть `disorder`, `disorder2`, `split`, `split2`, `ipfrag2`.
|
|
||||||
Может быть полезно, когда у провайдера стоит не один DPI.
|
|
||||||
|
|
||||||
### РЕЖИМ SYNACK
|
### РЕЖИМ SYNACK
|
||||||
|
|
||||||
В документации по geneva это называется "TCB turnaround". Попытка ввести DPI в заблуждение относительно
|
В документации по geneva это называется "TCB turnaround". Попытка ввести DPI в заблуждение относительно
|
||||||
@ -617,10 +529,10 @@ chrome рандомизирует фингерпринт TLS. SNI может о
|
|||||||
При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется.
|
При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется.
|
||||||
|
|
||||||
Есть специальная поддержка всех вариантов tcp сплита для многосегментного TLS.
|
Есть специальная поддержка всех вариантов tcp сплита для многосегментного TLS.
|
||||||
Если указать позицию сплита больше длины первого пакета или использовать --dpi-desync-split-tls,
|
Если указать позицию сплита больше длины первого пакета, то разбивка происходит не обязательно первого пакета, а того,
|
||||||
то разбивка происходит не обязательно первого пакета, а того, на который пришлась итоговая позиция.
|
на который пришлась итоговая позиция.
|
||||||
Если, допустим, клиент послал TLS ClientHello длиной 2000, а SNI начинается с 1700,
|
Если, допустим, клиент послал TLS ClientHello длиной 2000, SNI начинается с 1700,
|
||||||
и заданы опции fake,split2, то перед первым пакетом идет fake, затем первый пакет в оригинале,
|
и заданы опции `fake,multisplit`, то перед первым пакетом идет fake, затем первый пакет в оригинале,
|
||||||
а последний пакет разбивается на 2 сегмента. В итоге имеем фейк в начале и 3 реальных сегмента.
|
а последний пакет разбивается на 2 сегмента. В итоге имеем фейк в начале и 3 реальных сегмента.
|
||||||
|
|
||||||
### ПОДДЕРЖКА UDP
|
### ПОДДЕРЖКА UDP
|
||||||
@ -737,6 +649,115 @@ L7 протокол становится известен обычно посл
|
|||||||
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подержки ipset в ядре.
|
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подержки ipset в ядре.
|
||||||
> Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.
|
> Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.
|
||||||
|
|
||||||
|
### IPTABLES ДЛЯ NFQWS
|
||||||
|
|
||||||
|
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` :
|
||||||
|
* 1 - для работы методов десинхронизации 0-й фазы и корректной работы conntrack
|
||||||
|
* 2 - иногда данные идут в 3-м пакете 3-way handshake
|
||||||
|
* 3 - стандартная ситуация приема одного пакета запроса
|
||||||
|
* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например)
|
||||||
|
|
||||||
|
Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть тоже самое).
|
||||||
|
Можно построить фильтр на tcp flags и модуле u32 для поиска характерных паттернов http redirect, но проще использовать connbytes.
|
||||||
|
|
||||||
|
Для режима autohostlist необходимо перенаправление нескольких входящих пакетов, чтобы засечь RST или http redirect.
|
||||||
|
Так же стоит увеличить лимит исходящих пакетов в connbytes, чтобы в него вошли все возможные ретрансмиссии, после которых идет реакция по autoostlist.
|
||||||
|
|
||||||
|
`
|
||||||
|
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`. Не обязательно пропускать весь трафик через offload. Можно исключить из
|
||||||
|
offload соединения, которые должны попасть на tpws или nfqws. openwrt не предусматривает выборочного управления offload.
|
||||||
|
Поэтому скрипты zapret поддерживают свою систему выборочного управления offload в openwrt.
|
||||||
|
|
||||||
|
iptables target `FLOWOFFLOAD` - это проприетарное изобретение openwrt.
|
||||||
|
Управление offload в nftables реализовано в базовом ядре linux без патчей.
|
||||||
|
|
||||||
|
|
||||||
## tpws
|
## tpws
|
||||||
|
|
||||||
tpws - это transparent proxy.
|
tpws - это transparent proxy.
|
||||||
@ -791,10 +812,10 @@ tpws - это transparent proxy.
|
|||||||
--skip-nodelay ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split.
|
--skip-nodelay ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split.
|
||||||
--local-tcp-user-timeout=<seconds> ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение)
|
--local-tcp-user-timeout=<seconds> ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение)
|
||||||
--remote-tcp-user-timeout=<seconds> ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение)
|
--remote-tcp-user-timeout=<seconds> ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение)
|
||||||
|
--fix-seg ; исправлять неудачи tcp сегментации ценой задержек для всех клиентов и замедления
|
||||||
|
|
||||||
--split-http-req=method|host ; способ разделения http запросов на сегменты : около метода (GET,POST) или около заголовка Host
|
--split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации
|
||||||
--split-pos=<offset> ; делить все посылы на сегменты в указанной позиции. единственная опция, работающая на не-http. при указании split-http-req он имеет преимущество на http.
|
--split-any-protocol ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS)
|
||||||
--split-any-protocol ; применять split-pos к любым пакетам. по умолчанию - только к http и TLS ClientHello
|
|
||||||
--disorder[=http|tls] ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса
|
--disorder[=http|tls] ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса
|
||||||
--oob[=http|tls] ; отправить байт out-of-band data (OOB) в конце первой части сплита
|
--oob[=http|tls] ; отправить байт out-of-band data (OOB) в конце первой части сплита
|
||||||
--oob-data=<char>|0xHEX ; переопределить байт OOB. по умолчанию 0x00.
|
--oob-data=<char>|0xHEX ; переопределить байт OOB. по умолчанию 0x00.
|
||||||
@ -808,10 +829,8 @@ tpws - это transparent proxy.
|
|||||||
--methodspace ; добавить пробел после метода : "GET /" => "GET /"
|
--methodspace ; добавить пробел после метода : "GET /" => "GET /"
|
||||||
--methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /"
|
--methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /"
|
||||||
--unixeol ; конвертировать 0D0A в 0A и использовать везде 0A
|
--unixeol ; конвертировать 0D0A в 0A и использовать везде 0A
|
||||||
--tlsrec=sni|sniext ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI или между байтами длины SNI extension. Если SNI нет - отмена.
|
--tlsrec=N|-N|marker+N|marker-N ; разбивка TLS ClientHello на 2 TLS records на указанной позиции. Минимальное смещение - 6.
|
||||||
--tlsrec-pos=<pos> ; разбивка TLS ClientHello на 2 TLS records. режем на указанной позиции, если длина слишком мелкая - на позиции 1.
|
|
||||||
--mss=<int> ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
|
--mss=<int> ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
|
||||||
--mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию
|
|
||||||
--tamper-start=[n]<pos> ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока)
|
--tamper-start=[n]<pos> ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока)
|
||||||
--tamper-cutoff=[n]<pos> ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока)
|
--tamper-cutoff=[n]<pos> ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока)
|
||||||
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
|
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
|
||||||
@ -834,6 +853,115 @@ tpws - это transparent proxy.
|
|||||||
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
|
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TCP СЕГМЕНТАЦИЯ В TPWS
|
||||||
|
|
||||||
|
tpws, как и nfqws, поддерживает множественную сегментацию запросов. Сплит позиции задаются в `--split-pos`.
|
||||||
|
Указываются маркеры через запятую. Описание маркеров см в разделе [nfqws](#tcp-сегментация).
|
||||||
|
|
||||||
|
На прикладном уровне в общем случае нет гарантированного средства заставить ядро выплюнуть
|
||||||
|
блок данных, порезанным в определенном месте. ОС держит буфер отсылки (SNDBUF) у каждого сокета.
|
||||||
|
Если у сокета включена опция TCP_NODELAY и буфер пуст, то каждый send приводит к отсылке
|
||||||
|
отдельного ip пакета или группы пакетов, если блок не вмещается в один ip пакет.
|
||||||
|
Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему,
|
||||||
|
никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии,
|
||||||
|
что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI.
|
||||||
|
Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса.
|
||||||
|
Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае.
|
||||||
|
Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда,
|
||||||
|
когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан,
|
||||||
|
но и принят другой стороной, а следовательно буфер отсылки пуст, и следующие 2 send приведут
|
||||||
|
к отсылке сегментов данных разными ip пакетами.
|
||||||
|
|
||||||
|
Таким образом tpws обеспечивает сплит только за счет раздельных вызовов send, и это обычно работает надежно,
|
||||||
|
если разбивать не на слишком много частей и не на слишком мелкие подряд следующие части.
|
||||||
|
В последнем случае Linux все же может обьединить некоторые части, что приведет к несоответствию реальной сегментации
|
||||||
|
указанным сплит позициям. Другие ОС в этом вопросе ведут себя более предсказуемо. Спонтанного обьединения замечено не было.
|
||||||
|
Поэтому не стоит злоупотреблять сплитами и в особенности мелкими соседними пакетами.
|
||||||
|
|
||||||
|
Как показывается практика, проблемы могут начаться , если количество сплит позиций превышает 8.
|
||||||
|
При неудаче сегментации будет выводиться сообщение `WARNING ! segmentation failed`.
|
||||||
|
Если вы его видите, это повод снизить количество сплит позиций.
|
||||||
|
Если это не вариант, есть параметр `--fix-seg`. Он позволяет подождать завершение отсылки перед отправкой следующей части.
|
||||||
|
Но этот вариант ломает модель асинхронной обработки событий. Пока идет ожидание, все остальные соединения не обрабатываются
|
||||||
|
и кратковременно подвисают. На практике это может быть совсем небольшое ожидание - менее 10 мс.
|
||||||
|
И производится оно только , если происходит split, и в ожидании есть реальная необходимость.
|
||||||
|
В высоконагруженных системах данный вариант не рекомендуется. Но для домашнего использования может подойти, и вы эти задержки даже не заметите.
|
||||||
|
|
||||||
|
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 совместимо только в режиме socks при включенном удаленном ресолвинге хостов.
|
||||||
|
(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста
|
||||||
|
еще на этапе установления соединения.
|
||||||
|
Применяя данную опцию к сайтам 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 и удаленный ресолвинг хостов через прокси.
|
||||||
|
То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того,
|
||||||
|
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно.
|
||||||
|
В одной программе работает, в другой - нет.
|
||||||
|
Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом,
|
||||||
|
создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss.
|
||||||
|
Тогда при любом раскладе будет выполняться mss.
|
||||||
|
Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии.
|
||||||
|
Смотрите вывод `--debug`, чтобы убедиться в правильности настроек.
|
||||||
|
|
||||||
|
### СЛУЖЕБНЫЕ ПАРАМЕТРЫ
|
||||||
|
|
||||||
`--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл.
|
`--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл.
|
||||||
Может быть важен порядок следования опций. `--debug` лучше всего указывать в самом начале.
|
Может быть важен порядок следования опций. `--debug` лучше всего указывать в самом начале.
|
||||||
Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа `--debug` еще дело не дошло,
|
Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа `--debug` еще дело не дошло,
|
||||||
@ -850,28 +978,6 @@ tpws - это transparent proxy.
|
|||||||
Вместо удаления лучше использовать truncate.
|
Вместо удаления лучше использовать truncate.
|
||||||
В шелле это можно сделать через команду ": >filename"
|
В шелле это можно сделать через команду ": >filename"
|
||||||
|
|
||||||
Параметры манипуляции могут сочетаться в любых комбинациях.
|
|
||||||
|
|
||||||
В случае http запроса `split-http-req` имеет преимущество над split-pos.
|
|
||||||
split-pos по умолчанию работает только на http и TLS ClientHello.
|
|
||||||
Чтобы он работал на любых пакетах, укажите `--split-any-protocol`.
|
|
||||||
|
|
||||||
На прикладном уровне в общем случае нет гарантированного средства заставить ядро выплюнуть
|
|
||||||
блок данных, порезанным в определенном месте. ОС держит буфер отсылки (SNDBUF) у каждого сокета.
|
|
||||||
Если у сокета включена опция TCP_NODELAY и буфер пуст, то каждый send приводит к отсылке
|
|
||||||
отдельного ip пакета или группы пакетов, если блок не вмещается в один ip пакет.
|
|
||||||
Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему,
|
|
||||||
никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии,
|
|
||||||
что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI.
|
|
||||||
Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса.
|
|
||||||
Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае.
|
|
||||||
Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда,
|
|
||||||
когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан,
|
|
||||||
но и принят другой стороной, а следовательно буфер отсылки пуст, и следующие 2 send приведут
|
|
||||||
к отсылке сегментов данных разными ip пакетами.
|
|
||||||
Резюме : tpws гарантирует сплит только за счет раздельных вызовов send, что на практике
|
|
||||||
вполне достаточно для протоколов http(s).
|
|
||||||
|
|
||||||
tpws может биндаться на множество интерфейсов и IP адресов (до 32 шт).
|
tpws может биндаться на множество интерфейсов и IP адресов (до 32 шт).
|
||||||
Порт всегда только один.
|
Порт всегда только один.
|
||||||
Параметры `--bind-iface*` и `--bind-addr` создают новый бинд.
|
Параметры `--bind-iface*` и `--bind-addr` создают новый бинд.
|
||||||
@ -895,19 +1001,17 @@ tpws может биндаться на множество интерфейсо
|
|||||||
|
|
||||||
Параметры rcvbuf и sndbuf позволяют установить setsockopt SO_RCVBUF SO_SNDBUF для локального и удаленного соединения.
|
Параметры rcvbuf и sndbuf позволяют установить setsockopt SO_RCVBUF SO_SNDBUF для локального и удаленного соединения.
|
||||||
|
|
||||||
Если не указан ни один из параметров модификации содержимого, tpws работает в режиме `tcp proxy mode`.
|
`--skip-nodelay` может быть полезен, когда tpws используется без дурения, чтобы привести MTU к MTU системы, на которой работает tpws.
|
||||||
Он отличается тем, что в оба конца применяется splice для переброски данных из одного сокета в другой
|
Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения
|
||||||
без копирования в память процесса. Практически - это то же самое, но может быть чуть побыстрее.
|
подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.
|
||||||
TCP проксирование может быть полезно для обхода блокировок, когда DPI спотыкается на экзотических
|
|
||||||
хедерах IP или TCP. Вы вряд ли сможете поправить хедеры, исходящие от айфончиков и гаджетиков,
|
`--local-tcp-user-timeout` и `--remote-tcp-user-timeout` устанавливают значение таймаута в секундах
|
||||||
но на linux сможете влиять на них в какой-то степени через `sysctl`.
|
для соединений клиент-прокси и прокси-сервер. Этот таймаут соответствует опции сокета linux
|
||||||
Когда соединение проходит через tpws, фактически прокси-сервер сам устанавливает подключение к удаленному
|
TCP_USER_TIMEOUT. Под таймаутом подразумевается время, в течение которого буферизированные данные
|
||||||
узлу от своего имени, и на это распространяются настройки системы, на которой работает прокси.
|
не переданы или на переданные данные не получено подтверждение (ACK) от другой стороны.
|
||||||
tpws можно использовать на мобильном устройстве, раздающем интернет на тарифе сотового оператора,
|
Этот таймаут никак не касается времени отсутствия какой-либо передачи через сокет лишь потому,
|
||||||
где раздача запрещена, в socks режиме даже без рута. Соединения от tpws неотличимы от соединений
|
что данных для передачи нет. Полезно для сокращения время закрытия подвисших соединений.
|
||||||
с самого раздающего устройства. Отличить можно только по содержанию (типа обновлений windows).
|
Поддерживается только на Linux и MacOS.
|
||||||
Заодно можно и обойти блокировки. 2 зайца одним выстрелом.
|
|
||||||
Более подробную информацию по вопросу обхода ограничений операторов гуглите на 4pda.ru.
|
|
||||||
|
|
||||||
Режим `--socks` не требует повышенных привилегий (кроме бинда на привилегированные порты 1..1023).
|
Режим `--socks` не требует повышенных привилегий (кроме бинда на привилегированные порты 1..1023).
|
||||||
Поддерживаются версии socks 4 и 5 без авторизации. Версия протокола распознается автоматически.
|
Поддерживаются версии socks 4 и 5 без авторизации. Версия протокола распознается автоматически.
|
||||||
@ -921,82 +1025,77 @@ tpws поддерживает эту возможность асинхронно
|
|||||||
Если задан параметр `--no-resolve`, то подключения по именам хостов запрещаются, а пул ресолверов не создается.
|
Если задан параметр `--no-resolve`, то подключения по именам хостов запрещаются, а пул ресолверов не создается.
|
||||||
Тем самым экономятся ресурсы.
|
Тем самым экономятся ресурсы.
|
||||||
|
|
||||||
Параметр `--hostpad=<bytes>` добавляет паддинг-хедеров перед Host: на указанное количество байтов.
|
### IPTABLES ДЛЯ TPWS
|
||||||
Если размер `<bytes>` слишком большой, то идет разбивка на разные хедеры по 2K.
|
|
||||||
Общий буфер приема http запроса - 64K, больший паддинг не поддерживается, да и http сервера
|
|
||||||
такое уже не принимают.
|
|
||||||
Полезно против DPI, выполняющих реассемблинг TCP с ограниченным буфером.
|
|
||||||
Если техника работает, то после некоторого количества bytes http запрос начнет проходить до сайта.
|
|
||||||
Если при этом критический размер padding около MTU, значит скорее всего DPI не выполняет реассемблинг пакетов, и лучше будет использовать обычные опции `--split-…`
|
|
||||||
Если все же реассемблинг выполняется, то критический размер будет около размера буфера DPI. Он может быть 4K или 8K, возможны и другие значения.
|
|
||||||
|
|
||||||
`--disorder` - это попытка симулировать режим `disorder2 nfqws`, используя особенности ОС по реализации stream сокетов.
|
Для перенаправления tcp соединения на transparent proxy используются команды следующего вида :
|
||||||
Однако, в отличие от nfqws, здесь не требуются повышенные привилегии.
|
|
||||||
Реализовано это следующим образом. У сокета есть возможность выставить TTL. Все пакеты будут отправляться с ним.
|
|
||||||
Перед отправкой первого сегмента ставим TTL=1. Пакет будет дропнут на первом же роутере, он не дойдет ни до DPI, ни до сервера.
|
|
||||||
Затем возвращаем TTL в значение по умолчанию. ОС отсылает второй сегмент, и он уже доходит до сервера.
|
|
||||||
Сервер возвращает SACK, потому что не получил первый кусок, и ОС его отправляет повторно, но здесь уже мы ничего не делаем.
|
|
||||||
Этот режим работает как ожидается на Linux и MacOS. Однако, на FreeBSD и OpenBSD он работает не так хорошо.
|
|
||||||
Ядро этих ОС отсылает ретрансмиссию в виде полного пакета. Потому выходит, что до сервера идет сначала второй кусок,
|
|
||||||
а потом полный запрос без сплита. На него может отреагировать DPI штатным образом.
|
|
||||||
`--disorder` является дополнительным флагом к любому сплиту. Сам по себе он не делает ничего.
|
|
||||||
|
|
||||||
`--tlsrec` и `--tlsrec-pos` позволяют внутри одного tcp сегмента разрезать TLS ClientHello на 2 TLS records.
|
```
|
||||||
`--tlsrec=sni` режет между 1 и 2 символами hostname в SNI, делая невозможным бинарный поиск паттерна без анализа
|
iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988
|
||||||
структуры данных. В случае отсутствия SNI разбиение отменяется.
|
iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988
|
||||||
`--tlsrec-pos` режет на указанной позиции. Если длина блока данных TLS меньше указанной позиции, режем на позиции 1.
|
```
|
||||||
Параметр сочетается с `--split-pos`. В этом случае происходит сначала разделение на уровне TLS record layer, потом на уровне TCP.
|
|
||||||
Самая изощрённая атака `--tlsrec`, `--split-pos` и `--disorder` вместе.
|
|
||||||
`--tlsrec` ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах
|
|
||||||
без проблем принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN
|
|
||||||
или системы ddos-защиты. Поэтому применение `--tlsrec` без ограничителей вряд ли целесообразно.
|
|
||||||
В РФ `--tlsrec` обычно не работает с TLS 1.2, потому что цензор парсит сертификат сервера из ServerHello.
|
|
||||||
Работает только с TLS 1.3, поскольку там эта информация шифруется.
|
|
||||||
Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного.
|
|
||||||
|
|
||||||
`--mss` устанавливает опцию сокета TCP_MAXSEG. Клиент выдает это значение в tcp опциях SYN пакета.
|
Первая команда для соединений с самой системы, вторая - для проходящих через роутер соединений.
|
||||||
Сервер в ответ в SYN,ACK выдает свой MSS. На практике сервера обычно снижают размеры отсылаемых ими пакетов, но они
|
|
||||||
все равно не вписываются в низкий MSS, указанный клиентом. Обычно чем больше указал клиент, тем больше
|
|
||||||
шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет,
|
|
||||||
это может обмануть DPI, секущий ответ сервера.
|
|
||||||
Схема может значительно снизить скорость и сработать не на всех сайтах.
|
|
||||||
С фильтром по hostlist совместимо только в режиме socks при включенном удаленном ресолвинге хостов.
|
|
||||||
(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста
|
|
||||||
еще на этапе установления соединения.
|
|
||||||
Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже.
|
|
||||||
Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в
|
|
||||||
3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который
|
|
||||||
может привести к реакции DPI.
|
|
||||||
Использовать только когда нет ничего лучше или для отдельных ресурсов.
|
|
||||||
Для http использовать смысла нет, поэтому заводите отдельный desync profile с фильтром по порту 443.
|
|
||||||
Работает только на linux, не работает на BSD и MacOS.
|
|
||||||
|
|
||||||
`--skip-nodelay` может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws.
|
DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра
|
||||||
Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения
|
route_localnet :
|
||||||
подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.
|
|
||||||
|
|
||||||
`--local-tcp-user-timeout` и `--remote-tcp-user-timeout` устанавливают значение таймаута в секундах
|
`sysctl -w net.ipv4.conf.<внутренний_интерфейс>.route_localnet=1`
|
||||||
для соединений клиент-прокси и прокси-сервер. Этот таймаут соответствует опции сокета linux
|
|
||||||
TCP_USER_TIMEOUT. Под таймаутом подразумевается время, в течение которого буферизированные данные
|
|
||||||
не переданы или на переданные данные не получено подтверждение (ACK) от другой стороны.
|
|
||||||
Этот таймаут никак не касается времени отсутствия какой-либо передачи через сокет лишь потому,
|
|
||||||
что данных для передачи нет. Полезно для сокращения время закрытия подвисших соединений.
|
|
||||||
Поддерживается только на Linux и MacOS.
|
|
||||||
|
|
||||||
### МНОЖЕСТВЕННЫЕ СТРАТЕГИИ
|
Можно использовать `-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
|
||||||
|
```
|
||||||
|
|
||||||
Работают аналогично `nfqws`, кроме некоторых моментов.
|
|
||||||
Нет параметра `--filter-udp`, поскольку `tpws` udp не поддерживает.
|
|
||||||
Методы нулевой фазы (`--mss`) могут работать по хостлисту в одном единственном случае:
|
|
||||||
если используется режим socks и удаленный ресолвинг хостов через прокси.
|
|
||||||
То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того,
|
|
||||||
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно.
|
|
||||||
В одной программе работает, в другой - нет.
|
|
||||||
Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом,
|
|
||||||
создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss.
|
|
||||||
Тогда при любом раскладе будет выполняться mss.
|
|
||||||
Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии.
|
|
||||||
Смотрите вывод `--debug`, чтобы убедиться в правильности настроек.
|
|
||||||
|
|
||||||
## Способы получения списка заблокированных IP
|
## Способы получения списка заблокированных IP
|
||||||
|
|
||||||
@ -1078,17 +1177,17 @@ Cкрипты с названием `get_antifilter_*` оперируют спи
|
|||||||
в отдельный ipset `ipban`. Он может использоваться для принудительного завертывания всех
|
в отдельный ipset `ipban`. Он может использоваться для принудительного завертывания всех
|
||||||
соединений на прозрачный proxy `redsocks` или на VPN.
|
соединений на прозрачный proxy `redsocks` или на VPN.
|
||||||
|
|
||||||
IPV6 : если включен ipv6, то дополнительно создаются листы с таким же именем, но с "6" на конце перед расширением.
|
**IPV6** : если включен ipv6, то дополнительно создаются листы с таким же именем, но с "6" на конце перед расширением.
|
||||||
`zapret-ip.txt` => `zapret-ip6.txt`
|
`zapret-ip.txt` => `zapret-ip6.txt`
|
||||||
Создаются ipset-ы zapret6 и ipban6.
|
Создаются ipset-ы zapret6 и ipban6.
|
||||||
Листы с antifilter не содержат список ipv6 адресов.
|
Листы с antifilter не содержат список ipv6 адресов.
|
||||||
|
|
||||||
СИСТЕМА ИСКЛЮЧЕНИЯ IP. Все скрипты ресолвят файл `zapret-hosts-user-exclude.txt`, создавая `zapret-ip-exclude.txt` и `zapret-ip-exclude6.txt`.
|
**СИСТЕМА ИСКЛЮЧЕНИЯ IP**. Все скрипты ресолвят файл `zapret-hosts-user-exclude.txt`, создавая `zapret-ip-exclude.txt` и `zapret-ip-exclude6.txt`.
|
||||||
Они загоняются в ipset-ы nozapret и nozapret6. Все правила, создаваемые init скриптами, создаются с учетом этих ipset.
|
Они загоняются в ipset-ы nozapret и nozapret6. Все правила, создаваемые init скриптами, создаются с учетом этих ipset.
|
||||||
Помещенные в них IP не участвуют в процессе.
|
Помещенные в них IP не участвуют в процессе.
|
||||||
`zapret-hosts-user-exclude.txt` может содержать домены, ipv4 и ipv6 адреса или подсети.
|
`zapret-hosts-user-exclude.txt` может содержать домены, ipv4 и ipv6 адреса или подсети.
|
||||||
|
|
||||||
FreeBSD. Скрипты ipset/*.sh работают так же на FreeBSD. Вместо ipset они создают lookup таблицы ipfw с аналогичными именами.
|
**FreeBSD**. Скрипты ipset/*.sh работают так же на FreeBSD. Вместо ipset они создают lookup таблицы ipfw с аналогичными именами.
|
||||||
ipfw таблицы в отличие от ipset могут содержать как ipv4, так и ipv6 адреса и подсети в одной таблице, поэтому разделения нет.
|
ipfw таблицы в отличие от ipset могут содержать как ipv4, так и ipv6 адреса и подсети в одной таблице, поэтому разделения нет.
|
||||||
|
|
||||||
Параметр конфига LISTS_RELOAD задает произвольную команду для перезагрузки листов.
|
Параметр конфига LISTS_RELOAD задает произвольную команду для перезагрузки листов.
|
||||||
@ -1516,7 +1615,7 @@ nfqws начнет получать адреса пакетов из локал
|
|||||||
```
|
```
|
||||||
TPWS_SOCKS_OPT="
|
TPWS_SOCKS_OPT="
|
||||||
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
||||||
--filter-tcp=443 --split-tls=sni --disorder <HOSTLIST>"
|
--filter-tcp=443 --split-pos=1,midsld --disorder <HOSTLIST>"
|
||||||
```
|
```
|
||||||
|
|
||||||
***Включение стандартной опции tpws в прозрачном режиме***\
|
***Включение стандартной опции tpws в прозрачном режиме***\
|
||||||
@ -1529,7 +1628,7 @@ TPWS_SOCKS_OPT="
|
|||||||
```
|
```
|
||||||
TPWS_OPT="
|
TPWS_OPT="
|
||||||
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
||||||
--filter-tcp=443 --split-tls=sni --disorder <HOSTLIST>"
|
--filter-tcp=443 --split-pos=1,midsld --disorder <HOSTLIST>"
|
||||||
```
|
```
|
||||||
|
|
||||||
***Включение стандартной опции nfqws***\
|
***Включение стандартной опции nfqws***\
|
||||||
@ -1566,9 +1665,9 @@ NFQWS_PORTS_UDP_KEEPALIVE=
|
|||||||
***Параметры nfqws***
|
***Параметры nfqws***
|
||||||
```
|
```
|
||||||
NFQWS_OPT="
|
NFQWS_OPT="
|
||||||
--filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
--filter-tcp=80 --dpi-desync=fake,multisplit --dpi-desync-split-pos=method+2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
||||||
--filter-tcp=443 --dpi-desync=fake,disorder2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-fooling=badseq,md5sig <HOSTLIST> --new
|
||||||
--filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 <HOSTLIST_NOAUTO>"
|
--filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 <HOSTLIST_NOAUTO>
|
||||||
```
|
```
|
||||||
|
|
||||||
***Режим фильтрации хостов:***
|
***Режим фильтрации хостов:***
|
||||||
@ -1943,11 +2042,12 @@ zapret_custom_firewall_nft поднимает правила nftables.
|
|||||||
"не просто" до "почти невозможно". Если только вы не найдете готовое собранное ядро под ваш девайс.
|
"не просто" до "почти невозможно". Если только вы не найдете готовое собранное ядро под ваш девайс.
|
||||||
|
|
||||||
tpws будет работать в любом случае, он не требует чего-либо особенного.
|
tpws будет работать в любом случае, он не требует чего-либо особенного.
|
||||||
В android нет /etc/passwd, потому опция `--user` не будет работать. Вместо нее можно
|
|
||||||
пользоваться числовыми user id и опцией `--uid`.
|
Хотя linux варианты под Android работают, рекомендуется использовать специально собранные под bionic бинарники.
|
||||||
|
У них не будет проблем с DNS, с локальным временем и именами юзеров и групп.\
|
||||||
Рекомендую использовать gid 3003 (AID_INET). Иначе можете получить permission denied на создание сокета.
|
Рекомендую использовать gid 3003 (AID_INET). Иначе можете получить permission denied на создание сокета.
|
||||||
Например: `--uid 1:3003`\
|
Например: `--uid 1:3003`\
|
||||||
В iptables укажите: "! --uid-owner 1" вместо "! --uid-owner tpws".\
|
В iptables укажите: `! --uid-owner 1` вместо `! --uid-owner tpws`.\
|
||||||
Напишите шелл скрипт с iptables и tpws, запускайте его средствами вашего рут менеджера.
|
Напишите шелл скрипт с iptables и tpws, запускайте его средствами вашего рут менеджера.
|
||||||
Скрипты автозапуска лежат тут:\
|
Скрипты автозапуска лежат тут:\
|
||||||
magisk : /data/adb/service.d\
|
magisk : /data/adb/service.d\
|
||||||
@ -1956,10 +2056,6 @@ supersu: /system/su.d
|
|||||||
`nfqws` может иметь такой глюк. При запуске с uid по умолчанию (0x7FFFFFFF) при условии работы на сотовом интерфейсе
|
`nfqws` может иметь такой глюк. При запуске с uid по умолчанию (0x7FFFFFFF) при условии работы на сотовом интерфейсе
|
||||||
и отключенном кабеле внешнего питания система может частично виснуть. Перестает работать тач и кнопки,
|
и отключенном кабеле внешнего питания система может частично виснуть. Перестает работать тач и кнопки,
|
||||||
но анимация на экране может продолжаться. Если экран был погашен, то включить его кнопкой power невозможно.
|
но анимация на экране может продолжаться. Если экран был погашен, то включить его кнопкой power невозможно.
|
||||||
Это, видимо, связано с переводом в suspend процессов с определенным UID. UID соответствует приложению или
|
|
||||||
системному сервису. По UID android определяет политику power saving.
|
|
||||||
Так же возможно, что глюк связан с кривым драйвером сотового интерфейса от китайцев, поскольку при использовании
|
|
||||||
wifi такого не наблюдается. suspend обработчика nfqueue на обычном linux не вызывает подобных фатальных последствий.
|
|
||||||
Изменение UID на низкий (--uid 1 подойдет) позволяет решить эту проблему.
|
Изменение UID на низкий (--uid 1 подойдет) позволяет решить эту проблему.
|
||||||
Глюк был замечен на android 8.1 на девайсе, основанном на платформе mediatek.
|
Глюк был замечен на android 8.1 на девайсе, основанном на платформе mediatek.
|
||||||
|
|
||||||
|
@ -549,7 +549,7 @@ service_install_systemd()
|
|||||||
|
|
||||||
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
|
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
|
||||||
rm -f "$INIT_SCRIPT"
|
rm -f "$INIT_SCRIPT"
|
||||||
ln -fs "$EXEDIR/init.d/systemd/zapret.service" "$SYSTEMD_SYSTEM_DIR"
|
cp -f "$EXEDIR/init.d/systemd/zapret.service" "$SYSTEMD_SYSTEM_DIR"
|
||||||
"$SYSTEMCTL" daemon-reload
|
"$SYSTEMCTL" daemon-reload
|
||||||
"$SYSTEMCTL" enable zapret || {
|
"$SYSTEMCTL" enable zapret || {
|
||||||
echo could not enable systemd service
|
echo could not enable systemd service
|
||||||
@ -567,8 +567,8 @@ timer_install_systemd()
|
|||||||
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
|
if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then
|
||||||
"$SYSTEMCTL" disable zapret-list-update.timer
|
"$SYSTEMCTL" disable zapret-list-update.timer
|
||||||
"$SYSTEMCTL" stop zapret-list-update.timer
|
"$SYSTEMCTL" stop zapret-list-update.timer
|
||||||
ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR"
|
cp -f "$EXEDIR/init.d/systemd/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR"
|
||||||
ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.timer" "$SYSTEMD_SYSTEM_DIR"
|
cp -f "$EXEDIR/init.d/systemd/zapret-list-update.timer" "$SYSTEMD_SYSTEM_DIR"
|
||||||
"$SYSTEMCTL" daemon-reload
|
"$SYSTEMCTL" daemon-reload
|
||||||
"$SYSTEMCTL" enable zapret-list-update.timer || {
|
"$SYSTEMCTL" enable zapret-list-update.timer || {
|
||||||
echo could not enable zapret-list-update.timer
|
echo could not enable zapret-list-update.timer
|
||||||
|
232
tpws/helpers.c
232
tpws/helpers.c
@ -11,6 +11,10 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#include "andr/ifaddrs.h"
|
#include "andr/ifaddrs.h"
|
||||||
#else
|
#else
|
||||||
@ -34,9 +38,9 @@ static int cmp_size_t(const void * a, const void * b)
|
|||||||
{
|
{
|
||||||
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
|
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
|
||||||
}
|
}
|
||||||
void qsort_size_t(size_t *array,size_t ct)
|
void qsort_size_t(size_t *array, size_t ct)
|
||||||
{
|
{
|
||||||
qsort(array,ct,sizeof(*array),cmp_size_t);
|
qsort(array, ct, sizeof(*array), cmp_size_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,10 +52,10 @@ void rtrim(char *s)
|
|||||||
|
|
||||||
void replace_char(char *s, char from, char to)
|
void replace_char(char *s, char from, char to)
|
||||||
{
|
{
|
||||||
for(;*s;s++) if (*s==from) *s=to;
|
for (; *s; s++) if (*s == from) *s = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *strncasestr(const char *s,const char *find, size_t slen)
|
char *strncasestr(const char *s, const char *find, size_t slen)
|
||||||
{
|
{
|
||||||
char c, sc;
|
char c, sc;
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -92,9 +96,9 @@ bool load_file(const char *filename, void *buffer, size_t *buffer_size)
|
|||||||
|
|
||||||
bool append_to_list_file(const char *filename, const char *s)
|
bool append_to_list_file(const char *filename, const char *s)
|
||||||
{
|
{
|
||||||
FILE *F = fopen(filename,"at");
|
FILE *F = fopen(filename, "at");
|
||||||
if (!F) return false;
|
if (!F) return false;
|
||||||
bool bOK = fprintf(F,"%s\n",s)>0;
|
bool bOK = fprintf(F, "%s\n", s) > 0;
|
||||||
fclose(F);
|
fclose(F);
|
||||||
return bOK;
|
return bOK;
|
||||||
}
|
}
|
||||||
@ -102,7 +106,7 @@ bool append_to_list_file(const char *filename, const char *s)
|
|||||||
void ntop46(const struct sockaddr *sa, char *str, size_t len)
|
void ntop46(const struct sockaddr *sa, char *str, size_t len)
|
||||||
{
|
{
|
||||||
if (!len) return;
|
if (!len) return;
|
||||||
*str=0;
|
*str = 0;
|
||||||
switch (sa->sa_family)
|
switch (sa->sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
@ -112,50 +116,50 @@ void ntop46(const struct sockaddr *sa, char *str, size_t len)
|
|||||||
inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len);
|
inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(str,len,"UNKNOWN_FAMILY_%d",sa->sa_family);
|
snprintf(str, len, "UNKNOWN_FAMILY_%d", sa->sa_family);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ntop46_port(const struct sockaddr *sa, char *str, size_t len)
|
void ntop46_port(const struct sockaddr *sa, char *str, size_t len)
|
||||||
{
|
{
|
||||||
char ip[40];
|
char ip[40];
|
||||||
ntop46(sa,ip,sizeof(ip));
|
ntop46(sa, ip, sizeof(ip));
|
||||||
switch (sa->sa_family)
|
switch (sa->sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
snprintf(str,len,"%s:%u",ip,ntohs(((struct sockaddr_in*)sa)->sin_port));
|
snprintf(str, len, "%s:%u", ip, ntohs(((struct sockaddr_in*)sa)->sin_port));
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
snprintf(str,len,"[%s]:%u",ip,ntohs(((struct sockaddr_in6*)sa)->sin6_port));
|
snprintf(str, len, "[%s]:%u", ip, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(str,len,"%s",ip);
|
snprintf(str, len, "%s", ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void print_sockaddr(const struct sockaddr *sa)
|
void print_sockaddr(const struct sockaddr *sa)
|
||||||
{
|
{
|
||||||
char ip_port[48];
|
char ip_port[48];
|
||||||
|
|
||||||
ntop46_port(sa,ip_port,sizeof(ip_port));
|
ntop46_port(sa, ip_port, sizeof(ip_port));
|
||||||
printf("%s",ip_port);
|
printf("%s", ip_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -1 = error, 0 = not local, 1 = local
|
// -1 = error, 0 = not local, 1 = local
|
||||||
bool check_local_ip(const struct sockaddr *saddr)
|
bool check_local_ip(const struct sockaddr *saddr)
|
||||||
{
|
{
|
||||||
struct ifaddrs *addrs,*a;
|
struct ifaddrs *addrs, *a;
|
||||||
|
|
||||||
if (is_localnet(saddr))
|
if (is_localnet(saddr))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (getifaddrs(&addrs)<0) return false;
|
if (getifaddrs(&addrs) < 0) return false;
|
||||||
a = addrs;
|
a = addrs;
|
||||||
|
|
||||||
bool bres=false;
|
bool bres = false;
|
||||||
while (a)
|
while (a)
|
||||||
{
|
{
|
||||||
if (a->ifa_addr && sacmp(a->ifa_addr,saddr))
|
if (a->ifa_addr && sacmp(a->ifa_addr, saddr))
|
||||||
{
|
{
|
||||||
bres=true;
|
bres = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
a = a->ifa_next;
|
a = a->ifa_next;
|
||||||
@ -177,7 +181,7 @@ void print_addrinfo(const struct addrinfo *ai)
|
|||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str)))
|
if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str)))
|
||||||
printf( "%s\n", str);
|
printf("%s\n", str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ai = ai->ai_next;
|
ai = ai->ai_next;
|
||||||
@ -189,23 +193,23 @@ void print_addrinfo(const struct addrinfo *ai)
|
|||||||
bool saismapped(const struct sockaddr_in6 *sa)
|
bool saismapped(const struct sockaddr_in6 *sa)
|
||||||
{
|
{
|
||||||
// ::ffff:1.2.3.4
|
// ::ffff:1.2.3.4
|
||||||
return !memcmp(sa->sin6_addr.s6_addr,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff",12);
|
return !memcmp(sa->sin6_addr.s6_addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 12);
|
||||||
}
|
}
|
||||||
bool samappedcmp(const struct sockaddr_in *sa1,const struct sockaddr_in6 *sa2)
|
bool samappedcmp(const struct sockaddr_in *sa1, const struct sockaddr_in6 *sa2)
|
||||||
{
|
{
|
||||||
return saismapped(sa2) && !memcmp(sa2->sin6_addr.s6_addr+12,&sa1->sin_addr.s_addr,4);
|
return saismapped(sa2) && !memcmp(sa2->sin6_addr.s6_addr + 12, &sa1->sin_addr.s_addr, 4);
|
||||||
}
|
}
|
||||||
bool sacmp(const struct sockaddr *sa1,const struct sockaddr *sa2)
|
bool sacmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
|
||||||
{
|
{
|
||||||
return (sa1->sa_family==AF_INET && sa2->sa_family==AF_INET && !memcmp(&((struct sockaddr_in*)sa1)->sin_addr,&((struct sockaddr_in*)sa2)->sin_addr,sizeof(struct in_addr))) ||
|
return (sa1->sa_family == AF_INET && sa2->sa_family == AF_INET && !memcmp(&((struct sockaddr_in*)sa1)->sin_addr, &((struct sockaddr_in*)sa2)->sin_addr, sizeof(struct in_addr))) ||
|
||||||
(sa1->sa_family==AF_INET6 && sa2->sa_family==AF_INET6 && !memcmp(&((struct sockaddr_in6*)sa1)->sin6_addr,&((struct sockaddr_in6*)sa2)->sin6_addr,sizeof(struct in6_addr))) ||
|
(sa1->sa_family == AF_INET6 && sa2->sa_family == AF_INET6 && !memcmp(&((struct sockaddr_in6*)sa1)->sin6_addr, &((struct sockaddr_in6*)sa2)->sin6_addr, sizeof(struct in6_addr))) ||
|
||||||
(sa1->sa_family==AF_INET && sa2->sa_family==AF_INET6 && samappedcmp((struct sockaddr_in*)sa1,(struct sockaddr_in6*)sa2)) ||
|
(sa1->sa_family == AF_INET && sa2->sa_family == AF_INET6 && samappedcmp((struct sockaddr_in*)sa1, (struct sockaddr_in6*)sa2)) ||
|
||||||
(sa1->sa_family==AF_INET6 && sa2->sa_family==AF_INET && samappedcmp((struct sockaddr_in*)sa2,(struct sockaddr_in6*)sa1));
|
(sa1->sa_family == AF_INET6 && sa2->sa_family == AF_INET && samappedcmp((struct sockaddr_in*)sa2, (struct sockaddr_in6*)sa1));
|
||||||
}
|
}
|
||||||
uint16_t saport(const struct sockaddr *sa)
|
uint16_t saport(const struct sockaddr *sa)
|
||||||
{
|
{
|
||||||
return htons(sa->sa_family==AF_INET ? ((struct sockaddr_in*)sa)->sin_port :
|
return htons(sa->sa_family == AF_INET ? ((struct sockaddr_in*)sa)->sin_port :
|
||||||
sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0);
|
sa->sa_family == AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0);
|
||||||
}
|
}
|
||||||
bool saconvmapped(struct sockaddr_storage *a)
|
bool saconvmapped(struct sockaddr_storage *a)
|
||||||
{
|
{
|
||||||
@ -223,13 +227,13 @@ bool saconvmapped(struct sockaddr_storage *a)
|
|||||||
|
|
||||||
void sacopy(struct sockaddr_storage *sa_dest, const struct sockaddr *sa)
|
void sacopy(struct sockaddr_storage *sa_dest, const struct sockaddr *sa)
|
||||||
{
|
{
|
||||||
switch(sa->sa_family)
|
switch (sa->sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
memcpy(sa_dest,sa,sizeof(struct sockaddr_in));
|
memcpy(sa_dest, sa, sizeof(struct sockaddr_in));
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
memcpy(sa_dest,sa,sizeof(struct sockaddr_in6));
|
memcpy(sa_dest, sa, sizeof(struct sockaddr_in6));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sa_dest->ss_family = 0;
|
sa_dest->ss_family = 0;
|
||||||
@ -243,9 +247,9 @@ void sa46copy(sockaddr_in46 *sa_dest, const struct sockaddr *sa)
|
|||||||
bool is_localnet(const struct sockaddr *a)
|
bool is_localnet(const struct sockaddr *a)
|
||||||
{
|
{
|
||||||
// match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0
|
// match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0
|
||||||
return (a->sa_family==AF_INET && (IN_LOOPBACK(ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr)) ||
|
return (a->sa_family == AF_INET && (IN_LOOPBACK(ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr)) ||
|
||||||
INADDR_ANY == ntohl((((struct sockaddr_in *)a)->sin_addr.s_addr)))) ||
|
INADDR_ANY == ntohl((((struct sockaddr_in *)a)->sin_addr.s_addr)))) ||
|
||||||
(a->sa_family==AF_INET6 && (IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)a)->sin6_addr) ||
|
(a->sa_family == AF_INET6 && (IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)a)->sin6_addr) ||
|
||||||
IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)a)->sin6_addr) ||
|
IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)a)->sin6_addr) ||
|
||||||
(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)a)->sin6_addr) && (IN_LOOPBACK(ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))) ||
|
(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)a)->sin6_addr) && (IN_LOOPBACK(ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))) ||
|
||||||
INADDR_ANY == ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))))));
|
INADDR_ANY == ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))))));
|
||||||
@ -253,7 +257,7 @@ bool is_localnet(const struct sockaddr *a)
|
|||||||
bool is_linklocal(const struct sockaddr_in6 *a)
|
bool is_linklocal(const struct sockaddr_in6 *a)
|
||||||
{
|
{
|
||||||
// fe80::/10
|
// fe80::/10
|
||||||
return a->sin6_addr.s6_addr[0]==0xFE && (a->sin6_addr.s6_addr[1] & 0xC0)==0x80;
|
return a->sin6_addr.s6_addr[0] == 0xFE && (a->sin6_addr.s6_addr[1] & 0xC0) == 0x80;
|
||||||
}
|
}
|
||||||
bool is_private6(const struct sockaddr_in6* a)
|
bool is_private6(const struct sockaddr_in6* a)
|
||||||
{
|
{
|
||||||
@ -265,23 +269,23 @@ bool is_private6(const struct sockaddr_in6* a)
|
|||||||
|
|
||||||
bool set_keepalive(int fd)
|
bool set_keepalive(int fd)
|
||||||
{
|
{
|
||||||
int yes=1;
|
int yes = 1;
|
||||||
return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int))!=-1;
|
return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)) != -1;
|
||||||
}
|
}
|
||||||
bool set_ttl(int fd, int ttl)
|
bool set_ttl(int fd, int ttl)
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))!=-1;
|
return setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) != -1;
|
||||||
}
|
}
|
||||||
bool set_hl(int fd, int hl)
|
bool set_hl(int fd, int hl)
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hl, sizeof(hl))!=-1;
|
return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hl, sizeof(hl)) != -1;
|
||||||
}
|
}
|
||||||
bool set_ttl_hl(int fd, int ttl)
|
bool set_ttl_hl(int fd, int ttl)
|
||||||
{
|
{
|
||||||
bool b1,b2;
|
bool b1, b2;
|
||||||
// try to set both but one may fail if family is wrong
|
// try to set both but one may fail if family is wrong
|
||||||
b1=set_ttl(fd, ttl);
|
b1 = set_ttl(fd, ttl);
|
||||||
b2=set_hl(fd, ttl);
|
b2 = set_hl(fd, ttl);
|
||||||
return b1 || b2;
|
return b1 || b2;
|
||||||
}
|
}
|
||||||
int get_so_error(int fd)
|
int get_so_error(int fd)
|
||||||
@ -289,8 +293,8 @@ int get_so_error(int fd)
|
|||||||
// getsockopt(SO_ERROR) clears error
|
// getsockopt(SO_ERROR) clears error
|
||||||
int errn;
|
int errn;
|
||||||
socklen_t optlen = sizeof(errn);
|
socklen_t optlen = sizeof(errn);
|
||||||
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &errn, &optlen) == -1)
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errn, &optlen) == -1)
|
||||||
errn=errno;
|
errn = errno;
|
||||||
return errn;
|
return errn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,53 +304,53 @@ int fprint_localtime(FILE *F)
|
|||||||
time_t now;
|
time_t now;
|
||||||
|
|
||||||
time(&now);
|
time(&now);
|
||||||
localtime_r(&now,&t);
|
localtime_r(&now, &t);
|
||||||
return fprintf(F, "%02d.%02d.%04d %02d:%02d:%02d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
|
return fprintf(F, "%02d.%02d.%04d %02d:%02d:%02d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t file_mod_time(const char *filename)
|
time_t file_mod_time(const char *filename)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return stat(filename,&st)==-1 ? 0 : st.st_mtime;
|
return stat(filename, &st) == -1 ? 0 : st.st_mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pf_in_range(uint16_t port, const port_filter *pf)
|
bool pf_in_range(uint16_t port, const port_filter *pf)
|
||||||
{
|
{
|
||||||
return port && (((!pf->from && !pf->to) || (port>=pf->from && port<=pf->to)) ^ pf->neg);
|
return port && (((!pf->from && !pf->to) || (port >= pf->from && port <= pf->to)) ^ pf->neg);
|
||||||
}
|
}
|
||||||
bool pf_parse(const char *s, port_filter *pf)
|
bool pf_parse(const char *s, port_filter *pf)
|
||||||
{
|
{
|
||||||
unsigned int v1,v2;
|
unsigned int v1, v2;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
if (!s) return false;
|
if (!s) return false;
|
||||||
if (*s=='*' && s[1]==0)
|
if (*s == '*' && s[1] == 0)
|
||||||
{
|
{
|
||||||
pf->from=1; pf->to=0xFFFF;
|
pf->from = 1; pf->to = 0xFFFF;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (*s=='~')
|
if (*s == '~')
|
||||||
{
|
{
|
||||||
pf->neg=true;
|
pf->neg = true;
|
||||||
s++;
|
s++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pf->neg=false;
|
pf->neg = false;
|
||||||
if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2)
|
if (sscanf(s, "%u-%u%c", &v1, &v2, &c) == 2)
|
||||||
{
|
{
|
||||||
if (v1>65535 || v2>65535 || v1>v2) return false;
|
if (v1 > 65535 || v2 > 65535 || v1 > v2) return false;
|
||||||
pf->from=(uint16_t)v1;
|
pf->from = (uint16_t)v1;
|
||||||
pf->to=(uint16_t)v2;
|
pf->to = (uint16_t)v2;
|
||||||
}
|
}
|
||||||
else if (sscanf(s,"%u%c",&v1,&c)==1)
|
else if (sscanf(s, "%u%c", &v1, &c) == 1)
|
||||||
{
|
{
|
||||||
if (v1>65535) return false;
|
if (v1 > 65535) return false;
|
||||||
pf->to=pf->from=(uint16_t)v1;
|
pf->to = pf->from = (uint16_t)v1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
// deny all case
|
// deny all case
|
||||||
if (!pf->from && !pf->to) pf->neg=true;
|
if (!pf->from && !pf->to) pf->neg = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool pf_is_empty(const port_filter *pf)
|
bool pf_is_empty(const port_filter *pf)
|
||||||
@ -357,12 +361,12 @@ bool pf_is_empty(const port_filter *pf)
|
|||||||
|
|
||||||
bool set_env_exedir(const char *argv0)
|
bool set_env_exedir(const char *argv0)
|
||||||
{
|
{
|
||||||
char *s,*d;
|
char *s, *d;
|
||||||
bool bOK=false;
|
bool bOK = false;
|
||||||
if ((s = strdup(argv0)))
|
if ((s = strdup(argv0)))
|
||||||
{
|
{
|
||||||
if ((d = dirname(s)))
|
if ((d = dirname(s)))
|
||||||
setenv("EXEDIR",s,1);
|
setenv("EXEDIR", s, 1);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
return bOK;
|
return bOK;
|
||||||
@ -372,23 +376,23 @@ bool set_env_exedir(const char *argv0)
|
|||||||
static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a)
|
static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a)
|
||||||
{
|
{
|
||||||
if (plen >= 128)
|
if (plen >= 128)
|
||||||
memset(a->s6_addr,0xFF,16);
|
memset(a->s6_addr, 0xFF, 16);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t n = plen >> 3;
|
uint8_t n = plen >> 3;
|
||||||
memset(a->s6_addr,0xFF,n);
|
memset(a->s6_addr, 0xFF, n);
|
||||||
memset(a->s6_addr+n,0x00,16-n);
|
memset(a->s6_addr + n, 0x00, 16 - n);
|
||||||
a->s6_addr[n] = (uint8_t)(0xFF00 >> (plen & 7));
|
a->s6_addr[n] = (uint8_t)(0xFF00 >> (plen & 7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct in6_addr ip6_mask[129];
|
struct in6_addr ip6_mask[129];
|
||||||
void mask_from_preflen6_prepare(void)
|
void mask_from_preflen6_prepare(void)
|
||||||
{
|
{
|
||||||
for (int plen=0;plen<=128;plen++) mask_from_preflen6_make(plen, ip6_mask+plen);
|
for (int plen = 0; plen <= 128; plen++) mask_from_preflen6_make(plen, ip6_mask + plen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(__llvm__)
|
#if defined(__GNUC__) && !defined(__llvm__)
|
||||||
__attribute__((optimize ("no-strict-aliasing")))
|
__attribute__((optimize("no-strict-aliasing")))
|
||||||
#endif
|
#endif
|
||||||
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result)
|
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result)
|
||||||
{
|
{
|
||||||
@ -400,64 +404,116 @@ void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restric
|
|||||||
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr)
|
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr)
|
||||||
{
|
{
|
||||||
char s_ip[16];
|
char s_ip[16];
|
||||||
*s_ip=0;
|
*s_ip = 0;
|
||||||
inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip));
|
inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip));
|
||||||
snprintf(s,s_len,cidr->preflen<32 ? "%s/%u" : "%s", s_ip, cidr->preflen);
|
snprintf(s, s_len, cidr->preflen < 32 ? "%s/%u" : "%s", s_ip, cidr->preflen);
|
||||||
}
|
}
|
||||||
void print_cidr4(const struct cidr4 *cidr)
|
void print_cidr4(const struct cidr4 *cidr)
|
||||||
{
|
{
|
||||||
char s[19];
|
char s[19];
|
||||||
str_cidr4(s,sizeof(s),cidr);
|
str_cidr4(s, sizeof(s), cidr);
|
||||||
printf("%s",s);
|
printf("%s", s);
|
||||||
}
|
}
|
||||||
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr)
|
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr)
|
||||||
{
|
{
|
||||||
char s_ip[40];
|
char s_ip[40];
|
||||||
*s_ip=0;
|
*s_ip = 0;
|
||||||
inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip));
|
inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip));
|
||||||
snprintf(s,s_len,cidr->preflen<128 ? "%s/%u" : "%s", s_ip, cidr->preflen);
|
snprintf(s, s_len, cidr->preflen < 128 ? "%s/%u" : "%s", s_ip, cidr->preflen);
|
||||||
}
|
}
|
||||||
void print_cidr6(const struct cidr6 *cidr)
|
void print_cidr6(const struct cidr6 *cidr)
|
||||||
{
|
{
|
||||||
char s[44];
|
char s[44];
|
||||||
str_cidr6(s,sizeof(s),cidr);
|
str_cidr6(s, sizeof(s), cidr);
|
||||||
printf("%s",s);
|
printf("%s", s);
|
||||||
}
|
}
|
||||||
bool parse_cidr4(char *s, struct cidr4 *cidr)
|
bool parse_cidr4(char *s, struct cidr4 *cidr)
|
||||||
{
|
{
|
||||||
char *p,d;
|
char *p, d;
|
||||||
bool b;
|
bool b;
|
||||||
unsigned int plen;
|
unsigned int plen;
|
||||||
|
|
||||||
if ((p = strchr(s, '/')))
|
if ((p = strchr(s, '/')))
|
||||||
{
|
{
|
||||||
if (sscanf(p + 1, "%u", &plen)!=1 || plen>32)
|
if (sscanf(p + 1, "%u", &plen) != 1 || plen > 32)
|
||||||
return false;
|
return false;
|
||||||
cidr->preflen = (uint8_t)plen;
|
cidr->preflen = (uint8_t)plen;
|
||||||
d=*p; *p=0; // backup char
|
d = *p; *p = 0; // backup char
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cidr->preflen = 32;
|
cidr->preflen = 32;
|
||||||
b = (inet_pton(AF_INET, s, &cidr->addr)==1);
|
b = (inet_pton(AF_INET, s, &cidr->addr) == 1);
|
||||||
if (p) *p=d; // restore char
|
if (p) *p = d; // restore char
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
bool parse_cidr6(char *s, struct cidr6 *cidr)
|
bool parse_cidr6(char *s, struct cidr6 *cidr)
|
||||||
{
|
{
|
||||||
char *p,d;
|
char *p, d;
|
||||||
bool b;
|
bool b;
|
||||||
unsigned int plen;
|
unsigned int plen;
|
||||||
|
|
||||||
if ((p = strchr(s, '/')))
|
if ((p = strchr(s, '/')))
|
||||||
{
|
{
|
||||||
if (sscanf(p + 1, "%u", &plen)!=1 || plen>128)
|
if (sscanf(p + 1, "%u", &plen) != 1 || plen > 128)
|
||||||
return false;
|
return false;
|
||||||
cidr->preflen = (uint8_t)plen;
|
cidr->preflen = (uint8_t)plen;
|
||||||
d=*p; *p=0; // backup char
|
d = *p; *p = 0; // backup char
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cidr->preflen = 128;
|
cidr->preflen = 128;
|
||||||
b = (inet_pton(AF_INET6, s, &cidr->addr)==1);
|
b = (inet_pton(AF_INET6, s, &cidr->addr) == 1);
|
||||||
if (p) *p=d; // restore char
|
if (p) *p = d; // restore char
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void msleep(unsigned int ms)
|
||||||
|
{
|
||||||
|
struct timespec time = {
|
||||||
|
.tv_nsec = (ms % 1000) * 1000000,
|
||||||
|
.tv_sec = ms / 1000
|
||||||
|
};
|
||||||
|
nanosleep(&time, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
bool socket_has_notsent(int sfd)
|
||||||
|
{
|
||||||
|
struct tcp_info tcpi;
|
||||||
|
socklen_t ts = sizeof(tcpi);
|
||||||
|
|
||||||
|
if (getsockopt(sfd, IPPROTO_TCP, TCP_INFO, (char *)&tcpi, &ts) < 0)
|
||||||
|
return false;
|
||||||
|
if (tcpi.tcpi_state != 1)
|
||||||
|
return false;
|
||||||
|
size_t s = (char *)&tcpi.tcpi_notsent_bytes - (char *)&tcpi.tcpi_state;
|
||||||
|
if (ts < s)
|
||||||
|
return false;
|
||||||
|
return !!tcpi.tcpi_notsent_bytes;
|
||||||
|
}
|
||||||
|
bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms)
|
||||||
|
{
|
||||||
|
struct timespec tres;
|
||||||
|
unsigned int mtick;
|
||||||
|
|
||||||
|
if (wasted_ms) *wasted_ms=0;
|
||||||
|
if (!socket_has_notsent(sfd)) return true;
|
||||||
|
|
||||||
|
if (clock_getres(CLOCK_MONOTONIC,&tres))
|
||||||
|
{
|
||||||
|
tres.tv_nsec = 10000000;
|
||||||
|
tres.tv_sec = 0;
|
||||||
|
}
|
||||||
|
mtick = (unsigned int)(tres.tv_sec*1000) + (unsigned int)(tres.tv_nsec/1000000);
|
||||||
|
if (mtick<1) mtick=1;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
msleep(mtick);
|
||||||
|
if (wasted_ms) *wasted_ms+=mtick;
|
||||||
|
if (!socket_has_notsent(sfd)) return true;
|
||||||
|
if (delay_ms<=mtick) break;
|
||||||
|
delay_ms-=mtick;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -117,3 +117,9 @@ static inline const struct in6_addr *mask_from_preflen6(uint8_t preflen)
|
|||||||
{
|
{
|
||||||
return ip6_mask+preflen;
|
return ip6_mask+preflen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void msleep(unsigned int ms);
|
||||||
|
#ifdef __linux__
|
||||||
|
bool socket_has_notsent(int sfd);
|
||||||
|
bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms);
|
||||||
|
#endif
|
||||||
|
@ -104,6 +104,7 @@ struct params_s
|
|||||||
uint8_t proxy_type;
|
uint8_t proxy_type;
|
||||||
bool no_resolve;
|
bool no_resolve;
|
||||||
bool skip_nodelay;
|
bool skip_nodelay;
|
||||||
|
bool fix_seg;
|
||||||
bool droproot;
|
bool droproot;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
11
tpws/tpws.c
11
tpws/tpws.c
@ -169,6 +169,9 @@ static void exithelp(void)
|
|||||||
" --uid=uid[:gid]\t\t\t; drop root privs\n"
|
" --uid=uid[:gid]\t\t\t; drop root privs\n"
|
||||||
#if defined(__FreeBSD__)
|
#if defined(__FreeBSD__)
|
||||||
" --enable-pf\t\t\t\t; enable PF redirector support. required in FreeBSD when used with PF firewall.\n"
|
" --enable-pf\t\t\t\t; enable PF redirector support. required in FreeBSD when used with PF firewall.\n"
|
||||||
|
#endif
|
||||||
|
#if defined(__linux__)
|
||||||
|
" --fix-seg\t\t\t\t; fix segmentation failures at the cost of possible slowdown\n"
|
||||||
#endif
|
#endif
|
||||||
" --debug=0|1|2|syslog|@<filename>\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n"
|
" --debug=0|1|2|syslog|@<filename>\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n"
|
||||||
" --debug-level=0|1|2\t\t\t; specify debug level\n"
|
" --debug-level=0|1|2\t\t\t; specify debug level\n"
|
||||||
@ -635,8 +638,9 @@ void parse_params(int argc, char *argv[])
|
|||||||
{ "local-tcp-user-timeout",required_argument,0,0 }, // optidx=62
|
{ "local-tcp-user-timeout",required_argument,0,0 }, // optidx=62
|
||||||
{ "remote-tcp-user-timeout",required_argument,0,0 }, // optidx=63
|
{ "remote-tcp-user-timeout",required_argument,0,0 }, // optidx=63
|
||||||
{ "mss",required_argument,0,0 }, // optidx=64
|
{ "mss",required_argument,0,0 }, // optidx=64
|
||||||
|
{ "fix-seg",no_argument,0,0 }, // optidx=65
|
||||||
#ifdef SPLICE_PRESENT
|
#ifdef SPLICE_PRESENT
|
||||||
{ "nosplice",no_argument,0,0 }, // optidx=65
|
{ "nosplice",no_argument,0,0 }, // optidx=66
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
{ "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility
|
{ "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility
|
||||||
@ -1228,8 +1232,11 @@ void parse_params(int argc, char *argv[])
|
|||||||
exit_clean(1);
|
exit_clean(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 65: /* fix-seg */
|
||||||
|
params.fix_seg = true;
|
||||||
|
break;
|
||||||
#ifdef SPLICE_PRESENT
|
#ifdef SPLICE_PRESENT
|
||||||
case 65: /* nosplice */
|
case 66: /* nosplice */
|
||||||
params.nosplice = true;
|
params.nosplice = true;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1244,7 +1244,21 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
|
|||||||
if (wr>0) conn->partner->twr += wr;
|
if (wr>0) conn->partner->twr += wr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef __linux__
|
||||||
|
if (params.fix_seg)
|
||||||
|
{
|
||||||
|
unsigned int wasted;
|
||||||
|
if (!socket_wait_notsent(conn->partner->fd, 20, &wasted))
|
||||||
|
DLOG_ERR("WARNING ! segmentation failed\n");
|
||||||
|
if (wasted)
|
||||||
|
VPRINT("WARNING ! wasted %u ms to fix segmenation\n", wasted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (socket_has_notsent(conn->partner->fd))
|
||||||
|
DLOG_ERR("WARNING ! segmentation failed\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
from = to;
|
from = to;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user